home *** CD-ROM | disk | FTP | other *** search
- /* create -- make new files
- Copyright (C) 1990 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* Usage: create [-dfnpqtP] [-m mode] [-x prefix] [+directory] [+fifo] [+pipe]
- [+mode mode] [+no-create] [+path] [+quiet] [+silent] [+temporary]
- [+prefix prefix] [+portability] file...
-
- Construct a valid pathname including each FILE name, make sure that
- file does not already exist, and create an empty file with that name.
- Validation consists of making sure that all directories in the
- constructed pathname exist and have x permission and whether
- strlen (constructed_pathname) <= PATH_MAX
- && strlen (each_directory_in_path) <= NAME_MAX
- Default permissions for created files are rw-rw-rw- - umask.
-
- The created files' names are constructed from:
- [directory/] from -t (TMPDIR or /tmp)
- [prefix] from -x
- file command line args
- [suffix] from -t
-
- Exit status:
- 0 Valid pathnames constructed and (unless -n given)
- files created.
- 1 Length of constructed pathname or component
- exceeds limit.
- 2 If -P, constructed pathname contains characters
- not in the portable filename character set.
- 3 Constructed pathname existed and -p not given;
- or -t given and could not construct nonexisting
- pathname.
- >3 -n not given and couldn't create file because of
- permissions, file system full, etc.
-
- All errors are fatal.
-
- Options:
- -d, +directory Make a directory instead of a regular file.
- Default permissions are rwxrwxrwx - umask.
-
- -f, +fifo, +pipe Make a fifo instead of a regular file.
-
- -m, +mode mode Set the mode of created files to MODE, which
- is symbolic as in chmod and uses the umask as a
- point of departure.
-
- -n, +no-create Don't check for the file's existence or try to
- create it.
-
- -p, +path Do not check whether the path prefix or the file
- to be created exist.
- An error occurs if any constructed pathname
- already exists and has a different type than the
- file to create. Otherwise, if -m is given,
- set the file's permission modes.
- Make any missing parent directories for each argument.
- Parent dirs default to umask modified by `u+wx'.
-
- -q, +quiet, +silent When given with -t or -x, do not write pathname.
-
- -t, +temporary Append a SUFFIX of chars in the portable filename
- character set to the FILE name such that the
- resulting pathname is unique and still valid.
- If TMPDIR is set in the environment and neither
- FILE nor PREFIX contains a slash, use the
- value of TMPDIR as the directory pathname;
- otherwise, use /tmp.
- If -m is not given, do go= on the default mode.
- Write the constructed pathname to stdout.
-
- -x, +prefix prefix Use PREFIX when constructing path names.
- Write the constructed pathname to stdout.
-
- -P, +portability Instead of performing length checks on the
- underlying filesystem, test the length of the
- pathname and its components against the POSIX.1
- minimum limits for portability, _POSIX_NAME_MAX
- and _POSIX_PATH_MAX in 2.9.2. Also check that
- the pathname contains no characters not in the
- portable filename character set.
-
- David MacKenzie <djm@ai.mit.edu> */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include "system.h"
- #include "getopt.h"
- #include "modechange.h"
-
- #ifdef STDC_HEADERS
- #include <errno.h>
- #include <stdlib.h>
- #else
- char *getenv ();
- char *malloc ();
-
- extern int errno;
- #endif
-
- #ifndef _POSIX_SOURCE
- #define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
- #endif
-
- int mktemp ();
-
- char *construct_path ();
- char *xmalloc ();
- void ensure_path_exists ();
- void error ();
- void strip_trailing_slashes ();
- void usage ();
- void validate_new_path ();
-
- /* The name this program was run with. */
- char *program_name;
-
- struct option longopts[] =
- {
- {"directory", 0, NULL, 'd'},
- {"fifo", 0, NULL, 'f'},
- {"pipe", 0, NULL, 'f'},
- {"mode", 1, NULL, 'm'},
- {"no-create", 0, NULL, 'n'},
- {"path", 0, NULL, 'p'},
- {"quiet", 0, NULL, 'q'},
- {"silent", 0, NULL, 'q'},
- {"temporary", 0, NULL, 't'},
- {"prefix", 1, NULL, 'x'},
- {"portability", 0, NULL, 'P'},
- {NULL, 0, NULL, 0}
- };
-
- void
- main (argc, argv)
- int argc;
- char **argv;
- {
- unsigned filetype; /* The type of file to create. */
- int make_missing_parents; /* If nonzero, ensure that a path exists. */
- int create_file; /* If nonzero, try to create the file. */
- int echo_pathnames; /* If nonzero, write pathnames to stdout. */
- int make_temporary; /* If nonzero, make file in a temp dir. */
- int check_portability; /* If nonzero, check POSIX portability. */
- char *tmpdir; /* Value of TMPDIR variable or "/tmp". */
- int tmpdir_length; /* Length of `tmpdir' + '/'. */
- unsigned short newmode;
- unsigned short parent_mode;
- struct mode_change *change;
- char *symbolic_mode;
- char *prefix;
- int prefix_length;
- int silent;
- int optc;
- int ind;
-
- program_name = argv[0];
- filetype = S_IFREG;
- make_missing_parents = 0;
- create_file = 1;
- echo_pathnames = 0;
- make_temporary = 0;
- check_portability = 0;
- tmpdir = prefix = "";
- tmpdir_length = 0;
- prefix_length = 0;
- symbolic_mode = NULL;
- silent = 0;
-
- while ((optc = getopt_long (argc, argv, "dfnpqtPm:x:", longopts, &ind))
- != EOF)
- {
- if (optc == 0 && longopts[ind].flag == 0)
- optc = longopts[ind].val;
- switch (optc)
- {
- case 0: /* Long option. */
- break;
- case 'd':
- filetype = S_IFDIR;
- break;
- case 'f':
- #ifdef S_IFIFO
- filetype = S_IFIFO;
- #else
- error (4, 0, "fifo files are not supported on this system");
- #endif
- break;
- case 'm':
- symbolic_mode = optarg;
- break;
- case 'n':
- create_file = 0;
- break;
- case 'p':
- make_missing_parents = 1;
- break;
- case 'q':
- silent = 1;
- break;
- case 't':
- make_temporary = 1;
- echo_pathnames = 1;
- break;
- case 'x':
- prefix = optarg;
- prefix_length = strlen (prefix);
- echo_pathnames = 1;
- break;
- case 'P':
- check_portability = 1;
- break;
- default:
- usage ();
- }
- }
-
- if (optind == argc)
- usage ();
-
- if (silent)
- echo_pathnames = 0;
-
- newmode = 0777 & ~umask (0);
- parent_mode = newmode | 0300; /* u+wx */
- if (filetype != S_IFDIR)
- newmode &= ~0111;
- if (symbolic_mode)
- {
- change = mode_compile (symbolic_mode, MODE_MASK_EQUALS | MODE_MASK_PLUS);
- if (change == MODE_INVALID)
- error (4, 0, "invalid mode");
- else if (change == MODE_MEMORY_EXHAUSTED)
- error (4, 0, "virtual memory exhausted");
- newmode = mode_adjust (newmode, change);
- }
- else if (make_temporary)
- newmode &= ~0077; /* go= */
-
- if (make_temporary || prefix)
- {
- tmpdir = getenv ("TMPDIR");
- if (tmpdir)
- tmpdir_length = strlen (tmpdir) + 1;
- }
-
- for (; optind < argc; ++optind)
- {
- char *file = argv[optind];
-
- strip_trailing_slashes (file);
- if (make_temporary || prefix)
- file = construct_path (file, make_temporary, tmpdir, tmpdir_length,
- prefix, prefix_length);
-
- if (echo_pathnames)
- puts (file);
-
- if (make_missing_parents)
- ensure_path_exists (file, filetype, newmode, parent_mode);
- else
- validate_new_path (file, check_portability);
- if (create_file)
- {
- switch (filetype)
- {
- case S_IFREG:
- ind = open (file, O_WRONLY | O_CREAT | O_EXCL, newmode);
- if (ind == -1)
- error (4, errno, "%s", file);
- else
- close (ind);
- break;
- #ifdef S_IFIFO
- case S_IFIFO:
- if (mkfifo (file, newmode))
- error (4, errno, "%s", file);
- break;
- #endif
- case S_IFDIR:
- if (mkdir (file, newmode))
- error (4, errno, "%s", file);
- break;
- }
- }
- if (file != argv[optind])
- free (file);
- }
-
- exit (0);
- }
-
- /* Return a path with the form:
- [directory/] TMPDIR or /tmp if MAKE_TEMPORARY, else empty
- [prefix] PREFIX if set, else empty
- file BASE
- [suffix] letter+pid if MAKE_TEMPORARY, else empty */
-
- char *
- construct_path (base, make_temporary, tmpdir, tmpdir_length,
- prefix, prefix_length)
- char *base;
- int make_temporary;
- char *tmpdir;
- int tmpdir_length;
- char *prefix;
- int prefix_length;
- {
- char *path, *p;
- int base_length;
-
- base_length = strlen (base);
- if (make_temporary)
- {
- if (tmpdir == NULL || index (base, '/') || index (prefix, '/'))
- {
- tmpdir = "/tmp";
- tmpdir_length = 5; /* Count a trailing slash. */
- }
- }
- path = xmalloc (tmpdir_length + prefix_length + base_length
- + (make_temporary ? 6 : 0) + 1);
-
- p = path;
- if (make_temporary)
- {
- strcpy (p, tmpdir);
- p += tmpdir_length - 1;
- *p++ = '/';
- }
- strcpy (p, prefix);
- p += prefix_length;
- strcpy (p, base);
- if (make_temporary)
- {
- p += base_length;
- strcpy (p, "XXXXXX");
- mktemp (path);
- }
- return path;
- }
-
- /* Make sure file PATH, which has type PATHTYPE,
- and all leading directories exist,
- and give it permission mode MODE.
- If any leading directories are created, give them permission
- mode PARENT_MODE.
- Exit if an error occurs. */
-
- void
- ensure_path_exists (path, pathtype, mode, parent_mode)
- char *path;
- unsigned pathtype;
- unsigned short mode;
- unsigned short parent_mode;
- {
- char *slash;
- struct stat stats;
-
- if (stat (path, &stats))
- {
- slash = path;
- while (slash = index (slash, '/'))
- {
- *slash = 0;
- if (stat (path, &stats))
- {
- if (mkdir (path, parent_mode))
- error (4, errno, "cannot make directory `%s'", path);
- }
- else if ((stats.st_mode & S_IFMT) != S_IFDIR)
- error (4, 0, "`%s' is not a directory", path);
- *slash++ = '/';
- }
- }
- else if ((stats.st_mode & S_IFMT) != pathtype)
- error (4, 0, "`%s' has the wrong type", path);
- else if (chmod (path, mode))
- error (4, errno, "cannot change mode of `%s'", path);
- }
-
- /* Each element is nonzero if the corresponding ASCII character is
- in the POSIX portable character set, and zero if it is not.
- In addition, the entry for `/' is nonzero to simplify checking. */
- char portable_chars[] =
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
- 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
-
- /* Make sure that PATH does not exist, but that all directories along
- the way do exist and have `x' permission, and that
- strlen (PATH) <= PATH_MAX
- && strlen (each-directory-in-PATH) <= NAME_MAX
- If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
- _POSIX_NAME_MAX instead, and make sure that PATH contains no
- characters not in the POSIX portable filename character set, which
- consists of A-Z, a-z, 0-9, ., _, -.
- Exit if an error occurs. */
-
- void
- validate_new_path (path, portability)
- char *path;
- int portability;
- {
- char *start, *end;
- int name_max, path_max;
- struct stat stats;
-
- /* Perform the cheaper tests first. */
-
- path_max = portability ? _POSIX_PATH_MAX : PATH_MAX;
- name_max = portability ? _POSIX_NAME_MAX : NAME_MAX;
-
- if (strlen (path) > path_max)
- error (1, 0, "path `%s' exceeds length limit", path);
-
- if (portability)
- {
- for (start = path; *start; ++start)
- if (portable_chars[*start] == 0)
- error (2, 0, "path `%s' contains nonportable character `%c'",
- path, *start);
- }
-
- if (stat (path, &stats) == 0)
- error (3, 0, "`%s' already exists", path);
-
- start = path;
- do
- {
- end = index (start + 1, '/');
- if (end == NULL)
- end = index (start + 1, 0);
- if (end - start - (*start == '/') > name_max)
- error (1, 0, "name `%s' exceeds length limit", path);
- if (*end == '/')
- {
- *end = 0;
- if (stat (path, &stats) == -1)
- error (4, errno, "%s", path);
- else if ((stats.st_mode & S_IFMT) != S_IFDIR)
- error (4, 0, "`%s' is not a directory", path);
- *end = '/';
- }
- start = end;
- }
- while (*start);
- }
-
- /* Remove trailing slashes from PATH; they cause some system calls to fail. */
-
- void
- strip_trailing_slashes (path)
- char *path;
- {
- int last;
-
- last = strlen (path) - 1;
- while (last > 0 && path[last] == '/')
- path[last--] = '\0';
- }
-
- /* Allocate N bytes of memory dynamically, with error checking. */
-
- char *
- xmalloc (n)
- unsigned n;
- {
- char *p;
-
- p = malloc (n);
- if (p == 0)
- error (4, 0, "virtual memory exhausted");
- return p;
- }
-
- void
- usage ()
- {
- fprintf (stderr, "\
- Usage: %s [-dfnpqtP] [-m mode] [-x prefix] [+directory] [+fifo] [+pipe]\n\
- [+mode mode] [+no-create] [+path] [+quiet] [+silent] [+temporary]\n\
- [+prefix prefix] [+portability] file...\n",
- program_name);
- exit (1);
- }
-
-