rsalz@uunet.UU.NET (Rich Salz) (11/30/87)
Submitted-by: John Gilmore <hoptoad!gnu@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: rs@mirror.TMC.COM (Rich Salz) Newsgroups: net.sources Subject: Small shell-style pattern matcher Message-ID: <596@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