[comp.os.minix] V1.3 posting #10, selected files from commands

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