/* * Copyright 1996-1997 Devin Reade . * All rights reserved. * * For copying and distribution information, see the file "COPYING" * accompanying this file. * * $Id: inst.c,v 1.6 1999/01/16 19:30:56 gdr-ftp Exp $ */ #define VERSION "1.3" /* version also in inst.rez, inst.1, inst.desc */ #define EMAIL "" #define __USE_DYNAMIC_GSSTRING__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* actions */ #define ACTION_CHANGE_MODE 0x0001 #define ACTION_CHANGE_OWNER 0x0002 #define ACTION_CHANGE_GROUP 0x0004 /* permissions */ #define S_USER 0700 #define S_GROUP 0070 #define S_OTHER 0007 #define S_ALL 0777 #define S_READ 0444 #define S_WRITE 0222 #define S_EXECUTE 0111 char *versionMsg = "Version %s by Devin Reade %s\n"; int dFlag, verbose; /* * usage * * display usage information and exit */ void usage(void) { fputs("Usage: install [-cdhsvV] [-o owner] [-g group] [-m mode] ", stderr); fputs("source [...] dest\n\n", stderr); fputs("Options:\n", stderr); fputs("\t-c Ignored. (Backwards Unix compatibility)\n", stderr); fputs("\t-d Create the specified directories\n", stderr); fputs("\t-g group Specify group id (not implemented)\n", stderr); fputs("\t-h Show usage information and exit.\n", stderr); fputs("\t-m mode Specify (Unix) access mode\n", stderr); fputs("\t-o owner Specify owner id (not implemented)\n", stderr); fputs("\t-s Strip binary (not implemented)\n", stderr); fputs("\t-V Print each file as it is created (verbose).\n", stderr); fputs("\t-v Show version number\n\n", stderr); fprintf(stderr, versionMsg, VERSION, EMAIL); exit(1); } /* * translateMode * * set mode to the value corresponding to the permission bit string * . If the first char of is a digit, then it is assumed * to be an octal number. Otherwise it is assumed to be a string of * the form "ug+rx" (in the usual chmod(1) format). * * If these assumptions don't hold, then return non-zero. Returns * zero and sets mode on success. mode may be modified, even on * failure. */ static int translateMode(char *str, mode_t *mode) { mode_t who = 0L; mode_t perm = 0L; char *p, *q; /* octal number? */ if (isdigit(*str)) { errno = 0; *mode = strtoul(str, NULL, 8); return errno; } /* it's not an absolute octal; treat as a string */ if (((p = strchr(str, '=')) == NULL) && ((p = strchr(str, '+')) == NULL) && ((p = strchr(str, '-')) == NULL)) { errno = EINVAL; return errno; } /* * Since we use mode 0000 as our starting point, using '-' in the * symbolic mode is equivalent to setting the mode to 0000 (absolute). * Similarly, using '+' and '=' are equivalent. * * If '-' was specified, ignore the "ugo" and "rwx" parts of the string. */ if (*p == '-') { *mode = 0000; return 0; } *p++ = '\0'; if (*str == '\0') { /* this should probably be derived from the umask */ who |= S_ALL; } else { for (q = str; *q; q++) { switch (*q) { case 'u': who |= S_USER; break; case 'g': who |= S_GROUP; break; case 'o': who |= S_OTHER; break; case 'a': who |= S_ALL; break; default: errno = EINVAL; return errno; } } } if (*p == '\0') { /* this should probaby be taken from the umask */ perm |= S_READ | S_WRITE | S_EXECUTE; } else { for (; *p; p++) { switch (*p) { case 'r': perm |= S_READ; break; case 'w': perm |= S_WRITE; break; case 'x': perm |= S_EXECUTE; break; case 's': perm |= S_EXECUTE; break; default: errno = EINVAL; return errno; } } } *mode = who & perm; return 0; } /* * mkdirs * * argv is assumed to be an array of argc pathnames. mkdirs will * create all the listed directories, including their parents if * necessary. * * Returns zero on success. Returns non-zero and prints a suitable * error message on failure. */ int mkdirs(int argc, char **argv) { static struct stat statbuf; /* reduce stack space */ char *path, *p; size_t pathlen; int result = 0; int makeit; /* do we try a mkdir()? */ int abortpath; /* we saw an error; don't both with rest of path */ int i, j; int coloncount, isVolume; /* loop over each of the pathnames in the array */ for (i = 0; i < argc; i++) { /* expand to a full pathname */ if ((path = LC_ExpandPath(argv[i])) == NULL) { warn("couldn't expand the pathname of %s (ignored)", argv[i]); continue; } pathlen = strlen(path); /* is this pathname legal? */ /* place a call to JudgeName() [custom] here */ /* find out how many path components there are */ coloncount = 0; p = path; while (*p) { if (*p == ':') { coloncount++; } p++; } /* skip the first part if it's a volume or device name */ abortpath = 0; if ((*path == ':') || (*path == '.')) { j = 1; p = path + 1; if ((p = strchr(p, ':')) == NULL) { p = path + pathlen; abortpath = 1; } else { p++; } } else { j = 0; p = path; } /* create each component in path */ for (; !abortpath && j < coloncount; j++) { if ((p = strchr(p, ':')) == NULL) { p = path + pathlen; } *p = '\0'; if (stat(path, &statbuf) != 0) { if (errno == ENOENT) { makeit = 1; } else { warn("couldn't stat %s", path); makeit = 0; abortpath = 1; result = 1; } } else { makeit = 0; if (statbuf.st_mode & S_IFDIR == 0) { warnx("%s exists and is not a directory\n", path); abortpath = 1; result = 1; } /* else it exists and is a directory */ } /* go ahead and create the directory */ if (makeit) { if (verbose) { fprintf(stderr, "%s\n", path); } if (mkdir(path)) { warn("couldn't create directory %s", path); abortpath = 1; result = 1; } } /* reinstate the ':' that we "nulled-out" */ if (p != path + pathlen) { *p++ = ':'; } } } return result; } /* * copyfiles * * is assumed to be an array of filenames. * * This routine copies all but the last specified file to the directory * or filename specified by the last filename. If argc>2, the last element * _must_ be a directory. * * Returns zero on success. On failure, returns the last non-zero errno * and prints error conditions to stderr via the warn(3) routines. * * If the ACTION_CHANGE_MODE bit is set in , chmod(2) is invoked * on the resulting file. ACTION_CHANGE_USER and ACTION_CHANGE_GROUP * are currently ignored. */ static int copyfiles(int argc, char **argv, int action, mode_t mode) { static FileInfoRecGS inforec; GSStringPtr src, dest, newname; int total, i, result, printWhen; unsigned short flags; char sep; #define PRINT_NEVER 0 /* used for printWhen (verbose mode) */ #define PRINT_BEFORE 1 #define PRINT_AFTER 2 static const char *nodup = "couldn't duplicate %s file name \"%s\""; assert(argc > 1); /* initialization */ result = 0; total = argc - 1; flags = LC_COPY_DATA | LC_COPY_REZ | LC_COPY_KEEPBUF; /* duplicate the destination name */ if ((dest = __C2GSMALLOC(argv[total])) == NULL) { err(1, nodup, "destination", argv[total]); /*NOTREACHED*/ } /* * If we're copying more than one file, make sure the last * is a directory. */ if (argc > 2) { inforec.pCount = 5; inforec.pathname = dest; GetFileInfoGS(&inforec); if (_toolErr != 0) { errno = _mapErr(_toolErr); err(1, "couldn't stat %s", dest->text); /*NOTREACHED*/ } if ((inforec.storageType != 0x0D) && (inforec.storageType != 0x0F)) { errno = ENOTDIR; errx(1, "%s is not a directory", dest->text); } } /* copy each file */ if (verbose) { printWhen = (argc == 2) ? PRINT_BEFORE : PRINT_AFTER; } else { printWhen = PRINT_NEVER; } if (total > 0) { sep = (strchr(argv[total], ':') != NULL) ? ':' : '/'; } for (i=0; itext); } if (action & ACTION_CHANGE_MODE) { if (chmod(newname->text, mode) < 0) { result = errno; warn("couldn't change %s to mode %o", newname->text, mode); } } } GIfree(src); } GIfree(dest); return result; } /* * obvious ... */ int main(int argc, char **argv) { mode_t newmode; int c, nfiles; int action = 0; __REPORT_STACK(); if (needsgno() == 0) { errx(1, "requires GNO"); } /* initialize */ dFlag = 0; newmode = 0L; /* parse command line */ while ((c = getopt(argc, argv, "cdg:hm:o:svV")) != EOF) { switch (c) { case 'v': errx(1, versionMsg, VERSION, EMAIL); /*NOTREACHED*/ break; case 'm': action |= ACTION_CHANGE_MODE; if (translateMode(optarg, &newmode)) { usage(); } break; case 'V': verbose=1; break; case 'd': dFlag++; case 'c': /* not implemented */ case 'g': /* not implemented */ case 'o': /* not implemented */ case 's': /* not implemented */ break; case 'h': default: usage(); } } nfiles = argc - optind; #if 1 if (nfiles < (dFlag ? 1 : 2)) { usage(); } c = 0; /* using c as a temp variable */ BeginSessionGS(&c); if (_toolErr) { errno = _mapErr(_toolErr); err(EXIT_FAILURE, "BeginSessionGS failed (0x%x)", _toolErr); } if (dFlag) { c = mkdirs(nfiles, &argv[optind]); } else { /* ignore new user and group for now */ c = copyfiles(nfiles, &argv[optind], action, newmode); } action = 0; /* using action as a temp variable */ EndSessionGS(&action); if (_toolErr) { errno = _mapErr(_toolErr); err(EXIT_FAILURE, "EndSessionGS failed (0x%x)", _toolErr); } #else if (dFlag) { if (nfiles < 1) { usage(); } c = mkdirs(nfiles, &argv[optind]); } else { if (nfiles < 2) { usage(); } /* ignore new user and group for now */ c = copyfiles(nfiles, &argv[optind], action, newmode); } #endif return c; }