Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

v12i070: Public-domain TAR, Part03/03

2 views
Skip to first unread message

Rich Salz

unread,
Nov 29, 1987, 7:00:58 PM11/29/87
to
Submitted-by: John Gilmore <hoptoad!g...@UUNET.UU.NET>
Posting-number: Volume 12, Issue 70
Archive-name: pdtar/part03

: To unbundle, sh this file
echo list.c
cat >list.c <<'@@@ Fin de list.c'
/*
* List a tar archive.
*
* Also includes support routines for reading a tar archive.
*
* Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
*
* @(#)list.c 1.31 11/5/87 Public Domain - gnu
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef MSDOS
#include <sys/file.h>
#endif /* MSDOS */

#ifdef USG
#include <sys/sysmacros.h> /* major() and minor() defined here */
#endif

char *ctime(); /* From libc.a */

#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )

#include "tar.h"
#include "port.h"

long from_oct(); /* Decode octal number */
void demode(); /* Print file mode */

union record *head; /* Points to current archive header */
struct stat hstat; /* Stat struct corresponding */
int head_standard; /* Tape header is in ANSI format */

void print_header();
void skip_file();


/*
* Main loop for reading an archive.
*/
void
read_and(do_something)
void (*do_something)();
{
int status = 3; /* Initial status at start of archive */
int prev_status;

name_gather(); /* Gather all the names */
open_archive(1); /* Open for reading */

for(;;) {
prev_status = status;
status = read_header();
switch (status) {

case 1: /* Valid header */
/* We should decode next field (mode) first... */
/* Ensure incoming names are null terminated. */
head->header.name[NAMSIZ-1] = '\0';

if (!name_match(head->header.name)) {
/* Skip past it in the archive */
userec(head);
/* Skip to the next header on the archive */
skip_file((long)hstat.st_size);
continue;
}

(*do_something)();
continue;

/*
* If the previous header was good, tell them
* that we are skipping bad ones.
*/
case 0: /* Invalid header */
userec(head);
switch (prev_status) {
case 3: /* Error on first record */
annorec(stderr, tar);
fprintf(stderr,
"Hmm, this doesn't look like a tar archive.\n");
/* FALL THRU */
case 2: /* Error after record of zeroes */
case 1: /* Error after header rec */
annorec(stderr, tar);
fprintf(stderr,
"Skipping to next file header...\n");
case 0: /* Error after error */
break;
}
continue;

case 2: /* Record of zeroes */
userec(head);
status = prev_status; /* If error after 0's */
if (f_ignorez)
continue;
/* FALL THRU */
case EOF: /* End of archive */
break;
}
break;
};

close_archive();
names_notfound(); /* Print names not found */
}


/*
* Print a header record, based on tar options.
*/
void
list_archive()
{

/* Save the record */
saverec(&head);

/* Print the header record */
if (f_verbose) {
if (f_verbose > 1)
decode_header(head, &hstat, &head_standard, 0);
print_header(stdout);
}

/* Skip past it in the archive */
saverec((union record **) 0); /* Unsave it */
userec(head);

/* Skip to the next header on the archive */
skip_file((long)hstat.st_size);
}


/*
* Read a record that's supposed to be a header record.
* Return its address in "head", and if it is good, the file's
* size in hstat.st_size.
*
* Return 1 for success, 0 if the checksum is bad, EOF on eof,
* 2 for a record full of zeros (EOF marker).
*
* You must always userec(head) to skip past the header which this
* routine reads.
*/
int
read_header()
{
register int i;
register long sum, recsum;
register char *p;
register union record *header;

header = findrec();
head = header; /* This is our current header */
if (NULL == header) return EOF;

recsum = from_oct(8, header->header.chksum);

sum = 0;
p = header->charptr;
for (i = sizeof(*header); --i >= 0;) {
/*
* We can't use unsigned char here because of old compilers,
* e.g. V7.
*/
sum += 0xFF & *p++;
}

/* Adjust checksum to count the "chksum" field as blanks. */
for (i = sizeof(header->header.chksum); --i >= 0;)
sum -= 0xFF & header->header.chksum[i];
sum += ' '* sizeof header->header.chksum;

if (sum == recsum) {
/*
* Good record. Decode file size and return.
*/
if (header->header.linkflag == LF_LINK)
hstat.st_size = 0; /* Links 0 size on tape */
else
hstat.st_size = from_oct(1+12, header->header.size);
return 1;
}

if (sum == 8*' ') {
/*
* This is a zeroed record...whole record is 0's except
* for the 8 blanks we faked for the checksum field.
*/
return 2;
}

return 0;
}


/*
* Decode things from a file header record into a "struct stat".
* Also set "*stdp" to !=0 or ==0 depending whether header record is "Unix
* Standard" tar format or regular old tar format.
*
* read_header() has already decoded the checksum and length, so we don't.
*
* If wantug != 0, we want the uid/group info decoded from Unix Standard
* tapes (for extraction). If == 0, we are just printing anyway, so save time.
*
* decode_header should NOT be called twice for the same record, since the
* two calls might use different "wantug" values and thus might end up with
* different uid/gid for the two calls. If anybody wants the uid/gid they
* should decode it first, and other callers should decode it without uid/gid
* before calling a routine, e.g. print_header, that assumes decoded data.
*/
decode_header(header, st, stdp, wantug)
register union record *header;
register struct stat *st;
int *stdp;
int wantug;
{

st->st_mode = from_oct(8, header->header.mode);
st->st_mtime = from_oct(1+12, header->header.mtime);

if (0==strcmp(header->header.magic, TMAGIC)) {
/* Unix Standard tar archive */
*stdp = 1;
if (wantug) {
#ifdef NONAMES
st->st_uid = from_oct(8, header->header.uid);
st->st_gid = from_oct(8, header->header.gid);
#else
st->st_uid = finduid(header->header.uname);
st->st_gid = findgid(header->header.gname);
#endif
}
switch (header->header.linkflag)
case LF_BLK: case LF_CHR:
st->st_rdev = makedev(from_oct(8, header->header.devmajor),
from_oct(8, header->header.devminor));
} else {
/* Old fashioned tar archive */
*stdp = 0;
st->st_uid = from_oct(8, header->header.uid);
st->st_gid = from_oct(8, header->header.gid);
st->st_rdev = 0;
}
}


/*
* Quick and dirty octal conversion.
*
* Result is -1 if the field is invalid (all blank, or nonoctal).
*/
long
from_oct(digs, where)
register int digs;
register char *where;
{
register long value;

while (isspace(*where)) { /* Skip spaces */
where++;
if (--digs <= 0)
return -1; /* All blank field */
}
value = 0;
while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
value = (value << 3) | (*where++ - '0');
--digs;
}

if (digs > 0 && *where && !isspace(*where))
return -1; /* Ended on non-space/nul */

return value;
}


/*
* Actually print it.
*
* Plain and fancy file header block logging.
* Non-verbose just prints the name, e.g. for "tar t" or "tar x".
* This should just contain file names, so it can be fed back into tar
* with xargs or the "-T" option. The verbose option can give a bunch
* of info, one line per file. I doubt anybody tries to parse its
* format, or if they do, they shouldn't. Unix tar is pretty random here
* anyway.
*
* Note that print_header uses the globals <head>, <hstat>, and
* <head_standard>, which must be set up in advance. This is not very clean
* and should be cleaned up. FIXME.
*/
#define UGSWIDTH 11 /* min width of User, group, size */
#define DATEWIDTH 19 /* Last mod date */
static int ugswidth = UGSWIDTH; /* Max width encountered so far */

void
print_header(outfile)
FILE *outfile;
{
char modes[11];
char *timestamp;
char uform[11], gform[11]; /* These hold formatted ints */
char *user, *group;
char size[24]; /* Holds a formatted long or maj, min */
long longie; /* To make ctime() call portable */
int pad;

annofile(outfile, (char *)NULL);

if (f_verbose <= 1) {
/* Just the fax, mam. */
fprintf(outfile, "%s\n", head->header.name);
return;
} else {
/* File type and modes */
modes[0] = '?';
switch (head->header.linkflag) {
case LF_NORMAL:
case LF_OLDNORMAL:
case LF_LINK:
modes[0] = '-';
if ('/' == head->header.name[strlen(head->header.name)-1])
modes[0] = 'd';
break;
case LF_DIR: modes[0] = 'd'; break;
case LF_SYMLINK:modes[0] = 'l'; break;
case LF_BLK: modes[0] = 'b'; break;
case LF_CHR: modes[0] = 'c'; break;
case LF_FIFO: modes[0] = 'p'; break;
case LF_CONTIG: modes[0] = 'C'; break;
}

demode((unsigned)hstat.st_mode, modes+1);

/* Timestamp */
longie = hstat.st_mtime;
timestamp = ctime(&longie);
timestamp[16] = '\0';
timestamp[24] = '\0';

/* User and group names */
if (*head->header.uname && head_standard) {
user = head->header.uname;
} else {
user = uform;
(void)sprintf(uform, "%d", (int)hstat.st_uid);
}
if (*head->header.gname && head_standard) {
group = head->header.gname;
} else {
group = gform;
(void)sprintf(gform, "%d", (int)hstat.st_gid);
}

/* Format the file size or major/minor device numbers */
switch (head->header.linkflag) {
case LF_CHR:
case LF_BLK:
(void)sprintf(size, "%d,%d",
major(hstat.st_rdev),
minor(hstat.st_rdev));
break;

default:
(void)sprintf(size, "%ld", (long)hstat.st_size);
}

/* Figure out padding and print the whole line. */
pad = strlen(user) + strlen(group) + strlen(size) + 1;
if (pad > ugswidth) ugswidth = pad;

fprintf(outfile, "%s %s/%s %*s%s %s %s %.*s",
modes,
user,
group,
ugswidth - pad,
"",
size,
timestamp+4, timestamp+20,
sizeof(head->header.name),
head->header.name);

switch (head->header.linkflag) {
case LF_SYMLINK:
fprintf(outfile, " -> %s\n", head->header.linkname);
break;

case LF_LINK:
fprintf(outfile, " link to %s\n", head->header.linkname);
break;

default:
fprintf(outfile, " unknown file type '%c'\n",
head->header.linkflag);
break;

case LF_OLDNORMAL:
case LF_NORMAL:
case LF_CHR:
case LF_BLK:
case LF_DIR:
case LF_FIFO:
case LF_CONTIG:
putc('\n', outfile);
break;
}
}
}

/*
* Print a similar line when we make a directory automatically.
*/
void
pr_mkdir(pathname, length, mode, outfile)
char *pathname;
int length;
int mode;
FILE *outfile;
{
char modes[11];

if (f_verbose > 1) {
/* File type and modes */
modes[0] = 'd';
demode((unsigned)mode, modes+1);

annofile(outfile, (char *)NULL);
fprintf(outfile, "%s %*s %.*s\n",
modes,
ugswidth+DATEWIDTH,
"Creating directory:",
length,
pathname);
}
}


/*
* Skip over <size> bytes of data in records in the archive.
*/
void
skip_file(size)
register long size;
{
union record *x;

while (size > 0) {
x = findrec();
if (x == NULL) { /* Check it... */
annorec(stderr, tar);
fprintf(stderr, "Unexpected EOF on archive file\n");
exit(EX_BADARCH);
}
userec(x);
size -= RECORDSIZE;
}
}


/*
* Decode the mode string from a stat entry into a 9-char string and a null.
*/
void
demode(mode, string)
register unsigned mode;
register char *string;
{
register unsigned mask;
register char *rwx = "rwxrwxrwx";

for (mask = 0400; mask != 0; mask >>= 1) {
if (mode & mask)
*string++ = *rwx++;
else {
*string++ = '-';
rwx++;
}
}

if (mode & S_ISUID)
if (string[-7] == 'x')
string[-7] = 's';
else
string[-7] = 'S';
if (mode & S_ISGID)
if (string[-4] == 'x')
string[-4] = 's';
else
string[-4] = 'S';
if (mode & S_ISVTX)
if (string[-1] == 'x')
string[-1] = 't';
else
string[-1] = 'T';
*string = '\0';
}
@@@ Fin de list.c
echo names.c
cat >names.c <<'@@@ Fin de names.c'
/*
* Look up user and/or group names.
*
* This file should be modified for non-unix systems to do something
* reasonable.
*
* @(#)names.c 1.3 10/30/87 Public Domain - gnu
*/
#include <sys/types.h>
#include "tar.h"

extern char *strncpy();

#ifndef NONAMES
/* Whole module goes away if NONAMES defined. Otherwise... */
#include <pwd.h>
#include <grp.h>

static int saveuid = -993;
static char saveuname[TUNMLEN];
static int my_uid = -993;

static int savegid = -993;
static char savegname[TGNMLEN];
static int my_gid = -993;

#define myuid ( my_uid < 0? (my_uid = getuid()): my_uid )
#define mygid ( my_gid < 0? (my_gid = getgid()): my_gid )

/*
* Look up a user or group name from a uid/gid, maintaining a cache.
* FIXME, for now it's a one-entry cache.
* FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
*
* This is ifdef'd because on Suns, it drags in about 38K of "yellow
* pages" code, roughly doubling the program size. Thanks guys.
*/
void
finduname(uname, uid)
char uname[TUNMLEN];
int uid;
{
struct passwd *pw;
extern struct passwd *getpwuid ();

if (uid != saveuid) {
saveuid = uid;
saveuname[0] = '\0';
pw = getpwuid(uid);
if (pw)
strncpy(saveuname, pw->pw_name, TUNMLEN);
}
strncpy(uname, saveuname, TUNMLEN);
}

int
finduid(uname)
char uname[TUNMLEN];
{
struct passwd *pw;
extern struct passwd *getpwnam();

if (uname[0] != saveuname[0] /* Quick test w/o proc call */
|| 0!=strncmp(uname, saveuname, TUNMLEN)) {
strncpy(saveuname, uname, TUNMLEN);
pw = getpwnam(uname);
if (pw) {
saveuid = pw->pw_uid;
} else {
saveuid = myuid;
}
}
return saveuid;
}


void
findgname(gname, gid)
char gname[TGNMLEN];
int gid;
{
struct group *gr;
extern struct group *getgrgid ();

if (gid != savegid) {
savegid = gid;
savegname[0] = '\0';
(void)setgrent();
gr = getgrgid(gid);
if (gr)
strncpy(savegname, gr->gr_name, TGNMLEN);
}
(void) strncpy(gname, savegname, TGNMLEN);
}


int
findgid(gname)
char gname[TUNMLEN];
{
struct group *gr;
extern struct group *getgrnam();

if (gname[0] != savegname[0] /* Quick test w/o proc call */
|| 0!=strncmp(gname, savegname, TUNMLEN)) {
strncpy(savegname, gname, TUNMLEN);
gr = getgrnam(gname);
if (gr) {
savegid = gr->gr_gid;
} else {
savegid = mygid;
}
}
return savegid;
}
#endif
@@@ Fin de names.c
echo diffarch.c
cat >diffarch.c <<'@@@ Fin de diffarch.c'
/*
* Diff files from a tar archive.
*
* Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#) diffarch.c 1.10 87/11/11 Public Domain - gnu
*/

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef BSD42
#include <sys/file.h>
#endif

#ifdef USG
#include <fcntl.h>
#endif

/* Some systems don't have these #define's -- we fake it here. */
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#ifndef O_NDELAY
#define O_NDELAY 0
#endif

extern int errno; /* From libc.a */
extern char *valloc(); /* From libc.a */

#include "tar.h"
#include "port.h"

extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */

extern void print_header();
extern void skip_file();

char *filedata; /* Pointer to area for reading
file contents into */

/*
* Initialize for a diff operation
*/
diff_init()
{

/*NOSTRICT*/
filedata = (char *) valloc((unsigned)blocksize);
if (!filedata) {
fprintf(stderr,
"tar: could not allocate memory for diff buffer of %d bytes\n",
blocking);
exit(EX_ARGSBAD);
}
}

/*
* Diff a file against the archive.
*/
void
diff_archive()
{
register char *data;
int fd, check, namelen, written;
int err, firsttime;
long size;
struct stat filestat;
char linkbuf[NAMSIZ+3];

errno = EPIPE; /* FIXME, remove perrors */

saverec(&head); /* Make sure it sticks around */
userec(head); /* And go past it in the archive */
decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */

/* Print the record from 'head' and 'hstat' */
if (f_verbose)
print_header(stdout);

switch (head->header.linkflag) {

default:
annofile(stderr, tar);
fprintf(stderr,
"Unknown file type '%c' for %s, diffed as normal file\n",
head->header.linkflag, head->header.name);
/* FALL THRU */

case LF_OLDNORMAL:
case LF_NORMAL:
case LF_CONTIG:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen(head->header.name)-1;
if (head->header.name[namelen] == '/')
goto really_dir;

fd = open(head->header.name, O_NDELAY|O_RDONLY);

if (fd < 0) {
if (errno == ENOENT) {
/* Expected error -- to stdout */
annofile(stdout, (char *)NULL);
fprintf(stdout, "%s: does not exist\n",
head->header.name);
} else {
annofile(stderr, (char *)NULL);
perror(head->header.name);
}
skip_file((long)hstat.st_size);
goto quit;
}

err = fstat(fd, &filestat);
if (err < 0) {
annofile(stdout, (char *)NULL);
fprintf(stdout, "Cannot fstat file ");
perror(head->header.name);
skip_file((long)hstat.st_size);
goto qclose;
}

if ((filestat.st_mode & S_IFMT) != S_IFREG) {
annofile(stdout, (char *)NULL);
fprintf(stdout, "%s: not a regular file\n",
head->header.name);
skip_file((long)hstat.st_size);
goto qclose;
}

filestat.st_mode &= ~S_IFMT;
if (filestat.st_mode != hstat.st_mode)
sigh("mode");
if (filestat.st_uid != hstat.st_uid)
sigh("uid");
if (filestat.st_gid != hstat.st_gid)
sigh("gid");
if (filestat.st_size != hstat.st_size) {
sigh("size");
skip_file((long)hstat.st_size);
goto qclose;
}
if (filestat.st_mtime != hstat.st_mtime)
sigh("mod time");

firsttime = 0;

for (size = hstat.st_size;
size > 0;
size -= written) {
/*
* Locate data, determine max length
* writeable, write it, record that
* we have used the data, then check
* if the write worked.
*/
data = findrec()->charptr;
if (data == NULL) { /* Check it... */
annorec(stderr, tar);
fprintf(stderr, "Unexpected EOF on archive file\n");
break;
}
written = endofrecs()->charptr - data;
if (written > size) written = size;
errno = 0;
check = read (fd, filedata, written);
/*
* The following is in violation of strict
* typing, since the arg to userec
* should be a struct rec *. FIXME.
*/
userec(data + written - 1);
if (check == written) {
/* The read worked, now compare the data */
if (bcmp(data, filedata, check) == 0)
continue; /* It compares */
if (firsttime++) {
annofile(stdout, (char *)NULL);
fprintf(stdout, "%s: data differs\n",
head->header.name);
}
}

/*
* Error in reading from file.
* Print it, skip to next file in archive.
*/
annofile(stderr, tar);
fprintf(stderr,
"Tried to read %d bytes from file, could only read %d:\n",
written, check);
perror(head->header.name);
skip_file((long)(size - written));
break; /* Still do the close, mod time, chmod, etc */
}

qclose:
check = close(fd);
if (check < 0) {
annofile(stderr, tar);
fprintf(stderr, "Error while closing ");
perror(head->header.name);
}

quit:
break;

case LF_LINK:
check = 1; /* FIXME deal with this */
/* check = link (head->header.linkname,
head->header.name); */
/* FIXME, don't worry uid, gid, etc... */
if (check == 0)
break;
annofile(stderr, tar);
fprintf(stderr, "Could not link %s to ",
head->header.name);
perror(head->header.linkname);
break;

#ifdef S_IFLNK
case LF_SYMLINK:
check = readlink(head->header.name, linkbuf,
(sizeof linkbuf)-1);

if (check < 0) {
if (errno == ENOENT) {
annofile(stdout, (char *)NULL);
fprintf(stdout,
"%s: no such file or directory\n",
head->header.name);
} else {
annofile(stderr, tar);
fprintf(stderr, "Could not read link");
perror(head->header.name);
}
break;
}

linkbuf[check] = '\0'; /* Null-terminate it */
if (strncmp(head->header.linkname, linkbuf, check) != 0) {
annofile(stdout, (char *)NULL);
fprintf(stdout, "%s: symlink differs\n",
head->header.linkname);
}
break;
#endif

case LF_CHR:
hstat.st_mode |= S_IFCHR;
goto make_node;

#ifdef S_IFBLK
/* If local system doesn't support block devices, use default case */
case LF_BLK:
hstat.st_mode |= S_IFBLK;
goto make_node;
#endif

#ifdef S_IFIFO
/* If local system doesn't support FIFOs, use default case */
case LF_FIFO:
hstat.st_mode |= S_IFIFO;
hstat.st_rdev = 0; /* FIXME, do we need this? */
goto make_node;
#endif

make_node:
/* FIXME, deal with umask */

check = 1; /* FIXME, implement this */
/* check = mknod(head->header.name, (int) hstat.st_mode,
(int) hstat.st_rdev); */
if (check != 0) {
annofile(stderr, tar);
fprintf(stderr, "Could not make ");
perror(head->header.name);
break;
};
break;

case LF_DIR:
/* Check for trailing / */
namelen = strlen(head->header.name)-1;
really_dir:
while (namelen && head->header.name[namelen] == '/')
head->header.name[namelen--] = '\0'; /* Zap / */

check = 1; /* FIXME, implement this */
/* check = mkdir(head->header.name, 0300 | (int)hstat.st_mode); */
if (check != 0) {
annofile(stderr, tar);
fprintf(stderr, "Could not make directory ");
perror(head->header.name);
break;
}

break;

}

/* We don't need to save it any longer. */
saverec((union record **) 0); /* Unsave it */
}

/*
* Sigh about something that differs.
*/
sigh(what)
char *what;
{

annofile(stdout, (char *)NULL);
fprintf(stdout, "%s: %s differs\n",
head->header.name, what);
}
@@@ Fin de diffarch.c
echo port.c
cat >port.c <<'@@@ Fin de port.c'
/*
* @(#)port.c 1.15 87/11/05 Public Domain, by John Gilmore, 1986
*
* These are routines not available in all environments.
*
* I know this introduces an extra level of subroutine calls and is
* slightly slower. Frankly, my dear, I don't give a damn. Let the
* Missed-Em Vee losers suffer a little. This software is proud to
* have been written on a BSD system.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>

#ifdef MSDOS
#include <fcntl.h>
#else
#include <sys/file.h>
#endif

#include "tar.h"

extern char **environ;

#ifndef NULL
#define NULL 0
#endif

/*
* Some people (e.g. V7) don't have a #define for these.
*/
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#endif


#include "port.h"


/*
* Some computers are not so crass as to align themselves into the BSD
* or USG camps. If a system supplies all of the routines we fake here,
* add it to the list in the #ifndefs below and it'll all be skipped.
* Make sure to add a matching #endif at the end of the file!
*
* We are avoiding #if defined() here for the sake of Minix, which comes
* with the severely broken Amsterdam Compiler Kit. Thanks, Andy!
*/
#ifndef mc300
#ifndef mc500
#ifndef mc700


#ifndef BSD42
/*
* lstat() is a stat() which does not follow symbolic links.
* If there are no symbolic links, just use stat().
*/
int
lstat (path, buf)
char *path;
struct stat *buf;
{
extern int stat ();
return (stat (path, buf));
}

/*
* valloc() does a malloc() on a page boundary. On some systems,
* this can make large block I/O more efficient.
*/
char *
valloc (size)
unsigned size;
{
extern char *malloc ();
return (malloc (size));
}

/*
* NMKDIR.C
*
* Written by Robert Rother, Mariah Corporation, August 1985.
*
* I wrote this out of shear disgust with myself because I couldn't
* figure out how to do this in /bin/sh.
*
* If you want it, it's yours. All I ask in return is that if you
* figure out how to do this in a Bourne Shell script you send me
* a copy.
* sdcsvax!rmr or rmr@uscd
*
* Severely hacked over by John Gilmore to make a 4.2BSD compatible
* subroutine. 11Mar86; hoptoad!gnu
*
* Modified by rmtodd@uokmax 6-28-87 -- when making an already existing dir,
* subroutine didn't return EEXIST. It does now.
*/

/*
* Make a directory. Compatible with the mkdir() system call on 4.2BSD.
*/
#ifndef MSDOS
int
mkdir(dpath, dmode)
char *dpath;
int dmode;
{
int cpid, status;
struct stat statbuf;
extern int errno;

if (stat(dpath,&statbuf) == 0) {
errno = EEXIST; /* Stat worked, so it already exists */
return -1;
}

/* If stat fails for a reason other than non-existence, return error */
if (errno != ENOENT) return -1;

switch (cpid = fork()) {

case -1: /* Error in fork() */
return(-1); /* Errno is set already */

case 0: /* Child process */
/*
* Cheap hack to set mode of new directory. Since this
* child process is going away anyway, we zap its umask.
* FIXME, this won't suffice to set SUID, SGID, etc. on this
* directory. Does anybody care?
*/
status = umask(0); /* Get current umask */
status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
execl("/bin/mkdir", "mkdir", dpath, (char *)0);
_exit(-1); /* Can't exec /bin/mkdir */

default: /* Parent process */
while (cpid != wait(&status)) ; /* Wait for kid to finish */
}

if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
errno = EIO; /* We don't know why, but */
return -1; /* /bin/mkdir failed */
}

return 0;
}
#endif /* MSDOS */
#endif

/* This next bit is called "doing OR on Minix cpp", e.g. without defined(). */
#undef WANTSTRING
#ifdef USG
#define WANTSTRING
#endif
#ifdef MSDOS
#define WANTSTRING
#endif

#ifdef WANTSTRING
/*
* Translate V7 style into Sys V style.
*/
#include <string.h>
#include <memory.h>

char *
index (s, c)
char *s;
int c;
{
return (strchr (s, c));
}

void
bcopy (s1, s2, n)
char *s1, *s2;
int n;
{
(void) memcpy (s2, s1, n);
}

void
bzero (s1, n)
char *s1;
int n;
{
(void) memset(s1, 0, n);
}

int
bcmp(s1, s2, n)
char *s1, *s2;
int n;
{
return memcmp(s1, s2, n);
}
#endif

#ifdef MINIX
/* Minix has bcopy but not bzero, and no memset. Thanks, Andy. */
void
bzero (s1, n)
register char *s1;
register int n;
{
while (n--) *s1++ = '\0';
}

/* It also has no bcmp() */
int
bcmp (s1, s2, n)
register char *s1,*s2;
register int n;
{
for ( ; n-- ; ++s1, ++s2) {
if (*s1 != *s2) return *s1 - *s2;
}
return 0;
}

/*
* Groan, Minix doesn't have execlp either!
*
* execlp(file,arg0,arg1...argn,(char *)NULL)
* exec a program, automatically searching for the program through
* all the directories on the PATH.
*
* This version is naive about variable argument lists, it assumes
* a straightforward C calling sequence. If your system has odd stacks
* *and* doesn't have execlp, YOU get to fix it.
*/
int
execlp(filename, arg0)
char *filename, *arg0;
{
extern char *malloc(), *getenv(), *index();
extern int errno;
register char *p, *path;
char **argstart = &arg0;
register char *fnbuffer;
struct stat statbuf;

if ((p = getenv("PATH")) == NULL) {
/* couldn't find path variable -- try to exec given filename */
return execve(filename, argstart, environ);
}

/*
* make a place to build the filename. We malloc larger than we
* need, but we know it will fit in this.
*/
fnbuffer = malloc( strlen(p) + 1 + strlen(filename) );
if (fnbuffer == NULL) {
errno = ENOMEM;
return -1;
}

/*
* try each component of the path to see if the file's there
* and executable.
*/
for (path = p ; path ; path = p) {
/* construct full path name to try */
if ((p = index(path,':')) == NULL) {
strcpy(fnbuffer, path);
} else {
strncpy(fnbuffer, path, p-path);
fnbuffer[p-path] = '\0';
p++; /* Skip : for next time */
}
if (strlen(fnbuffer) != 0)
strcat(fnbuffer,"/");
strcat(fnbuffer,filename);

/* check to see if file is there and is a normal file */
if (stat(fnbuffer, &statbuf) < 0) {
if (errno == ENOENT)
continue; /* file not there,keep on looking */
else
goto fail; /* failed for some reason, return */
}
if ( (statbuf.st_mode & S_IFMT) != S_IFREG) continue;

if (execve(fnbuffer, argstart, environ) < 0
&& errno != ENOENT
&& errno != ENOEXEC) {
/* failed, for some other reason besides "file
* not found" or "not a.out format"
*/
goto fail;
}

/*
* If we got error ENOEXEC, the file is executable but is
* not an object file. Try to execute it as a shell script,
* returning error if we can't execute /bin/sh.
*
* FIXME, this code is broken in several ways. Shell
* scripts should not in general be executed by the user's
* SHELL variable program. On more mature systems, the
* script can specify with #!/bin/whatever. Also, this
* code clobbers argstart[-1] if the exec of the shell
* fails.
*/
if (errno == ENOEXEC) {
char *shell;

/* Try to execute command "sh arg0 arg1 ..." */
if ((shell = getenv("SHELL")) == NULL)
shell = "/bin/sh";
argstart[-1] = shell;
argstart[0] = fnbuffer;
execve(shell, &argstart[-1], environ);
goto fail; /* Exec didn't work */
}

/*
* If we succeeded, the execve() doesn't return, so we
* can only be here is if the file hasn't been found yet.
* Try the next place on the path.
*/
}

/* all attempts failed to locate the file. Give up. */
errno = ENOENT;

fail:
free(fnbuffer);
return -1;
}
#endif /* MINIX */


#ifdef EMUL_OPEN3
#include "open3.h"
/*
* open3 -- routine to emulate the 3-argument open system
* call that is present in most modern Unix systems.
* This version attempts to support all the flag bits except for O_NDELAY
* and O_APPEND, which are silently ignored. The emulation is not as efficient
* as the real thing (at worst, 4 system calls instead of one), but there's
* not much I can do about that.
*
* Written 6/10/87 by rmtodd@uokmax
*
* open3(path, flag, mode)
* Attempts to open the file specified by
* the given pathname. The following flag bits (#defined in tar.h)
* specify options to the routine:
* O_RDONLY file open for read only
* O_WRONLY file open for write only
* O_RDWR file open for both read & write
* (Needless to say, you should only specify one of the above).
* O_CREAT file is created with specified mode if it needs to be.
* O_TRUNC if file exists, it is truncated to 0 bytes
* O_EXCL used with O_CREAT--routine returns error if file exists
* Function returns file descriptor if successful, -1 and errno if not.
*/

/*
* array to give arguments to access for various modes
* FIXME, this table depends on the specific integer values of O_XXX,
* and also contains integers (args to 'access') that should be #define's.
*/
static int modes[] =
{
04, /* O_RDONLY */
02, /* O_WRONLY */
06, /* O_RDWR */
06, /* invalid but we'd better cope -- O_WRONLY+O_RDWR */
};

/* Shut off the automatic emulation of open(), we'll need it. */
#undef open

int
open3(path, flags, mode)
char *path;
int flags, mode;
{
extern int errno;
int exists = 1;
int call_creat = 0;
int fd;
/*
* We actually do the work by calling the open() or creat() system
* call, depending on the flags. Call_creat is true if we will use
* creat(), false if we will use open().
*/

/*
* See if the file exists and is accessible in the requested mode.
*
* Strictly speaking we shouldn't be using access, since access checks
* against real uid, and the open call should check against euid.
* Most cases real uid == euid, so it won't matter. FIXME.
* FIXME, the construction "flags & 3" and the modes table depends
* on the specific integer values of the O_XXX #define's. Foo!
*/
if (access(path,modes[flags & 3]) < 0) {
if (errno == ENOENT) {
/* the file does not exist */
exists = 0;
} else {
/* probably permission violation */
if (flags & O_EXCL) {
/* Oops, the file exists, we didn't want it. */
/* No matter what the error, claim EEXIST. */
errno = EEXIST;
}
return -1;
}
}

/* if we have the O_CREAT bit set, check for O_EXCL */
if (flags & O_CREAT) {
if ((flags & O_EXCL) && exists) {
/* Oops, the file exists and we didn't want it to. */
errno = EEXIST;
return -1;
}
/*
* If the file doesn't exist, be sure to call creat() so that
* it will be created with the proper mode.
*/
if (!exists) call_creat = 1;
} else {
/* If O_CREAT isn't set and the file doesn't exist, error. */
if (!exists) {
errno = ENOENT;
return -1;
}
}

/*
* If the O_TRUNC flag is set and the file exists, we want to call
* creat() anyway, since creat() guarantees that the file will be
* truncated and open()-for-writing doesn't.
* (If the file doesn't exist, we're calling creat() anyway and the
* file will be created with zero length.)
*/
if ((flags & O_TRUNC) && exists) call_creat = 1;
/* actually do the call */
if (call_creat) {
/*
* call creat. May have to close and reopen the file if we
* want O_RDONLY or O_RDWR access -- creat() only gives
* O_WRONLY.
*/
fd = creat(path,mode);
if (fd < 0 || (flags & O_WRONLY)) return fd;
if (close(fd) < 0) return -1;
/* Fall out to reopen the file we've created */
}

/*
* calling old open, we strip most of the new flags just in case.
*/
return open(path, flags & (O_RDONLY|O_WRONLY|O_RDWR|O_BINARY));
}
#endif

#endif /* MASSCOMP mc700 */
#endif /* MASSCOMP mc500 */
#endif /* MASSCOMP mc300 */

#ifdef MSDOS
/* Fake mknod by complaining */
int
mknod(path, mode, dev)
char *path;
unsigned short mode;
dev_t dev;
{
extern int errno;
int fd;

errno = ENXIO; /* No such device or address */
return -1; /* Just give an error */
}

/* Fake links by copying */
int
link(path1, path2)
char *path1;
char *path2;
{
char buf[256];
int ifd, ofd;
int nrbytes;
int nwbytes;

fprintf(stderr, "%s: %s: cannot link to %s, copying instead\n",
tar, path1, path2);
if ((ifd = open(path1, O_RDONLY|O_BINARY)) < 0)
return -1;
if ((ofd = creat(path2, 0666)) < 0)
return -1;
setmode(ofd, O_BINARY);
while ((nrbytes = read(ifd, buf, sizeof(buf))) > 0) {
if ((nwbytes = write(ofd, buf, nrbytes)) != nrbytes) {
nrbytes = -1;
break;
}
}
/* Note use of "|" rather than "||" below: we want to close
* the files even if an error occurs.
*/
if ((nrbytes < 0) | (0 != close(ifd)) | (0 != close(ofd))) {
unlink(path2);
return -1;
}
return 0;
}

/* everyone owns everything on MS-DOS (or is it no one owns anything?) */
int
chown(path, uid, gid)
char *path;
int uid;
int gid;
{
return 0;
}

int
geteuid()
{
return 0;
}
#endif /* MSDOS */
@@@ Fin de port.c
echo wildmat.c
cat >wildmat.c <<'@@@ Fin de wildmat.c'
/*
* @(#)wildmat.c 1.3 87/11/06 Public Domain.
*
From: r...@mirror.TMC.COM (Rich Salz)
Newsgroups: net.sources
Subject: Small shell-style pattern matcher
Message-ID: <5...@mirror.TMC.COM>
Date: 27 Nov 86 00:06:40 GMT

There have been several regular-expression subroutines and one or two
filename-globbing routines in mod.sources. They handle lots of
complicated patterns. This small piece of code handles the *?[]\
wildcard characters the way the standard Unix(tm) shells do, with the
addition that "[^.....]" is an inverse character class -- it matches
any character not in the range ".....". Read the comments for more
info.

For my application, I had first ripped off a copy of the "glob" routine
from within the find(1) source, but that code is bad news: it recurses
on every character in the pattern. I'm putting this replacement in the
public domain. It's small, tight, and iterative. Compile with -DTEST
to get a test driver. After you're convinced it works, install in
whatever way is appropriate for you.

I would like to hear of bugs, but am not interested in additions; if I
were, I'd use the code I mentioned above.
*/
/*
** Do shell-style pattern matching for ?, \, [], and * characters.
** Might not be robust in face of malformed patterns; e.g., "foo[a-"
** could cause a segmentation violation.
**
** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
*/

/*
* Modified 6Nov87 by John Gilmore (hoptoad!gnu) to return a "match"
* if the pattern is immediately followed by a "/", as well as \0.
* This matches what "tar" does for matching whole subdirectories.
*
* The "*" code could be sped up by only recursing one level instead
* of two for each trial pattern, perhaps, and not recursing at all
* if a literal match of the next 2 chars would fail.
*/
#define TRUE 1
#define FALSE 0


static int
Star(s, p)
register char *s;
register char *p;
{
while (wildmat(s, p) == FALSE)
if (*++s == '\0')
return(FALSE);
return(TRUE);
}


int
wildmat(s, p)
register char *s;
register char *p;
{
register int last;
register int matched;
register int reverse;

for ( ; *p; s++, p++)
switch (*p) {
case '\\':
/* Literal match with following character; fall through. */
p++;
default:
if (*s != *p)
return(FALSE);
continue;
case '?':
/* Match anything. */
if (*s == '\0')
return(FALSE);
continue;
case '*':
/* Trailing star matches everything. */
return(*++p ? Star(s, p) : TRUE);
case '[':
/* [^....] means inverse character class. */
if (reverse = p[1] == '^')
p++;
for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p)
/* This next line requires a good C compiler. */
if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
matched = TRUE;
if (matched == reverse)
return(FALSE);
continue;
}

/* For "tar" use, matches that end at a slash also work. --hoptoad!gnu */
return(*s == '\0' || *s == '/');
}


#ifdef TEST
#include <stdio.h>

extern char *gets();


main()
{
char pattern[80];
char text[80];

while (TRUE) {
printf("Enter pattern: ");
if (gets(pattern) == NULL)
break;
while (TRUE) {
printf("Enter text: ");
if (gets(text) == NULL)
exit(0);
if (text[0] == '\0')
/* Blank line; go back and get a new pattern. */
break;
printf(" %d\n", wildmat(text, pattern));
}
}
exit(0);
}
#endif /* TEST */
@@@ Fin de wildmat.c
exit 0e

0 new messages