mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-12-21 23:29:16 +00:00
425 lines
9.5 KiB
C
425 lines
9.5 KiB
C
/*
|
|
* Copyright 1996-1997 Devin Reade <gdr@myrias.com>.
|
|
* All rights reserved.
|
|
*
|
|
* For copying and distribution information, see the file "COPYING"
|
|
* accompanying this file.
|
|
*
|
|
* $Id: inst.c,v 1.3 1997/10/30 04:14:21 gdr Exp $
|
|
*/
|
|
|
|
#define __USE_DYNAMIC_GSSTRING__
|
|
|
|
#include <sys/types.h>
|
|
#include <types.h>
|
|
#include <sys/stat.h>
|
|
#include <gno/gno.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <orca.h>
|
|
#include <gsos.h>
|
|
#include <err.h>
|
|
|
|
#include "contrib.h"
|
|
|
|
/* 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
|
|
|
|
#define VERSION "1.1"
|
|
#define EMAIL "<gdr@myrias.com>"
|
|
|
|
char *versionMsg = "Version %s by Devin Reade %s\n";
|
|
int dFlag;
|
|
|
|
#ifdef __STACK_CHECK__
|
|
/*
|
|
* printStack
|
|
*
|
|
* prints stack usage (debugging only)
|
|
*/
|
|
static void
|
|
printStack (void) {
|
|
fprintf(stderr, "stack usage: %d bytes\n", _endStackCheck());
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* usage
|
|
*
|
|
* display usage information and exit
|
|
*/
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fputs("Usage: install [-cdhsv] [-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 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
|
|
* <str>. If the first char of <str> 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 && 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
|
|
*
|
|
* <argv> is assumed to be an array of <argc> 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 <action>, 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;
|
|
unsigned short flags;
|
|
|
|
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[argc - 1])) == NULL) {
|
|
err(1, nodup, "destination", argv[argc - 1]);
|
|
/*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 */
|
|
for (i=0; i<total; i++) {
|
|
if ((src = __C2GSMALLOC(argv[i])) == NULL) {
|
|
err(1, nodup, "source", argv[i]);
|
|
/*NOTREACHED*/
|
|
}
|
|
newname = LC_CopyFileGS(src, dest, flags);
|
|
if (newname == NULL) {
|
|
result = errno;
|
|
warn("install of %s to %s failed", argv[i],
|
|
argv[argc-1]);
|
|
} else {
|
|
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;
|
|
|
|
#ifdef __STACK_CHECK__
|
|
_beginStackCheck();
|
|
atexit(printStack);
|
|
#endif
|
|
|
|
if (needsgno() == 0) {
|
|
errx(1, "requires GNO");
|
|
}
|
|
|
|
/* initialize */
|
|
dFlag = 0;
|
|
newmode = 0L;
|
|
|
|
/* parse command line */
|
|
while ((c = getopt(argc, argv, "cdg:hm:o:sv")) != 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 '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 (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);
|
|
}
|
|
|
|
|
|
return c;
|
|
}
|