Initial checkin of install(1) for GNO, version 1.0.

This commit is contained in:
gdr 1996-03-31 23:38:34 +00:00
parent 6f6fee285e
commit f8c4d4b6d5
13 changed files with 1351 additions and 0 deletions

33
usr.bin/install/COPYING Normal file
View File

@ -0,0 +1,33 @@
Copyright 1996 Devin Reade <gdr@myrias.com>.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the following disclaimer
in the documentation and/or other materials provided with the
distribution. If the binary form is created using Orca/C, then
the following Byteworks' copyright notice must also be included
in the same location.
3. The name of the developer may not be used to endorse or promote
products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE DEVELOPER "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
The binary distribution of this program contains material from the Orca/C
Run-Time Libraries, copyright 1987-1996 by Byte Works, Inc. Used with
permission.

View File

@ -0,0 +1,70 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: basename.c,v 1.1 1996/03/31 23:38:30 gdr Exp $
*/
#include <string.h>
#include <stdio.h>
#include "install.h"
/*
* basename
*
* returns the filename component of <path>. If <path> contains colons,
* they are assumed to be the directory separators, otherwise any '/' is
* assumed to be a directory separator.
*
* If no directory separators are found, then the full path is returned.
*
* No check is done as to whether the pathname is valid on the any
* given filesystem.
*/
char *
basename (char *path)
{
char delim, *p;
delim = strchr(path,':') ? ':' : '/';
p = strrchr(path,delim);
return p ? p+1 : path;
}
/*
* dirname
*
* Returns a pointer to an internal buffer that contains a string that
* matches the directory component
* of <path>. If <path> contains at least one ':', then it is assumed
* that colons are directory separators, otherwise any '/' character
* is treated as a directory separator.
*
* If <path> contains no pathname separators, then dirname() will
* return an empty (zero-length) string.
*
* No check is done as to whether the pathname is valid on the any
* given filesystem.
*/
char *
dirname (char *path)
{
char delim, *p;
static char dir[FILENAME_MAX];
strncpy(dir,path,FILENAME_MAX-1);
dir[FILENAME_MAX-1] = '\0';
delim = strchr(dir,':') ? ':' : '/';
p = strchr(dir,delim);
if (p == NULL) {
*dir = '\0';
} else {
*p = '\0';
}
return dir;
}

37
usr.bin/install/c2gs.c Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: c2gs.c,v 1.1 1996/03/31 23:38:31 gdr Exp $
*/
#include <types.h>
#include <gsos.h>
#include <string.h>
#include "install.h"
/*
* __C2GS
*
* Converts a null-terminated C string into a class 1 GS/OS string.
* Space for the GS/OS string must already be allocated, and the
* length of s must not be more than 255 chars.
*
* If the s is too long, __C2GS will return NULL, otherwise it will
* return the GS/OS string g.
*/
GSString255Ptr
__C2GS(char *s, GSString255Ptr g)
{
size_t len;
len = strlen(s);
if (len > 255) return NULL; /* the string won't fit */
g->length = len;
strncpy(g->text,s,255);
return g;
}

284
usr.bin/install/copyfile.c Normal file
View File

@ -0,0 +1,284 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: copyfile.c,v 1.1 1996/03/31 23:38:31 gdr Exp $
*/
#include <errno.h>
#include <gsos.h>
#include <orca.h>
#include <string.h>
#include <stdlib.h>
#include "install.h"
/* the chunk size in which we copy files */
#define COPY_BUFFER_SIZE 1024
/*
* copyfile
*
* copy a file from the pathname <from> to the location <to>, which
* may be a directory. Ensure that file types and other information
* (except for the backup bit) is matched.
*
* Returns NULL and sets errno on failure. On success, returns a
* pointer to an internal buffer containing the final pathname.
*
* +++ THIS ROUTINE IS NOT REENTRANT +++
*/
char *
copyfile (char *from, char *to)
{
static char buffer[COPY_BUFFER_SIZE];
static FileInfoRecGS inforec;
static OpenRecGS openrec;
static ExpandPathRecGS expandrec;
static ResultBuf255 resultbuf;
static struct {
Word pCount;
Word refNum;
Longword dataBuffer;
Longword requestCount;
Longword transferCount;
Word cachePriority;
} iobuf;
static struct {
Word pCount;
Word refNum;
} closerec;
static GSString255 fromGS, toGS;
static char *result = NULL; /* we only use this if our path is */
/* exactly 255 chars long */
size_t len1, len2;
Word refNumIn, refNumOut; /* GS/OS ref numbers for I/O */
int isDir, i, j, k, done;
char *p, *q, *r;
/* concheck and convert filenames to GSString255 type */
if (!from || !to ||
((len1 = strlen(from)) > 254) ||
((len2 = strlen(to)) > 254) )
{
errno = EINVAL;
return NULL;
}
fromGS.length = len1;
toGS.length = len2;
strcpy(fromGS.text,from);
strcpy(toGS.text,to);
/* expand the original file name */
expandrec.pCount = 3;
expandrec.inputPath = &fromGS;
expandrec.outputPath = &resultbuf;
expandrec.flags = 0x0000;
resultbuf.bufSize = 255;
ExpandPathGS(&expandrec);
if ((i = toolerror()) != 0) {
errno = _mapErr(i);
return NULL;
}
strcpyGSString255(&fromGS,&(resultbuf.bufString));
/* expand the destination name */
expandrec.pCount = 3;
expandrec.inputPath = &toGS;
expandrec.outputPath = &resultbuf;
expandrec.flags = 0x0000;
resultbuf.bufSize = 255;
ExpandPathGS(&expandrec);
if ((i = toolerror()) != 0) {
errno = _mapErr(i);
return NULL;
}
strcpyGSString255(&toGS,&(resultbuf.bufString));
/* find out if <to> is a directory */
inforec.pCount = 5;
inforec.pathname = &toGS;
GetFileInfoGS(&inforec);
i = toolerror();
switch(i) {
case 0:
isDir = ((inforec.storageType == 0x0D) ||
(inforec.storageType == 0x0F)) ? 1 : 0;
break;
case fileNotFound:
isDir = 0;
break;
default:
errno = _mapErr(i);
return NULL;
}
/* it's a directory? tack on the file name */
if (isDir) {
/* expand the directory name */
expandrec.pCount = 3;
expandrec.inputPath = &toGS;
expandrec.outputPath = &resultbuf;
expandrec.flags = 0x0000;
resultbuf.bufSize = 255;
ExpandPathGS(&expandrec);
if ((i = toolerror()) != 0) {
errno = _mapErr(i);
return NULL;
}
/* tack on the final component */
p = basename(from);
len1 = strlen(p);
if (len1 + toGS.length + 1 > 255) {
errno = EINVAL;
return NULL;
}
q = &(toGS.text[toGS.length]);
r = p;
*q++ = ':';
for (i=0; i<len1; i++) {
*q++ = *r++;
}
toGS.length += len1 + 1;
}
/* check to see it's not the same file */
if (strcmpGSString255(&fromGS, &toGS) == 0) {
errno = EINVAL;
return NULL;
}
/* get the file info of the original file */
inforec.pCount = 7;
inforec.pathname = &fromGS;
GetFileInfoGS(&inforec);
if ((i = toolerror()) != 0) {
errno = _mapErr(i);
return NULL;
}
/* destroy the old target file if it exists */
inforec.pCount = 1;
inforec.pathname = &toGS; DestroyGS(&inforec);
i = toolerror();
switch(i) {
case 0:
case fileNotFound:
break;
default:
errno = _mapErr(i);
return NULL;
}
/* create the new file */
inforec.pCount = 5;
inforec.pathname = &toGS;
CreateGS(&inforec);
if ((i = toolerror()) != 0) {
errno = _mapErr(i);
return NULL;
}
/* copy both forks, if necessary */
for (i=0; i< ((inforec.storageType == extendedFile) ? 2 : 1); i++) {
/* open the input file */
openrec.pCount = 4;
openrec.pathname = &fromGS;
openrec.requestAccess = readEnable;
switch (i) {
case 0:
openrec.resourceNumber = 0x0000;
break;
case 1:
openrec.resourceNumber = 0x0000;
break;
}
OpenGS(&openrec);
if ((j = toolerror()) != 0) {
errno = _mapErr(j);
return NULL;
}
refNumIn = openrec.refNum;
/* open the output file */
openrec.pathname = &toGS;
openrec.requestAccess = writeEnable;
OpenGS(&openrec);
if ((j = toolerror()) != 0) {
closerec.pCount = 1;
closerec.refNum = refNumIn;
CloseGS(&closerec);
errno = _mapErr(j);
return NULL;
}
refNumOut = openrec.refNum;
/* transfer the data */
done = 0;
iobuf.pCount = 5;
iobuf.dataBuffer = (Longword) &buffer;
iobuf.cachePriority = cacheOn;
while (!done) {
iobuf.refNum = refNumIn;
iobuf.requestCount = COPY_BUFFER_SIZE;
ReadGS(&iobuf);
k = toolerror();
switch (k) {
case 0:
break;
case eofEncountered:
done = 1;
break;
default:
closerec.pCount = 1;
closerec.refNum = refNumIn;
CloseGS(&closerec);
closerec.refNum = refNumOut;
CloseGS(&closerec);
errno = _mapErr(k);
return NULL;
}
iobuf.refNum = refNumOut;
iobuf.requestCount = iobuf.transferCount;
WriteGS(&iobuf);
if ((k = toolerror()) != 0) {
closerec.pCount = 1;
closerec.refNum = refNumIn;
CloseGS(&closerec);
closerec.refNum = refNumOut;
CloseGS(&closerec);
errno = _mapErr(k);
return NULL;
}
} /* end loop over buffering */
closerec.pCount = 1;
closerec.refNum = refNumIn;
CloseGS(&closerec);
closerec.refNum = refNumOut;
CloseGS(&closerec);
} /* end loop over forks */
/* set file information to match original file */
inforec.pCount = 7;
inforec.pathname = &toGS;
SetFileInfoGS(&inforec);
if ((i = toolerror()) != 0) {
errno = _mapErr(i);
return NULL;
}
if (toGS.length == 255) {
if (result) free(result);
result = __GS2CMALLOC(&toGS);
return result;
}
toGS.text[toGS.length]='\0';
return toGS.text;
}

125
usr.bin/install/errnoGS.c Normal file
View File

@ -0,0 +1,125 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: errnoGS.c,v 1.1 1996/03/31 23:38:31 gdr Exp $
*/
#include <gsos.h>
#include <stdarg.h>
#include <stdio.h>
#include "install.h"
#pragma lint -1
#pragma debug 0
#pragma optimize -1
#define NONE "no error"
#define UNKNOWN "unknown error"
segment "errnoGS___";
typedef struct errEntry {
unsigned short num;
char *str;
} errEntry;
static errEntry
sys_errlistGS[] = {
{ badSystemCall, "bad system call number" },
{ invalidPcount, "invalid parameter count" },
{ gsosActive, "GS/OS already active" },
{ devNotFound, "device not found" },
{ invalidDevNum, "invalid device number" },
{ drvrBadReq, "bad request or command" },
{ drvrBadCode, "bad control or status code" },
{ drvrBadParm, "bad call parameter" },
{ drvrNotOpen, "character device not open" },
{ drvrPriorOpen, "character device already open" },
{ irqTableFull, "interrupt table full" },
{ drvrNoResrc, "resources not available" },
{ drvrIOError, "I/O error" },
{ drvrNoDevice, "device not connected" },
{ drvrBusy, "call aborted; driver is busy" },
{ drvrWrtProt, "device is write protected" },
{ drvrBadCount, "invalid byte count" },
{ drvrBadBlock, "invalid block address" },
{ drvrDiskSwitch, "disk has been switched" },
{ drvrOffLine, "device off line/ no media present" },
{ badPathSyntax, "invalid pathname syntax" },
{ tooManyFilesOpen, "too many files open on server volume" },
{ invalidRefNum, "invalid reference number" },
{ pathNotFound, "subdirectory does not exist" },
{ volNotFound, "volume not found" },
{ fileNotFound, "file not found" },
{ dupPathname, "create or rename with existing name" },
{ volumeFull, "volume full error" },
{ volDirFull, "volume directory full" },
{ badFileFormat, "version error (incompatible file format)" },
{ badStoreType, "unsupported (or incorrect) storage type" },
{ eofEncountered, "end-of-file encountered" },
{ outOfRange, "position out of range" },
{ invalidAccess, "access not allowed" },
{ buffTooSmall, "buffer too small" },
{ fileBusy, "file is already open" },
{ dirError, "directory error" },
{ unknownVol, "unknown volume type" },
{ paramRangeErr, "parameter out of range" },
{ outOfMem, "out of memory" },
{ dupVolume, "duplicate volume name" },
{ notBlockDev, "not a block device" },
{ invalidLevel, "specifield level outside legal range" },
{ damagedBitMap, "block number too large" },
{ badPathNames, "invalid pathnames for ChangePath" },
{ notSystemFile, "not an executable file" },
{ osUnsupported, "Operating System not supported" },
{ stackOverflow, "too many applications on stack" },
{ dataUnavail, "data unavailable" },
{ endOfDir, "end of directory has been reached" },
{ invalidClass, "invalid FST call class" },
{ resForkNotFound, "file does not contain required resource" },
{ invalidFSTID, "error - FST ID is invalid" },
{ invalidFSTop, "invalid FST operation" },
{ fstCaution, "FST handled call, but result is weird" },
{ devNameErr, "device exists with same name as replacement name" },
{ defListFull, "device list is full" },
{ supListFull, "supervisor list is full" },
{ fstError, "generic FST error" },
{ resExistsErr, "cannot expand file, resource already exists" },
{ resAddErr, "cannot add resource fork to this type file" },
{ networkError, "generic network error" },
{ 0, NONE } /* we shouldn't see this */
};
unsigned short errnoGS = 0;
char *
strerrorGS(unsigned short num)
{
int i;
if (num == 0) return NONE;
i = 0;
while (sys_errlistGS[i].num) {
if (sys_errlistGS[i].num == num) {
return sys_errlistGS[i].str;
}
i++;
}
return UNKNOWN;
}
void
perrorGS(char *format, ...)
{
va_list ap;
va_start(ap,format);
vfprintf(stderr,format,ap);
fprintf(stderr,": %s\n",strerrorGS(errnoGS));
va_end(ap);
return;
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: expandpath.c,v 1.1 1996/03/31 23:38:32 gdr Exp $
*/
#include <types.h>
#include <gsos.h>
#include <errno.h>
#include <orca.h>
#include "install.h"
/*
* expandpath
*
* Uses the GS/OS facilities to expand the pathname <path>. On
* success, returns a pointer to the malloc'd expanded path. On
* failure it will return NULL and set errno.
*
* Note that in using this function, all directory separators will
* be converted to colons.
*
* Unfortunately, this routine uses a little over 0.5k of stack space ...
*/
char *
expandpath (char *path)
{
ExpandPathRecGS expand;
GSString255 inStr;
ResultBuf255 outBuf;
int i;
if (__C2GS(path,&inStr) == NULL) {
errno = EINVAL;
return NULL;
}
expand.pCount = 3;
expand.inputPath = &inStr;
expand.outputPath = &outBuf;
expand.flags = 0x0000;
outBuf.bufSize = 255;
ExpandPathGS(&expand);
if ((i = toolerror()) != 0) {
errno = _mapErr(i);
return NULL;
}
return __GS2CMALLOC(&(outBuf.bufString));
}

107
usr.bin/install/inst.1 Normal file
View File

@ -0,0 +1,107 @@
.\" Copyright 1996 Devin Reade <gdr@myrias.com>
.\"
.\" $Id: inst.1,v 1.1 1996/03/31 23:38:32 gdr Exp $
.\"
.TH INSTALL 1 "Commands and Applications" "31 Mar 96" "Version 1.0"
.SH NAME
install \- copy files and set their attributes
.SH SYNOPSIS
.BR install
[
.I options
] [
.BR -s
]
.I source
.I dest
.br
.BR install
[
.I options
] [
.BR -s
]
.I source
[ ... ]
.I directory
.br
.BR install
[
.I options
]
[
.BR -d
]
.I directory
[ ... ]
.SH DESCRIPTION
.BR install
copies files and sets their permission modes and, if possible, their
owner and group. It is used similarily to
.BR cp (1);
typically used in Makefiles to copy programs into their destination
directories. It can also be used to create the destination directories
and any leading directories, and to set the directories modes.
.LP
Some of the options listed below are not implemented or are implemented
in a restricted sense. Such options are recognised to maximize
.BR install 's
compatibility with other Unix versions, in order to minimize problems
with ported shell scripts and makefiles. Where options are not fully
implemented, it is usually due to differences between Unix and GS/OS.
.SH OPTIONS
.IP \fB-c\fR
Ignored. This option is included for backwards compatibility
with old Unix versions of
.BR install .
.IP \fB-d\fR
Create each given directory and its leading directories, if they
do not already exist.
.IP "\fB-g\fR \fIgroup\fR"
Set the group ownership of the installed file or directory to the group
ID of
.I group
(default is the processes current group).
.I group
may also be a numeric group ID.
.sp
\fBThis is currently ignored under GNO.\fR
.IP \fB-h\fR
Show usage information and exit.
.IP "\fB-m\fR \fImode\fR"
Set the permission mode for the installed file or directory to
.IR mode ,
which can be either an octal number, or a symbolic mode as in the
Unix chmod command,
with 0 as the point of departure. The default mode is 0755.
.sp
Note that currently under GNO, the
.I mode
is interpreted in the traditional Unix sense in that it only affects
read, write, and (to a limited extent) execute permissions.
Furthermore, the only bits interpreted are those for the
user permissions; the
.I mode
is effectively bitwise `anded' with the constant 0700.
.sp
An execute modification is only permitted when the original file
is of type TXT or SRC. If the `execute bit' is enabled, then
the file type will be changed to SRC and the auxilliary type to EXEC.
This is equivalent to making the file an executable shell script.
If the `execute bit' is disabled, then the file type will be changed
to TXT and the auxilliary type to 0x0000.
.IP "\fB-o\fR \fIowner\fR"
If run as root, set the ownership of the installed file to the user ID of
.IR owner .
.I owner
may also be numeric user ID.
.sp
\fBThis is currently ignored under GNO.\fR
.IP \fB-s\fR
Strip the symbol tables from the installed programs.
.sp
\fBThis is currently ignored under GNO.\fR
.IP \fB-v\fR
Show version number.
.SH AUTHOR
Devin Reade <gdr@eddore.myrias.com>

444
usr.bin/install/inst.c Normal file
View File

@ -0,0 +1,444 @@
/*
* Copyright 1996 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.1 1996/03/31 23:38:33 gdr Exp $
*/
#include <stdio.h>
#include <getopt.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <types.h>
#include <sys/stat.h>
#include <orca.h>
#include "install.h"
/* actions */
#define NOCHANGE 0
#define ASSIGN 1
#define REMOVE 2
#define ADD 3
/* 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 TYPE_TXT 0x04
#define TYPE_SRC 0xB0
#define TYPE_EXEC 0x00000006
#define TYPE_NONE 0x00000000
#define VERSION "1.0"
#define EMAIL "<gdr@myrias.com>"
char *versionMsg = "Version %s by Devin Reade %s\n";
int dFlag;
extern int mkdir(const char *);
extern int needsgno(void);
extern void begin_stack_check(void);
extern int end_stack_check(void);
/*
* 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);
}
/*
* getmode
*
* 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). Also sets action
* to be the type of action to take, whether we're removing, adding,
* or assigning the permission bits.
*
* If these assumptions don't hold, then return non-zero. Returns
* zero and sets mode on success.
*
* Since the IIgs currently doesn't have the concept of "group" and
* "other" permissions, we take everything from the user permissions.
*/
int
getmode (char *str, unsigned long *mode, int *action)
{
unsigned long who = 0L;
unsigned long perm = 0L;
char *p, *q;
/* octal number? */
if (isdigit(*str)) {
*action = ASSIGN;
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;
}
switch (*p) {
case '+': *action = ADD; break;
case '-': *action = REMOVE; break;
case '=': *action = ASSIGN; break;
default: assert(0);
}
/*
* this condition should really be deduced from the umask, if it
* were supported.
*/
if (str == p) who |= S_USER;
for (q = str; q<p; 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;
}
}
for (q = p+1; *q; q++) {
switch (*q) {
case 'r': perm |= S_READ; break;
case 'w': perm |= S_WRITE; break;
case 'x': perm |= S_EXECUTE; break;
case 's': /* ignored */ break;
default: errno = EINVAL; return errno;
}
}
/* currently: ignore all but user permissions */
if (!(who & S_USER)) {
*action = NOCHANGE;
}
*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;
/* loop over each of the pathnames in the array */
for (i=0; i<argc; i++) {
/* expand to a full pathname */
if ((path = expandpath(argv[i]))==NULL) {
perror(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++;
}
p = path + 1;
/* skip the volume name */
assert((p = strchr(p,':'))!=NULL);
p++;
--coloncount;
/* create each component in path */
abortpath = 0;
for (j=0; !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 {
perror(path);
makeit = 0;
abortpath = 1;
result = 1;
}
} else {
makeit = 0;
if (statbuf.st_mode & S_IFDIR == 0) {
fprintf(stderr,"%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)) {
perror(path);
abortpath = 1;
result = 1;
}
/* reinstate the ':' that we "nulled-out" */
if (p != path + pathlen) {
*p++ = ':';
}
}
free(path);
}
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.
*
* If action is not NOCHANGE, this routine will also set file permissions
* as specified in the install(1) man page. This may involve changing
* the file type.
*/
static int
copyfiles (int argc, char **argv, int action, unsigned long mode)
{
static FileInfoRecGS inforec;
static GSString255 filenameGS;
int i,j;
int result=0;
char *destination;
Word newaccess;
if (argc < 2) {
errno = EINVAL;
perror("internal error: not enough arguments to copyfiles()");
return errno;
}
if (argc > 2) {
/* find out if argv[argc-1] is a directory */
if (__C2GS(argv[argc-1], &filenameGS) == NULL) {
errno = EINVAL;
perror("destination path too long");
return errno;
}
inforec.pCount = 5;
inforec.pathname = &filenameGS;
GetFileInfoGS(&inforec);
if ((errnoGS = toolerror()) != 0) {
perrorGS("%s",argv[argc-1]);
errno = _mapErr(errnoGS);
return -1;
}
if ((inforec.storageType != 0x0D) && (inforec.storageType != 0x0F)) {
errno = ENOTDIR;
perror(argv[argc-1]);
return errno;
}
}
--argc;
for (i=0; i<argc; i++) {
if ((destination = copyfile (argv[i],argv[argc])) == NULL) {
errnoGS = toolerror();
perrorGS("install of %s to %s",argv[i],argv[argc]);
result = errno = _mapErr(errnoGS);
}
if (action == NOCHANGE) continue;
/* get the file info for the source file */
assert(__C2GS(argv[i],&filenameGS));
inforec.pCount = 7;
inforec.pathname = &filenameGS;
GetFileInfoGS(&inforec);
if ((errnoGS = toolerror()) != 0) {
perrorGS("GetFileInfo for %s failed",argv[i]);
result = errno = _mapErr(errnoGS);
}
/* modify the permissions as necessary */
switch (action) {
case ASSIGN:
newaccess = 0xFFFF;
if (!(mode & S_READ)) newaccess &= ~readEnable;
if (!(mode & S_WRITE)) newaccess &= ~writeEnable;
inforec.access &= newaccess;
if ((mode & S_EXECUTE) &&
(inforec.fileType == TYPE_TXT) || (inforec.fileType == TYPE_SRC)) {
inforec.fileType = TYPE_SRC;
inforec.auxType = TYPE_EXEC;
}
break;
case ADD:
if (mode & S_READ) inforec.access |= readEnable;
if (mode & S_WRITE) inforec.access |= writeEnable;
if ((mode & S_EXECUTE) &&
(inforec.fileType == TYPE_TXT) || (inforec.fileType == TYPE_SRC)) {
inforec.fileType = TYPE_SRC;
inforec.auxType = TYPE_EXEC;
}
break;
case REMOVE:
if (mode & S_READ) inforec.access &= ~readEnable;
if (mode & S_WRITE) inforec.access &= ~writeEnable;
if ((mode & S_EXECUTE) &&
(inforec.fileType == TYPE_TXT) || (inforec.fileType == TYPE_SRC)) {
inforec.fileType = TYPE_TXT;
inforec.auxType = TYPE_NONE;
}
break;
default:
assert(0);
}
/* set the modified file info for the destination file */
assert(__C2GS(destination,&filenameGS));
SetFileInfoGS(&inforec);
if ((errnoGS = toolerror()) != 0) {
perrorGS("SetFileInfo for %s failed",destination);
result = errno = _mapErr(errnoGS);
}
}
return result;
}
/*
* obvious ...
*/
int
main (int argc, char **argv)
{
unsigned long mode;
int c, nfiles;
int action = NOCHANGE;
#ifdef CHECK_STACK
begin_stack_check();
#endif
if (needsgno()==0) {
fprintf(stderr,"Requires GNO/ME\n");
exit(1);
}
/* initialize */
dFlag = 0;
mode = 0L;
/* parse command line */
while ((c = getopt(argc,argv,"cdg:hm:o:sv")) != EOF) {
switch (c) {
case 'v':
fprintf(stderr,versionMsg,VERSION,EMAIL);
exit(1);
break;
case 'm':
if (getmode(optarg,&mode,&action)) 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();
c = copyfiles(nfiles, &argv[optind], action, mode);
}
#ifdef CHECK_STACK
fprintf(stderr,"stack usage: %d bytes\n",end_stack_check());
#endif
return c;
}

12
usr.bin/install/inst.desc Normal file
View File

@ -0,0 +1,12 @@
Name: install
Version: 1.0 (31 Mar 96)
Shell: GNO/ME
Author: Devin Reade
Contact: gdr@myrias.com
Where: /usr/bin
FTP: apple2.caltech.edu, ground.isca.uiowa.edu
Install is similar to cp(1) in that it copies files. It will
also create directory hierarchies, and modify some access bits.
It is intended for use with Makefiles or other scripts to install
files into their destination directories.

25
usr.bin/install/inst.rez Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: inst.rez,v 1.1 1996/03/31 23:38:34 gdr Exp $
*/
#include "Types.Rez"
resource rVersion (0x1, purgeable3, nocrossbank) {
{ 1, 0, 0, /* version 1.0.0 */
release, /* development|alpha|beta|final|release */
0 /* non-final release number */
},
verBritain, /* close enough */
"install",
"Unix-style install program --\n"
" copies files and creates directories\n"
"Devin Reade <gdr@myrias.com>\n"
"Canada"
};

43
usr.bin/install/install.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: install.h,v 1.1 1996/03/31 23:38:33 gdr Exp $
*/
#ifndef __GSOS__
#include <gsos.h>
#endif
/* these are from libc */
extern GSString255Ptr __C2GSMALLOC (char *s);
extern char * __GS2CMALLOC (GSString255Ptr g);
extern char * __GS2C (char *s, GSString255Ptr g);
extern int _mapErr (int err);
/* from basename.c */
extern char *dirname (char *path);
extern char *basename (char *path);
/* from c2gs.c */
extern GSString255Ptr __C2GS(char *s, GSString255Ptr g);
/* from copyfile.c */
extern char *copyfile (char *from, char *to);
/* from errnoGS.c */
extern unsigned short errnoGS;
extern char *strerrorGS (unsigned short num);
extern void perrorGS (char *format, ...);
/* from expandpath.c */
extern char *expandpath (char *path);
/* from stringGS.c */
extern void strcpyGSString255 (GSString255Ptr to, GSString255Ptr from);
extern void strcatGSString255 (GSString255Ptr to, GSString255Ptr from);
extern int strcmpGSString255 (GSString255Ptr a, GSString255Ptr b);

View File

@ -0,0 +1,40 @@
#
# This makefile is for use with dmake(1).
#
# $Id: makefile.mk,v 1.1 1996/03/31 23:38:34 gdr Exp $
#
DEFINES = -DCHECK_STACK
STACK = -s1280
MAINFLAGS = $(DEFINES) $(STACK) -w -O
CFLAGS = $(DEFINES) $(STACK) -w -O -r
LDFLAGS = -v
LDLIBS = -l/usr/lib/stack
BINDIR = /usr/bin
MANDIR = /usr/man
OBJS = install.o basename.o c2gs.o copyfile.o errnoGS.o \
expandpath.o stringGS.o
install: $(OBJS) install.r
$(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
copyfork install.r install -r
@echo 'type \"dmake doinstall\" to install this program'
install.o: install.c install.h
$(CC) -c $(MAINFLAGS) install.c -o $@
doinstall:
./install -m755 -obin -gsys ./install $(BINDIR)
./install -m644 -obin -gsys ./install.1 $(MANDIR)/man1
clean clobber:
$(RM) $(OBJS) install.root install.r
basename.o :: install.h
c2gs.o :: install.h
copyfile.o :: install.h
errnoGS.o :: install.h
expandpath.o :: install.h
stringGS.o :: install.h

View File

@ -0,0 +1,78 @@
/*
* Copyright 1996 Devin Reade <gdr@myrias.com>.
* All rights reserved.
*
* For copying and distribution information, see the file "COPYING"
* accompanying this file.
*
* $Id: stringGS.c,v 1.1 1996/03/31 23:38:34 gdr Exp $
*/
#include <gsos.h>
#include "install.h"
/*
* strcpyGSString255
*
* copies the GSString255 pointed to by <from> to that pointed
* to by <to>
*/
void
strcpyGSString255 (GSString255Ptr to, GSString255Ptr from)
{
int i;
char *p = from->text;
char *q = to->text;
for (i=0; i<from->length; i++) *q++ = *p++;
to->length = from->length;
return;
}
/*
* strcatGSString255
*
* concatenates the string <from> onto <to>, to a maximum of 255
* chars total in <to>.
*/
void
strcatGSString255 (GSString255Ptr to, GSString255Ptr from)
{
int i, count;
char *p = from->text;
char *q = to->text;
q+= to->length;
count = from->length;
if (count > 255 - to->length) count = 255 - to->length;
for (i=0; i<count; i++) {
*q++ = *p++;
}
to->length += count;
return;
}
/*
* like strcmp(3), but for GSString255Ptr args.
*/
int
strcmpGSString255 (GSString255Ptr a, GSString255Ptr b)
{
int i, count;
char *p, *q;
count = a->length - b->length;
if (count) return count;
p = a->text;
q = b->text;
for (i=0; i<count; i++, p++, q++) {
if (*p == *q) continue;
else if (*p > *q) return 1;
else return -1;
}
return 0;
}