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