"Fossies" - the Fresh Open Source Software Archive

Member "littleutils-1.2.4/littleutils/tempname.c" (28 Mar 2021, 12344 Bytes) of package /linux/privat/littleutils-1.2.4.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.3_vs_1.2.4.

    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 <stdio.h>
   24 #ifdef HAVE_STDLIB_H
   25 # include <stdlib.h>
   26 #endif
   27 #ifdef HAVE_STRING_H
   28 # include <string.h>
   29 #endif
   30 #include <time.h>
   31 #ifdef HAVE_SYS_TYPES_H
   32 # include <sys/types.h>
   33 #endif
   34 #ifdef HAVE_SYS_STAT_H
   35 # include <sys/stat.h>
   36 #endif
   37 #include <fcntl.h>
   38 #include <errno.h>
   39 
   40 #ifdef HAVE_GETOPT_H
   41 #include <getopt.h>
   42 #endif
   43 #ifdef HAVE_UNISTD_H
   44 # include <unistd.h>
   45 # define OPTEND -1
   46 #else
   47 # define OPTEND EOF
   48 #endif
   49 
   50 #ifdef __MINGW32__
   51 extern int getopt (int argc, char * const *argv, const char *optstring);
   52 extern char *optarg;
   53 extern int optind;
   54 #include <process.h>
   55 #endif
   56 
   57 #ifdef DJGPP
   58 unsigned short _djstat_flags = 63;  /* speed up stat command for DJGPP */
   59 #endif
   60 
   61 #define GT_FILE 1
   62 #define GT_DIR 2
   63 #define GT_NOCREATE 3
   64 
   65 
   66 /* help function */
   67 
   68 static void
   69 help (FILE *where)
   70 {
   71   fprintf (where,
   72     "tempname " PACKAGE_VERSION "\n"
   73     "usage: tempname [-c(reate_not)] [-d dirname] [-h(elp)] [-n(o_random_portion)]\n"
   74     "         [-q(uiet)] [-s suffix] [-v(erbose)] [-w(ildcard)] [-D(irectory)]\n"
   75     "         filename_prefix\n"
   76     "note:  using the \"-w\" option implies the \"-c\" option and the \"-n\" option,\n"
   77     "         the \"-n\" option also implies the \"-c\" option\n");
   78 }
   79 
   80 
   81 /* directory check routine */
   82 
   83 static int
   84 good_dir (const char *path, int verbose)
   85 {
   86   struct stat file_stats;
   87 #if (!defined(__MINGW32__) && !defined(DJGPP))
   88   uid_t uid = (uid_t) 0;
   89   gid_t gid = (gid_t) 0;
   90 #endif
   91 
   92   if (path == NULL)
   93     return (0);
   94   if (stat (path, &file_stats))  /* doesn't exist? */
   95     {
   96       if (verbose)
   97         fprintf (stderr, "tempname error: %s doesn't exist\n", path);
   98       return (0);
   99     }
  100   if ((file_stats.st_mode & S_IFDIR) != S_IFDIR)  /* not a directory? */
  101     {
  102       if (verbose)
  103         fprintf (stderr, "tempname error: %s isn't a directory\n", path);
  104       return (0);
  105     }
  106   if (strchr (path, ' ') != NULL)  /* contains a space character? */
  107     {
  108       if (verbose)
  109         fprintf (stderr, "tempname error: %s contains a space character\n", path);
  110       return (0);
  111     }
  112 #if defined(__MINGW32__)
  113   if ((file_stats.st_mode & (S_IREAD | S_IWRITE)) == (S_IREAD | S_IWRITE))  /* writeable? */
  114     return (1);
  115   if (verbose)
  116     fprintf (stderr, "tempname error: you don't have permission to write to %s\n", path);
  117   return (0);
  118 #elif defined(DJGPP)
  119   if ((file_stats.st_mode & S_IRWXU) == S_IRWXU)  /* writeable? */
  120     return (1);
  121   if (verbose)
  122     fprintf (stderr, "tempname error: you don't have permission to write to %s\n", path);
  123   return (0);
  124 #else
  125   if ((file_stats.st_mode & S_IRWXO) == S_IRWXO)  /* writeable? */
  126     return (1);
  127   uid = getuid ();
  128   if ((file_stats.st_uid == uid) && ((file_stats.st_mode & S_IRWXU) == S_IRWXU))
  129     return (1);
  130   gid = getgid ();
  131   if ((file_stats.st_gid == gid) && ((file_stats.st_mode & S_IRWXG) == S_IRWXG))
  132     return (1);
  133   if (verbose)
  134     {
  135       if (file_stats.st_uid == uid)
  136         fprintf (stderr, "tempname error: permissions are incorrectly set on %s\n", path);
  137       else
  138         fprintf (stderr, "tempname error: you don't have permission to write to %s\n", path);
  139     }
  140   return (0);
  141 #endif
  142 }
  143 
  144 
  145 /* random seed function */
  146 
  147 static void
  148 rand_seed ()
  149 {
  150   int URANDOM, urandom_worked = 1;
  151   ssize_t bytes;
  152   time_t junk;
  153   unsigned short seed[3];
  154 
  155   /* pre-populate array for seed using /dev/urandom */
  156   URANDOM = open("/dev/urandom", O_RDONLY);
  157   if (URANDOM < 0) {
  158     urandom_worked = 0;
  159   }
  160   else {
  161     /* use read instead of buffered fread to avoid grabbing extra bytes */
  162     bytes = read(URANDOM, seed, sizeof seed);
  163     if (bytes < 6) {
  164       urandom_worked = 0;
  165     }
  166   }
  167   close(URANDOM);
  168 
  169   /* fall back to time and PID combo if /dev/urandom failed */
  170   if (urandom_worked == 0) {
  171     (void) time (&junk);
  172     seed[0] = (unsigned short) (junk & 0xFFFF);
  173     seed[1] = (unsigned short) (getpid () & 0xFFFF);
  174     seed[2] = (unsigned short) ((junk >> 16) & 0xFFFF);
  175   }
  176 
  177   /* initialize random number generator seed */
  178 #ifdef HAVE_LRAND48
  179   (void) seed48 (seed);
  180 #elif defined(HAVE_RANDOM)
  181   srandom (((unsigned int) seed[0] << 16) + (unsigned int) seed[1]);
  182 #else
  183   srand (((unsigned int) seed[0] << 16) + (unsigned int) seed[1]);
  184 #endif
  185 }
  186 
  187 
  188 /* special variant of mkstemp, allows X's to occur in non-end locations */
  189 
  190 static int
  191 mkstemp_custom (char *tmpl, int kind)
  192 {
  193   char *copy;
  194   int count, fd, rc;
  195   int save_errno = errno;
  196   size_t i, len;
  197   struct stat file_stats;
  198 
  199   /* characters used in temporary file names */
  200   static const char letters[] =
  201   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  202 
  203   /* determine length of template and allocate storage */
  204   len = strlen (tmpl);
  205   copy = (char *) malloc ((len + 1) * sizeof (char));
  206 
  207   /* initialize random number generator */
  208   rand_seed ();
  209 
  210   /* generate the random name */
  211   for (count = 0; count < TMP_MAX; ++count)
  212     {
  213       strcpy (copy, tmpl);
  214 
  215       for (i = 0; i < len; ++i)
  216         if (copy[i] == '|')
  217 #if defined(HAVE_LRAND48)
  218           copy[i] = letters[(int) lrand48 () % 62];
  219 #elif defined(HAVE_RANDOM)
  220           copy[i] = letters[(int) random () % 62];
  221 #else
  222           copy[i] = letters[(int) rand () % 62];
  223 #endif
  224 
  225       switch (kind)
  226         {
  227         case GT_FILE:
  228           fd = open (copy, O_RDWR | O_CREAT | O_EXCL, 0600);
  229           if (fd >= 0)
  230             {
  231               errno = save_errno;
  232               strcpy (tmpl, copy);
  233               free (copy);
  234               return (fd);
  235             }
  236           else if (errno != EEXIST)
  237             {
  238               /* any other error will apply also to other names we might
  239                  try, and there are VERY many of them, so give up now */
  240               free (copy);
  241               return (-1);
  242             }
  243           break;
  244 
  245         case GT_DIR:
  246           rc = mkdir (copy, 0700);
  247           if (rc == 0)
  248             {
  249               errno = save_errno;
  250               strcpy (tmpl, copy);
  251               free (copy);
  252               return (0);
  253             }
  254           else if (errno != EEXIST)
  255             {
  256               /* any other error will apply also to other names we might
  257                  try, and there are VERY many of them, so give up now */
  258               free (copy);
  259               return (-1);
  260             }
  261           break;
  262 
  263         case GT_NOCREATE:
  264           rc = stat (copy, &file_stats);
  265           if (rc < 0)
  266             {
  267               if (errno == ENOENT)
  268                 {
  269                   errno = save_errno;
  270                   strcpy (tmpl, copy);
  271                   free (copy);
  272                   return (0);
  273                 }
  274               else
  275                 {
  276                   /* any other error will apply also to other names we might
  277                      try, and there are VERY many of them, so give up now */
  278                   free (copy);
  279                   return (-1);
  280                 }
  281             }
  282           break;
  283 
  284         default:
  285           fprintf (stderr, "tempname assertion failure: bad switch logic\n");
  286           return (-2);
  287         }
  288     }
  289 
  290   /* tried too many times, bailing... */
  291   errno = EEXIST;
  292   free (copy);
  293   return (-1);
  294 }
  295 
  296 
  297 /* main program */
  298 
  299 int
  300 main (int argc, char *argv[])
  301 {
  302   char *newname, *path, *suffix;
  303   int add_random, add_suffix, add_wild, argo, create, directory, file_id,
  304     i, length, opt, path_ok, rc, user_dir, verbose;
  305 
  306   /* parse options */
  307   add_random = 1;
  308   add_suffix = 0;
  309   add_wild = 0;
  310   create = 1;
  311   directory = 0;
  312   path = "";
  313   user_dir = 0;
  314   suffix = "";
  315   verbose = 0;
  316   while ((opt = getopt (argc, argv, "cd:hnqs:vwD")) != OPTEND)
  317     switch (opt)
  318       {
  319       case 'c':
  320         create = 0;
  321         break;
  322       case 'd':
  323         user_dir = 1;
  324         path = optarg;
  325         break;
  326       case 'h':
  327         help (stdout);
  328         return (0);
  329       case 'n':
  330         add_random = 0;
  331         create = 0;
  332         break;
  333       case 'q':
  334         verbose = -1;
  335         break;
  336       case 's':
  337         add_suffix = 1;
  338         suffix = optarg;
  339         break;
  340       case 'v':
  341         verbose = 1;
  342         break;
  343       case 'w':
  344         add_wild = 1;
  345         add_random = 0;
  346         create = 0;
  347         break;
  348       case 'D':
  349         directory = 1;
  350         break;
  351       case '?':
  352         help (stderr);
  353         return (1);
  354       }
  355 
  356   /* if no arguments given, print help */
  357   argo = argc - optind;
  358   if (argo == 0)
  359     {
  360       help (stdout);
  361       return (0);
  362     }
  363 
  364   /* if incorrect number of arguments given, print help */
  365   if (argo != 1)
  366     {
  367       help (stderr);
  368       return (1);
  369     }
  370 
  371   /* find a writeable path */
  372   path_ok = 0;
  373   if (user_dir)
  374     {
  375       if (verbose == -1)
  376         path_ok = good_dir (path, 0);
  377       else
  378         path_ok = good_dir (path, 1);
  379     }
  380   if (!path_ok)
  381     {
  382       path = getenv ("TMPDIR");
  383       path_ok = good_dir (path, verbose);
  384     }
  385   if (!path_ok)
  386     {
  387       path = getenv ("TEMP");
  388       path_ok = good_dir (path, verbose);
  389     }
  390   if (!path_ok)
  391     {
  392       path = getenv ("TMP");
  393       path_ok = good_dir (path, verbose);
  394     }
  395   if (!path_ok)
  396     {
  397       path = "/tmp";
  398       path_ok = good_dir (path, verbose);
  399     }
  400   if (!path_ok)
  401     {
  402       path = "/var/tmp";
  403       path_ok = good_dir (path, verbose);
  404     }
  405   if (!path_ok)
  406     {
  407       path = ".";
  408       path_ok = good_dir (path, verbose);
  409     }
  410   if (!path_ok)
  411     {
  412       fprintf (stderr, "tempname error: can't find writeable directory\n");
  413       return (2);
  414     }
  415 
  416   /* append a filename prefix, unique letter block, and optional suffix */
  417   length = (int) strlen (path) + (int) strlen (argv[optind]) + (int) strlen(suffix) + 10;
  418   newname = (char *) malloc (length * sizeof (char));
  419   strcpy (newname, "");
  420   strcat (newname, path);
  421 #if (defined(__MINGW32__) || defined(DJGPP))
  422   if (newname[strlen (newname) - 1] != '\\')
  423     strcat (newname, "\\");
  424 #else
  425   if (newname[strlen (newname) - 1] != '/')
  426     strcat (newname, "/");
  427 #endif
  428   strcat (newname, argv[optind]);
  429   if (add_wild)
  430     strcat (newname, "_*");
  431   else if (add_random)
  432     strcat (newname, "_||||||");  /* use | instead of X in case path contains X */
  433   if (add_suffix)
  434     strcat (newname, suffix);
  435 
  436   /* replace spaces with underscores to reduce risk of errors in upstream scripts */
  437   for (i = 0; i <= strlen (newname); i++)
  438     {
  439       if (newname[i] == ' ')
  440         newname[i] = '_';
  441     }
  442 
  443   /* scramble unique letter block and optionally create temporary file */
  444   if (add_random)
  445     {
  446       if (directory)
  447         {
  448           rc = mkstemp_custom (newname, GT_DIR);
  449           if (rc < 0)
  450             {
  451               fprintf (stderr, "tempname error: can't open a tempdir for writing\n");
  452               return (3);
  453             }
  454         }
  455       else if (create)
  456         {
  457           file_id = mkstemp_custom (newname, GT_FILE);
  458           if (file_id > 0)
  459             close (file_id);
  460           else
  461             {
  462               fprintf (stderr, "tempname error: can't open a tempfile for writing\n");
  463               return (3);
  464             }
  465         }
  466       else
  467         {
  468           rc = mkstemp_custom (newname, GT_NOCREATE);
  469           if (rc < 0)
  470             {
  471               fprintf (stderr, "tempname error: can't create a tempname for writing\n");
  472               return (3);
  473             }
  474         }
  475    }
  476 
  477   /* print out filename of temporary file */
  478   if (strlen (newname) == 0)
  479     {
  480       fprintf (stderr, "tempname error: can't form unique filename\n");
  481       free (newname);
  482       return (3);
  483     }
  484   else
  485     fprintf (stdout, "%s\n", newname);
  486 
  487   /* free memory and indicate successful finish */
  488   free (newname);
  489   return (0);
  490 }