"Fossies" - the Fresh Open Source Software Archive 
Member "littleutils-1.2.5/littleutils/tempname.c" (29 Oct 2021, 11328 Bytes) of package /linux/privat/littleutils-1.2.5.tar.lz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "tempname.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
1.2.4_vs_1.2.5.
1 /* tempname: Creates a unique temporary file (or filename) for use by shell
2 scripts.
3
4 Copyright (C) 2004-2021 by Brian Lindholm.
5 This file is part of the littleutils utility set.
6
7 The tempname utility is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option) any
10 later version.
11
12 The tempname utility is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 more details.
16
17 You should have received a copy of the GNU General Public License along with
18 the littleutils. If not, see <https://www.gnu.org/licenses/>. */
19
20
21 #include <config.h>
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #ifdef HAVE_STDIO_H
26 # include <stdio.h>
27 #endif
28 #ifdef HAVE_STDLIB_H
29 # include <stdlib.h>
30 #endif
31 #ifdef HAVE_STRING_H
32 # include <string.h>
33 #endif
34 #ifdef HAVE_SYS_STAT_H
35 # include <sys/stat.h>
36 #endif
37 #ifdef HAVE_SYS_TYPES_H
38 # include <sys/types.h>
39 #endif
40
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 # define OPTEND -1
44 #else
45 # define OPTEND EOF
46 #endif
47 #ifdef HAVE_GETOPT_H
48 # include <getopt.h>
49 #endif
50
51 #include "rand_funcs.h"
52
53 #ifdef __MINGW32__
54 extern int getopt (int argc, char * const *argv, const char *optstring);
55 extern char *optarg;
56 extern int optind;
57 #include <process.h>
58 #endif
59
60 #ifdef DJGPP
61 unsigned short _djstat_flags = 63; /* speed up stat command for DJGPP */
62 #endif
63
64 #define GT_FILE 1
65 #define GT_DIR 2
66 #define GT_NOCREATE 3
67
68
69 /* help function */
70
71 static void
72 help (FILE *where)
73 {
74 fprintf (where,
75 "tempname " PACKAGE_VERSION "\n"
76 "usage: tempname [-c(reate_not)] [-d dirname] [-h(elp)] [-n(o_random_portion)]\n"
77 " [-q(uiet)] [-s suffix] [-v(erbose)] [-w(ildcard)] [-D(irectory)]\n"
78 " filename_prefix\n"
79 "note: using the \"-w\" option implies the \"-c\" option and the \"-n\" option,\n"
80 " the \"-n\" option also implies the \"-c\" option\n");
81 }
82
83
84 /* directory check routine */
85
86 static int
87 good_dir (const char *path, int verbose)
88 {
89 struct stat file_stats;
90 #if (!defined(__MINGW32__) && !defined(DJGPP))
91 uid_t uid = (uid_t) 0;
92 gid_t gid = (gid_t) 0;
93 #endif
94
95 if (path == NULL)
96 return (0);
97 if (stat (path, &file_stats)) /* doesn't exist? */
98 {
99 if (verbose)
100 fprintf (stderr, "tempname error: %s doesn't exist\n", path);
101 return (0);
102 }
103 if ((file_stats.st_mode & S_IFDIR) != S_IFDIR) /* not a directory? */
104 {
105 if (verbose)
106 fprintf (stderr, "tempname error: %s isn't a directory\n", path);
107 return (0);
108 }
109 if (strchr (path, ' ') != NULL) /* contains a space character? */
110 {
111 if (verbose)
112 fprintf (stderr, "tempname error: %s contains a space character\n", path);
113 return (0);
114 }
115 #if defined(__MINGW32__)
116 if ((file_stats.st_mode & (S_IREAD | S_IWRITE)) == (S_IREAD | S_IWRITE)) /* writeable? */
117 return (1);
118 if (verbose)
119 fprintf (stderr, "tempname error: you don't have permission to write to %s\n", path);
120 return (0);
121 #elif defined(DJGPP)
122 if ((file_stats.st_mode & S_IRWXU) == S_IRWXU) /* writeable? */
123 return (1);
124 if (verbose)
125 fprintf (stderr, "tempname error: you don't have permission to write to %s\n", path);
126 return (0);
127 #else
128 if ((file_stats.st_mode & S_IRWXO) == S_IRWXO) /* writeable? */
129 return (1);
130 uid = getuid ();
131 if ((file_stats.st_uid == uid) && ((file_stats.st_mode & S_IRWXU) == S_IRWXU))
132 return (1);
133 gid = getgid ();
134 if ((file_stats.st_gid == gid) && ((file_stats.st_mode & S_IRWXG) == S_IRWXG))
135 return (1);
136 if (verbose)
137 {
138 if (file_stats.st_uid == uid)
139 fprintf (stderr, "tempname error: permissions are incorrectly set on %s\n", path);
140 else
141 fprintf (stderr, "tempname error: you don't have permission to write to %s\n", path);
142 }
143 return (0);
144 #endif
145 }
146
147
148 /* special variant of mkstemp, allows X's to occur in non-end locations */
149
150 static int
151 mkstemp_custom (char *tmpl, int kind)
152 {
153 char *copy;
154 int count, fd, rc;
155 int save_errno = errno;
156 size_t i, len;
157 struct stat file_stats;
158
159 /* characters used in temporary file names */
160 static const char letters[] =
161 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
162
163 /* determine length of template and allocate storage */
164 len = strlen (tmpl);
165 copy = (char *) malloc ((len + 1) * sizeof (char));
166
167 /* initialize random number generator */
168 rand_seed ();
169
170 /* generate the random name */
171 for (count = 0; count < TMP_MAX; ++count)
172 {
173 strcpy (copy, tmpl);
174
175 for (i = 0; i < len; ++i)
176 if (copy[i] == '|')
177 #if defined(HAVE_LRAND48)
178 copy[i] = letters[(int) lrand48 () % 62];
179 #elif defined(HAVE_RANDOM)
180 copy[i] = letters[(int) random () % 62];
181 #else
182 copy[i] = letters[(int) rand () % 62];
183 #endif
184
185 switch (kind)
186 {
187 case GT_FILE:
188 fd = open (copy, O_RDWR | O_CREAT | O_EXCL, 0600);
189 if (fd >= 0)
190 {
191 errno = save_errno;
192 strcpy (tmpl, copy);
193 free (copy);
194 return (fd);
195 }
196 else if (errno != EEXIST)
197 {
198 /* any other error will apply also to other names we might
199 try, and there are VERY many of them, so give up now */
200 free (copy);
201 return (-1);
202 }
203 break;
204
205 case GT_DIR:
206 rc = mkdir (copy, 0700);
207 if (rc == 0)
208 {
209 errno = save_errno;
210 strcpy (tmpl, copy);
211 free (copy);
212 return (0);
213 }
214 else if (errno != EEXIST)
215 {
216 /* any other error will apply also to other names we might
217 try, and there are VERY many of them, so give up now */
218 free (copy);
219 return (-1);
220 }
221 break;
222
223 case GT_NOCREATE:
224 rc = stat (copy, &file_stats);
225 if (rc < 0)
226 {
227 if (errno == ENOENT)
228 {
229 errno = save_errno;
230 strcpy (tmpl, copy);
231 free (copy);
232 return (0);
233 }
234 else
235 {
236 /* any other error will apply also to other names we might
237 try, and there are VERY many of them, so give up now */
238 free (copy);
239 return (-1);
240 }
241 }
242 break;
243
244 default:
245 fprintf (stderr, "tempname assertion failure: bad switch logic\n");
246 return (-2);
247 }
248 }
249
250 /* tried too many times, bailing... */
251 errno = EEXIST;
252 free (copy);
253 return (-1);
254 }
255
256
257 /* main program */
258
259 int
260 main (int argc, char *argv[])
261 {
262 char *newname, *path, *suffix;
263 int add_random, add_suffix, add_wild, argo, create, directory, file_id,
264 i, length, opt, path_ok, rc, user_dir, verbose;
265
266 /* parse options */
267 add_random = 1;
268 add_suffix = 0;
269 add_wild = 0;
270 create = 1;
271 directory = 0;
272 path = "";
273 user_dir = 0;
274 suffix = "";
275 verbose = 0;
276 while ((opt = getopt (argc, argv, "cd:hnqs:vwD")) != OPTEND)
277 switch (opt)
278 {
279 case 'c':
280 create = 0;
281 break;
282 case 'd':
283 user_dir = 1;
284 path = optarg;
285 break;
286 case 'h':
287 help (stdout);
288 return (0);
289 case 'n':
290 add_random = 0;
291 create = 0;
292 break;
293 case 'q':
294 verbose = -1;
295 break;
296 case 's':
297 add_suffix = 1;
298 suffix = optarg;
299 break;
300 case 'v':
301 verbose = 1;
302 break;
303 case 'w':
304 add_wild = 1;
305 add_random = 0;
306 create = 0;
307 break;
308 case 'D':
309 directory = 1;
310 break;
311 case '?':
312 help (stderr);
313 return (1);
314 }
315
316 /* if no arguments given, print help */
317 argo = argc - optind;
318 if (argo == 0)
319 {
320 help (stdout);
321 return (0);
322 }
323
324 /* if incorrect number of arguments given, print help */
325 if (argo != 1)
326 {
327 help (stderr);
328 return (1);
329 }
330
331 /* find a writeable path */
332 path_ok = 0;
333 if (user_dir)
334 {
335 if (verbose == -1)
336 path_ok = good_dir (path, 0);
337 else
338 path_ok = good_dir (path, 1);
339 }
340 if (!path_ok)
341 {
342 path = getenv ("TMPDIR");
343 path_ok = good_dir (path, verbose);
344 }
345 if (!path_ok)
346 {
347 path = getenv ("TEMP");
348 path_ok = good_dir (path, verbose);
349 }
350 if (!path_ok)
351 {
352 path = getenv ("TMP");
353 path_ok = good_dir (path, verbose);
354 }
355 if (!path_ok)
356 {
357 path = "/tmp";
358 path_ok = good_dir (path, verbose);
359 }
360 if (!path_ok)
361 {
362 path = "/var/tmp";
363 path_ok = good_dir (path, verbose);
364 }
365 if (!path_ok)
366 {
367 path = ".";
368 path_ok = good_dir (path, verbose);
369 }
370 if (!path_ok)
371 {
372 fprintf (stderr, "tempname error: can't find writeable directory\n");
373 return (2);
374 }
375
376 /* append a filename prefix, unique letter block, and optional suffix */
377 length = (int) strlen (path) + (int) strlen (argv[optind]) + (int) strlen(suffix) + 10;
378 newname = (char *) malloc (length * sizeof (char));
379 strcpy (newname, "");
380 strcat (newname, path);
381 #if (defined(__MINGW32__) || defined(DJGPP))
382 if (newname[strlen (newname) - 1] != '\\')
383 strcat (newname, "\\");
384 #else
385 if (newname[strlen (newname) - 1] != '/')
386 strcat (newname, "/");
387 #endif
388 strcat (newname, argv[optind]);
389 if (add_wild)
390 strcat (newname, "_*");
391 else if (add_random)
392 strcat (newname, "_||||||||"); /* use | instead of X in case path contains X */
393 if (add_suffix)
394 strcat (newname, suffix);
395
396 /* replace spaces with underscores to reduce risk of errors in upstream scripts */
397 for (i = 0; i <= strlen (newname); i++)
398 {
399 if (newname[i] == ' ')
400 newname[i] = '_';
401 }
402
403 /* scramble unique letter block and optionally create temporary file */
404 if (add_random)
405 {
406 if (directory)
407 {
408 rc = mkstemp_custom (newname, GT_DIR);
409 if (rc < 0)
410 {
411 fprintf (stderr, "tempname error: can't open a tempdir for writing\n");
412 return (3);
413 }
414 }
415 else if (create)
416 {
417 file_id = mkstemp_custom (newname, GT_FILE);
418 if (file_id > 0)
419 close (file_id);
420 else
421 {
422 fprintf (stderr, "tempname error: can't open a tempfile for writing\n");
423 return (3);
424 }
425 }
426 else
427 {
428 rc = mkstemp_custom (newname, GT_NOCREATE);
429 if (rc < 0)
430 {
431 fprintf (stderr, "tempname error: can't create a tempname for writing\n");
432 return (3);
433 }
434 }
435 }
436
437 /* print out filename of temporary file */
438 if (strlen (newname) == 0)
439 {
440 fprintf (stderr, "tempname error: can't form unique filename\n");
441 free (newname);
442 return (3);
443 }
444 else
445 fprintf (stdout, "%s\n", newname);
446
447 /* free memory and indicate successful finish */
448 free (newname);
449 return (0);
450 }