"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 }