[comp.os.minix] 1.3 checksums and bugs

rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) (10/07/88)

It's a very good idea to post checksums of the 1.3 files.
I have checked all files and found many differences.
Below is a list of missing or wrong files.
The first path component indicates in which version the last update was
(probably I have forgotten a step).
If crc is wrong, crc and length are listed.

Remarks:
Some directories have makefile, at_makefile, and pc_makefile ??
1.3c/tools/{at,pc}_makefile  had bad counts by unsharig it.
Assert.h is only in amoeba.
Amoeba and test dirs are not checked.
I don't have dis88/*, diskcheck.c, cmd/tty.c, paste.c  seen in the news.
I hope, some files will be reposted by Andy if other people have same problems.

BUGS
The 1.3c tty driver has a bug in its flow control. Type ls -l and
^S, then ^Q. The total ..  line appears repeated on the interrupted place.


Where are the diffs for fsck.c (Typing new letters on boot menue).

At last: I'm very interested to have an improved fs process, which
doesn't block the system when waiting for floppy or disk tasks.
If I type :
cp image /dev/fd0 &
ls -l		,
I don't believe minix is a multitasking system, because the ls runs after
the cp has finished!

Here is the used checking program:
The crcfile must have no empty lines, no comments, only crc output!
The script is very slow (about 4 mins on a sun 3), but i need it not every day
---------------------------------------------------------------------
#!/bin/sh
#verify the crc's given in a file $1

if test $# -ne 1
then echo usage: $0 crcfile
exit
fi
count=1
file=$1
set `wc -l $file`
len=$1

while test $count -le $len
do
set `head -$count $file |tail -1`
#sort information in checkfile line
name=$3
crc=$1
lenght=$2
#locate last version of program

skip=0
if test -f 1.3c/$name
#customize for your own structure
else echo $name not found
        skip=1
fi

#run crc
if test $skip -eq 0
then
        set `c/crc $name`
        if test $crc -ne $1
        then echo crc of $name '        ' WRONG : $1 $2
        elif test $lenght -ne $2
        then echo lenght of $name wrong
        else echo $name '       ' OK
        fi
fi
count=`expr $count + 1`
done
----------------------------------------------------------------

crc of 1.2/mm/makefile 	 WRONG : 36165 1747
crc of 1.3a/kernel/xt_wini.c 	 WRONG : 07511 26312
crc of 1.3c/kernel/makefile 	 WRONG : 42475 3015
kernel/pc_makefile not found
crc of 1.1/tools/build.c 	 WRONG : 61224 19650
crc of 1.1/tools/fsck.c 	 WRONG : 00735 46061
crc of 1.2/tools/makefile 	 WRONG : 41663 4850
crc of 1.3c/tools/at_makefile 	 WRONG : 01259 2245
crc of 1.3c/tools/pc_makefile 	 WRONG : 01259 2245
crc of 1.3a/lib/run 	 WRONG : 65445 1643
crc of 1.3a/lib/getpwent.c 	 WRONG : 19615 1732
crc of 1.3a/lib/regexp.c 	 WRONG : 55285 27651
crc of 1.3a/lib/signal.c 	 WRONG : 16064 843
crc of 1.3a/lib/stty.c 	 WRONG : 64333 98
doc/lib.doc not found
crc of 1.3c/doc/USER_GUIDE 	 WRONG : 19863 21661
crc of 1.3c/doc/net.man 	 WRONG : 24986 44692
doc/elle.man not found
crc of 1.3a/commands/chgrp.c 	 WRONG : 26761 771
crc of 1.3a/commands/compress.c 	 WRONG : 02538 46934
crc of 1.3c/commands/cp.c 	 WRONG : 42393 3520
crc of 1.3c/commands/df.c 	 WRONG : 09795 3579
commands/diskcheck.c not found
crc of 1.3a/commands/ed.c 	 WRONG : 60732 40869
commands/elle.a not found
crc of 1.3b/commands/lorder.c 	 WRONG : 63891 7644
crc of 1.3c/commands/ls.c 	 WRONG : 27607 13457
crc of 1.3a/commands/makefile 	 WRONG : 07225 35
commands/paste.c not found
crc of 1.3b/commands/pwd.c 	 WRONG : 32283 1639
crc of 1.1/commands/run 	 WRONG : 22133 88
crc of 1.3c/commands/sed.c 	 WRONG : 52187 45809
crc of 1.3a/commands/strings.c 	 WRONG : 32820 3884
commands/termcap.c not found
commands/tty.c not found
crc of 1.3c/commands/vol.c 	 WRONG : 48724 2965
commands/who.c not found
crc of 1.3a/commands/whoami.c 	 WRONG : 18110 659
crc of 1.3a/commands/sh/makefile 	 WRONG : 06920 158
crc of 1.3a/commands/sh/sh1.c 	 WRONG : 34511 14561
crc of 1.3a/commands/sh/sh2.c 	 WRONG : 13571 11571
crc of 1.3a/commands/sh/sh3.c 	 WRONG : 55663 16897
crc of 1.3a/commands/sh/sh4.c 	 WRONG : 38764 12389
crc of 1.3a/commands/sh/sh5.c 	 WRONG : 05008 9219
commands/dis88/README not found
commands/dis88/dis.h not found
commands/dis88/makefile not found
commands/dis88/disfp.c not found
commands/dis88/dishand.c not found
commands/dis88/dismain.c not found
commands/dis88/disrel.c not found
commands/dis88/distabs.c not found
commands/dis88/dis88 not found
crc of 1.3c/commands/make/main.c 	 WRONG : 54326 4348
crc of 1.3c/commands/make/make.c 	 WRONG : 07811 7681
crc of 1.3c/commands/make/rules.c 	 WRONG : 06575 5144
crc of 1.3c/commands/make/makefile 	 WRONG : 36093 492
commands/mined/makefile not found
include/assert.h not found
crc of 1.3a/include/sgtty.h 	 WRONG : 38485 1015


				Robert Regn
				rtregn.faui32.uucp

ast@cs.vu.nl (Andy Tanenbaum) (10/09/88)

In article <658@faui44.informatik.uni-erlangen.de> rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) writes:
>It's a very good idea to post checksums of the 1.3 files.
>I have checked all files and found many differences.
>Below is a list of missing or wrong files.

I recently posted many files again based on Johan's list.  You list some files
that he said were ok.  My working assumption is that you must have missed
some postings, since he apparently has been able to construct up to date files.
If more people claim that there are still files that cannot be reconstructed
from the postings, please post a message to that effect.  With a little bit of
luck we should be able to make sure enough stuff has been posted and logged in
the archives to get everyone who wants to make the effort up to 1.3.

Andy Tanenbaum (ast@cs.vu.nl)

linas@hpuarca.HP.COM (Linas Petras) (10/12/88)

Could I please ask you Andy, that you post the lastest and greatest versions of 
'ar' 'lorder' and 'tsort'.

I still have not been able to get a version of these programs that have 
the correct sum's and crc's. The problem with 'ar' appears to be that at 1.3a a
different base version of ar was used to generate the diff file than version 1.2
which is currently the only base version of 'ar' that I can find.

brucee@runx.ips.oz (Bruce Evans) (10/16/88)

Robert Regn writes:
>At last: I'm very interested to have an improved fs process, which
>doesn't block the system when waiting for floppy or disk tasks.
>If I type :
>cp image /dev/fd0 &
>ls -l		,
>I don't believe minix is a multitasking system, because the ls runs after
>the cp has finished!

Let me emphasize this problem again. Minix really is multi-tasking, but fs
kills it. What happens is that cp is very "efficient", and makes a few
large requests, while ls makes many smaller ones. The processes probably
take it in turns to get into fs, so cp finishes before ls.

If fs were cleverer, it would often be able to handle ls's requests from the
cache while cp was held up waiting for physical i/o. Of course when ls also
needs physical i/o, some delay is necessary. 

Apart from the apparently major rewrite of fs needed to solve this, a hog
program like cp may be difficult to handle because it will gobble up Minix's
tiny cache. My solution would be to junk the RAM disk and use the memory for
a cache instead. The main obstacle is that a large cache couldn't all be in
fs's 64K data space.

The way cp and ls take turns reminds me of another scheduling problem.
Suppose 2 processes are running,

 (H) a CPU Hog which makes no system calls
 (I) and Inefficient I/O bound process which does nothing much other than
     making system calls, e.g. single-char output.

The processes will take turns, resulting in (I) running at the glacial
speed of 1 I/O operation per quantum (1/10 sec or so).

This is relatively easy to fix.

>[shell program to check file system against crc's posted]

It requires a "then" and a non-comment statement in the "if" statement along
with the "#customize for your own structure".

It still fails on Minix when the sh runs out of string space. The following
one-liner demonstrates the problem:

  while :; set 1; done
 
Bruce Evans
Internet: brucee@runx.ips.oz.au    UUCP: uunet!runx.ips.oz.au!brucee

ast@cs.vu.nl (Andy Tanenbaum) (10/17/88)

In article <1070004@hpuarca.HP.COM> linas@hpuarca.HP.COM (Linas Petras) writes:
>
>Could I please ask you Andy, that you post the lastest and greatest versions of 
>'ar' 'lorder' and 'tsort'.
Ok.  Here they are.  Actually, I have a subsequent patch for tsort, but that
will not be in 1.3, so it is not included here.  These are the 1.3 files.

Andy Tanenbaum (ast@cs.vu.nl)

: 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:/usr/ucb
echo Extracting 'ar.c'
sed 's/^X//' > 'ar.c' << '+ END-OF-FILE ''ar.c'
X/* ar - archiver		Author: Michiel Huisjes */
X/* V7 upgrade			Author:	Monty Walls */
X
X/*
X * Usage: ar 'key' [posname] archive [file] ...
X *
X *	where 'key' is one of: qrqtpmx
X *
X *	  q: quickly append to the end of the archive file
X *	  m: move named files 
X *	  r: replace (append when not in archive)
X *	  d: delete
X *	  t: print contents of archive
X *	  p: print named files
X *	  x: extract
X *
X *	concatencated with one or more of: vuaibcl
X *  
X *	  l: local temporary file for work instead of /tmp/ar.$$$$$
X *	  v: verbose
X *	  a: after 'posname'
X *	  b: before 'posname'
X *	  i: before 'posname'
X *	  c: create  (suppresses creation message)
X *	  u: replace only if dated later than member in archive
X */
X
X/* mods:
X *	1.2 upgrade.
X *	local directory support	(mrw).
X *	full V7 functionality + complete rewrite (mrw).
X *	changed verbose mode to give member name on print (mrw).
X *	fixed up error messages to give more info (mrw).
X *
X * notes:
X *	pdp11 long format & Intel long format are different.
X *
X * change log:
X *	forgot that ar_size is a long for printing - 2/14/88 - mrw
X *	got the mode bit maps mirrored - 2/19/88 - mrw
X *	print & extract member logic fixed - 2/19/88 - mrw
X */
X
X/* include files */
X#include <stdio.h>
X#include <sys/stat.h>
X#include <signal.h>
X
X/* ar.c header file V7 */
X
X#define ARMAG	0177545l
Xstruct ar_hdr {
X	char ar_name[14];
X	long ar_date;		/* not Intel format */
X	char ar_uid;
X	char ar_gid;
X	int ar_mode;
X	long ar_size;		/* not Intel format */
X};
X
X
X/* macro functions */
X#define FOREVER		(32766)
X#define odd(nr)		(nr & 1)
X#define even(nr)	(odd(nr) ? nr + 1 : nr)
X#define quit(pid,sig)	(kill(pid,sig),sleep(FOREVER))
X#ifndef tell 
X#	define tell(f)	(lseek(f, 0l, 1))
X#endif
X
X/* option switches */
X/* major options */
X#define EXTRACT		0x01
X#define REPLACE		0x02
X#define PRINT		0x04
X#define TABLE		0x08
X#define DELETE		0x10
X#define APPEND		0x20
X#define MOVE		0x40
X
X/* minor options */
X#define BEFORE		0x01
X#define AFTER		0x02
X#define LOCAL		0x01
X#define VERBOSE		0x01
X#define CREATE		0x01
X#define ONLY		0x01
X
X/* mode bits maps */
X#define EXEC_OWNER	00100
X#define EXEC_GROUP	00010
X#define EXEC_ALL	00001
X#define READ_OWNER	00400
X#define READ_GROUP	00040
X#define READ_ALL	00004
X#define WRITE_OWNER	00200
X#define WRITE_GROUP	00020
X#define WRITE_ALL	00002
X#define SET_UID		04000
X#define SET_GID		02000
X
X/* global defines */
X#define BUFFERSIZE	4096
X#define WRITE		2		/* both read & write */
X#define READ		0
X#define MAGICSIZE	sizeof(short)	/* size of magic number in file */
X
X/* option switches */
Xchar verbose = 0;
Xchar local = 0;
Xchar create = 0;
Xchar only = 0;
Xchar major = 0;
Xchar minor = 0;
X
X/* global variables */
Xchar *tmp1;
Xchar *tmp2;
Xchar *progname;
Xchar *posname = NULL;
Xchar *afile;
Xchar buffer[BUFFERSIZE];
Xlong pos_offset = -1;
Xint mypid;
X
X/* keep track of member moves using this struct */
Xstruct mov_list {
X	long pos;
X	struct mov_list *next;
X} *moves = NULL;
X
X/* forward declarations and external references */
Xextern char *malloc();
Xextern char *mktemp(), *rindex();
Xextern int strncmp();
Xextern print_date();
Xextern user_abort(), usage();
Xextern long lseek();
Xextern char *basename();
X
Xint
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int ac, opts_seen = 0, rc;
X	char *av;
X	
X	progname = argv[0];		
X	if (argc < 3) 
X		usage();
X
X	for (av = argv[1]; *av; ++av) {	
X		switch (*av) {		/* major option */
X			case 'q':
X				major |= APPEND;
X				++opts_seen;
X				break;
X			case 'r':
X				major |= REPLACE;
X				++opts_seen;
X				break;
X			case 'x':
X				major |= EXTRACT;
X				++opts_seen;
X				break;
X			case 'p':
X				major |= PRINT;
X				++opts_seen;
X				break;
X			case 'm':
X				major |= MOVE;
X				++opts_seen;
X				break;
X			case 'd':
X				major |= DELETE;
X				++opts_seen;
X				break;
X			case 't':
X				major |= TABLE;
X				++opts_seen;
X				break;
X			case 'l':
X				local |= LOCAL;
X				break;
X			case 'a':
X				minor |= AFTER;
X				break;
X			case 'i':
X			case 'b':
X				minor |= BEFORE;
X				break;
X			case 'v':
X				verbose |= VERBOSE;
X				break;
X			case 'c':
X				create |= CREATE;
X				break;
X			case 'u':
X				only |= ONLY;
X				break;
X			default:
X				usage();
X		}
X	}
X
X	if (opts_seen != 1) 
X		usage();
X
X	/* now do edits on options */
X	if (!(major & (REPLACE | MOVE))) {
X		if (minor & (AFTER | BEFORE))
X			usage();
X	}
X	else if (major & MOVE) {
X		if (!(minor & (AFTER | BEFORE)))
X			usage();
X	}
X	else if (only & ONLY)
X		if (!(major & REPLACE))
X			usage();
X	
X	if (local) 
X		tmp1 = mktemp("./ar.1.XXXXXX");
X	else
X		tmp1 = mktemp("/tmp/ar.1.XXXXXX");
X			
X	/* now if minor says AFTER or BEFORE - then get posname */
X	if (minor & (AFTER | BEFORE) && argc >= 4) {
X		posname = argv[2];
X		afile = argv[3];
X		ac = 4;
X	}
X	else {
X		posname = (char *)NULL;
X		afile = argv[2];
X		ac = 3;
X	}
X	
X	/* exit logic consists of doing a kill on my pid to insure that we */
X	/* get the current clean up and exit logic */
X	mypid = getpid();	
X	signal(SIGINT, user_abort);
X	
X	switch (major) {
X		case REPLACE:
X		case DELETE:
X		case MOVE:
X			ar_members(ac, argc, argv);
X			break;
X		case EXTRACT:
X		case TABLE:
X		case PRINT:
X			ar_common(ac, argc, argv);
X			break;
X		case APPEND:
X			append_members(ac, argc, argv);
X			break;
X		default:
X			usage();
X	}
X
X	for (rc = 0; ac < argc; ++ac) {
X		if (*argv[ac] != '\0') {
X			/* no processing done on this name */
X			fprintf(stderr,"Error %s: %s not found in %s\n", progname, argv[ac], afile);
X			rc = 1;
X		}
X	}
X	fflush(stdout);
X	exit(rc);
X}
X
Xusage()
X{
X	fprintf(stderr,"Usage: %s [qrxdpmt][abivulc] [posname] afile name ... \n",progname);
X	exit(1);
X}
X
Xuser_abort()
X{
X	unlink(tmp1);
X	exit(1);
X}
X
Xinsert_abort()
X{
X	unlink(tmp1);
X	unlink(tmp2);
X	exit(1);
X}
X
Xmwrite(fd, address, bytes)
Xint fd;
Xregister char *address;
Xregister int bytes;
X{
X  if (write(fd, address, bytes) != bytes) {
X	fprintf(stderr," Error: %s - Write error\n",progname);
X	quit(mypid, SIGINT);
X  }
X}
X
Xlong
Xswap(l)
Xlong l;
X{
X	union {
X		struct {
X			int word1, word2;
X		} words;
X		long n;
X	} u_in, u_out;
X	
X	u_in.n = l;
X	u_out.words.word1 = u_in.words.word2;
X	u_out.words.word2 = u_in.words.word1;
X	return (u_out.n);
X	
X}
X
Xaddmove(pos)
Xlong pos;
X{
X	struct mov_list *newmove;
X
X	newmove = (struct mov_list *)malloc(sizeof(struct mov_list));
X	newmove->pos = pos;
X	newmove->next = moves;
X	moves = newmove;		
X}
X
Xstruct ar_hdr *
Xget_member(fd)
Xint fd;
X{
X	int ret;
X	static struct ar_hdr member;
X	
X	if ((ret = read(fd,  &member, sizeof(struct ar_hdr))) <= 0) 
X		return ((struct ar_hdr *)NULL);
X	if (ret != sizeof(struct ar_hdr)) {
X		fprintf(stderr,"Error: ar corrupted archive %s\n",afile);
X		quit(mypid,SIGINT);
X	}
X
X	/* the archive long format is pdp11 not intel
X	 * therefore we must reformat them for our internal use
X	 */
X
X	member.ar_date = swap(member.ar_date);
X	member.ar_size = swap(member.ar_size);
X	return (&member);
X}
X
Xint
Xopen_archive(filename, opt, to_create)
Xchar *filename;
Xint opt;
X{
X	static unsigned short magic;
X	int fd;
X	
X	/* to_create can have values of 0,1,2 */
X	/* 0 - don't create a file. */
X	/* 1 - create file but use create switch message mode */
X	/* 2 - create file but don't talk about it */
X	
X	if (to_create) {
X		if ((fd = creat(filename, 0644)) < 0) {
X			fprintf(stderr, "Error: %s can not create %s\n",progname, filename);
X			quit(mypid,SIGINT);
X		}
X		if (!create && to_create == 1) fprintf(stderr, "%s:%s created\n", progname, filename);
X		magic = ARMAG;
X		mwrite(fd, &magic, MAGICSIZE);
X		return (fd);
X	}
X	else {
X		if ((fd = open(filename, opt)) < 0) {
X			if (opt == WRITE) 
X				return (open_archive(filename, opt, 1));
X			else {
X				fprintf(stderr, "Error: %s can not open %s\n",progname, filename);
X				quit(mypid,SIGINT);
X			}
X		}
X		/* now check the magic number for ar V7 file */
X		lseek(fd, 0l, 0);
X		read(fd, &magic, MAGICSIZE);
X		if (magic != ARMAG) {
X			fprintf(stderr, "Error: not %s V7 format - %s\n",progname, filename);
X			quit(mypid,SIGINT);
X		}
X		if (major & APPEND)
X			lseek(fd, 0l, 2);	/* seek eof position */
X			
X		return (fd);
X	}		
X}
X
X
Xint
Xrebuild(fd, tempfd)
Xregister int fd, tempfd;
X{
X	register int n;
X
X	/* after we have built the archive to a temporary file and */
X	/* everything has worked out- we copy the archive back to */
X	/* original file */
X		
X	signal(SIGINT, SIG_IGN);
X	close(fd);
X	close(tempfd);
X	fd = open_archive(afile, WRITE, 2);
X	tempfd = open_archive(tmp1, WRITE, 0);
X	while ((n = read(tempfd, buffer, BUFFERSIZE)) > 0) 
X		mwrite(fd, buffer, n);
X	close(tempfd);
X	unlink(tmp1);
X	return (fd);
X}
X
Xprint_mode(mode)
Xshort mode;
X{
X	char g_ex, o_ex, all_ex;
X	char g_rd, o_rd, all_rd;
X	char g_wr, o_wr, all_wr;
X	
X	g_ex = EXEC_GROUP & mode ? 'x' : '-';
X	o_ex = EXEC_OWNER & mode ? 'x' : '-';
X	all_ex = EXEC_ALL & mode ? 'x' : '-';
X	
X	g_ex = SET_GID & mode ? 's' : g_ex;
X	o_ex = SET_UID & mode ? 's' : o_ex;
X	
X	g_rd = READ_GROUP & mode ? 'r' : '-';
X	o_rd = READ_OWNER & mode ? 'r' : '-';
X	all_rd = READ_ALL & mode ? 'r' : '-';
X	
X	g_wr = WRITE_GROUP & mode ? 'w' : '-';
X	o_wr = WRITE_OWNER & mode ? 'w' : '-';
X	all_wr = WRITE_ALL & mode ? 'w' : '-';
X
X	fprintf(stdout,"%c%c%c",o_rd, o_wr, o_ex);
X	fprintf(stdout,"%c%c%c",g_rd, g_wr, g_ex);
X	fprintf(stdout,"%c%c%c",all_rd, all_wr, all_ex);
X}
X
Xprint_header(member)
Xstruct ar_hdr *member;
X{
X	if (verbose) {
X		print_mode(member->ar_mode);
X		fprintf(stdout,"%3.3d",member->ar_uid);
X		fprintf(stdout,"/%-3.3d ",member->ar_gid);
X		fprintf(stdout,"%5.5D",member->ar_size);	/* oops is long - mrw */
X		print_date(member->ar_date);
X	}
X	fprintf(stdout,"%-14.14s\n",member->ar_name);
X}
X
Xprint(fd,member)
Xint fd;
Xstruct ar_hdr *member;
X{
X	int outfd;
X	long size;
X	register int cnt, ret;
X	int do_align;
X
X	if (major & EXTRACT) {
X		if ((outfd = creat(member->ar_name,0666)) < 0) {
X			fprintf(stderr,"Error: %s could not creat %-14.14s\n",progname, member->ar_name);
X			quit(mypid,SIGINT);
X		}
X		if (verbose)
X			fprintf(stdout,"x - %-14.14s\n",member->ar_name);
X	}
X	else {
X		if (verbose) {
X			fprintf(stdout,"p - %-14.14s\n",member->ar_name);
X			fflush(stdout);
X		}
X		outfd = fileno(stdout);
X	}
X	/* changed loop to use long size for correct extracts */	
X	for (size = member->ar_size; size > 0; size -= ret) {
X		cnt = (size < BUFFERSIZE ? size : BUFFERSIZE);
X		ret = read(fd, buffer, cnt);
X		if (ret > 0)
X			write(outfd,buffer, ret);
X	}
X	if (odd(member->ar_size))
X		lseek(fd,1l,1);		/* realign ourselves */
X		
X	if (major & EXTRACT) {
X		close(outfd);
X		chmod(member->ar_name, member->ar_mode);
X	}		
X}
X
X/* copy a given member from fd1 to fd2 */
Xcopy_member(infd, outfd, member)
Xint infd, outfd;
Xstruct ar_hdr *member;
X{
X	int n, cnt;
X	long m, size;
X
X	/* save copies for our use */
X	m = size = member->ar_size;
X
X	/* format for disk usage */
X	member->ar_size = swap(member->ar_size);
X	member->ar_date = swap(member->ar_date);
X
X	mwrite(outfd, member, sizeof(struct ar_hdr));
X	for (; m > 0; m -= n) {
X		cnt = (m < BUFFERSIZE ? m : BUFFERSIZE);
X		if ((n = read(infd, buffer, cnt)) != cnt) {
X			fprintf(stderr,"Error: %s - read error on %-14.14s\n",progname, member->ar_name);
X			quit(mypid, SIGINT);
X		}
X		mwrite(outfd, buffer, n);
X	}	
X	if (odd(size)) {		/* pad to word boundary */
X		mwrite(outfd, buffer, 1);
X		lseek(infd,1l,1);		/* realign reading fd */ 
X	}
X}
X
X/* insert at current offset - name file */
Xinsert(fd, name, mess, oldmember)
Xint fd;
Xchar *name, *mess;
Xstruct ar_hdr *oldmember;
X{
X	static struct ar_hdr member;
X	static struct stat status;
X	int in_fd;
X
X	if (stat(name, &status) < 0) {
X		fprintf(stderr,"Error: %s cannot find file %s\n",progname,name);
X		quit(mypid,SIGINT);
X	}
X	else if ((in_fd = open(name, READ)) < 0) {
X		fprintf(stderr,"Error: %s cannot open file %s\n",progname,name);
X		quit(mypid,SIGINT);
X	}
X	strncpy(member.ar_name, basename(name),14);
X	member.ar_uid = status.st_uid;
X	member.ar_gid = status.st_gid;
X	member.ar_mode = status.st_mode & 07777;
X	member.ar_date = status.st_mtime;
X	member.ar_size = status.st_size;
X	if (only & ONLY)
X		if (oldmember != (struct ar_hdr *)NULL)  
X			if (member.ar_date <= oldmember->ar_date) {
X				close(in_fd);
X				if (verbose) fprintf(stdout, "not %-14.14s - %-14.14s\n",mess, name);
X				return (-1);
X			}
X		
X	copy_member(in_fd, fd, &member); 
X	if (verbose) 
X		fprintf(stdout, "%s - %-14.14s\n",mess, name);
X	close(in_fd);
X	return (1);
X}
X
Xint
Xar_move(oldfd, arfd,mov)
Xint oldfd, arfd;
Xstruct mov_list *mov;
X{
X	long pos;
X	int cnt, want, a, newfd;
X	struct ar_hdr *member;
X
X	if (local) 
X		tmp2 = mktemp("./ar.2.XXXXXX");
X	else
X		tmp2 = mktemp("/tmp/ar.2.XXXXXX");
X
X	close(oldfd);				/* close old temp file */
X	signal(SIGINT, insert_abort);		/* set new signal handler */
X	newfd = open_archive(tmp2, WRITE, 2);	/* open new tmp file */
X	oldfd = open_archive(tmp1, WRITE, 0);	/* reopen old tmp file */
X
X	/* copy archive till we get to pos_offset */
X	for (pos = pos_offset; pos > 0; pos -= cnt) {
X		want = (pos < BUFFERSIZE ? pos : BUFFERSIZE);
X		if ((cnt = read(oldfd, buffer, want)) > 0)
X			mwrite(newfd, buffer, cnt);
X	}
X	/* if minor = 'a' then skip over posname */
X	if (minor & AFTER) {
X		if ((member = get_member(oldfd)) != NULL)
X			copy_member(oldfd, newfd, member);
X	}
X	/* move members in the library */
X	while (mov != NULL) {
X		lseek(arfd, mov->pos, 0);
X		if ((member = get_member(arfd)) != NULL)
X			copy_member(arfd, newfd, member);
X		mov = mov->next;
X		if (verbose) fprintf(stdout, "m - %-14.14s\n", member->ar_name);
X	}
X
X	/* copy rest of library into new tmp file */
X	while ((member = get_member(oldfd)) != NULL) 
X		copy_member(oldfd, newfd, member);
X
X	/* detach old temp file */
X	close(oldfd);
X	unlink(tmp1);
X
X	/* change context temp file */
X	tmp1 = tmp2;
X	return (newfd);
X}
X
Xint
Xar_insert(oldfd, ac, argc, argv)
Xint oldfd;
Xint ac, argc;
Xchar **argv;
X{
X	long pos;
X	int cnt, want, a, newfd;
X	struct ar_hdr *member;
X
X	if (local) 
X		tmp2 = mktemp("./ar.2.XXXXXX");
X	else
X		tmp2 = mktemp("/tmp/ar.2.XXXXXX");
X
X	close(oldfd);				/* close old temp file */
X	signal(SIGINT, insert_abort);		/* set new signal handler */
X	newfd = open_archive(tmp2, WRITE, 2);	/* open new tmp file */
X	oldfd = open_archive(tmp1, WRITE, 0);	/* reopen old tmp file */
X
X	/* copy archive till we get to pos_offset */
X	for (pos = pos_offset; pos > 0; pos -= cnt) {
X		want = (pos < BUFFERSIZE ? pos : BUFFERSIZE);
X		if ((cnt = read(oldfd, buffer, want)) > 0)
X			mwrite(newfd, buffer, cnt);
X	}
X	/* if minor = 'a' then skip over posname */
X	if (minor & AFTER) {
X		if ((member = get_member(oldfd)) != NULL)
X			copy_member(oldfd, newfd, member);
X	}
X	
X	/* copy new members into the library */
X	for (a = ac+1; a <= argc; ++a)
X		if (argv[a-1] && *argv[a-1] != '\0') {
X			insert(newfd, argv[a-1], "a", (struct ar_hdr *)NULL);
X			*argv[a-1] = '\0';
X		}
X
X	/* copy rest of library into new tmp file */
X	while ((member = get_member(oldfd)) != NULL) 
X		copy_member(oldfd, newfd, member);
X
X	/* detach old temp file */
X	close(oldfd);
X	unlink(tmp1);
X
X	/* change context temp file */
X	tmp1 = tmp2;
X	return (newfd);
X}
X
Xar_common(ac, argc, argv)
Xint ac, argc;
Xchar **argv;
X{
X	int a, fd, did_print;
X	struct ar_hdr *member;
X
X	fd = open_archive(afile, READ, 0);
X	while ((member = get_member(fd)) != NULL) {
X		did_print = 0;
X		if (ac < argc) {
X			for (a = ac+1; a <= argc; ++a) {
X				if (strncmp(basename(argv[a-1]),member->ar_name,14) == 0) {
X					if (major & TABLE)
X						print_header(member);
X					else if (major & (PRINT | EXTRACT)) {
X						print(fd, member);
X						did_print = 1;
X					}
X					*argv[a-1] = '\0';
X					break;
X				}
X			}
X		}
X		else {
X			if (major & TABLE)
X				print_header(member);
X			else if (major & (PRINT | EXTRACT)) {
X				print(fd, member);
X				did_print = 1;
X			}
X		}
X		/* if we didn't print the member or didn't use it we will
X		 * have to seek over its contents
X		 */
X		if (!did_print)
X			lseek(fd, (long)even(member->ar_size), 1);
X	}
X}
X
Xar_members(ac, argc, argv)
Xint ac, argc;
Xchar **argv;
X{
X	int a, fd, tempfd, rc;
X	struct ar_hdr *member;
X	long *lpos;
X
X	fd = open_archive(afile, WRITE, 0);
X	tempfd = open_archive(tmp1, WRITE, 2);
X	while ((member = get_member(fd)) != NULL) {
X
X		/* if posname specified check for our member */
X		/* if our member save his starting pos in our working file*/
X		if (posname && strncmp(posname, member->ar_name, 14) == 0)
X			pos_offset = tell(tempfd) - MAGICSIZE;
X
X		if (ac < argc) { /* we have a list of members to check */
X			for (a = ac+1; a <= argc; ++a)
X				if (strncmp(basename(argv[a-1]),member->ar_name,14) == 0) {
X					if (major & REPLACE) {
X						if (insert(tempfd,argv[a-1],"r", member) < 0)
X							copy_member(fd, tempfd, member);
X						else 
X							lseek(fd, (long)even(member->ar_size), 1);
X					}
X					else if (major & MOVE) {
X						/* cheat by saving pos in archive */
X						addmove((tell(fd) - sizeof(struct ar_hdr)));
X						lseek(fd, (long)even(member->ar_size), 1);
X					}
X					*argv[a-1] = '\0';
X					break;
X				}
X		}
X		if (ac >= argc || a > argc) 	/*nomatch on a member name */
X			copy_member(fd, tempfd, member);
X		else if (major & DELETE) {
X			if (verbose) fprintf(stdout,"d - %-14.14s\n",member->ar_name);
X			lseek(fd, (long)even(member->ar_size), 1);
X		}
X	}
X	if (major & MOVE) {
X		if (posname == NULL) 
X			pos_offset = lseek(fd, 0l, 2);
X		else if (pos_offset == (-1)) {
X			fprintf(stderr,"Error: %s cannot find file %-14.14s\n",progname,posname);
X			quit(mypid,SIGINT);
X		}
X		tempfd = ar_move(tempfd, fd, moves);
X	}
X	else if (major & REPLACE) {
X		/* take care to add left overs */
X		/* if the posname is not found we just add to end of ar */
X		if (posname && pos_offset != (-1)) {
X			tempfd = ar_insert(tempfd, ac, argc, argv);
X		}
X		else {
X			for (a = ac+1; a <= argc; ++a)
X				if (*argv[a-1]) {
X					insert(tempfd, argv[a-1], "a", (struct ar_hdr *)NULL);
X					*argv[a-1] = '\0';
X				}
X		}
X	}
X	fd = rebuild(fd, tempfd);
X	close(fd);
X}
X
Xappend_members(ac, argc, argv)
Xint ac, argc;
Xchar **argv;
X{
X	int a, fd;
X	struct ar_hdr *member;
X	
X	/* quickly append members don't worry about dups in ar */
X	fd = open_archive(afile, WRITE, 0);
X	if (ac < argc) {
X		if (odd(lseek(fd, 0l, 2)))
X			mwrite(fd, buffer, 1);
X		/* while not end of member list insert member at end */
X		for (a = ac+1; a <= argc; ++a) {
X			insert(fd, argv[a-1], "a", (struct ar_hdr *)NULL);
X			*argv[a-1] = '\0';
X		}
X	}
X	close(fd);
X}
X
X
Xchar *basename(path)
Xchar *path;
X{
X  register char *ptr = path;
X  register char *last = (char *)NULL;
X
X  while (*ptr != '\0') {
X	if (*ptr == '/')
X		last = ptr;
X	ptr++;
X  }
X  if (last == (char *)NULL)
X	return path;
X  if (*(last + 1) == '\0') {
X	*last = '\0';
X	return basename(path);
X  }
X  return last + 1;
X}
X
X
X#define MINUTE	60L
X#define HOUR	(60L * MINUTE)
X#define DAY	(24L * HOUR)
X#define YEAR	(365L * DAY)
X#define LYEAR	(366L * DAY)
X
Xint mo[] = {
X  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X};
X
Xchar *moname[] = {
X  " Jan ", " Feb ", " Mar ", " Apr ", " May ", " Jun ",
X  " Jul ", " Aug ", " Sep ", " Oct ", " Nov ", " Dec "
X};
X
X/* Print the date.  This only works from 1970 to 2099. */
Xprint_date(t)
Xlong t;
X{
X  int i, year, day, month, hour, minute;
X  long length, time(), original;
X
X  year = 1970;
X  original = t;
X  while (t > 0) {
X	length = (year % 4 == 0 ? LYEAR : YEAR);
X	if (t < length)
X		break;
X	t -= length;
X	year++;
X  }
X
X /* Year has now been determined.  Now the rest. */
X  day = (int) (t / DAY);
X  t -= (long) day * DAY;
X  hour = (int) (t / HOUR);
X  t -= (long) hour * HOUR;
X  minute = (int) (t / MINUTE);
X
X /* Determine the month and day of the month. */
X  mo[1] = (year % 4 == 0 ? 29 : 28);
X  month = 0;
X  i = 0;
X  while (day >= mo[i]) {
X	month++;
X	day -= mo[i];
X	i++;
X  }
X
X  /* At this point, 'year', 'month', 'day', 'hour', 'minute'  ok */
X  fprintf(stdout, "%s%2.2d ",moname[month],++day);
X  if (time((long *)NULL) - original >= YEAR / 2L)
X	fprintf(stdout,"%4.4D ",(long)year);
X  else 
X	fprintf(stdout,"%02.2d:%02.2d ",hour, minute);
X}
X
+ END-OF-FILE ar.c
chmod 'u=rw,g=r,o=r' 'ar.c'
set `wc -c 'ar.c'`
count=$1
case $count in
19498)	:;;
*)	echo 'Bad character count in ''ar.c' >&2
		echo 'Count should be 19498' >&2
esac
echo Extracting 'lorder.c'
sed 's/^X//' > 'lorder.c' << '+ END-OF-FILE ''lorder.c'
X/*
X * lorder: find ordering relations for object library
X *
X * author:	Monty Walls
X * written:	1/29/88
X * Copyright:	Copyright (c) 1988 by Monty Walls.
X *		Not derived from licensed software.
X *
X *		Permission to copy and/or distribute granted under the
X *		following conditions:
X *	
X *		1). This notice must remain intact.
X *		2). The author is not responsible for the consequences of use
X *			this software, no matter how awful, even if they
X *			arise from defects in it.
X *		3). Altered version must not be represented as being the 
X *			original software.
X *
X * change log:
X *		corrected & rewrote scanner to avoid lex. - 2/22/88 - mrw
X *		oops reversed output filename order. 3/14/88 - mrw
X *		progname = argv[0] - should be first. 5/25/88 - mbeck
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <ar.h>
X
X#define MAXLINE		256
X
XFILE *lexin;
Xchar *yyfile;
Xchar *tmpfile;
Xchar *progname;
Xchar template[] = "lorder.XXXXXX";
X
Xstruct filelist {
X	char *name;
X	struct filelist *next;
X};
X
Xstruct node {
X	char *name;
X	char *file;
X	struct filelist *list;
X	struct node *left, *right;
X};
X
Xstruct filelist *list;
Xstruct node *tree, *lastnode;
Xextern char *malloc(), *mktemp();
Xextern FILE *popen(), *fopen();
Xextern char *addfile();
Xextern void user_abort();
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int i;
X	char cmdstr[MAXLINE];
X
X	progname = argv[0];
X	if (argc > 1) {
X		signal(SIGINT, user_abort);
X		for (i = 1; argv[i] && *argv[i]; ++i ) {
X			/* the following code is caused by 
X			 * not enough memory on floppy systems.
X			 *
X			 * so instead of ar | libupack ->to us. we use
X			 * ar >tmpfle; libupack <tmpfile ->to us
X			 */
X			if (is_liba(argv[i])) {
X				tmpfile = mktemp(template);
X				sprintf(cmdstr,"ar pv %s >%s",argv[i],tmpfile);
X				system(cmdstr);
X				sprintf(cmdstr,"libupack <%s",tmpfile);
X			}
X			else {
X				yyfile = addfile(argv[i]);
X				sprintf(cmdstr, "libupack <%s", argv[i]);
X			}
X			if ((lexin = popen(cmdstr, "r")) != (FILE *)NULL) {
X				while (yylex() != EOF) ;
X				pclose(lexin);
X				if (tmpfile)
X					unlink(tmpfile);
X			}
X			else {
X				fprintf(stderr,"Error: %s could not open %s\n",progname, argv[i]);
X				exit(1);
X			}
X		}
X		printtree(tree);
X		/* then print list of files for ar also */
X		for (; list; list = list->next) 
X			fprintf(stdout,"%s %s\n",list->name, list->name);
X	}
X	else {
X		fprintf(stderr,"Usage: %s file ....\n",progname);
X		exit(1);
X	}
X}
X
Xvoid
Xuser_abort()
X{
X	unlink(tmpfile);
X	exit(1);
X}
X
Xchar *
Xxalloc(n)
Xint n; 
X{
X	char *p;
X
X	if ((p = malloc(n)) == (char *)NULL) {
X		fprintf(stderr, "Error %s - out of memory\n", progname);
X		exit(1);
X	}
X	return (p);
X}
X
Xint
Xis_liba(s)	/* error handling done later */
Xchar *s;
X{
X	unsigned short key;
X	FILE *fp;
X	int ret = 0;
X
X	if ((fp = fopen(s,"r")) != (FILE *)NULL) {
X		fread(&key, sizeof(key), 1, fp);
X		if (key == ARMAG) ret = 1;
X		fclose(fp);
X	}
X	return (ret);
X}
X
Xchar *
Xstrsave(s)
Xchar *s;
X{
X	char *p;
X
X	p = xalloc(strlen(s) + 1);
X	strcpy(p,s);
X	return (p);
X}
X
Xchar *
Xaddfile(s)
Xchar *s;
X{
X	struct filelist *p;
X
X	p = (struct filelist *)xalloc(sizeof(struct filelist));
X	p->name = strsave(s);
X	if (list)
X		p->next = list;
X	else
X		p->next = NULL;
X	list = p;
X	return (p->name);
X}
X
Xprinttree(t)
Xstruct node *t;
X{
X	struct filelist *fp;
X
X	if (t) {
X		if (t->file) {
X			for (fp = t->list; fp && fp->name; fp = fp->next)
X				if (t->file != fp->name)
X					fprintf(stdout,"%s %s\n",fp->name, t->file);
X		}
X		printtree(t->right);
X		printtree(t->left);
X	}
X}
X
X
Xstruct node *
Xfinddef(s)
Xchar *s;
X{
X	struct node *n;
X	int cmp;
X
X	if (tree) {
X		lastnode = n = tree;
X		while (n && n->name) {
X			lastnode = n;
X			if (!(cmp=strcmp(s,n->name)))
X				return (n);
X			else if (cmp > 0)
X				n = n->left;
X			else 
X				n = n->right;
X		}			
X	}
X	return ((struct node *)NULL);
X}
X
Xstruct node *
Xmakedef(s)
Xchar *s;
X{
X	struct node *n;
X	int cmp;
X
X	n = (struct node *)xalloc(sizeof(struct node));
X	n->name = strsave(s);
X	n->left = (struct node *)NULL;
X	n->right = (struct node *)NULL;
X	if (tree) {
X		cmp = strcmp(s, lastnode->name);
X		if (cmp > 0)
X			lastnode->left = n;
X		else
X			lastnode->right = n;
X	}
X	else
X		tree = n;
X
X	return (n);
X}
X
Xvoid
Xdodef(s) 
Xchar *s;
X{
X	struct node *n;
X
X	if (n = finddef(s)) {
X		if (n->file != NULL) 
X			fprintf(stderr,"Error %s - %s defined twice in %s and %s", progname, s, n->file, yyfile);
X		else
X			n->file = yyfile;
X	}
X	else {
X		n = makedef(s);
X		n->file = yyfile;
X		n->list = (struct filelist *)NULL;
X	}
X}
X
Xvoid
Xusedef(s) 
Xchar *s;
X{
X	struct node *n;
X	struct filelist *fp, *lastfp;
X
X	if (n = finddef(s)) {
X		/* scan file list for match */
X		if (n->list) {
X			for (fp = n->list; fp ; fp = fp->next) {
X				if (fp->name == yyfile) {
X					return;
X				}
X				lastfp = fp;
X			}
X			/* reached here with no match */
X			lastfp->next = (struct filelist *)xalloc(sizeof(struct filelist));
X			lastfp->next->name = yyfile;
X			lastfp->next->next  = (struct filelist *)NULL;
X		}
X		else {
X			/* empty list so far */
X			n->list = (struct filelist *)xalloc(sizeof(struct filelist));
X			n->list->name = yyfile;
X			n->list->next = (struct filelist *)NULL;
X		}
X	}
X	else {
X		n = makedef(s);
X		n->file = (char *)NULL;
X		n->list = (struct filelist *) xalloc(sizeof(struct filelist));
X		n->list->name = yyfile;
X		n->list->next = (struct filelist *)NULL;
X	}
X}
X
X/*
X * yylex - scanner for lorder
X *
X */
X#define MAXNAME 33
X#define is_first_char(c)	((c) == '.' || (c) == '_')
X#define is_second_char(c)	((c) == '_' || isalpha((c)))
X#define is_other_char(c)	((c) == '_' || isalnum((c)))
X
Xint yylex()
X{
X	int col = 0;
X	int i = 0;
X	int is_member = 0;
X	int in_define = 0;
X	int lastch = 0;
X	char s[MAXNAME];
X
X
X	while ((lastch = fgetc(lexin)) != EOF) {
X		col++;				/* increment col */
X		if (isspace(lastch)) {
X			EOS:			/* eos comes here */
X			if (i) {		/* we have a string */
X				s[i] = '\0';	/* set eos */
X				i = 0;
X				/*
X				 * if we are in a define use dodef to add location
X				 *   of defining member and global to symbol table.
X				 */
X				if (in_define)
X					dodef(s);
X				/*
X				 * if we are on a 'p -' line for an ar lib define
X				 *   this member as the file we are using.
X				 */
X				else if (is_member > 0) {
X					is_member = 0;
X					yyfile = addfile(s);
X				}
X				/*
X				 * if we have a '.define' mark this line as in_define.
X				 */
X				else if (strcmp(s,".define") == 0)
X					in_define = 1;
X				/*
X				 * just a reference in the code to a var, so add this
X				 *   reference to our symbol table.
X				 */
X				else
X					usedef(s);
X			}
X			/*
X			 * we are at the eol: reset our counters and switches
X			 */
X			if (lastch == '\n') {
X				col = 0;
X				is_member = 0;
X				in_define = 0;
X			}
X			/*
X			 * lets do another character
X			 */
X			continue;
X		}
X		/*
X		 *  not a space and i == 0
X		 */
X		if (i == 0) {
X			/*
X			 *  are we seeing 'p' in col 1
X			 */
X			if (lastch == 'p' && col == 1) {
X				is_member = -1;
X				continue;
X			}
X			/*
X			 *  are we seeing '-' that follows 'p' in col 1
X		 	 */
X			else if (lastch == '-' && is_member < 0 && col == 3) {
X				is_member = 1;
X				continue;
X			}
X			/*
X			 *  if we have seen 'p -' now we are reading the name or
X			 *    the first character of a global symbol
X			 */
X			if (is_member > 0 || is_first_char(lastch)) {
X				s[i++] = lastch;
X				if (is_member < 0) 
X					is_member = 0;
X			}
X			continue;
X		}
X		/*
X		 *  do the second char of a name 
X		 */
X		else if (i == 1) {
X			if (is_member > 0 || is_second_char(lastch)) {
X				s[i++] = lastch;
X			}
X			else
X				is_member = 0;
X		}
X		/*
X		 *  do the rest of a symbol or member name
X		 */
X		else if (is_member > 0 || is_other_char(lastch)) {
X			s[i++] = lastch;
X			continue;
X		}
X		else
X	            goto EOS;
X	}
X	/*
X	 *  returns EOF on end of file
X	 */
X	return (lastch);
X}
X
+ END-OF-FILE lorder.c
chmod 'u=rw,g=r,o=r' 'lorder.c'
set `wc -c 'lorder.c'`
count=$1
case $count in
7645)	:;;
*)	echo 'Bad character count in ''lorder.c' >&2
		echo 'Count should be 7645' >&2
esac
echo Extracting 'tsort.c'
sed 's/^X//' > 'tsort.c' << '+ END-OF-FILE ''tsort.c'
X/*
X * tsort - do a topological sort on the ordered pairs of names
X *
X * syntax - tsort [ file ]
X *
X * based on the discussion in 'The AWK programming Language', by
X * Aho, Kernighan, & Weinberger.
X *	
X * author:	Monty Walls
X * written:	1/28/88
X * Copyright:	Copyright (c) 1988 by Monty Walls.
X *		Not derived from licensed software.
X *
X *		Permission to copy and/or distribute granted under the
X *		following conditions:
X *	
X *		1). This notice must remain intact.
X *		2). The author is not responsible for the consequences of use
X *			this software, no matter how awful, even if they
X *			arise from defects in it.
X *		3). Altered version must not be represented as being the 
X *			original software.
X *
X * change log:
X *	possible bug in ungetc(), fixed readone() to avoid - 2/19/88 - mrw
X *	massive design error, rewrote dump logic - 3/15/88 - mrw
X */
X
X#include <stdio.h>
X#include <errno.h>
X#include <ctype.h>
X
X#define printmem(_s)	(fprintf(stdout,"%s ",(_s)))
X#define MAXNAMELEN	32
X#define MAXMEMBERS	1024
X
Xstruct dependents {
X	struct node *nd;
X	struct dependents *next;
X};
X
Xstruct node {
X	char *name;
X	int pcnt, scnt, visited;
X	struct dependents *succ, *pred;
X	struct node *left, *right;
X};
X
Xchar *progname;
X
Xextern struct node *readnode(), *findnode();
Xextern char *malloc(), *xalloc(), *readone(), *strsave();
Xextern struct dependents *finddep();
Xextern void dumptree(), walktree(), emptytree(), addqueue(), walklist();
X
Xextern int errno;
Xextern char *sys_errlist[];
X
Xstruct node *tree, *lastnode, *q[MAXMEMBERS];
Xstruct dependents *lastdepd;
Xint node_cnt, rc, front, back;
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	progname = argv[0];
X	if (argc > 1) 
X		if (freopen(argv[1], "r", stdin) == (FILE *)NULL) {
X			fprintf(stderr,"Error: %s - %s\n",progname, sys_errlist[errno]);
X			exit(1);
X		}
X
X	/* read in the tree of entries */
X	while (readnode() != (struct node *)NULL) 
X		;	
X	dumptree(tree);
X	fflush(stdout);
X
X	if (node_cnt != back) {
X		fprintf(stderr,"Error: %s - input contains a cycle\n",progname);
X		rc = 1;
X	}
X
X	exit(rc);
X}
X
X
Xstruct node *
Xreadnode()
X{
X	char *s1, *s2;
X	register struct node *n1, *n2;
X	struct dependents *pd;
X
X	if ((s1 = readone()) != (char *)NULL) {
X		if ((n1 = findnode(s1)) == (struct node *)NULL) {
X			/* is a new node so build it */
X			n1 = (struct node *)xalloc(sizeof(struct node));
X			n1->name = strsave(s1);
X			n1->succ = (struct dependents *)NULL;
X			n1->pred = (struct dependents *)NULL;
X			n1->left = (struct node *)NULL;
X			n1->right = (struct node *)NULL;
X			n1->pcnt = 0;
X			n1->scnt = 0;
X			n1->visited = 0;
X			linknode(n1);
X		}
X		if ((s2 = readone()) != (char *)NULL) {
X			if ((n2 = findnode(s2)) == (struct node *)NULL) {
X				/* is a new node so build it */
X				n2 = (struct node *)xalloc(sizeof(struct node));
X				n2->name = strsave(s2);
X				n2->succ = (struct dependents *)NULL;
X				n2->pred = (struct dependents *)NULL;
X				n2->left = (struct node *)NULL;
X				n2->right = (struct node *)NULL;
X				n2->pcnt = 0;
X				n2->scnt = 0;
X				n2->visited = 0;
X				linknode(n2);
X			}
X			if (n1 != n2) {
X				if (finddep(n2->pred,s1) == (struct dependents *)NULL) {
X					/* new dependence here */
X					pd = (struct dependents *)xalloc(sizeof(struct dependents));
X					pd->nd = n1;
X					pd->next = (struct dependents *)NULL;
X					n2->pcnt++;
X					if (n2->pred == (struct dependents *)NULL) 
X						n2->pred = pd;
X					else
X						lastdepd->next = pd;
X				}
X				if (finddep(n1->succ,s2) == (struct dependents *)NULL) {
X					/* new dependence here */
X					pd = (struct dependents *)xalloc(sizeof(struct dependents));
X					pd->nd = n2;
X					pd->next = (struct dependents *)NULL;
X					n1->scnt++;
X					if (n1->succ == (struct dependents *)NULL) 
X						n1->succ = pd;
X					else
X						lastdepd->next = pd;
X				}
X			}
X			return (n1);
X		}
X		else
X			return ((struct node *)NULL);
X	}
X	else
X		return ((struct node *)NULL);
X}
X
Xvoid
Xdumptree(top)
Xregister struct node *top;
X{
X	walktree(top);		/* get all entries in order with no predecessors */
X	for (front = 1; front <= back; front++) {
X		printmem(q[front]->name);
X		walklist(q[front]->succ);
X	}
X	emptytree(top);		/* dumps all isolated nodes left over */
X}
X
Xvoid
Xwalklist(s)
Xregister struct dependents *s;
X{
X	for (; s; s = s->next) {
X		if (--s->nd->pcnt == 0) {
X			addqueue(s->nd);
X			s->nd->visited = 1;
X			walklist(s->nd->succ);
X		}
X	}
X}
X
Xvoid
Xwalktree(t)
Xregister struct node *t;
X{
X	if (t) {
X		node_cnt++;
X		walktree(t->right);
X		if (t->pcnt == 0) {
X			addqueue(t);
X			t->visited = 1;
X		}
X		walktree(t->left);
X	}
X}
X
Xvoid
Xemptytree(t)
Xregister struct node *t;
X{
X	struct dependents *s;
X
X	/* t  - represents a remaining entry which is in a cycle */
X	if (t) {
X		emptytree(t->right);
X		if (t->visited == 0) {
X			t->visited = 1;
X			printmem(t->name);
X			for (s = t->succ; s; s = s->next)
X				if (s->nd->visited == 0) {
X					fprintf(stderr,"Error: %s - %s and %s are in a cycle\n",progname, t->name, s->nd->name);
X				}
X		}
X		emptytree(t->left);
X	}
X}
X
Xvoid
Xaddqueue(t)
Xstruct node *t;
X{
X	if (++back >= MAXMEMBERS) {
X		fprintf(stderr,"Error: %s - member queue overflow\n",progname);
X		exit(1);
X	}
X	else
X		q[back] = t;
X}
X
Xchar *
Xreadone()
X{
X	register int c, n = 0;
X	static char name[MAXNAMELEN];
X
X	/* eat up leading spaces */
X	while ((c = getchar()) != EOF && isspace(c))
X		;
X
X	if (c != EOF) {
X		name[n++] = c;	/* save into name first non blank */
X		while ((c = getchar()) != EOF && !isspace(c)) {
X			if (n < MAXNAMELEN)
X				name[n++] = c;
X		}
X		name[n] = '\0';
X		return (name);
X	}
X	else
X		return ((char *)NULL);
X
X}
X
Xstruct node *
Xfindnode(s)
Xchar *s;
X{
X	register struct node *n;
X	register int cmp;
X
X	if (tree) {
X		lastnode = n = tree;
X		while (n && n->name) {
X			lastnode = n;
X			if (!(cmp = strcmp(s,n->name)))
X				return (n);
X			else if (cmp > 0)
X				n = n->left;
X			else
X				n = n->right;
X		}
X	}
X	return ((struct node *)NULL);
X}
X
Xstruct dependents *
Xfinddep(dp, s)
Xregister struct dependents *dp;
Xregister char *s;
X{
X	lastdepd = (struct dependents *)NULL;
X	while (dp && dp->nd) {
X		lastdepd = dp;
X		if (strcmp(dp->nd->name,s) == 0)
X			return (dp);
X		else {
X			dp = dp->next;
X		}
X	}
X	return ((struct dependents *)NULL);
X}
X
Xlinknode(n)
Xregister struct node *n;
X{
X	register int cmp;
X
X	if (tree) {
X		cmp = strcmp(n->name,lastnode->name);
X		if (cmp > 0)
X			lastnode->left = n;
X		else
X			lastnode->right = n;
X	}
X	else
X		tree = n;
X}
X
Xchar *
Xxalloc(n)
Xint n;
X{
X	char *p;
X	
X	if ((p = malloc(n)) != (char *)NULL)
X		return (p);
X	else {
X		fprintf(stderr,"Error: %s out of memory\n",progname);
X		exit(1);
X	}
X}
X
Xchar *
Xstrsave(s)
Xchar *s;
X{
X	char *p;
X
X	p = xalloc(strlen(s)+1);
X	strcpy(p,s);
X	return (p);
X}
+ END-OF-FILE tsort.c
chmod 'u=rw,g=r,o=r' 'tsort.c'
set `wc -c 'tsort.c'`
count=$1
case $count in
6563)	:;;
*)	echo 'Bad character count in ''tsort.c' >&2
		echo 'Count should be 6563' >&2
esac
exit 0