"Fossies" - the Fresh Open Source Software Archive

Member "littleutils-1.0.43/littleutils/tempname.c" (11 Sep 2019, 11775 Bytes) of package /linux/privat/littleutils-1.0.43.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 last Fossies "Diffs" side-by-side code changes report: 1.0.38_vs_1.0.39.

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