ast@cs.vu.nl (Andy Tanenbaum) (05/30/88)
Here is a listing of new files in commands: -rw-r--r-- 1 ast 1615 May 16 15:03 ascii.c -rw-r--r-- 1 ast 14134 May 16 15:03 badblocks.c -rw-r--r-- 1 ast 749 May 16 15:03 chgrp.c -rw-r--r-- 1 ast 38532 May 16 15:03 compress.c -rw-r--r-- 1 ast 3885 May 16 15:03 diskcheck.c -rw-r--r-- 1 ast 42036 May 16 15:03 ed.c -rw-r--r-- 1 ast 693 May 16 15:03 factor.c -rw-rw-rw- 1 ast 5443 May 29 15:13 fgrep.c -rw-r--r-- 1 ast 3073 May 16 15:03 file.c -rw-r--r-- 1 ast 7542 May 16 15:04 lorder.c -rw-r--r-- 1 ast 2025 May 16 15:04 prep.c -rw-r--r-- 1 ast 441 May 16 15:04 readall.c -rw-r--r-- 1 ast 45735 May 16 15:04 sed.c -rw-r--r-- 1 ast 4052 May 16 15:04 strings.c -rw-r--r-- 1 ast 6938 May 16 15:04 treecmp.c -rw-r--r-- 1 ast 6563 May 16 15:04 tsort.c -rw-r--r-- 1 ast 437 May 16 15:04 tty.c -rw-r--r-- 1 ast 279 May 16 15:04 whoami.c I believe all of these have been posted. Correct me if I am wrong. Below are some of the smaller ones. : 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 'ascii.c' sed 's/^X//' > 'ascii.c' << '+ END-OF-FILE ''ascii.c' X/* ascii - list lines with/without ASCII chars Author: Andy Tanenbaum */ X X#define BUFSIZE 30000 X Xchar buf[BUFSIZE]; /* input buffer */ Xchar *next; /* start of line */ Xchar *limit; /* last char of line */ Xint count; /* # chars in buffer not yet processed */ Xint used; /* how many chars used at start of buf */ Xint eof; /* set when eof seen */ Xint nflag; /* set if -n option given */ Xint exitstatus; /* 0 if pure ASCII, 1 if junk seen */ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X int yes; X char *p; X X if (argc > 3) usage(); X if (strcmp(argv[1], "-n") == 0) nflag++; X X if ((argc == 2 && nflag == 0) || argc == 3) { X close(0); X if (open(argv[argc-1], 0) < 0) { X std_err("ascii: cannot open "); X std_err(argv[1]); X std_err("\n"); X exit(1); X } X } X X while(eof == 0) { X yes = getline(); X if (nflag != yes) output(); X next = limit; X } X exit(exitstatus); X} X Xint getline() X{ X char *p, c; X int asc = 1; X X if (count == 0) load(); X if (eof) exit(exitstatus); X X p = next; X while (count > 0) { X c = *p++; X if (c & 0200) {asc = 0; exitstatus = 1;} X count--; X if (c == '\n') { X limit = p; X return(asc); X } X if (count == 0) { X /* Move the residual characters to the bottom of buf */ X used = &buf[BUFSIZE] - next; X copy(next, buf, used); X load(); X p = &buf[used]; X used = 0; X if (eof) return(asc); X } X } X} X Xload() X{ X count = read(0, &buf[used], BUFSIZE-used); X if (count <= 0) eof = 1; X next = buf; X} X Xoutput() X{ X write(1, next, limit-next); X} X Xusage() X{ X std_err("Usage: ascii [-n] file\n"); X exit(1); X} X Xcopy(s,d,ct) Xregister char *s, *d; Xint ct; X{ X while (ct--) *d++ = *s++; X} X + END-OF-FILE ascii.c chmod 'u=rw,g=r,o=r' 'ascii.c' set `wc -c 'ascii.c'` count=$1 case $count in 1615) :;; *) echo 'Bad character count in ''ascii.c' >&2 echo 'Count should be 1615' >&2 esac echo Extracting 'badblocks.c' sed 's/^X//' > 'badblocks.c' << '+ END-OF-FILE ''badblocks.c' X/* badblocks - collect bad blocks in a file Author: Jacob Bunschoten */ X X/* Usage "badblocks block_special" */ X X/* X * This program is written to handle BADBLOCKS on a hard or floppy disk. X * The program asks for block_numbers. These numbers can be obtained with X * the program disk_check; written by A. Tanenbaum. It then creates a X * file on the disk containing up to 7 bad blocks. X * X * BUG: X * X * When the zone_size > block_size it can happen that X * the zone is already allocated. This means some X * file is using this zone and may use all the blocks including X * the bad one. This can be cured by inspecting the zone_bitmap X * (is already done) and change the file if this zone is used. X * This means that another zone must be allocated and X * the inode wich claims this zone must be found and changed. X * X */ X X#include <sys/stat.h> X#include <stdio.h> X#include <minix/type.h> X X X/* Super block table. X * X * The disk layout is: X * X * Item # block X * boot block 1 X * super block 1 X * inode map s_imap_blocks X * zone map s_zmap_blocks X * inodes (s_ninodes + 1 + INODES_PER_BLOCK - 1)/INODES_PER_BLOCK X * unused X * data zones (s_nzones - s_firstdatazone) << s_log_zone_size X * X */ X X Xstruct super_block { X inode_nr s_ninodes; /* # usable inodes on the minor device */ X zone_nr s_nzones; /* total device size, including bit maps etc */ X unshort s_imap_block; /* # of block used by inode bit map */ X unshort s_zmap_block; /* # of block used by zone bit map */ X zone_nr s_firstdatazone; /* number of first data zone */ X short int s_log_zone_size; /* log2 of block/zone */ X file_pos s_max_size; /* maximum file size on this device */ X int s_magic; /* magic number to recognize super-block */ X} super_block; X X#define SUPER_MAGIC 0x137F X X X#define NR_ZONE_NUMS 9 X#define NR_DZONE_NUMS (NR_ZONE_NUMS -2 ) X Xstruct d_inode { /* disk inode. */ X mask_bits i_mode; /* file type, protection, etc. */ X uid i_uid; /* user id of the file's owner */ X file_pos i_size; /* current file size in bytes */ X real_time i_modtime; /* when was file data last changed */ X gid i_gid; /* group number */ X links i_nlinks; /* how many links to this file */ X zone_nr i_zone[NR_ZONE_NUMS]; /* blocks nums for direct, ind, and dbl ind */ X} d_inode; X X X X#define OK 0 X#define NOT_OK 1 X#define QUIT 2 X X#define READ 0 X#define WRITE 1 X X#define HARMLESS 0 X#define DIR_CREATED 1 X#define DEV_MOUNTED 2 X#define FILE_EXISTS 3 X#define SUCCESS 4 X X#define BLOCK_SIZE 1024 X#define INODES_PER_BLOCK (BLOCK_SIZE/sizeof(struct d_inode)) X X#define INODE_SIZE (sizeof (struct d_inode) ) X#define SUPER_SIZE (sizeof (struct super_block) ) X#define INT_SIZE (sizeof (int) ) X X /* ====== globals ======= */ X Xchar *dev_name; Xchar f_name[] = ".Bad_XXXXXX"; Xchar file_name[50]; Xchar dir_name[] = "/tmpXXXXXX"; X Xint block[NR_DZONE_NUMS+1]; /* last block contains zero */ X XFILE *fp, *fopen(); Xint fd; Xint eofseen; /* set if '\n' seen */ X Xstruct stat stat_buf; Xstruct d_inode *ip; Xstruct super_block *sp; X Xextern char *strcat(); X#ifdef DEBUG Xextern char *itoa(); Xchar *utobin(); X#endif X X /* ====== super block routines ======= */ X Xrw_super(flag) /* read or write a superblock */ X{ X int rwd; X X lseek(fd, 0L, 0); /* rewind */ X lseek(fd, (long) BLOCK_SIZE, 0); /* seek */ X X if (flag == READ ) X rwd = read( fd, sp, SUPER_SIZE ); X else X rwd = write (fd, sp, SUPER_SIZE ); X if (rwd != SUPER_SIZE ) { /* ok ? */ X printf("Bad %s in get_super() (should be %d is %d)\n", X flag == READ ? "read" : "write", X SUPER_SIZE, rwd ); X done(DIR_CREATED); X } X} X Xget_super() X /* get super_block. global pointer sp is used */ X{ X rw_super(READ); X X if (sp->s_magic != SUPER_MAGIC) { /* check */ X printf("Bad magic number in super_block?!\n"); X done(DIR_CREATED); X } X#ifdef DEBUG X /* give some information of the super_block */ X X printf("# Inodes %d\n",sp->s_ninodes); X printf("# Zones %d\n",sp->s_nzones); X printf("# inode map block %d\n",sp->s_imap_block); X printf("# zone map block %d\n",sp->s_zmap_block); X printf(" Firstdatazone %d\n\n",sp->s_firstdatazone); X#endif X} X X Xput_super() X{ X rw_super(WRITE); X} X X /* ========== inode routines =========== */ X Xrw_inode(stat_ptr, rw_mode) X struct stat *stat_ptr; X{ X int rwd, i_num; X long blk, offset; X X X i_num = stat_ptr->st_ino; X X blk = (long ) (2 + sp->s_imap_block + sp->s_zmap_block); X blk += (long) ((i_num -1 ) / INODES_PER_BLOCK ); X blk *= (long) (BLOCK_SIZE); /* this block */ X X offset = (long) ((i_num -1 ) % INODES_PER_BLOCK); X offset *= (long) (INODE_SIZE); /* and this offset */ X X lseek(fd, 0L, 0); /* rewind */ X lseek(fd, (long) (blk + offset), 0); /* seek */ X X /* pointer is at the inode */ X if (rw_mode == READ) { /* read it */ X rwd = read(fd, ip, INODE_SIZE ); X } else { /* write it */ X rwd = write (fd, ip, INODE_SIZE ); X } X if (rwd != INODE_SIZE ) { /* ok ? */ X printf("Bad %s in get_inode()\n",(rw_mode == READ)? "read": X "write"); X done(DIR_CREATED); X } X X} X Xget_inode( stat_ptr) X struct stat *stat_ptr; X{ X X#ifdef DEBUG X int tst; X#endif X int cnt; X X rw_inode(stat_ptr, READ); X X#ifdef DEBUG X tst = 0; X printf("\ni_zone[0]=%d, st_rdev=%d\n", (int) ip->i_zone[0], X (int) stat_ptr->st_rdev); X printf("File size %D\n",(long) ip->i_size); X for (cnt =0 ; cnt < NR_ZONE_NUMS; cnt++ ) X if (ip->i_zone[cnt] != 0 ) { X tst = 1; X printf("zone[%d] contains %d\n",cnt, X (int) ip->i_zone[cnt]); X } X if ( tst ) { X printf("Possible wrong inode. Inode must be clean"); X done(DIR_CREATED); X } X printf("\n"); X#endif X for (cnt=0; cnt < NR_ZONE_NUMS; cnt++) X ip->i_zone[cnt] = 0; /* Just to be safe */ X} X Xput_inode(stat_ptr) X struct stat *stat_ptr; X{ X rw_inode(stat_ptr, WRITE); X} X X X /* ============== main program ================= */ Xmain(argc, argv) X char *argv[]; X{ X int cnt, blk_nr, finished; X struct stat dev_stat; X X sp = &super_block; X ip = &d_inode; X X if (argc != 2) { X fprintf(stderr,"Usage: %s block_special\n",argv[0]); X done(HARMLESS); X } X X /* Do some test. */ X if (geteuid()) { X printf("Sorry, not in superuser mode \n"); X printf("Set_uid bit must be on or you must become super_user\n"); X done(HARMLESS); X } X X dev_name = argv[1]; X mktemp(dir_name); X#ifdef DEBUG X printf("Dir_name is %s\n",dir_name); X#endif X if (mknod(dir_name,040777,0) == -1) { X fprintf(stderr,"%s is already used in system\n",dir_name); X done(HARMLESS); X } X X /* Mount device. This call may fail. */ X mount(dev_name, dir_name, 0); X /* succes. dev was mounted, try to umount */ X X /* Umount device. Playing with the file system while X * other processes have access to this device X * is asking for trouble X */ X if (umount(dev_name) == -1 ) { X printf("Could not umount device %s.\n",dev_name); X done(HARMLESS); X } X X X mktemp(f_name); X /* create "/tmpXXXXpid/.BadXXpid" */ X strcat(file_name, dir_name); X strcat(file_name, "/"); X strcat(file_name, f_name); X X if (mount(dev_name, dir_name, 0) == -1) { /* this call should work */ X fprintf(stderr,"Could not mount device anymore\n"); X done(HARMLESS); X } X if (stat( file_name, &stat_buf) != -1 ) { X printf("File %s does already exists\n",file_name); X done(DEV_MOUNTED); X } X if ( (fp = fopen( file_name, "w" )) == NULL) { X printf("Can not create file %s\n", file_name); X done(DEV_MOUNTED); X } X chmod(file_name, 0); /* "useless" file */ X if (stat( file_name, &stat_buf) == -1 ) { X printf("What? Second call from stat failed\n"); X done(FILE_EXISTS); X } X /* stat buf must be safed. We can now calculate the inode on disk */ X fclose(fp); X X /* ===== the badblock file is created ===== */ X X if (umount(dev_name) == -1 ) { X printf("Can not umount device anymore??? \n"); X done(DIR_CREATED); X } X X if ( (fd = open(dev_name, 2)) == -1 ) { X printf("Can not open device %s\n",dev_name); X done(DEV_MOUNTED); X } X if (fstat(fd, &dev_stat) == -1 ) { X printf("fstat on device %s failed\n",dev_name); X done(DEV_MOUNTED); X } X if ( (dev_stat.st_mode & S_IFMT ) != S_IFBLK ) { X printf("Device \"%s\" is not a block_special.\n",dev_name); X done(DEV_MOUNTED); X } X X get_super(); X if (sp->s_log_zone_size ) { X printf("Block_size != zone_size."); X printf("This program can not handle it\n"); X done(DIR_CREATED); X } X get_inode(&stat_buf); X X for(finished = 0; !finished; ) { X printf("Give up to %d bad block numbers separated by spaces\n", X NR_DZONE_NUMS); X reset_blks(); X cnt = 0; /* cnt keep track of the zone's */ X while (cnt < NR_DZONE_NUMS ) { X int tst; X X blk_nr = rd_num(); X tst = blk_ok(blk_nr); X /* test if this block is free */ X if (tst == OK ) { X cnt++; X save_blk( blk_nr ); X } X else if (tst == QUIT ) X break; X } X show_blks(); X if (!cnt) X done(FILE_EXISTS); X switch( ok("All these blocks ok <y/n/q> (y:Device will change) ") ) { X case OK: finished = 1; break; X case NOT_OK: break; X case QUIT: done(FILE_EXISTS); X } X } X X modify(cnt); X close(fd); /* free device */ X done(FILE_EXISTS); X} X Xmodify(nr_blocks) X{ X int i; X X if (nr_blocks == 0) return; X for (i=0; i<nr_blocks; i++) { X set_bit(block[i]); X ip->i_zone[i] = block[i]; X } X ip->i_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */ X put_inode(&stat_buf); /* save the inode on disk */ X put_super(); /* bit_maps too */ X} X X Xstatic blk_cnt=0; X Xsave_blk(blk_num) X int blk_num; X{ X block[blk_cnt++] = blk_num; X} X Xreset_blks() X{ X int i; X X for (i=0;i<=NR_DZONE_NUMS;i++) X block[i] = 0; /* Note: Last block_number is set to zero */ X blk_cnt =0; X} X Xshow_blks() X{ X int i; X X for (i=0; i<blk_cnt; i++) X printf("Block[%d] = %d\n",i,block[i]); X} X Xblk_is_used(blk_num) X int blk_num; X{ /* return TRUE(1) if used */ X int i; X X for (i=0; block[i] && block[i] != blk_num; i++); X return (block[i] != 0)? 1: 0; X} X X X /* ===== bitmap handling ====== */ X X#define BIT_MAP_SHIFT 13 X#define INT_BITS (INT_SIZE << 3) X Xblk_ok(num) /* is this zone free (y/n) */ X int num; X{ X long blk_offset; X int rd; X int block, offset, words, bit, tst_word; X int z_num; X X if ( num < 0 ) { X return QUIT; /* negative number is not allowed */ X } X if (blk_is_used (num)) { X printf("Duplicate block (%d) given\n",num); X return NOT_OK; X } X /* assumption zone_size == block_size */ X X z_num = num + 1 - sp->s_firstdatazone; /* account offset */ X X /* calculate the word in the bitmap */ X block = z_num >> BIT_MAP_SHIFT; /* which block */ X offset = z_num - (block << BIT_MAP_SHIFT); /* offset */ X words = offset/INT_BITS; /* which word */ X X blk_offset = (long) (2 + sp->s_imap_block); /* zone map */ X blk_offset *= (long) BLOCK_SIZE; /* of course in block */ X blk_offset += (long) (words * INT_SIZE) ; /* offset */ X X X lseek(fd, 0L, 0); /* rewind */ X lseek(fd, blk_offset, 0); /* set pointer at word */ X X rd = read(fd, &tst_word, INT_SIZE ); X if (rd != INT_SIZE ) { X printf("Read error in bitmap\n"); X done(DIR_CREATED); X } X X /* we have the tst_word, check if bit was off */ X bit = offset % INT_BITS; X X if ( ((tst_word >> bit )&01 ) == 0) /* free */ X return OK; X else { X printf("Bad number %d. ",num); X printf("This zone (block) is marked in bitmap\n"); X return NOT_OK; X } X} X Xset_bit(num) /* write in the bitmap */ X int num; X{ X int rwd; X long blk_offset; X int block, offset, words, tst_word, bit; X int z_num; X char wrd_str[17], bit_str[17]; X X z_num = num + 1 - sp->s_firstdatazone; X X block = z_num >> BIT_MAP_SHIFT; /* which block */ X offset = z_num - (block << BIT_MAP_SHIFT); /* offset in block */ X words = offset/INT_BITS; /* which word */ X X blk_offset = (long) (2 + sp->s_imap_block) ; X blk_offset *= (long) BLOCK_SIZE ; X blk_offset += (long) (words * INT_SIZE); X X X lseek(fd, 0L, 0); /* rewind */ X lseek(fd, blk_offset, 0); X X rwd = read(fd, &tst_word, INT_SIZE ); X if (rwd != INT_SIZE ) { X printf("Read error in bitmap\n"); X done(DEV_MOUNTED); X } X X bit = offset % INT_BITS; X if ( ((tst_word >> bit) & 01 ) == 0) { /* free */ X lseek(fd, 0L, 0); /* rewind */ X lseek(fd, blk_offset, 0); X#ifdef DEBUG X printf("blk= %D ", blk_offset); X printf("block= %D ",(long) blk_offset/BLOCK_SIZE); X printf("offset= %d\n",offset); X printf("\t\tword= %s ", utobin(tst_word, wrd_str)); X printf("bit_nr = %d\n", bit); X#endif X tst_word |= (1 << bit); /* not free anymore */ X#ifdef DEBUG X printf("The word is now %s\n", utobin(tst_word, wrd_str) ); X#endif X rwd = write (fd, &tst_word, INT_SIZE ); X if (rwd != INT_SIZE ) { X printf("Bad write in zone map\n"); X printf("Check file system \n"); X done(DIR_CREATED); X } X return; X } X printf("Bit was on block %d; ==> internal error\n",num); X done(DIR_CREATED); X} X X /* ======= interactive interface ======= */ X Xrd_num() /* read a number from stdin */ X{ X static num; X int c; X X if (eofseen) return(-1); X do { X c = getchar(); X if (c == EOF || c == '\n') return(-1); X } while ( c != '-' && (c < '0' || c > '9') ); X X if (c == '-') { X printf("Block numbers must be positive\n"); X exit(1); X } X num = 0; X while (c >= '0' && c <= '9' ) { X num *= 10; X num += c - '0'; X c = getchar(); X if (c == '\n') eofseen = 1; X } X return num; X} X X X Xok(str) Xchar *str; X{ X int c; X X for (;;) { X printf("%s",str); X while ( ( c = getchar() ) != EOF && X c != 'y' && c != 'n' && c != 'q' ) X if (c != '\n' ) X printf(" Bad character %c\n", (char) c); X switch ( c ) { X case EOF: X return QUIT; X case 'y': X return OK; X case 'n': X return NOT_OK; X case 'q': X return QUIT; X#ifdef DEBUG X default: X printf("Unknown option %c\n\t",*c); X#endif X } X printf("\n"); X } X} X X Xdone(nr) Xint nr; X{ X switch(nr) { X case FILE_EXISTS: unlink(file_name); X case SUCCESS: X case DEV_MOUNTED: umount(dev_name); X case DIR_CREATED: unlink(dir_name); X case HARMLESS: ; X } X sync(); X exit(nr == SUCCESS ? 0: 1); X} X X#ifdef DEBUG X /* ===== print integer in binairy format ====== */ X Xchar *chtobin(num, str) X /* char to binair. Convert the numerical value num X to a binairy string (0|1) X str[] is at least 8 chars wide X */ X unsigned char num; X char *str; X{ X int i; X X for (i=7; i>=0; i-- ) { X str[i] = (num&01)? '1':'0'; X num >>= 1; X num &= 0xFF; X } X str[8] = '\0'; X return str; X} X X Xchar *utobin(num, str) X /* unsigned to binair. Used to print a binairy word */ X unsigned num; X char *str; X{ X chtobin( (num >> 8 ) & 0xFF, str); X chtobin( num & 0xFF , &str[8]); X return str; X} X#endif DEBUG + END-OF-FILE badblocks.c chmod 'u=rw,g=r,o=r' 'badblocks.c' set `wc -c 'badblocks.c'` count=$1 case $count in 14134) :;; *) echo 'Bad character count in ''badblocks.c' >&2 echo 'Count should be 14134' >&2 esac echo Extracting 'factor.c' sed 's/^X//' > 'factor.c' << '+ END-OF-FILE ''factor.c' Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X/* Factor a number */ X X long i, n, flag = 0, first(), atol(); X X if (argc != 2 || (n = atol(argv[1])) < 2) { X printf("Usage: factor n (2 <= n < 2**31)\n"); X exit(1); X } X if (n == 2) { X printf("2 is a prime\n"); X exit(0); X } X X while (1) { X i = first(n); X if (i == 0) { X if (flag == 0) X printf("%D is a prime\n", n); X else X printf("%D\n", n); X exit(0); X } X printf("%D ", i); X n = n / i; X flag = 1; X } X} X X Xlong first(k) Xlong k; X{ X/* Return the first factor of k. If it is a prime, return 0; */ X X long i; X X if (k == 2) return(0); X if (k % 2 == 0) return(2); X for (i = 3; i <= k/3; i+=2) X if (k % i == 0) return(i); X return(0); X} + END-OF-FILE factor.c chmod 'u=rw,g=r,o=r' 'factor.c' set `wc -c 'factor.c'` count=$1 case $count in 693) :;; *) echo 'Bad character count in ''factor.c' >&2 echo 'Count should be 693' >&2 esac echo Extracting 'fgrep.c' sed 's/^X//' > 'fgrep.c' << '+ END-OF-FILE ''fgrep.c' X/* fgrep - fast grep Author: Jan Christiaan van Winkel */ X X#include <stdio.h> X Xint argc; Xchar **argv; Xint stringlen,offset,boundary; Xint i,j,k,count,linum; Xchar stringarea[1024]; Xint strptr; Xint strcount; Xunsigned char tbl[32][256]; Xint lengths[32]; Xchar *tststring[32]; Xchar string[512]; Xchar tmpstring[512]; Xint vflag,cflag,nofname,hadone,lflag,nflag,sflag,hflag,eflag,fflag; Xint fp; X Xmain(oargc,oargv) Xint oargc; Xchar * oargv[]; X X{ X int find(); X void exparg(); X void getargs(); X void maktbl(); X void gotone(); X X argc=oargc; X argv=oargv; X getargs(); X X fp=0; X if (i>=argc-2 || hflag) /* stdin, 1 file, hflag */ X nofname=1; X X do { X if(i<argc && (fp=open(argv[i],0)) <0) { X fprintf(stderr, "%s: can't open %s\n",argv[0],argv[i]); X continue; X } X count=0; X linum=0; X X while ((stringlen=getlin(fp,string,512))!=EOF) { X linum++; X for (j=0; j<strcount; j++) { X if (find(tststring[j],tbl[j],lengths[j])!=vflag) { X gotone(); X break; X } X } X if (lflag && count) break; X } X close(fp); X X if (cflag) { X printf("%s: %d times\n",argv[i],count); X } X X if (lflag && count>0) { X printf("%s\n",argv[i]); X } X } while (++i<argc); X X fflush(stdout); X if (hadone) exit(0); else exit(1); X} X Xvoid getargs() X{ X int tmp; X for (i=1; i< argc && argv[i][0]=='-'; i++) { X switch (argv[i][1]) { X case 'e': X eflag=1; X if (fflag) { X fprintf(stderr,"%s: can't have -e and -f at the same time\n",argv[0]); X exit(2); X } X if (i<argc-1) { X i++; X tststring[0]=argv[i]; X strcount=1; X } else { X fprintf(stderr,"%s: not enough arguments\n"); X exit(2); X } X break; X case 'v': X vflag=1; X break; X case 'c': X cflag=1; X break; X case 'l': X lflag=1; X break; X case 's': X sflag=1; X break; X case 'h': X hflag=1; X break; X case 'n': X nflag=1; X break; X case 'f': X fflag=1; X if (eflag) { X fprintf(stderr,"%s: can't have -e and -f at the same time\n",argv[0]); X exit(2); X } X if (i>=argc-1) { X fprintf(stderr,"%s: not enough arguments\n"); X exit(2); X } else { X i++; X if ((fp=open(argv[i],0))<0) { X fprintf(stderr,"%s: can't open %s\n",argv[0],argv[i]); X exit(2); X } X strcount=0; X while ((tmp=getlin(fp,&stringarea[strptr],128))!=EOF) { X tststring[strcount++] = &stringarea[strptr]; X strptr = strptr+tmp+1; X if (strptr>=1024-128 || strcount==32) { X fprintf(stderr,"%s: not enough room\n",argv[0]); X exit(2); X } X } X close(fp); X } X break; X default: X fprintf(stderr,"%s: invalid command line option\n",argv[0]); X exit(2); X break; X } X if (cflag && lflag) { X fprintf(stderr,"%s: cannot have -l and -c at the same time\n",argv[0]); X exit(2); X } X } X X if (! eflag && ! fflag) { X if (i<argc) { X tststring[0]=argv[i++]; X strcount=1; X } else { X fprintf(stderr,"%s: no search string.\n",argv[0]); X exit(2); X } X } X X for (j=0; j<strcount; j++) { X if (tststring[j][0]=='"') { X count=strlen(tststring[j]); X movmem(&tststring[j][1],tststring[j],count-2); X tststring[j][count-2]='\0'; X } X maktbl(tststring[j],tbl[j],&lengths[j]); X } X} X X Xmovmem(src, dst, len) Xchar *src, *dst; Xint len; X{ X while (len--) *dst++ = *src++; X} X Xsetmem(mem, len, filler) Xchar *mem; Xint len; Xchar filler; X{ X while (len--) *mem++ = filler; X} X X Xint find(findword,table,wordlen) Xunsigned char * findword; Xunsigned char * table; Xint wordlen; X{ X auto int lastletter,tmp; X X boundary=stringlen-wordlen; X lastletter=wordlen-1; X offset=0; X while (offset<=boundary) { X tmp=table[string[offset+lastletter]]; X if (tmp) { X offset+=tmp; X } else { X for(k=lastletter-1; k>=0; k--) { X if ((string[k+offset])!=findword[k]) { X offset++; X break; X } X } X if (k<0) return (1); X } X } X return(0); X} X X Xvoid maktbl(findword,table,wordlen) Xunsigned char * findword; Xunsigned char * table; Xint * wordlen; X{ X X auto int i,len; X X *wordlen=len=strlen(findword); X setmem(table,256,len); X for (i=0; i<len; i++) X table[findword[i]]=len-i-1; X} X X Xvoid gotone() X{ X hadone=1; X X if (cflag || lflag || sflag) X { X count++; X return; X } X X if (! nofname) printf("%s:",argv[i]); X X if (nflag) printf("%d:",linum); X X printf("%s\n",string); X} X X Xint getlin(infile, buf, maxlen) Xint infile; Xchar *buf; Xint maxlen; X{ X static char mybuf[2048]; X static char * low; X static char * high; X X auto int status; X auto char * p=buf; X auto int endline; X X *p='\0'; X maxlen--; X while (1) X { X endline=0; X while (low < high && ! endline) X { X if (p >= &buf[maxlen]) X { /* overflow, skip all until \n */ X while (low < high && *low != '\n') low++; X endline = (*low == '\n'); X } X else endline =((*p++ = *low++) == '\n'); X X } /* exhausted buffer or found \n */ X X if (endline) break; /* don't continue if \n found */ X status=read(infile,mybuf,2048); X if (status==0) break; X X low=mybuf; X high= &mybuf[status]; X X } X X if (endline) X { X *(p-1)='\0'; X return (p-buf-1); X } X X /* empty line or a bit filled ? */ X *p='\0'; X if (p-buf) return(p-buf); X return(EOF); X X} /* of getlin() */ X + END-OF-FILE fgrep.c chmod 'u=rw,g=rw,o=rw' 'fgrep.c' set `wc -c 'fgrep.c'` count=$1 case $count in 5443) :;; *) echo 'Bad character count in ''fgrep.c' >&2 echo 'Count should be 5443' >&2 esac echo Extracting 'prep.c' sed 's/^X//' > 'prep.c' << '+ END-OF-FILE ''prep.c' X/* prep - prepare file for statistics Author: Andy Tanenbaum */ X X#include <stdio.h> X#include <ctype.h> X X#define TROFF_CHAR '.' /* troff commands begin with this char */ X#define EOL '\n' /* end of line char */ X#define APOSTROPHE 047 /* single quote */ X#define BACKSLASH '\\' /* troff code */ X Xint lfread; /* set when last char read was lf */ Xint lfwritten; /* set when last char written was lf */ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X X char c, backslash(); X FILE *freopen(); X X if (argc > 2) usage(); X if (argc == 2) { X if (freopen(argv[1], "r", stdin) == NULL) { X printf("prep: cannot open %s\n", argv[1]); X exit(1); X } X } X X while ( (c = getchar()) != EOF) { X /* Lines beginning with "." are troff commands -- skip them. */ X if (lfread && c == TROFF_CHAR) { X skipline(); X continue; X } X if (c == BACKSLASH) c = backslash(); /* eat troff stuff */ X X if (isupper(c)) { X putchar(tolower(c)); X lfwritten = 0; X lfread = 0; X continue; X } X if (islower(c)) { X putchar(c); X lfwritten = 0; X lfread = 0; X continue; X } X if (c == APOSTROPHE) { X putchar(c); X lfwritten = 0; X lfread = 0; X continue; X } X lfread = (c == EOL ? 1 : 0); X if (lfwritten) continue; X putchar(EOL); X lfwritten = 1; X } X} X X Xskipline() X{ X char c; X X while ( (c = getchar()) != EOL) ; X} X X Xchar backslash() X{ X/* A backslash has been seen. Eat troff stuff. */ X X char c; X X c = getchar(); X switch(c) { X case 'f': X c = getchar(); X c = getchar(); X return(c); X X case 's': /* \s7 or \s14 */ X c = getchar(); X c = getchar(); X if (isdigit(c)) c = getchar(); X return(c); X X case 'n': /* \na or \n(xx */ X c = getchar(); X if (c == '(') { X c = getchar(); X c = getchar(); X } X c = getchar(); X return(c); X X case '*': /* / * (XX */ X c = getchar(); X if (c == '(') { X c = getchar(); X c = getchar(); X c = getchar(); X return(c); X } X X case '(': /* troff 4-character escape sequence */ X c = getchar(); X c = getchar(); X c = getchar(); X return(c); X X } X} X Xusage() X{ X printf("Usage: prep [file]\n"); X exit(1); X} X + END-OF-FILE prep.c chmod 'u=rw,g=r,o=r' 'prep.c' set `wc -c 'prep.c'` count=$1 case $count in 2025) :;; *) echo 'Bad character count in ''prep.c' >&2 echo 'Count should be 2025' >&2 esac echo Extracting 'readall.c' sed 's/^X//' > 'readall.c' << '+ END-OF-FILE ''readall.c' X#define BLK 30 X Xchar a[32000]; X Xmain(argc,argv) Xint argc; Xchar *argv[]; X{ X int fd; X unsigned b=0; X if (argc != 2) {printf("Usage: readall file\n"); exit(1);} X fd = open(argv[1], 0); X if (fd < 0) {printf("%s is not readable\n", argv[1]); exit(1);} X X while(1) { X if (read(fd, a, 1024*BLK) == 0) {output(b); exit(0);} X b++; X if (b % 100 == 0) output(b); X } X} X Xoutput(b) Xunsigned b; X{ X printf("%D blocks read\n",(long)b * (long) BLK); X} + END-OF-FILE readall.c chmod 'u=rw,g=r,o=r' 'readall.c' set `wc -c 'readall.c'` count=$1 case $count in 441) :;; *) echo 'Bad character count in ''readall.c' >&2 echo 'Count should be 441' >&2 esac echo Extracting 'treecmp.c' sed 's/^X//' > 'treecmp.c' << '+ END-OF-FILE ''treecmp.c' X/* treecmp - compare two trees Author: Andy Tanenbaum */ X X/* This program recursively compares two trees and reports on differences. X * It can be used, for example, when a project consists of a large number X * of files and directories. When a new release (i.e., a new tree) has been X * prepared, the old and new tree can be compared to give a list of what has X * changed. The algorithm used is that the first tree is recursively X * descended and for each file or directory found, the corresponding one in X * the other tree checked. The two arguments are not completely symmetric X * because the first tree is descended, not the second one, but reversing X * the arguments will still detect all the differences, only they will be X * printed in a different order. The program needs lots of stack space X * because routines with local arrays are called recursively. The call is X * treecmp [-v] dir1 dir2 X * The -v flag (verbose) prints the directory names as they are processed. X */ X X#include <sys/stat.h> X X#define BUFSIZE 4096 /* size of file buffers */ X#define MAXPATH 128 /* longest acceptable path */ X#define DIRENTLEN 14 /* number of characters in a file name */ X Xstruct dirstruct { /* layout of a directory entry */ X unsigned inum; X char fname[DIRENTLEN]; X}; X Xstruct stat stat1, stat2; /* stat buffers */ X Xchar buf1[BUFSIZE]; /* used for comparing bufs */ Xchar buf2[BUFSIZE]; /* used for comparing bufs */ X Xint verbose; /* set if mode is verbose */ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X char *p; X X if (argc < 3 || argc > 4) usage(); X p = argv[1]; X if (argc == 4) { X if (*p == '-' && *(p+1) == 'v') X verbose++; X else X usage(); X } X X if (argc == 3) X compare(argv[1], argv[2]); X else X compare(argv[2], argv[3]); X X exit(0); X} X Xcompare(f1, f2) Xchar *f1, *f2; X{ X/* This is the main comparision routine. It gets two path names as arguments X * and stats them both. Depending on the results, it calls other routines X * to compare directories or files. X */ X X int type1, type2; X X if (stat(f1, &stat1) < 0) { X printf("Cannot stat %s\n", f1); X return; X } X X if (stat(f2, &stat2) < 0) { X printf("Missing file: %s\n", f2); X return; X } X X /* Examine the types of the files. */ X type1 = stat1.st_mode & S_IFMT; X type2 = stat2.st_mode & S_IFMT; X if (type1 != type2) { X printf("Type diff: %s and %s\n", f1, f2); X return; X } X X /* The types are the same. */ X switch(type1) { X case S_IFREG: regular(f1, f2); X break; X X case S_IFDIR: directory(f1, f2); X break; X X case S_IFCHR: X case S_IFBLK: break; X X default: printf("Unknown file type %o\n", type1); X } X return; X} X Xregular(f1, f2) Xchar *f1, *f2; X{ X/* Compare to regular files. If they are different, complain. */ X X int fd1, fd2, n1, n2, i; X unsigned bytes; X long count; X char *p1, *p2; X X if (stat1.st_size != stat2.st_size) { X printf("Size diff: %s and %s\n", f1, f2); X return; X } X X /* The sizes are the same. We actually have to read the files now. */ X fd1 = open(f1, 0); X if (fd1 < 0) { X printf("Cannot open %s for reading\n", f1); X return; X } X X fd2 = open(f2, 0); X if (fd2 < 0) { X printf("Cannot open %s for reading\n", f2); X return; X } X X count = stat1.st_size; X while (count > 0L) { X bytes = (unsigned) (count > BUFSIZE ? BUFSIZE : count); /* rd count */ X n1 = read(fd1, buf1, bytes); X n2 = read(fd2, buf2, bytes); X if (n1 != n2) { X printf("Length diff: %s and %s\n", f1, f2); X close(fd1); X close(fd2); X return; X } X X /* Compare the buffers. */ X i = n1; X p1 = buf1; X p2 = buf2; X while (i--) { X if (*p1++ != *p2++) { X printf("File diff: %s and %s\n", f1, f2); X close(fd1); X close(fd2); X return; X } X } X count -= n1; X } X close(fd1); X close(fd2); X} X Xdirectory(f1, f2) Xchar *f1, *f2; X{ X/* Recursively compare two directories by reading them and comparing their X * contents. The order of the entries need not be the same. X */ X X int fd1, fd2, n1, n2, ent1, ent2, i, used1 = 0, used2 = 0; X char *dir1buf, *dir2buf; X char name1buf[MAXPATH], name2buf[MAXPATH]; X struct dirstruct *dp1, *dp2; X unsigned dir1bytes, dir2bytes; X extern char *malloc(); X X /* Allocate space to read in the directories */ X dir1bytes = (unsigned) stat1.st_size; X dir1buf = malloc(dir1bytes); X if (dir1buf == 0) { X printf("Cannot process directory %s: out of memory\n", f1); X return; X } X X dir2bytes = (unsigned) stat2.st_size; X dir2buf = malloc(dir2bytes); X if (dir2buf == 0) { X printf("Cannot process directory %s: out of memory\n", f2); X free(dir1buf); X return; X } X X /* Read in the directories. */ X fd1 = open(f1, 0); X if (fd1 > 0) n1 = read(fd1, dir1buf, dir1bytes); X if (fd1 < 0 || n1 != dir1bytes) { X printf("Cannot read directory %s\n", f1); X free(dir1buf); X free(dir2buf); X if (fd1 > 0) close(fd1); X return; X } X close(fd1); X X fd2 = open(f2, 0); X if (fd2 > 0) n2 = read(fd2, dir2buf, dir2bytes); X if (fd2 < 0 || n2 != dir2bytes) { X printf("Cannot read directory %s\n", f2); X free(dir1buf); X free(dir2buf); X close(fd1); X if (fd2 > 0) close(fd2); X return; X } X close(fd2); X X /* Linearly search directories */ X ent1 = dir1bytes/sizeof(struct dirstruct); X dp1 = (struct dirstruct *) dir1buf; X for (i = 0; i < ent1; i++) { X if (dp1->inum != 0) used1++; X dp1++; X } X X ent2 = dir2bytes/sizeof(struct dirstruct); X dp2 = (struct dirstruct *) dir2buf; X for (i = 0; i < ent2; i++) { X if (dp2->inum != 0) used2++; X dp2++; X } X X if (verbose) printf("Directory %s: %d entries\n", f1, used1); X X /* Check to see if any entries in dir2 are missing from dir1. */ X dp1 = (struct dirstruct *) dir1buf; X dp2 = (struct dirstruct *) dir2buf; X for (i = 0; i < ent2; i++) { X if (dp2->inum == 0 || strcmp(dp2->fname, ".") == 0 || X strcmp(dp2->fname, "..") == 0) { X dp2++; X continue; X } X check(dp2->fname, dp1, ent1, f1); X dp2++; X } X X /* Recursively process all the entries in dir1. */ X dp1 = (struct dirstruct *) dir1buf; X for (i = 0; i < ent1; i++) { X if (dp1->inum == 0 || strcmp(dp1->fname, ".") == 0 || X strcmp(dp1->fname, "..") == 0) { X dp1++; X continue; X } X if (strlen(f1) + DIRENTLEN >= MAXPATH) { X printf("Path too long: %s\n", f1); X free(dir1buf); X free(dir2buf); X return; X } X if (strlen(f2) + DIRENTLEN >= MAXPATH) { X printf("Path too long: %s\n", f2); X free(dir1buf); X free(dir2buf); X return; X } X X strcpy(name1buf, f1); X strcat(name1buf, "/"); X strncat(name1buf, dp1->fname, DIRENTLEN); X strcpy(name2buf, f2); X strcat(name2buf, "/"); X strncat(name2buf, dp1->fname, DIRENTLEN); X X /* Here is the recursive call to process an entry. */ X compare(name1buf, name2buf); /* recursive call */ X dp1++; X } X X free(dir1buf); X free(dir2buf); X} X Xcheck(s, dp1, ent1, f1) Xchar *s; Xstruct dirstruct *dp1; Xint ent1; Xchar *f1; X{ X/* See if the file name 's' is present in the directory 'dirbuf'. */ X int i; X X for (i = 0; i < ent1; i++) { X if (strncmp(dp1->fname, s, DIRENTLEN) == 0) return; X dp1++; X } X printf("Missing file: %s/%s\n", f1, s); X} X Xusage() X{ X printf("Usage: treecmp [-v] dir1 dir2\n"); X exit(0); X} X + END-OF-FILE treecmp.c chmod 'u=rw,g=r,o=r' 'treecmp.c' set `wc -c 'treecmp.c'` count=$1 case $count in 6938) :;; *) echo 'Bad character count in ''treecmp.c' >&2 echo 'Count should be 6938' >&2 esac exit 0