[comp.os.minix] Rebroadcast of commands/new-files

ast@cs.vu.nl (Andy Tanenbaum) (07/02/87)

An hour before leaving for 3 weeks I decided to make one last check of the
news and found that the commands/new-files apparently got garbled.  Here
it is again.
Andy Tanenbaum
--------------
: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin
echo Extracting \c\p\d\i\r\.\c
sed 's/^X//' > \c\p\d\i\r\.\c << '+ END-OF-FILE '\c\p\d\i\r\.\c
X/* cpdir - copy directory  	 	Author: Erik Baalbergen */
X
X/* Use "cpdir [-v] src dst" to make a copy dst of directory src.
X   Cpdir should behave like the UNIX shell command
X	(cd src; tar cf - .) | (mkdir dst; cd dst; tar xf -)
X   but the linking structure of the tree, and the owner and time
X   information of files are not yet preserved. (See the work-yet-to-be-done list
X   below.)
X   The -v "verbose" flag enables you to see what's going on when running cpdir.
X
X   Work yet to be done:
X  - preserve link structure, times, etc...
X  - 'stat' optimization (stat() is invoked twice for normal files)
X  - link checks, i.e. am I not overwriting a file/directory by itself?
X	* has been solved by letting 'cpdir' create the target directory
X  - handle character and block special files
X	* they're simply not copied
X
X  Please report bugs and suggestions to erikb@cs.vu.nl
X*/
X
X#include "stdio.h"
X
X#define MKDIR1 "/bin/mkdir"
X#define MKDIR2 "/usr/bin/mkdir"
X#ifdef UNIX
X#include <sys/types.h>
X#include <sys/stat.h>
X#else !UNIX
X#include "stat.h"
X#endif
X
X#define BUFSIZE 1024
X#define PLEN	256
X#define DIRSIZ	16
X
Xchar *prog;
Xint vflag = 0;	/* verbose */
Xchar *strcpy();
X
Xmain(argc, argv)
X	char *argv[];
X{
X	int rc = 0;
X	char *p, *s;
X
X	prog = *argv++;
X	if ((p = *argv) && *p == '-') {
X		argv++;
X		argc--;
X		while (*++p) {
X			switch (*p) {
X			case 'v':
X				vflag = 1;
X				break;
X			default:
X				fatal("illegal flag %s", p);
X			}
X		}
X	}
X	if (argc != 3)
X		fatal("use: [-v] source destination");
X	if (ftype(s = *argv++) != S_IFDIR)
X		fatal("%s is not a directory", s);
X	cpdir(s, *argv);
X	exit(0);
X}
X
Xcpdir(s, d)
X	char *s, *d;
X{
X	char spath[PLEN], dpath[PLEN];
X	char ent[DIRSIZ + 1];
X	register char *send = spath, *dend = dpath;
X	int fd, n;
X
X	while (*send++ = *s++) {}
X	send--;
X	while (*dend++ = *d++) {}
X	if ((fd = open(spath, 0)) < 0) {
X		nonfatal("can't read directory %s", spath);
X		return;
X	}
X	*send++ = '/';
X	ent[DIRSIZ] = '\0';
X	mkdir(dpath);
X	dend[-1] = '/';
X	while ((n = read(fd, ent, DIRSIZ)) == DIRSIZ) {
X		if (!((ent[0] == '\0' && ent[1] == '\0')
X		|| (ent[2] == '.') &&
X			(ent[3] == '\0' || (ent[3] == '.' && ent[4] == '\0'))
X		)) {
X			strcpy(send, ent + 2);
X			strcpy(dend, ent + 2);
X			switch (ftype(spath)) {
X			case S_IFDIR:
X				cpdir(spath, dpath);
X				break;
X			case S_IFREG:
X				cp(spath, dpath);
X				break;
X			default:
X				nonfatal("can't copy special file %s", spath);
X			}
X		}
X	}
X	close(fd);
X	if (n)
X		fatal("error in reading directory %s", spath);
X}
X
Xmkdir(s)
X	char *s;
X{
X	int pid, status;
X
X	if (vflag)
X		printf("mkdir %s\n", s);
X	if ((pid = fork()) == 0) {
X		execl(MKDIR1, "mkdir", s, (char *)0);
X		execl(MKDIR2, "mkdir", s, (char *)0);
X		fatal("can't execute %s or %s", MKDIR1, MKDIR2);
X	}
X	if (pid == -1)
X		fatal("can't fork", prog);
X	wait(&status);
X	if (status)
X		fatal("can't create %s", s);
X}
X
Xcp(s, d)
X	char *s, *d;
X{
X	struct stat st;
X	char buf[BUFSIZE];
X	int sfd, dfd, n;
X
X	if (vflag)
X		printf("cp %s %s\n", s, d);
X	if ((sfd = open(s, 0)) < 0)
X		nonfatal("can't read %s", s);
X	else {
X		if (fstat(sfd, &st) < 0)
X			fatal("can't get file status of %s", s);
X		if ((dfd = creat(d, st.st_mode & 0777)) < 0)
X			fatal("can't create %s", d);
X		while ((n = read(sfd, buf, BUFSIZE)) > 0)
X			write(dfd, buf, n);
X		close(sfd);
X		close(dfd);
X		if (n)
X			fatal("error in reading file %s", s);
X	}
X}
X
Xftype(s)
X	char *s;
X{
X	struct stat st;
X
X	if (stat(s, &st) < 0)
X		fatal("can't get file status of %s", s);
X	return st.st_mode & S_IFMT;
X}
X
Xnonfatal(s, a)
X	char *s, *a;
X{
X	fprintf(stderr, "%s: ", prog);
X	fprintf(stderr, s, a);
X	fprintf(stderr, "\n");
X}
X
Xfatal(s, a)
X	char *s, *a;
X{
X	nonfatal(s, a);
X	exit(1);
X}
X
X
+ END-OF-FILE cpdir.c
chmod 'u=rw,g=r,o=r' \c\p\d\i\r\.\c
set `sum \c\p\d\i\r\.\c`
sum=$1
case $sum in
36999)	:;;
*)	echo 'Bad sum in '\c\p\d\i\r\.\c >&2
esac
echo Extracting \d\u\.\c
sed 's/^X//' > \d\u\.\c << '+ END-OF-FILE '\d\u\.\c
X/* du - report on disk usage		Author: Alistair G. Crooks */
X
X/*
X *	du.c		1.1	27/5/87		agc	Joypace Ltd.
X *
X *	Copyright 1987, Joypace Ltd., London UK. All rights reserved.
X *	This code may be freely distributed, provided that this notice
X *	remains attached.
X *
X *	du - a public domain interpretation of du(1).
X *
X */
X
X
X#include "stdio.h"
X
X#include "stat.h"
X#include "blocksize.h"
X
Xchar *prog;				/* program name */
Xchar *optstr = "as";			/* -a and -s arguments */
Xint silent = 0;				/* silent mode */
Xint all = 0;				/* all directory entries mode */
Xchar *startdir = ".";			/* starting from here */
X
X#define	LINELEN 256
X
X#define DIRNAMELEN 14
X#define	LSTAT stat
Xtypedef struct _dirstr {
X  int	inum;
X  char	d_name[DIRNAMELEN];
X} DIR;
XDIR dir;
X#define ino_t unsigned short
X
Xtypedef struct _alstr {
X  int al_dev;
X  ino_t	al_inum;
X} ALREADY;
X
X#define	MAXALREADY	50
XALREADY already[MAXALREADY];
Xint alc = 0;
X
X/*
X *	myindex - stop the scanf bug
X */
Xchar *myindex(s, c)
Xregister char	*s;
Xregister char	c;
X{
X  for (; *s; s++)
X	if (*s == c)
X		return(s);
X  return(NULL);
X}
X
X
X/*
X *	getopt - parse the arguments given.
X *	retrieved from net.sources
X */
Xint	opterr = 1;
Xint	optind = 1;
Xint	optopt;
Xchar	*optarg;
X
X#define BADCH	(int)'?'
X#define EMSG	""
X#define TELL(s)	fputs(*nargv, stderr); fputs(s, stderr);\
X	fputc(optopt, stderr); fputc('\n', stderr);\
X	return(BADCH);
X
Xint getopt(nargc, nargv, ostr)
Xint	nargc;
Xchar	**nargv;
Xchar	*ostr;
X{
X  register char	*oli;
X  static char	*place = EMSG;
X
X  if (!*place) {
X	if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place)
X		return(EOF);
X	if (*place == '-') {
X		++optind;
X		return(EOF);
X	}
X  }
X  if ((optopt = (int)*place++) == (int)':' || !(oli = myindex(ostr, optopt))) {
X	if (!*place)
X		++optind;
X	TELL(": illegal option -- ");
X  }
X  if (*++oli != ':') {
X	optarg = NULL;
X	if (!*place)
X		++optind;
X  } else {
X	if (*place)
X		optarg = place;
X	else if (nargc <= ++optind) {
X		place = EMSG;
X		TELL(": option requires an argument -- ");
X	} else
X		optarg = nargv[optind];
X	place = EMSG;
X	++optind;
X  }
X  return(optopt);
X}
X
X/*
X *	makedname - make the direcory entry from the directory name,
X *	and the file name, placing it in out. If this would overflow,
X *	return 0, otherwise 1.
X */
Xint makedname(d, f, out, outlen)
Xchar	*d;
Xchar	*f;
Xchar	*out;
Xint	outlen;
X{
X  char	*cp;
X
X  if (strlen(d) + strlen(f) + 2 > outlen)
X	return(0);
X  for (cp = out ; *d ; *cp++ = *d++)
X	;
X  if (*(cp-1) != '/')
X	*cp++ = '/';
X  while (*f)
X	*cp++ = *f++;
X  *cp = '\0';
X  return(1);
X}
X
X/*
X *	done - have we encountered (dev, inum) before? Returns 1 for yes,
X *	0 for no, and remembers (dev, inum).
X */
Xint done(dev, inum)
Xint	dev;
Xino_t	inum;
X{
X  register ALREADY	*ap;
X  register int		i;
X  int			ret = 0;
X
X  for (i = 0, ap = already ; i < alc ; ap++, i++)
X	if (ap->al_dev == dev && ap->al_inum == inum) {
X		ret = 1;
X		break;
X	}
X  if (alc < MAXALREADY) {
X	already[alc].al_dev = dev;
X	already[alc++].al_inum = inum;
X  }
X  return(ret);
X}
X
X/*
X *	dodir - process the directory d. Return the long size (in blocks)
X *	of d and its descendants.
X */
Xlong dodir(d)
Xchar	*d;
X{
X  struct stat	s;
X  long		total = 0L;
X  char		dent[LINELEN];
X  int		fd;
X  
X  if ((fd = open(d, 0)) < 0)
X	return(0L);
X  while (read(fd, &dir, sizeof(dir)) > 0) {
X	if (strcmp(dir.d_name, ".") == 0 ||
X	    strcmp(dir.d_name, "..") == 0)
X		continue;
X	if (dir.inum == 0)
X		continue;
X	if (!makedname(d, dir.d_name, dent, sizeof(dent)))
X		continue;
X	if (LSTAT(dent, &s) < 0)
X		continue;
X	if (s.st_nlink > 1 && done(s.st_dev, s.st_ino))
X		continue;
X	if ((s.st_mode & S_IFMT) == S_IFDIR)
X		total += dodir(dent);
X	switch(s.st_mode & S_IFMT) {
X	case S_IFREG:
X	case S_IFDIR:
X		total += (s.st_size + BLOCK_SIZE) / BLOCK_SIZE;
X		break;
X	}
X	if (all && (s.st_mode & S_IFMT) != S_IFDIR)
X		printf("%D\t%s\n", (s.st_size + BLOCK_SIZE) / BLOCK_SIZE, dent);
X  }
X  close(fd);
X  if (!silent)
X	printf("%D\t%s\n", total, d);
X  return(total);
X}
X
X/*
X *	OK, here goes...
X */
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X  long	tot;
X  int	c;
X
X  prog = argv[0];
X  while ((c = getopt(argc, argv, optstr)) != EOF)
X	switch(c) {
X	case 'a' :
X		all = 1;
X		break;
X	case 's' :
X		silent = 1;
X		break;
X	default :
X		fprintf(stderr, "Usage: %s [-a] [-s] [startdir]\n", prog);
X		exit(1);
X	}
X  if (optind < argc)
X	startdir = argv[optind];
X  tot = dodir(startdir);
X  if (silent)
X	printf("%D\t%s\n", tot, startdir);
X  exit(0);
X}
+ END-OF-FILE du.c
chmod 'u=rw,g=r,o=r' \d\u\.\c
set `sum \d\u\.\c`
sum=$1
case $sum in
42103)	:;;
*)	echo 'Bad sum in '\d\u\.\c >&2
esac
echo Extracting \e\x\p\r\.\c
sed 's/^X//' > \e\x\p\r\.\c << '+ END-OF-FILE '\e\x\p\r\.\c
X/* expr(1)  --  author Erik Baalbergen */
X
X/* expr accepts the following grammar:
X	expr ::= primary | primary operator expr ;
X	primary ::= '(' expr ')' | signed-integer ;
X  where the priority of the operators is taken care of.
X  The resulting value is printed on stdout.
X  Note that the ":"-operator is not implemented.
X*/
X
X#define EOI	0
X#define OR	1
X#define AND	2
X#define LT	3
X#define LE	4
X#define EQ	5
X#define NE	6
X#define GE	7
X#define GT	8
X#define PLUS	9
X#define MINUS	10
X#define TIMES	11
X#define DIV	12
X#define MOD	13
X#define COLON	14
X#define LPAREN	15
X#define RPAREN	16
X#define OPERAND	20
X
X#define MAXPRIO	6
X
Xstruct op {
X	char *op_text;
X	short op_num, op_prio;
X} ops[] = {
X	{"|",	OR,	6},
X	{"&",	AND,	5},
X	{"<",	LT,	4},
X	{"<=",	LE,	4},
X	{"=",	EQ,	4},
X	{"!=",	NE,	4},
X	{">=",	GE,	4},
X	{">",	GT,	4},
X	{"+",	PLUS,	3},
X	{"-",	MINUS,	3},
X	{"*",	TIMES,	2},
X	{"/",	DIV,	2},
X	{"%",	MOD,	2},
X	/* {":",	COLON,	1}, */
X	{"(",	LPAREN,	-1},
X	{")",	RPAREN,	-1},
X	{0, 0, 0}
X};
X
Xlong eval(), expr();
Xchar *prog;
Xchar **ip;
Xstruct op *ip_op;
X
Xmain(argc, argv)
X	char *argv[];
X{
X	long res;
X
X	prog = argv[0];
X	ip = &argv[1];
X	res = expr(lex(*ip), MAXPRIO);
X	if (*++ip != 0)
X		syntax();
X	printf("%D\n", res);
X	exit(0);
X}
X
Xlex(s)
X	register char *s;
X{
X	register struct op *op = ops;
X
X	if (s == 0) {
X		ip_op = 0;
X		return EOI;
X	}
X	while (op->op_text) {
X		if (strcmp(s, op->op_text) == 0) {
X			ip_op = op;
X			return op->op_num;
X		}
X		op++;
X	}
X	ip_op = 0;
X	return OPERAND;
X}
X
Xlong
Xnum(s)
X	register char *s;
X{
X	long l = 0;
X	long sign = 1;
X
X	if (*s == '\0')
X		syntax();
X	if (*s == '-') {
X		sign = -1;
X		s++;
X	}
X	while (*s >= '0' && *s <= '9')
X		l = l * 10 + *s++ - '0';
X	if (*s != '\0')
X		syntax();
X	return sign * l;
X}
X
Xsyntax()
X{
X	write(2, prog, strlen(prog));
X	write(2, ": syntax error\n", 15);
X	exit(1);
X}
X
Xlong
Xexpr(n, prio)
X{
X	long res;
X
X	if (n == EOI)
X		syntax();
X	if (n == LPAREN) {
X		if (prio == 0) {
X			res = expr(lex(*++ip), MAXPRIO);
X			if (lex(*++ip) != RPAREN)
X				syntax();
X		}
X		else
X			res = expr(n, prio - 1);
X	}
X	else
X	if (n == OPERAND) {
X		if (prio == 0)
X			return num(*ip);
X		res = expr(n, prio - 1);
X	}
X	else
X		syntax();
X	while ((n = lex(*++ip)) && ip_op && ip_op->op_prio == prio)
X		res = eval(res, n, expr(lex(*++ip), prio - 1));
X	ip--;
X	return res;
X}
X
Xlong
Xeval(l1, op, l2)
X	long l1, l2;
X{
X	switch (op) {
X	case OR:
X		return l1 ? l1 : l2;
X	case AND:
X		return (l1 && l2) ? l1 : 0;
X	case LT:
X		return l1 < l2;
X	case LE:
X		return l1 <= l2;
X	case EQ:
X		return l1 == l2;
X	case NE:
X		return l1 != l2;
X	case GE:
X		return l1 >= l2;
X	case GT:
X		return l1 > l2;
X	case PLUS:
X		return l1 + l2;
X	case MINUS:
X		return l1 - l2;
X	case TIMES:
X		return l1 * l2;
X	case DIV:
X		return l1 / l2;
X	case MOD:
X		return l1 % l2;
X	}
X	fatal();
X}
X
Xfatal()
X{
X	write(2, "fatal\n", 6);
X	exit(1);
X}
+ END-OF-FILE expr.c
chmod 'u=rw,g=r,o=r' \e\x\p\r\.\c
set `sum \e\x\p\r\.\c`
sum=$1
case $sum in
09684)	:;;
*)	echo 'Bad sum in '\e\x\p\r\.\c >&2
esac
echo Extracting \f\i\n\d\.\c
sed 's/^X//' > \f\i\n\d\.\c << '+ END-OF-FILE '\f\i\n\d\.\c
X/* find - look for files satisfying a predicat	Author: Erik Baalbergen */
X
X/*
X   *** Check the switches in the SWITCHES section below. ***
X
X   Differences from UNIX version 7 find(1):
X	* -name: no name allowed; only uid
X		e.g. find all core files: "find . -name core -a -print"
X
X   The "-atime" may not work well on Minix.
X   Please report bugs and suggestions to erikb@cs.vu.nl
X*/
X
X#include "stdio.h"
X
X/*######################## SWITCHES ##############################*/
X#include "stat.h"
X#define SHELL "/usr/bin/sh"
X
X#define PLEN	256	/* maximum path length; overflows are not detected */
X#define DIRSIZ	16	/* size of a directory entry */
X#define MAXARG	256	/* maximum length for an argv */
X#define NPATHS	256	/* maximum number of paths in path-list */
X#define BSIZE  1024	/* bytes per block */
X
X/*######################## DEFINITIONS ##############################*/
X#define SECS_PER_DAY	(24L * 60L * 60L)	/* check your planet */
X
Xstruct exec {
X	int e_cnt;
X	char *e_vec[MAXARG];
X};
X
X#define OP_NAME		1
X#define OP_PERM		2
X#define OP_TYPE		3
X#define OP_LINKS	4
X#define OP_USER		5
X#define OP_GROUP	6
X#define OP_SIZE		7
X#define OP_INUM		8
X#define OP_ATIME	9
X#define OP_MTIME	10
X#define OP_EXEC		11
X#define OP_OK		12
X#define OP_PRINT	13
X#define OP_NEWER	14
X#define OP_AND		15
X#define OP_OR		16
X
Xstruct oper {
X	char *op_str;
X	int op_val;
X} ops[] = {
X	{"name",	OP_NAME},
X	{"perm",	OP_PERM},
X	{"type",	OP_TYPE},
X	{"links",	OP_LINKS},
X	{"user",	OP_USER},
X	{"group",	OP_GROUP},
X	{"size",	OP_SIZE},
X	{"inum",	OP_INUM},
X	{"atime",	OP_ATIME},
X	{"mtime",	OP_MTIME},
X	{"exec",	OP_EXEC},
X	{"ok",		OP_OK},
X	{"print",	OP_PRINT},
X	{"newer",	OP_NEWER},
X	{"a",		OP_AND},
X	{"o",		OP_OR},
X	{0,		0}
X};
X
X#define EOI	-1
X#define NONE	0
X#define LPAR	20
X#define RPAR	21
X#define NOT	22
X
Xchar *prog, *strcpy(), *Malloc(), *find_bin();
X
Xstruct node {
X	int n_type;	/* any OP_ or NOT */
X	union {
X		char *n_str;
X		struct {
X			long n_val;
X			int n_sign;
X		} n_int;
X		struct exec *n_exec;
X		struct {
X			struct node *n_left, *n_right;
X		} n_opnd;
X	} n_info;
X};
Xstruct node *expr();
X
Xchar **ipp;
Xint tty;	/* fd for /dev/tty when using -ok */
Xlong current_time;
X
Xchar *
XMalloc(n)
X{
X	char *malloc(), *m;
X
X	if ((m = malloc(n)) == 0)
X		fatal("out of memory", "");
X	return m;
X}
X
Xchar *
XSalloc(s)
X	char *s;
X{
X	return strcpy(Malloc(strlen(s) + 1), s);
X}
X
Xmain(argc, argv)
X	char *argv[];
X{
X	char *pathlist[NPATHS];
X	int pathcnt = 0;
X	register i;
X	struct node *pred;
X
X	prog = *argv++;
X	while (--argc > 0 && lex(*argv) == NONE)
X		pathlist[pathcnt++] = *argv++;
X	if (pathcnt == 0 || argc == 0)
X		fatal("usage: path-list predicate-list", "");
X	ipp = argv;
X	time(&current_time);
X	pred = expr(lex(*ipp));
X	if (lex(*++ipp) != EOI)
X		fatal("syntax error: garbage at end of predicate", "");
X	for (i = 0; i < pathcnt; i++)
X		find(pathlist[i], pred, "");
X	exit(0);
X}
X
Xfind(path, pred, last)
X	char *path, *last;
X	struct node *pred;
X{
X	char spath[PLEN], ent[DIRSIZ + 1];
X	struct stat st;
X	register char *send = spath;
X	FILE *fp, *fopen();
X
X	if (path[1] == '\0' && *path == '/') {
X		*send++ = '/';
X		*send = '\0';
X	}
X	else
X		while (*send++ = *path++) {}
X	if (stat(spath, &st) == -1)
X		nonfatal("can't get status of ", spath);
X	else {
X		(void) check(spath, &st, pred, last);
X		if ((st.st_mode & S_IFMT) == S_IFDIR) {
X			if ((fp = fopen(spath, "r")) == NULL) {
X				nonfatal("can't read directory ", spath);
X				return;
X			}
X			send[-1] = '/';
X			ent[DIRSIZ] = '\0';
X			while (fread(ent, DIRSIZ, 1, fp) == 1) {
X				if (!((*ent == '\0' && ent[1] == '\0')
X				|| (ent[2] == '.') &&
X					(ent[3] == '\0'
X					|| (ent[3] == '.' && ent[4] == '\0'))
X				)) {
X					strcpy(send, ent + 2);
X					find(spath, pred, ent + 2);
X				}
X			}
X			fclose(fp);
X		}
X	}
X}
X
Xcheck(path, st, n, last)
X	char *path, *last;
X	register struct stat *st;
X	register struct node *n;
X{
X	switch (n->n_type) {
X	case OP_AND:
X		return  check(path, st, n->n_info.n_opnd.n_left, last) &&
X			check(path, st, n->n_info.n_opnd.n_right, last);
X	case OP_OR:
X		return  check(path, st, n->n_info.n_opnd.n_left, last) ||
X			check(path, st, n->n_info.n_opnd.n_right, last);
X	case NOT:
X		return !check(path, st, n->n_info.n_opnd.n_left, last);
X	case OP_NAME:
X		return smatch(last, n->n_info.n_str);
X	case OP_PERM:
X		if (n->n_info.n_int.n_sign < 0)
X			return st->st_mode == (int) n->n_info.n_int.n_val;
X		return (st->st_mode & 0777) == (int) n->n_info.n_int.n_val;
X	case OP_NEWER:
X		return st->st_mtime > n->n_info.n_int.n_val;
X	case OP_TYPE:
X		return (st->st_mode & S_IFMT) == n->n_info.n_int.n_val;
X	case OP_LINKS:
X		return ichk((long)(st->st_nlink), n);
X	case OP_USER:
X		return st->st_uid == n->n_info.n_int.n_val;
X	case OP_GROUP:
X		return st->st_gid == n->n_info.n_int.n_val;
X	case OP_SIZE:
X		return ichk((st->st_size == 0) ? 0L :
X			((st->st_size - 1) / BSIZE + 1), n);
X	case OP_INUM:
X		return ichk((long)(st->st_ino), n);
X	case OP_ATIME:
X		return ichk(st->st_atime, n);
X	case OP_MTIME:
X		return ichk(st->st_mtime, n);
X	case OP_EXEC:
X	case OP_OK:
X		return execute(n->n_type, n->n_info.n_exec, path);
X	case OP_PRINT:
X		prints("%s\n", path);
X		return 1;
X	}
X	fatal("ILLEGAL NODE", "");
X}
X
Xichk(val, n)
X	long val;
X	struct node *n;
X{
X	switch (n->n_info.n_int.n_sign) {
X	case 0:
X		return val == n->n_info.n_int.n_val;
X	case 1:
X		return val > n->n_info.n_int.n_val;
X	case -1:
X		return val < n->n_info.n_int.n_val;
X	}
X	fatal("internal: bad n_sign", "");
X}
X
Xlex(str)
X	char *str;
X{
X	if (str == 0)
X		return EOI;
X	if (*str == '-') {
X		register struct oper *op;
X
X		str++;
X		for (op = ops; op->op_str; op++)
X			if (strcmp(str, op->op_str) == 0)
X				break;
X		return op->op_val;
X	}
X	if (str[1] == 0) {
X		switch(*str) {
X		case '(':
X			return LPAR;
X		case ')':
X			return RPAR;
X		case '!':
X			return NOT;
X		}
X	}
X	return NONE;
X}
X
Xstruct node *
Xnewnode(t)
X{
X	struct node *n = (struct node*) Malloc(sizeof(struct node));
X
X	n->n_type = t;
X	return n;
X}
X
X/*########################### PARSER ###################################*/
X/* grammar:
X	expr : primary | primary OR expr;
X	primary : secondary | secondary AND primary | secondary primary;
X	secondary : NOT secondary | LPAR expr RPAR | simple;
X	simple : -OP args...
X*/
Xstruct node *expr(), *primary(), *secondary(), *simple();
X
Xnumber(str, base, pl, ps)
X	char *str;
X	long *pl;
X	int *ps;
X{
X	int up = '0' + base - 1;
X	long val = 0;
X
X	*ps = ((*str == '-' || *str == '+') ? ((*str++ == '-') ? -1 : 1) : 0);
X	while (*str >= '0' && *str <= up)
X		val = base * val + *str++ - '0';
X	if (*str)
X		fatal("syntax error: illegal numeric value", "");
X	*pl = val;
X}
X
Xstruct node *
Xexpr(t)
X{
X	struct node *nd, *p, *nd2;
X
X	nd = primary(t);
X	if ((t = lex(*++ipp)) == OP_OR) {
X		nd2 = expr(lex(*++ipp));
X		p = newnode(OP_OR);
X		p->n_info.n_opnd.n_left = nd;
X		p->n_info.n_opnd.n_right = nd2;
X		return p;
X	}
X	ipp--;
X	return nd;
X}
X
Xstruct node *
Xprimary(t)
X{
X	struct node *nd, *p, *nd2;
X
X	nd = secondary(t);
X	if ((t = lex(*++ipp)) != OP_AND) {
X		ipp--;
X		if (t == EOI || t == RPAR || t == OP_OR)
X			return nd;
X	}
X	nd2 = primary(lex(*++ipp));
X	p = newnode(OP_AND);
X	p->n_info.n_opnd.n_left = nd;
X	p->n_info.n_opnd.n_right = nd2;
X	return p;
X}
X
Xstruct node *
Xsecondary(t)
X{
X	struct node *n, *p;
X
X	if (t == LPAR) {
X		n = expr(lex(*++ipp));
X		if (lex(*++ipp) != RPAR)
X			fatal("syntax error, ) expected", "");
X		return n;
X	}
X	if (t == NOT) {
X		n = secondary(lex(*++ipp));
X		p = newnode(NOT);
X		p->n_info.n_opnd.n_left = n;
X		return p;
X	}
X	return simple(t);
X}
X
Xcheckarg(arg)
X	char *arg;
X{
X	if (arg == 0)
X		fatal("syntax error, argument expected", "");
X}
X
Xstruct node *
Xsimple(t)
X{
X	struct node *p = newnode(t);
X	struct exec *e;
X	struct stat est;
X	long l;
X
X	switch (t) {
X	case OP_TYPE:
X		checkarg(*++ipp);
X		switch (**ipp) {
X		case 'b':
X			p->n_info.n_int.n_val = S_IFBLK;
X			break;
X		case 'c':
X			p->n_info.n_int.n_val = S_IFCHR;
X			break;
X		case 'd':
X			p->n_info.n_int.n_val = S_IFDIR;
X			break;
X		case 'f':
X			p->n_info.n_int.n_val = S_IFREG;
X			break;
X		default:
X			fatal("-type needs b, c, d or f", "");
X		}
X		break;
X	case OP_LINKS:
X	case OP_USER:
X	case OP_GROUP:
X	case OP_SIZE:
X	case OP_INUM:
X	case OP_PERM:
X		checkarg(*++ipp);
X		number(*ipp, (t == OP_PERM) ? 8 : 10, &(p->n_info.n_int.n_val),
X			&(p->n_info.n_int.n_sign));
X		break;
X	case OP_ATIME:
X	case OP_MTIME:
X		checkarg(*++ipp);
X		number(*ipp, 10, &l, &(p->n_info.n_int.n_sign));
X		p->n_info.n_int.n_val = current_time - l * SECS_PER_DAY;
X		/* more than n days old means less than the absolute time */
X		p->n_info.n_int.n_sign *= -1;
X		break;
X	case OP_EXEC:
X	case OP_OK:
X		checkarg(*++ipp);
X		e = (struct exec *)Malloc(sizeof(struct exec));
X		e->e_cnt = 2;
X		e->e_vec[0] = SHELL;
X		p->n_info.n_exec = e;
X		while (*ipp) {
X			if (**ipp == ';' && (*ipp)[1] == '\0') {
X				e->e_vec[e->e_cnt] = 0;
X				break;
X			}
X			e->e_vec[(e->e_cnt)++] = 
X				(**ipp == '{' && (*ipp)[1] == '}'
X				&& (*ipp)[2] == '\0') ? (char *)(-1) : *ipp;
X			ipp++;
X		}
X		if (*ipp == 0)
X			fatal("-exec/-ok: ; missing", "");
X		if ((e->e_vec[1] = find_bin(e->e_vec[2])) == 0)
X			fatal("can't find program ", e->e_vec[2]);
X		if (t == OP_OK)
X			if ((tty = open("/dev/tty", 2)) < 0)
X				fatal("can't open /dev/tty", "");
X		break;
X	case OP_NEWER:
X		checkarg(*++ipp);
X		if (stat(*ipp, &est) == -1)
X			fatal("-newer: can't get status of ", *ipp);
X		p->n_info.n_int.n_val = est.st_mtime;
X		break;
X	case OP_NAME:
X		checkarg(*++ipp);
X		p->n_info.n_str = *ipp;
X		break;
X	case OP_PRINT:
X		break;
X	default:
X		fatal("syntax error, operator expected", "");
X	}
X	return p;
X}
X
X/*######################## DIAGNOSTICS ##############################*/
X
Xnonfatal(s1, s2)
X	char *s1, *s2;
X{
X	std_err(prog);
X	std_err(": ");
X	std_err(s1);
X	std_err(s2);
X	std_err("\n");
X}
X
Xfatal(s1, s2)
X	char *s1, *s2;
X{
X	nonfatal(s1, s2);
X	exit(1);
X}
X
X/*################### SMATCH #########################*/
X/* Don't try to understand the following one... */
Xsmatch(s, t)	/* shell-like matching */
X	char *s, *t;
X{
X	register n;
X
X	if (*t == '\0')
X		return *s == '\0';
X	if (*t == '*') {
X		++t;
X		do
X			if (smatch(s,t))
X				return 1;
X		while (*s++ != '\0');
X		return 0;
X	}
X	if (*s == '\0') 
X		return 0;
X	if (*t == '\\')
X		return (*s == *++t) ? smatch(++s, ++t) : 0;
X	if (*t == '?')
X		return smatch(++s, ++t);
X	if (*t == '[') {
X		while (*++t != ']') {
X			if (*t == '\\')
X				++t;
X			if (*(t+1) != '-')
X				if (*t == *s) {
X					while (*++t != ']')
X						if (*t == '\\')
X							++t;
X					return smatch(++s, ++t);
X				}
X				else
X					continue;
X			if (*(t+2) == ']')
X				return (*s == *t || *s == '-');
X			n =  (*(t+2) == '\\') ? 3 : 2;
X			if (*s >= *t && *s <= *(t+n)) {
X				while (*++t != ']')
X					if (*t == '\\')
X						++t;
X				return smatch(++s, ++t);
X			}
X			t += n;
X		}
X		return 0;
X	}
X	return  (*s == *t) ? smatch(++s, ++t) : 0;
X}
X
X/*####################### EXECUTE ###########################*/
X/* do -exec or -ok */
X
Xchar *epath = 0;
X
Xchar *
Xgetpath()
X{
X	extern char **environ;
X	register char **e = environ;
X
X	if (epath)	/* retrieve PATH only once */
X		return epath;
X	while (*e) {
X		if (strncmp("PATH=", *e, 5) == 0) {
X			return epath = *e + 5;
X		}
X		e++;
X	}
X	fatal("can't get PATH from environment", "");
X}
X
Xchar *
Xfind_bin(s)
X	char *s;
X{
X	char *f, *l, buf[PLEN];
X	
X	if (*s == '/') /* absolute path name */
X		return (access(s, 1) == 0) ? s : 0;
X	l = f = getpath();
X	for (;;) {
X		if (*l == ':' || *l == 0) {
X			if (l == f) {
X				if (access(s, 1) == 0)
X					return Salloc(s);
X				f++;
X			}
X			else {
X				register char *p = buf, *q = s;
X
X				while (f != l)
X					*p++ = *f++;
X				f++;
X				*p++ = '/';
X				while (*p++ = *q++) {}
X				if (access(buf, 1) == 0)
X					return Salloc(buf);
X			}
X			if (*l == 0)
X				break;
X		}
X		l++;
X	}
X	return 0;
X}
X
Xexecute(op, e, path)
X	struct exec *e;
X	char *path;
X{
X	int s, pid;
X	char *argv[MAXARG];
X	register char **p, **q;
X
X	for (p = e->e_vec, q = argv; *p;) /* replace the {}s */
X		if ((*q++ = *p++) == (char *)-1)
X			q[-1] = path;
X	*q = '\0';
X	if (op == OP_OK) {
X		char answer[10];
X
X		for (p = &argv[2]; *p; p++) {
X			write(tty, *p, strlen(*p));
X			write(tty, " ", 1);
X		}
X		write(tty, "? ", 2);
X		if (read(tty, answer, 10) < 2 || *answer != 'y')
X			return 0;
X	}
X	if ((pid = fork()) == -1)
X		fatal("can't fork", "");
X	if (pid == 0) {
X		register i = 3;
X
X		while (close(i++) == 0) {}		/* wow!!! */
X		execv(argv[1], &argv[2]);	/* binary itself? */
X		execv(argv[0], &argv[1]);	/* shell command? */
X		fatal("exec failure: ", argv[1]);	/* none of them! */
X		exit(127);
X	}
X	return wait(&s) == pid && s == 0;
X}
+ END-OF-FILE find.c
chmod 'u=rw,g=r,o=r' \f\i\n\d\.\c
set `sum \f\i\n\d\.\c`
sum=$1
case $sum in
54429)	:;;
*)	echo 'Bad sum in '\f\i\n\d\.\c >&2
esac
echo Extracting \f\i\x\.\c
sed 's/^X//' > \f\i\x\.\c << '+ END-OF-FILE '\f\i\x\.\c
X/* fix - combine file and diff listing	Author: Erik Baalbergen */
X
X/* Notes:
X   * files old and old.fix are equal after the following commands
X	   diff old new > difflist
X	   fix old difflist > old.fix
X   * the diff output is assumed to be produced by my diff program.
X   * the difflist has the following form:
X	   difflist ::= chunk*
X	   chunk ::= append | delete | change ;
X	   append ::= n1 'a' n2 [',' n3]? '\n' ['> ' line '\n'](n3 - n2 + 1)
X	   delete ::= n1 [',' n2]? 'd' n3 '\n' ['< ' line '\n'](n2 - n1 + 1)
X	   change ::= n1 [',' n2]? 'c' n3 [',' n4]? '\n'
X		      ['< ' line '\n'](n2 - n1 + 1)
X		      '---\n'
X		      ['> ' line '\n'](n4 - n3 + 1)
X	   where
X	   - n[1234] is an unsigned integer
X	   - "[pat](expr)" means "(expr) occurences of pat"
X	   - "[pat]?" means "either pat or nothing"
X   * the information in the diff listing is checked against the file to which
X     it is applied; an error is printed if there is a conflict
X*/
X
X#include <stdio.h>
X
Xextern char *fgets();
Xextern FILE *fopen();
X#define LINELEN	1024
X
Xchar *prog = 0;
X
Xchar *
Xgetline(fp, b)
X	FILE *fp;
X	char *b;
X{
X	if (fgets(b, LINELEN, fp) == NULL)
X		fatal("unexpected eof");
X	return b;
X}
X
X#define copy(str) printf("%s", str)
X
Xmain(argc, argv)
X	char **argv;
X{
X	char cmd, *fl, *fd, obuf[LINELEN], nbuf[LINELEN];
X	int o1, o2, n1, n2, here; 
X	FILE *fpf, *fpd;
X
X	prog = argv[0];
X	if (argc != 3)
X		fatal("use: %s original-file diff-list-file", prog);
X	if ((fpf = fopen(argv[1], "r")) == NULL)
X		fatal("can't read %s", argv[1]);
X	if ((fpd = fopen(argv[2], "r")) == NULL)
X		fatal("can't read %s", argv[2]);
X	here = 0;
X	while (getcommand(fpd, &o1, &o2, &cmd, &n1, &n2)) {
X		while (here < o1 - 1) {
X			here++;
X			copy(getline(fpf, obuf));
X		}
X		switch (cmd) {
X		case 'c':
X		case 'd':
X			if (cmd == 'd' && n1 != n2)
X				fatal("delete count conflict");
X			while (o1 <= o2) {
X				fl = getline(fpf, obuf);
X				here++;
X				fd = getline(fpd, nbuf);
X				if (strncmp(fd, "< ", 2))
X					fatal("illegal delete line");
X				if (strcmp(fl, fd + 2))
X					fatal("delete line conflict");
X				o1++;
X			}
X			if (cmd == 'd')
X				break;
X			if (strcmp(getline(fpd, nbuf), "---\n"))
X				fatal("illegal separator in chunk");
X			/*FALLTHROUGH*/
X		case 'a':
X			if (cmd == 'a') {
X				if (o1 != o2)
X					fatal("append count conflict");
X				copy(getline(fpf, obuf));
X				here++;
X			}
X			while (n1 <= n2) {
X				if (strncmp(getline(fpd, nbuf), "> ", 2))
X					fatal("illegal append line");
X				copy(nbuf + 2);
X				n1++;
X			}
X			break;
X		}
X	}
X	while (fgets(obuf, LINELEN, fpf) != NULL)
X		copy(obuf);
X	exit(0);
X}
X
Xisdigit(c)
X	char c;
X{
X	return c >= '0' && c <= '9';
X}
X
Xchar *
Xrange(s, p1, p2)
X	char *s;
X	int *p1, *p2;
X{
X	register int v1 = 0, v2;
X
X	while (isdigit(*s))
X		v1 = 10 * v1 + *s++ - '0';
X	v2 = v1;
X	if (*s == ',') {
X		s++;
X		v2 = 0;
X		while (isdigit(*s))
X			v2 = 10 * v2 + *s++ - '0';
X	}
X	if (v1 == 0 || v2 == 0 || v1 > v2)
X		fatal("illegal range");
X	*p1 = v1;
X	*p2 = v2;
X	return s;
X}
X
Xgetcommand(fp, o1, o2, pcmd, n1, n2)
X	FILE *fp;
X	int *o1, *o2, *n1, *n2;
X	char *pcmd;
X{
X	char buf[LINELEN];
X	register char *s;
X	char cmd;
X	
X	if ((s = fgets(buf, LINELEN, fp)) == NULL)
X		return 0;
X	s = range(s, o1, o2);
X	if ((cmd = *s++) != 'a' && cmd != 'c' && cmd != 'd')
X		fatal("illegal command");
X	s = range(s, n1, n2);
X	if (*s != '\n' && s[1] != '\0')
X		fatal("extra characters at end of command: %s", s);
X	*pcmd = cmd;
X	return 1;
X}
X
Xfatal(s, a)
X	char *s, *a;
X{
X	fprintf(stderr, "%s: fatal: ", prog);
X	fprintf(stderr, s, a);
X	fprintf(stderr, "\n");
X	exit(1);
X}
X
X
X
X
+ END-OF-FILE fix.c
chmod 'u=rw,g=r,o=r' \f\i\x\.\c
set `sum \f\i\x\.\c`
sum=$1
case $sum in
06970)	:;;
*)	echo 'Bad sum in '\f\i\x\.\c >&2
esac
echo Extracting \m\o\r\e\.\c
sed 's/^X//' > \m\o\r\e\.\c << '+ END-OF-FILE '\m\o\r\e\.\c
X/* more - terminal pager		Author: Brandon S. Allbery  */
X
X/* Pager commands:
X *	<space>	 display next page
X *	<return> scroll up 1 line
X *	q	 quit
X*/
X
X#define reverse()	write(1, "\033z\160", 3)	/* reverse video */
X#define normal()	write(1, "\033z\7", 3)		/* undo reverse() */
X#define clearln()	write(1, "\r\033~0", 4)		/* clear line */
X
X#define LINES		23	/* lines/screen (- 1 to retain last line) */
X#define COLS		80	/* columns/line */
X#define TABSTOP		8	/* tabstop expansion */
X
X#include <sgtty.h>
X#include <signal.h>
X
Xextern int byebye();
Xextern char *index();
X
Xint line = 0;			/* current terminal line */
Xint col = 0;			/* current terminal column */
Xint fd = -1;			/* terminal file descriptor (/dev/tty) */
Xstruct sgttyb ttymode;		/* and the terminal modes */
Xchar ibuf[1024];		/* input buffer */
Xchar obuf[1024];		/* output buffer */
Xint ibl = 0;			/* chars in input buffer */
Xint ibc = 0;			/* position in input buffer */
Xint obc = 0;			/* position in output buffer (== chars in) */
Xint isrewind = 0;		/* flag: ' command -- next input() rewind */
Xint isdone = 0;			/* flag: return EOF next read even if not */
X
Xmain(argc, argv)
Xchar **argv; {
X	char ch;
X	int fd, arg;
X
X	signal(SIGINT, byebye);
X	cbreak();
X	if (argc < 2)
X		while ((ch = input(fd)) != 0)
X			output(ch);
X	else
X		for (arg = 1; argv[arg] != (char *) 0; arg++) {
X			if ((fd = open(argv[arg], 0)) == -1) {
X				write(1, "more: cannot open ", 18);
X				write(1, argv[arg], strlen(argv[arg]));
X				write(1, "\n", 1);
X				nocbreak();
X				exit(1);
X			}
X			while ((ch = input(fd)) != 0)
X				output(ch);
X			close(fd);
X			if (argv[arg + 1] != (char *) 0) {
X				oflush();
X				if (isdone) {	/* 'n' command */
X					reverse();
X					write(1, "*** Skipping to next file ***\n", 30);
X					normal();
X					isdone = 0;
X				}
X				reverse();
X				write(1, "--More-- (Next file: ", 21);
X				write(1, argv[arg + 1], strlen(argv[arg + 1]));
X				write(1, ")", 1);
X				normal();
X				switch (wtch()) {
X				case ' ':
X				case '\'':
X				case 'n':
X				case 'N':
X					line = 0;
X					break;
X				case '\r':
X				case '\n':
X					line = LINES - 1;
X					break;
X				case 'q':
X				case 'Q':
X					clearln();
X					byebye();
X				}
X				clearln();
X			}
X		}
X	oflush();
X	byebye();
X}
X
Xinput(fd) {
X	if (isdone) {
X		ibl = 0;
X		ibc = 0;
X		return 0;
X	}
X	if (isrewind) {
X		lseek(fd, 0L, 0);
X		ibl = 0;
X		ibc = 0;
X		isrewind = 0;
X	}
X	if (ibc == ibl) {
X		ibc = 0;
X		if ((ibl = read(fd, ibuf, sizeof ibuf)) <= 0)
X			return 0;
X	}
X	return ibuf[ibc++];
X}
X
Xoutput(c)
Xchar c; {
X	if (obc == sizeof obuf) {
X		lwrite(1, obuf, sizeof obuf);
X		obc = 0;
X	}
X	if (!isrewind)
X		obuf[obc++] = c;
X}
X
Xoflush() {
X	if (!isdone)
X		lwrite(1, obuf, obc);
X	obc = 0;
X}
X
Xlwrite(fd, buf, len)
Xchar *buf;
Xunsigned len; {
X	unsigned here, start;
X	char cmd;
X
X	start = 0;
X	here = 0;
X	while (here != len) {
X		cmd = '\0';
X		switch (buf[here++]) {
X		case '\n':
X			col = 0;
X			if (++line == LINES) {
X				write(fd, buf + start, here - start);
X				reverse();
X				write(1, "--More--", 8);
X				normal();
X				cmd = wtch();
X				clearln();
X				line = 0;
X				start = here;
X			}
X			break;
X		case '\r':
X			col = 0;
X			break;
X		case '\b':
X			if (col != 0)
X				col--;
X			else {
X				line--;
X				col = COLS - 1;
X			}
X			break;
X		case '\t':
X			do {
X				col++;
X			} while (col % TABSTOP != 0);
X			break;
X		default:
X			if (++col == COLS) {
X				col = 0;
X				if (++line == LINES) {
X					write(fd, buf + start, here - start);
X					reverse();
X					write(1, "--More--", 8);
X					normal();
X					cmd = wtch();
X					clearln();
X					line = 0;
X					start = here;
X				}
X			}
X		}
X		switch (cmd) {
X		case '\0':
X			break;
X		case ' ':
X			line = 0;
X			break;
X		case '\r':
X		case '\n':
X			line = LINES - 1;
X			break;
X		case 'q':
X		case 'Q':
X			byebye();
X		case '\'':
X			isrewind = 1;
X			reverse();
X			write(1, "*** Back ***\n", 13);
X			normal();
X			return;
X		case 'n':
X		case 'N':
X			isdone = 1;
X			return;
X		default:
X			break;
X		}
X	}
X	if (here != start)
X		write(fd, buf + start, here - start);
X}
X
Xwtch() {
X	char ch;
X
X	do {
X		read(fd, &ch, 1);
X	} while (index(" \r\nqQ'nN", ch) == (char *) 0);
X	return ch;
X}
X
Xcbreak() {
X	if (fd != -1)
X		return;
X	if ((fd = open("/dev/tty", 0)) == -1) {
X		write(2, "OOPS -- can't open /dev/tty\n", 28);
X		exit(1);
X	}
X	ioctl(fd, TIOCGETP, &ttymode);
X	ttymode.sg_flags |= CBREAK;
X	ttymode.sg_flags &= ~ECHO;
X	ioctl(fd, TIOCSETP, &ttymode);	/* NB: add TIOCSETN! */
X}
X
Xnocbreak() {
X	if (fd == -1)
X		return;
X	ttymode.sg_flags &= ~CBREAK;
X	ttymode.sg_flags |= ECHO;
X	ioctl(fd, TIOCSETP, &ttymode);
X	close(fd);
X	fd = -1;
X}
X
Xbyebye() {
X	nocbreak();
X	exit(0);
X}
+ END-OF-FILE more.c
chmod 'u=rw,g=r,o=r' \m\o\r\e\.\c
set `sum \m\o\r\e\.\c`
sum=$1
case $sum in
15911)	:;;
*)	echo 'Bad sum in '\m\o\r\e\.\c >&2
esac
echo Extracting \r\e\a\d\f\s\.\c
sed 's/^X//' > \r\e\a\d\f\s\.\c << '+ END-OF-FILE '\r\e\a\d\f\s\.\c
X/* readfs - read a MINIX file system	Author: Paul Polderman */
X
X/*
XCommand: readfs - read and extract a MINIX filesystem.
X
XSyntax:  readfs [-li] block-special [directory]
X
XFlags:	 -l:	extract all directories and files from the filesystem
X		and produce a mkfs-listing on standard output.
X	 -i:	give the listing, but do not extract files.
X
XExamples: readfs /dev/fd1		# extract all files from /dev/fd1.
X	  readfs -i /dev/hd2		# see what's on /dev/hd2.
X	  readfs -l /dev/at0 rootfs	# extract and list the filesystem
X					# of /dev/at0 and put the tree
X					# in the directory `rootfs'.
X
X  Readfs reads a MINIX filesystem and extracts recursively all directories
Xand files, and (optionally) produces a mkfs-listing of them on standard output.
XThe root directory contents are placed in the current directory, unless
Xa directory is given as argument, in which case the contents are put there.
XReadfs tries to restore the attributes (mode/uid/gid/time) of the files
Xextracted to those of the original files.
XSpecial files are created as ordinary files, but the mkfs-listing
Xenables mkfs to restore them to original.
X*/
X
X#include <stdio.h>
X
X#include "fs/const.h"
X#include "h/type.h"
X#include "h/const.h"
X#include "fs/type.h"
X#include "fs/buf.h"
X#include "fs/super.h"
X
X#undef printf		/* Definition used only in the kernel */
X
Xextern file_pos lseek();
X
X/*
X * Compile with -I/user0/ast/minix
X * (i.e. the directory containing the MINIX system sources)
X *
X *	Author: Paul Polderman (polder@cs.vu.nl) April 1987
X */
X
Xchar verbose = 0;	/* give a mkfs-listing of the filesystem */
X			/* and extracts its contents. */
Xchar noaction = 0;	/* just give a mkfs-listing, do not extract files. */
X
Xstruct super_block sb;
Xchar pathname[1024];
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	switch (argc) {
X	case 2:
X		pathname[0] = '\0';
X		readfs(argv[1], pathname);
X		break;
X	case 3:
X		if (argv[1][0] == '-') {
X			get_flags(&argv[1][1]);
X			pathname[0] = '\0';
X			readfs(argv[2], pathname);
X		} else {
X			strcpy(pathname, argv[2]);
X			readfs(argv[1], pathname);
X		}
X		break;
X	case 4: if (argv[1][0] == '-') {
X			get_flags(&argv[1][1]);
X			strcpy(pathname, argv[3]);
X			readfs(argv[2], pathname);
X			break;
X		} /* else fall through .. */
X	default:
X		fprintf(stderr,"Usage: %s [-li] <special> [dirname]\n",argv[0]);
X		exit(1);
X	}
X	exit(0);
X}
X
Xget_flags(flags)
Xregister char *flags;
X{
X	while (*flags) {
X		switch (*flags) {
X		case 'L':
X		case 'l':
X			verbose = 1; break;
X		case 'I':
X		case 'i':
X			noaction = 1; verbose = 1; break;
X		default:
X			fprintf(stderr, "Bad flag: %c\n", *flags);
X			break;
X		}
X		flags++;
X	}
X}
X
Xreadfs(special_file, directory)
Xchar *special_file, *directory;
X/*
X * Readfs: opens the given special file (with MINIX filesystem),
X * and extracts its contents into the given directory.
X */
X{
X	d_inode root_inode;
X	int special, inum;
X	file_pos super_b;
X
X	umask(0);
X
X	/* Open the special file */
X	if ( (special = open(special_file, 0)) < 0) {
X		fprintf(stderr, "cannot open %s\n", special_file);
X		return;
X	}
X
X	/* Read the superblock */
X	super_b = (file_pos) SUPER_BLOCK * (file_pos) BLOCK_SIZE;
X	if (lseek(special, super_b, 0) != super_b) {
X		fprintf(stderr, "cannot seek to superblock\n");
X		return;
X	}
X
X	if (read(special, &sb, sizeof(struct super_block))
X		!= sizeof(struct super_block)) {
X			fprintf(stderr, "cannot read superblock\n");
X			return;
X	}
X
X	/* Is it really a MINIX filesystem ? */
X	if (sb.s_magic != SUPER_MAGIC) {
X		fprintf(stderr, "%s is not a valid MINIX filesystem\n",
X			special_file);
X		return;
X	}
X
X	/* Fetch the inode of the root directory */
X	if (get_inode(special, (inode_nr) ROOT_INODE, &root_inode) < 0) {
X		fprintf(stderr, "cannot get inode of root directory\n");
X		return;
X	}
X
X	/* Print number of zones and inodes */
X	if (verbose)
X		printf("boot\n%d %d\n", sb.s_nzones, sb.s_ninodes);
X
X	/* Extract (recursively) the root directory */
X	dump_dir(special, &root_inode, directory);
X}
X
X/*
X * different type of blocks:	(used in routine get_block for caching)
X */
X
X#define	B_INODE		0	/* Cache #0 is the inode cache */
X#define	B_INDIRECT	1	/* Cache #1 is the (dbl) indirect block cache */
X#define	B_DATA		2	/* No cache for data blocks (only read once) */
X
Xint get_inode(fd, inum, ip)
Xint fd;
Xinode_nr inum;
Xd_inode *ip;
X/*
X * Get inode `inum' from the MINIX filesystem. (Uses the inode-cache)
X */
X{
X	struct buf bp;
X	block_nr block;
X	block_nr ino_block;
X	unshort ino_offset;
X	int r;
X
X	/* Calculate start of i-list */
X	block = SUPER_BLOCK + 1 + sb.s_imap_blocks + sb.s_zmap_blocks;
X
X	/* Calculate block with inode inum */
X	ino_block = ((inum - 1) / INODES_PER_BLOCK);
X	ino_offset = ((inum - 1) % INODES_PER_BLOCK);
X	block += ino_block;
X
X	/* Fetch the block */
X	if (get_block(fd, block, &bp, B_INODE) == 0) {
X		bcopy(&bp.b_inode[ino_offset], ip, sizeof(d_inode));
X		return(0);
X	}
X
X	/* Oeps, foutje .. */
X	fprintf(stderr, "cannot find inode %d\n", inum);
X	return(-1);
X}
X
Xstatic int indent = 0;		/* current indent (used for mkfs-listing) */
X
Xdump_dir(special, ip, directory)
Xint special;
Xd_inode *ip;
Xchar *directory;
X/*
X * Make the given directory (if non-NULL),
X * and recursively extract its contents.
X */
X{
X	register dir_struct *dp;
X	register int n_entries;
X	register char *name;
X	block_nr b = 0;
X	d_inode dip;
X	struct buf bp;
X
X	if (verbose) {
X		show_info(directory, ip, "");
X		indent++;
X	}
X
X	if (!noaction && *directory) {
X		/* Try to make the directory if not already there */
X		if (mkdir(directory) != 0 || chdir(directory) < 0) {
X			fprintf(stderr,"mkdir %s failed\n", directory);
X			return;
X		}
X	}
X	for (name = directory; *name; name++)	/* Find end of pathname */
X		;
X	*name++ = '/';		/* Add trailing slash */
X
X	n_entries = (int) (ip->i_size / (file_pos) sizeof(dir_struct));
X	while (n_entries > 0) {
X
X		/* Read next block of the directory */
X		if (get_fileblock(special, ip, b, &bp) < 0)
X			return(-1);
X		dp = &bp.b_dir[0];
X		if (b++ == (block_nr) 0) {
X			dp += 2;	/* Skip "." and ".." */
X			n_entries -= 2;
X		}
X
X		/* Extract the files/directories listed in the block */
X		while (n_entries-- > 0 && dp < &bp.b_dir[NR_DIR_ENTRIES]) {
X			if (dp->d_inum != (inode_nr) 0) {
X				if (get_inode(special, dp->d_inum, &dip) < 0) {
X					/* Bad luck */
X					dp++;
X					continue;
X				}
X				/* Add new pathname-component to `pathname'. */
X				strncpy(name, dp->d_name, NAME_SIZE);
X				name[NAME_SIZE] = '\0';
X
X				/* Call the right routine */
X				if ((dip.i_mode & I_TYPE) == I_DIRECTORY)
X					dump_dir(special, &dip, name);
X				else
X					dump_file(special, &dip, name);
X			}
X			dp++;	/* Next entry, please. */
X		}
X	}
X	*--name = '\0';		/* Restore `pathname' to what it was. */
X	if (!noaction && *directory) {
X		chdir("..");		/* Go back up. */
X		restore(directory, ip);	/* Restore mode/owner/accesstime */
X	}
X
X	if (verbose) {
X		do_indent(--indent);	/* Let mkfs know we are done */
X		printf("$\n");		/* with this directory. */
X	}
X}
X
Xdump_file(special, ip, filename)
Xint special;
Xd_inode *ip;
Xchar *filename;
X/*
X * Extract given filename from the MINIX-filesystem,
X * and store it on the local filesystem.
X */
X{
X	dir_struct *dp;
X	int file;
X	block_nr b = 0;
X	struct buf bp;
X	file_pos size;
X
X	if (verbose)
X		show_info(filename, ip, pathname);
X
X	if (noaction) return(0);
X
X	if (access(filename, 0) == 0) {
X		/* Should not happen, but just in case .. */
X		fprintf(stderr, "Will not create %s: file exists\n", filename);
X		return(-1);
X	}
X	if ( (file = creat(filename, (ip->i_mode & ALL_MODES) )) < 0) {
X		fprintf(stderr, "cannot create %s\n", filename);
X		return(-1);
X	}
X	/* Don't try to extract /dev/hd0 */
X	if ((ip->i_mode & I_TYPE) == I_REGULAR) {
X		size = ip->i_size;
X		while (size > (file_pos) 0) {
X			/* Get next block of file */
X			if (get_fileblock(special, ip, b++, &bp) < 0) {
X				close(file);
X				return(-1);
X			}
X
X			/* Write it to the file */
X			if (size > (file_pos) BLOCK_SIZE)
X				write(file, bp.b_data, BLOCK_SIZE);
X			else
X				write(file, bp.b_data, (int) size);
X
X			size -= (file_pos) BLOCK_SIZE;
X		}
X	}
X	close(file);
X	restore(filename, ip);	/* Restore mode/owner/filetimes */
X	return(0);
X}
X
X#define	zone_shift	(sb.s_log_zone_size)	/* zone to block ratio */
X
Xint get_fileblock(special, ip, b, bp)
Xint special;
Xd_inode *ip;
Xblock_nr b;
Xstruct buf *bp;
X/*
X * Read the `b'-th block from the file whose inode is `ip'.
X */
X{
X	zone_nr zone, ind_zone;
X	block_nr z, zone_index;
X	int r;
X
X	/* Calculate zone in which the datablock number is contained */
X	zone = (zone_nr) (b >> zone_shift);
X
X	/* Calculate index of the block number in the zone */
X	zone_index = b - ((block_nr) zone << zone_shift);
X
X	/* Go get the zone */
X	if (zone < (zone_nr) NR_DZONE_NUM) {	/* direct block */
X		zone = ip->i_zone[zone];
X		z = ((block_nr) zone << zone_shift) + zone_index;
X		r = get_block(special, z, bp, B_DATA);
X		return(r);
X	}
X
X	/* The zone is not a direct one */
X	zone -= (zone_nr) NR_DZONE_NUM;
X
X	/* Is it single indirect ? */
X	if (zone < (zone_nr) NR_INDIRECTS) {	/* single indirect block */
X		ind_zone = ip->i_zone[NR_DZONE_NUM];
X	} else {				/* double indirect block */
X		/* Fetch the double indirect block */
X		ind_zone = ip->i_zone[NR_DZONE_NUM + 1];
X		r = get_block(special, (block_nr) ind_zone, bp, B_INDIRECT);
X		if (r < 0) return(r);
X
X		/* Extract the indirect zone number from it */
X		zone -= (zone_nr) NR_INDIRECTS;
X		ind_zone = bp->b_ind[zone / (zone_nr) NR_INDIRECTS];
X		zone %= (zone_nr) NR_INDIRECTS;
X	}
X	/* Extract the datablock number from the indirect zone */
X	r = get_block(special, (block_nr) ind_zone, bp, B_INDIRECT);
X	if (r < 0) return(r);
X	zone = bp->b_ind[zone];
X
X	/* Calculate datablock number to be fetched */
X	z = ((block_nr) zone << zone_shift) + zone_index;
X	r = get_block(special, z, bp, B_DATA);
X	return(r);
X}
X
X/*
X * The following routines simulate a LRU block cache.
X *
X * Definition of a cache block:
X */
X
Xstruct cache_block {
X	block_nr b_block;		/* block number of block */
X	long b_access;			/* counter value of last access */
X	char b_buf[BLOCK_SIZE];		/* buffer for block */
X};
X
X#define	NR_CACHES	2	/* total number of caches */
X#define	NR_CBLOCKS	5	/* number of blocks in a cache */
X
Xstatic struct cache_block cache[NR_CACHES][NR_CBLOCKS];
Xstatic long counter = 0L;	/* Counter used as a sense of time. */
X				/* Incremented after each cache operation. */
X
Xget_block(fd, block, bp, type)
Xint fd;
Xblock_nr block;
Xstruct buf *bp;
Xint type;
X/*
X * Get the requested block from the device with filedescriptor fd.
X * If it is in the cache, no (floppy-) disk access is needed,
X * if not, allocate a cache block and read the block into it.
X */
X{
X	register int i;
X	register struct cache_block *cache_p, *cp;
X
X	if (block == (block_nr) NO_ZONE) {
X		/* Should never happen in a good filesystem. */
X		fprintf(stderr, "get_block: NO_ZONE requested !\n");
X		return(-1);
X	}
X	if (type < 0 || type >= NR_CACHES)	/* No cache for this type */
X		return( get_rawblock(fd, block, bp) );
X
X	cache_p = cache[type];
X	cp = (struct cache_block *) 0;
X
X	/* First find out if block requested is in the cache */
X	for (i = 0; i < NR_CBLOCKS; i++) {
X		if (cache_p[i].b_block == block) {	/* found right block */
X			cp = &cache_p[i];
X			break;
X		}
X	}
X
X	if (cp == (struct cache_block *) 0) {	/* block is not in cache */
X		cp = cache_p;			/* go find oldest buffer */
X		for (i = 0; i < NR_CBLOCKS; i++) {
X			if (cache_p[i].b_access < cp->b_access)
X				cp = &cache_p[i];
X		}
X		/* Fill the buffer with the right block */
X		if (get_rawblock(fd, block, cp->b_buf) < 0)
X			return(-1);
X	}
X
X	/* Update/store last access counter */
X	cp->b_access = ++counter;
X	cp->b_block = block;
X	bcopy(cp->b_buf, bp, BLOCK_SIZE);
X	return(0);
X}
X
Xint get_rawblock(special, blockno, bufp)
Xint special;
Xblock_nr blockno;
Xchar *bufp;
X/*
X * Read a block from the disk.
X */
X{
X	int r;
X	file_pos pos;
X
X	/* Calculate the position of the block on the disk */
X	pos = (file_pos) blockno * (file_pos) BLOCK_SIZE;
X
X	/* Read the block from the disk */
X	if (lseek(special, pos, 0) == pos
X		&& read(special, bufp, BLOCK_SIZE) == BLOCK_SIZE)
X			return(0);
X
X	/* Should never get here .. */
X	fprintf(stderr, "read block %d failed\n", blockno);
X	return(-1);
X}
X
Xrestore(name, ip)
Xchar *name;
Xd_inode *ip;
X/*
X * Restores given file's attributes.
X * `ip' contains the attributes of the file on the MINIX filesystem,
X * `name' is the filename of the extracted file on the local filesystem.
X */
X{
X	long ttime[2];
X
X	chown(name, ip->i_uid, ip->i_gid);	/* Fails if not superuser */
X	chmod(name, (ip->i_mode & ALL_MODES));
X	ttime[0] = ttime[1] = ip->i_modtime;
X	utime(name, ttime);
X}
X
X/*
X * characters to use as prefix to `mkfs' mode field
X */
X
Xstatic char special_chars[] = {
X	'-',		/* I_REGULAR */
X	'c',		/* I_CHAR_SPECIAL */
X	'd',		/* I_DIRECTORY */
X	'b'		/* I_BLOCK_SPECIAL */
X};
X
Xshow_info(name, ip, path)
Xchar *name;
Xd_inode *ip;
Xchar *path;
X/*
X * Show information about the given file/dir in `mkfs'-format
X */
X{
X	char c1, c2, c3;
X
X	c1 = special_chars[(ip->i_mode >> 13) & 03];
X	c2 = ((ip->i_mode & ALL_MODES & ~RWX_MODES)==I_SET_UID_BIT) ? 'u' : '-';
X	c3 = ((ip->i_mode & ALL_MODES & ~RWX_MODES)==I_SET_GID_BIT) ? 'g' : '-';
X
X	if (*name) {
X		do_indent(indent);
X		printf("%-14s ", name);
X	}
X
X	printf("%c%c%c%03o %d %d", c1, c2, c3,
X		(ip->i_mode & RWX_MODES), ip->i_uid, ip->i_gid);
X
X	switch (ip->i_mode & I_TYPE) {
X		case I_DIRECTORY:
X			break;
X		case I_CHAR_SPECIAL:	/* Print major and minor dev numbers */
X			printf(" %d %d", (ip->i_zone[0] >> MAJOR) & 0377,
X				(ip->i_zone[0] >> MINOR) & 0377);
X			break;
X		case I_BLOCK_SPECIAL:	/* Print major and minor dev numbers */
X			printf(" %d %d", (ip->i_zone[0] >> MAJOR) & 0377,
X				(ip->i_zone[0] >> MINOR) & 0377);
X			/* Also print the number of blocks on the device */
X			printf(" %D", (ip->i_size / (file_pos) BLOCK_SIZE));
X			break;
X		default:		/* Just print the pathname */
X			printf(" %s", path);
X			break;
X	}
X	putchar('\n');
X}
X
X#define	INDENT_SIZE	4
X
Xdo_indent(i)
Xint i;
X{
X	i *= INDENT_SIZE;
X	while (i-- > 0)
X		putchar(' ');
X}
X
Xint mkdir(directory)
Xint directory;
X/*
X * Make a directory, return exit status.
X * This routine is not necessary on systems that
X * have a system call to make directories.
X */
X{
X	int pid, status;
X
X	if ( (pid = fork()) == 0) {
X		execl("/bin/mkdir", "mkdir", directory, 0);
X		execl("/usr/bin/mkdir", "mkdir", directory, 0);
X		exit(1);
X	} else if (pid < 0)
X		return(-1);
X	while (wait(&status) != pid)
X		;
X	return(status);
X}
X
Xbcopy(src, dest, bytes)
Xregister char *src, *dest;
Xregister int bytes;
X/*
X * Copy bytes from src to dest.
X * May also be in the standard libc.a on some systems.
X */
X{
X	while (bytes--)
X		*dest++ = *src++;
X}
+ END-OF-FILE readfs.c
chmod 'u=rw,g=r,o=r' \r\e\a\d\f\s\.\c
set `sum \r\e\a\d\f\s\.\c`
sum=$1
case $sum in
53130)	:;;
*)	echo 'Bad sum in '\r\e\a\d\f\s\.\c >&2
esac
echo Extracting \t\e\s\t\.\c
sed 's/^X//' > \t\e\s\t\.\c << '+ END-OF-FILE '\t\e\s\t\.\c
X/* test(1); version 7-like  --  author Erik Baalbergen */
X#include <stat.h>
X#include <sgtty.h>
X
X/* test(1) accepts the following grammar:
X	expr	::= bexpr | bexpr "-o" expr ;
X	bexpr	::= primary | primary "-a" bexpr ;
X	primary	::= unary-operator operand
X		| operand binary-operator operand
X		| operand
X		| "(" expr ")"
X		| "!" expr
X		;
X	unary-operator ::= "-r"|"-w"|"-f"|"-d"|"-s"|"-t"|"-z"|"-n";
X	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt";
X	operand ::= <any legal UNIX file name>
X*/
X
X#define EOI	0
X#define FILRD	1
X#define FILWR	2
X#define FILND	3
X#define FILID	4
X#define FILGZ	5
X#define FILTT	6
X#define STZER	7
X#define STNZE	8
X#define STEQL	9
X#define STNEQ	10
X#define INTEQ	11
X#define INTNE	12
X#define INTGE	13
X#define INTGT	14
X#define INTLE	15
X#define INTLT	16
X#define UNEGN	17
X#define BAND	18
X#define BOR	19
X#define LPAREN	20
X#define RPAREN	21
X#define OPERAND	22
X
X#define UNOP	1
X#define BINOP	2
X#define BUNOP	3
X#define BBINOP	4
X#define PAREN	5
X
Xstruct op {
X	char *op_text;
X	short op_num, op_type;
X} ops[] = {
X	{"-r", FILRD, UNOP},
X	{"-w", FILWR, UNOP},
X	{"-f", FILND, UNOP},
X	{"-d", FILID, UNOP},
X	{"-s", FILGZ, UNOP},
X	{"-t", FILTT, UNOP},
X	{"-z", STZER, UNOP},
X	{"-n", STNZE, UNOP},
X	{"=",  STEQL, BINOP},
X	{"!=", STNEQ, BINOP},
X	{"-eq", INTEQ, BINOP},
X	{"-ne", INTNE, BINOP},
X	{"-ge", INTGE, BINOP},
X	{"-gt", INTGT, BINOP},
X	{"-le", INTLE, BINOP},
X	{"-lt", INTLT, BINOP},
X	{"!", UNEGN, BUNOP},
X	{"-a", BAND, BBINOP},
X	{"-o", BOR, BBINOP},
X	{"(", LPAREN, PAREN},
X	{")", RPAREN, PAREN},
X	{0, 0, 0}
X};
X
Xlong num();
Xchar **ip;
Xchar *prog;
Xstruct op *ip_op;
X
Xmain(argc, argv)
X	char *argv[];
X{
X	prog = argv[0];
X	ip = &argv[1];
X	exit(!(expr(lex(*ip)) && *++ip == 0));
X}
X
Xexpr(n)
X{
X	int res;
X
X	if (n == EOI)
X		syntax();
X	res = bexpr(n);
X	if (lex(*++ip) == BOR)
X		return expr(lex(*++ip)) || res;
X	ip--;
X	return res;
X}
X
Xbexpr(n)
X{
X	int res;
X
X	if (n == EOI)
X		syntax();
X	res = primary(n);
X	if (lex(*++ip) == BAND)
X		return bexpr(lex(*++ip)) && res;
X	ip--;
X	return res;
X}
X
Xprimary(n)
X{
X	register char *opnd1, *opnd2;
X	int res;
X
X	if (n == EOI)
X		syntax();
X	if (n == UNEGN)
X		return !expr(lex(*++ip));
X	if (n == LPAREN) {
X		res = expr(lex(*++ip));
X		if (lex(*++ip) != RPAREN)
X			syntax();
X		return res;
X	}
X	if (n == OPERAND) {
X		opnd1 = *ip;
X		(void) lex(*++ip);
X		if (ip_op && ip_op->op_type == BINOP) {
X			struct op *op = ip_op;
X
X			if ((opnd2 = *++ip) == (char *)0)
X				syntax();
X			
X			switch (op->op_num) {
X			case STEQL:
X				return strcmp(opnd1, opnd2) == 0;
X			case STNEQ:
X				return strcmp(opnd1, opnd2) != 0;
X			case INTEQ:
X				return num(opnd1) == num(opnd2);
X			case INTNE:
X				return num(opnd1) != num(opnd2);
X			case INTGE:
X				return num(opnd1) >= num(opnd2);
X			case INTGT:
X				return num(opnd1) > num(opnd2);
X			case INTLE:
X				return num(opnd1) <= num(opnd2);
X			case INTLT:
X				return num(opnd1) < num(opnd2);
X			}
X		}
X		ip--;
X		return strlen(opnd1) > 0;
X	}
X	/* unary expression */
X	if (ip_op->op_type != UNOP || *++ip == 0)
X		syntax();
X	if (n == STZER)
X		return strlen(*ip) == 0;
X	if (n == STNZE)
X		return strlen(*ip) != 0;
X	return filstat(*ip, n);
X}
X
Xfilstat(nm, mode)
X	char *nm;
X{
X	struct stat s;
X	struct sgttyb t;
X	
X	switch (mode) {
X	case FILRD:
X		return access(nm, 4) == 0;
X	case FILWR:
X		return access(nm, 2) == 0;
X	case FILND:
X		return stat(nm, &s) == 0 && ((s.st_mode & S_IFMT) != S_IFDIR);
X	case FILID:
X		return stat(nm, &s) == 0 && ((s.st_mode & S_IFMT) == S_IFDIR);
X	case FILGZ:
X		return stat(nm, &s) == 0 && (s.st_size > 0L);
X	case FILTT:
X		return ioctl((int)num(nm), TIOCGETP, &t) == 0;
X	}
X}
X
Xint
Xlex(s)
X	register char *s;
X{
X	register struct op *op = ops;
X
X	if (s == 0)
X		return EOI;
X	while (op->op_text) {
X		if (strcmp(s, op->op_text) == 0) {
X			ip_op = op;
X			return op->op_num;
X		}
X		op++;
X	}
X	ip_op = (struct op *)0;
X	return OPERAND;
X}
X
Xlong
Xnum(s)
X	register char *s;
X{
X	long l = 0;
X	long sign = 1;
X
X	if (*s == '\0')
X		syntax();
X	if (*s == '-') {
X		sign = -1;
X		s++;
X	}
X	while (*s >= '0' && *s <= '9')
X		l = l * 10 + *s++ - '0';
X	if (*s != '\0')
X		syntax();
X	return sign * l;
X}
X
Xsyntax()
X{
X	write(2, prog, strlen(prog));
X	write(2, ": syntax error\n", 15);
X	exit(1);
X}
+ END-OF-FILE test.c
chmod 'u=rw,g=r,o=r' \t\e\s\t\.\c
set `sum \t\e\s\t\.\c`
sum=$1
case $sum in
57177)	:;;
*)	echo 'Bad sum in '\t\e\s\t\.\c >&2
esac
echo Extracting \u\u\d\e\c\o\d\e\.\c
sed 's/^X//' > \u\u\d\e\c\o\d\e\.\c << '+ END-OF-FILE '\u\u\d\e\c\o\d\e\.\c
X/* uudecode - decode a uuencoded file */
X
X/* call:   uudecode [input_file]	*/
X
X/* hgm - June 9, 1987 - added code to handle short lines and tabs */
X
X#include "stdio.h"
X#include "pwd.h"
X#include "stat.h"
X
Xextern FILE *fopen();
Xchar *fgets();
X#define NULLF (FILE *) 0
X#define NULLP (char *) 0
X
X/* single character decode */
X#define DEC(c)	(((c) - ' ') & 077)
X
Xmain(argc, argv)
Xchar **argv;
X{
X	FILE *in, *out;
X	struct stat sbuf;
X	int mode;
X	char dest[128];
X	char buf[80];
X
X	/* optional input arg */
X	if (argc > 1) {
X		if ((in = fopen(argv[1], "r")) == NULLF) {
X			perror(argv[1]);
X			xit(1);
X		}
X		argv++; argc--;
X	} else
X		in = stdin;
X
X	if (argc != 1) {
X		printf("Usage: uudecode [infile]\n");
X		xit(2);
X	}
X
X	/* search for header line */
X	for (;;) {
X		if (fgets(buf, sizeof buf, in) == NULLP) {
X			fprintf(stderr, "No begin line\n");
X			xit(3);
X		}
X		if (strncmp(buf, "begin ", 6) == 0)
X			break;
X	}
X	sscanf(buf, "begin %o %s", &mode, dest);
X
X	/* handle ~user/file format */
X	if (dest[0] == '~') {
X		char *sl;
X		struct passwd *getpwnam();
X		char *index();
X		struct passwd *user;
X		char dnbuf[100];
X
X		sl = index(dest, '/');
X		if (sl == NULLP) {
X			fprintf(stderr, "Illegal ~user\n");
X			xit(3);
X		}
X		*sl++ = 0;
X		user = getpwnam(dest+1);
X		if (user == NULL) {
X			fprintf(stderr, "No such user as %s\n", dest);
X			xit(4);
X		}
X		strcpy(dnbuf, user->pw_dir);
X		strcat(dnbuf, "/");
X		strcat(dnbuf, sl);
X		strcpy(dest, dnbuf);
X	}
X
X	/* create output file */
X	out = fopen(dest, "w");
X	if (out == NULLF) {
X		perror(dest);
X		xit(4);
X	}
X	chmod(dest, mode);
X
X	decode(in, out);
X
X	if (fgets(buf, sizeof buf, in) == NULLP || strcmp(buf, "end\n")) {
X		fprintf(stderr, "No end line\n");
X		xit(5);
X	}
X	xit(0);
X}
X
X/*
X * copy from in to out, decoding as you go along.
X */
Xdecode(in, out)
XFILE *in;
XFILE *out;
X{
X	char tbuf[82];
X	char buf[82];
X	char *bp;
X	char *p;
X	int n;
X	int col;
X
X	for (;;) {
X		/* for each input line */
X		if (fgets(tbuf, sizeof tbuf, in) == NULLP) {
X			printf("Short file\n");
X			xit(10);
X		}
X		n = tbuf[0] - ' ';
X		if (n <= 0)
X			break;
X
X		/* expand tabs */
X		for (col = 0, p = tbuf; ((*p) && (*p != '\n') && (col < 78)); p++) {
X			if (*p == '\t') {
X				do {
X					buf[col++] = ' ';
X				} while (col % 8);
X			} else {
X				buf[col++] = *p;
X			}
X		}
X
X		/* fill with trailing blanks */
X		for (; ((col < (4*n + 1)) && (col < 78)); col++)
X			buf[col] = ' ';
X		buf[col++] = '\n';
X		buf[col] = '\0';
X
X		bp = &buf[1];
X		while (n > 0) {
X			outdec(bp, out, n);
X			bp += 4;
X			n -= 3;
X		}
X	}
X}
X
X/*
X * output a group of 3 bytes (4 input characters).
X * the input chars are pointed to by p, they are to
X * be output to file f.  n is used to tell us not to
X * output all of them at the end of the file.
X */
Xoutdec(p, f, n)
Xchar *p;
XFILE *f;
X{
X	int c1, c2, c3;
X
X	c1 = DEC(*p) << 2 | DEC(p[1]) >> 4;
X	c2 = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
X	c3 = DEC(p[2]) << 6 | DEC(p[3]);
X	if (n >= 1)
X		putc(c1, f);
X	if (n >= 2)
X		putc(c2, f);
X	if (n >= 3)
X		putc(c3, f);
X}
X
X
X/* fr: like read but stdio */
Xint
Xfr(fd, buf, cnt)
XFILE *fd;
Xchar *buf;
Xint cnt;
X{
X	int c, i;
X
X	for (i=0; i<cnt; i++) {
X		c = getc(fd);
X		if (c == EOF)
X			return(i);
X		buf[i] = c;
X	}
X	return (cnt);
X}
X
X/*
X * Return the ptr in sp at which the character c appears;
X * NULL if not found
X */
X
X
Xchar *
Xindex(sp, c)
Xregister char *sp, c;
X{
X	do {
X		if (*sp == c)
X			return(sp);
X	} while (*sp++);
X	return(NULL);
X}
X
Xxit(n)
Xint n;
X{
X  _cleanup();
X  exit(n);
X}
+ END-OF-FILE uudecode.c
chmod 'u=rw,g=r,o=r' \u\u\d\e\c\o\d\e\.\c
set `sum \u\u\d\e\c\o\d\e\.\c`
sum=$1
case $sum in
15350)	:;;
*)	echo 'Bad sum in '\u\u\d\e\c\o\d\e\.\c >&2
esac
echo Extracting \u\u\e\n\c\o\d\e\.\c
sed 's/^X//' > \u\u\e\n\c\o\d\e\.\c << '+ END-OF-FILE '\u\u\e\n\c\o\d\e\.\c
X/* uuencode - encode a file for mailing to a remote system */
X
X/* Call:  uuencode [input] output
X * encodes a file to pure ASCII, with 3 bytes in the original file becoming
X * 4 bytes in the encoded file.  Uudecode decodes encoded files.
X */
X
X#include "stdio.h"
X#include "stat.h"
X
Xextern FILE *fopen();
X
X/* ENC is the basic 1 character encoding function to make a char printing */
X#define ENC(c) (((c) & 077) + ' ')
X
Xmain(argc, argv)
Xchar **argv;
X{
X	FILE *in;
X	struct stat sbuf;
X	int mode;
X
X	/* optional 1st argument */
X	if (argc > 2) {
X		if ((in = fopen(argv[1], "r")) == (FILE *) NULL) {
X			perror(argv[1]);
X			xit(1);
X		}
X		argv++; argc--;
X	} else
X		in = stdin;
X
X	if (argc != 2) {
X		printf("Usage: uuencode infile <infile >outfile\n");
X		xit(2);
X	}
X
X	/* figure out the input file mode */
X	fstat(fileno(in), &sbuf);
X	mode = sbuf.st_mode & 0777;
X	printf("begin %o %s\n", mode, argv[1]);
X
X	encode(in, stdout);
X
X	printf("end\n");
X	xit(0);
X}
X
X/*
X * copy from in to out, encoding as you go along.
X */
Xencode(in, out)
XFILE *in;
XFILE *out;
X{
X	char buf[80];
X	int i, n;
X
X	for (;;) {
X		/* 1 (up to) 45 character line */
X		n = fr(in, buf, 45);
X		putc(ENC(n), out);
X
X		for (i=0; i<n; i += 3)
X			outdec(&buf[i], out);
X
X		putc('\n', out);
X		if (n <= 0)
X			break;
X	}
X}
X
X/*
X * output one group of 3 bytes, pointed at by p, on file f.
X */
Xoutdec(p, f)
Xchar *p;
XFILE *f;
X{
X	int c1, c2, c3, c4;
X
X	c1 = *p >> 2;
X	c2 = (*p << 4) & 060 | (p[1] >> 4) & 017;
X	c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03;
X	c4 = p[2] & 077;
X	putc(ENC(c1), f);
X	putc(ENC(c2), f);
X	putc(ENC(c3), f);
X	putc(ENC(c4), f);
X}
X
X/* fr: like read but stdio */
Xint
Xfr(fd, buf, cnt)
XFILE *fd;
Xchar *buf;
Xint cnt;
X{
X	int c, i;
X
X	for (i=0; i<cnt; i++) {
X		c = getc(fd);
X		if (c == EOF)
X			return(i);
X		buf[i] = c;
X	}
X	return (cnt);
X}
X
Xxit(n)
Xint n;
X{
X  _cleanup();
X  exit(n);
X}
+ END-OF-FILE uuencode.c
chmod 'u=rw,g=r,o=r' \u\u\e\n\c\o\d\e\.\c
set `sum \u\u\e\n\c\o\d\e\.\c`
sum=$1
case $sum in
13877)	:;;
*)	echo 'Bad sum in '\u\u\e\n\c\o\d\e\.\c >&2
esac
exit 0