xref: /original-bsd/sbin/fsdb/fsdb.c (revision 5b340300)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Computer Consoles Inc.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that this notice is preserved and that due credit is given
10  * to the University of California at Berkeley. The name of the University
11  * may not be used to endorse or promote products derived from this
12  * software without specific prior written permission. This software
13  * is provided ``as is'' without express or implied warranty.
14  */
15 
16 #ifndef lint
17 char copyright[] =
18 "@(#) Copyright (c) 1988 Regents of the University of California.\n\
19  All rights reserved.\n";
20 #endif /* not lint */
21 
22 #ifndef lint
23 static char sccsid[] = "@(#)fsdb.c	5.1 (Berkeley) 05/05/88";
24 #endif /* not lint */
25 
26 /*	@(#)fsdb.c	1.4	*/
27 
28 /*  fsdb - file system debugger */
29 
30 /*  usage: fsdb [options] special
31  *  options:
32  *	-?		display usage
33  *	-o		override some error conditions
34  *	-p"string"	set prompt to string
35  *	-w		open for write
36  */
37 
38 #include <sys/param.h>
39 #include <signal.h>
40 #include <sys/fs.h>
41 #include <sys/inode.h>
42 #include <sys/dir.h>
43 #include <sys/file.h>
44 #include <stdio.h>
45 #include <setjmp.h>
46 
47 /*
48  * Never changing defines.
49  */
50 #define	OCTAL		8		/* octal base */
51 #define	DECIMAL		10		/* decimal base */
52 #define	HEX		16		/* hexadecimal base */
53 
54 /*
55  * Adjustable defines.
56  */
57 #define	NBUF		10		/* number of cache buffers */
58 #define PROMPTSIZE	80		/* size of user definable prompt */
59 #define	MAXFILES	40000		/* max number of files ls can handle */
60 #define FIRST_DEPTH	10		/* default depth for find and ls */
61 #define SECOND_DEPTH	100		/* second try at depth (maximum) */
62 #define INPUTBUFFER	1040		/* size of input buffer */
63 #define BYTESPERLINE	16		/* bytes per line of /dxo output */
64 #define	NREG		36		/* number of save registers */
65 
66 /*
67  * Values dependent on sizes of structs and such.
68  */
69 #define NUMB		3			/* these three are arbitrary, */
70 #define	BLOCK		5			/* but must be different from */
71 #define	FRAGMENT	7			/* the rest (hence odd).      */
72 #define	BITSPERCHAR	8			/* couldn't find it anywhere  */
73 #define	CHAR		(sizeof (char))
74 #define	SHORT		(sizeof (short))
75 #define	LONG		(sizeof (long))
76 #define	INODE		(sizeof (struct dinode))
77 #define	DIRECTORY	(sizeof (struct direct))
78 #define	CGRP		(sizeof (struct cg))
79 #define SB		(sizeof (struct fs))
80 #define	BLKSIZE		(fs->fs_bsize)		/* for clarity */
81 #define	FRGSIZE		(fs->fs_fsize)
82 #define	BLKSHIFT	(fs->fs_bshift)
83 #define	FRGSHIFT	(fs->fs_fshift)
84 
85 /*
86  * Messy macros that would otherwise clutter up such glamorous code.
87  */
88 #define itob(i)		((itod(fs, (i)) << FRGSHIFT) + itoo(fs, (i)) * INODE)
89 #define min(x, y)	((x) < (y) ? (x) : (y))
90 #define	STRINGSIZE(d)	((long)d->d_reclen - \
91 				((long)&d->d_name[0] - (long)&d->d_ino))
92 #define	letter(c)	((((c) >= 'a')&&((c) <= 'z')) ||\
93 				(((c) >= 'A')&&((c) <= 'Z')))
94 #define	digit(c)	(((c) >= '0') && ((c) <= '9'))
95 #define HEXLETTER(c)	(((c) >= 'A') && ((c) <= 'F'))
96 #define hexletter(c)	(((c) >= 'a') && ((c) <= 'f'))
97 #define octaldigit(c)	(((c) >= '0') && ((c) <= '7'))
98 #define uppertolower(c)	((c) - 'A' + 'a')
99 #define hextodigit(c)	((c) - 'a' + 10)
100 #define	numtodigit(c)	((c) - '0')
101 #define loword(X)	(((ushort *)&X)[1])
102 #define lobyte(X)	(((unsigned char *)&X)[1])
103 
104 /*
105  * buffer cache structure.
106  */
107 struct buf {
108 	struct	buf  *fwd;
109 	struct	buf  *back;
110 	char	*blkaddr;
111 	short	valid;
112 	long	blkno;
113 } buf[NBUF], bhdr;
114 
115 /*
116  * used to hold save registers (see '<' and '>').
117  */
118 struct	save_registers {
119 	long	sv_addr;
120 	long	sv_value;
121 	long	sv_objsz;
122 } regs[NREG];
123 
124 /*
125  * cd, find, and ls use this to hold filenames.  Each filename is broken
126  * up by a slash.  In other words, /usr/src/adm would have a len field
127  * of 2 (starting from 0), and filenames->fname[0-2] would hold usr,
128  * src, and adm components of the pathname.
129  */
130 struct filenames {
131 	long	ino;		/* inode */
132 	long	len;		/* number of components */
133 	char	flag;		/* flag if using SECOND_DEPTH allocator */
134 	char	find;		/* flag if found by find */
135 	char	**fname;	/* hold components of pathname */
136 } *filenames, *top;
137 
138 struct fs filesystem, *fs;	/* super block */
139 
140 /*
141  * Global data.
142  */
143 char		*input_path[MAXPATHLEN];
144 char		*stack_path[MAXPATHLEN];
145 char		*current_path[MAXPATHLEN];
146 char		input_buffer[INPUTBUFFER];
147 char		*prompt;
148 char		*buffers;
149 char		scratch[64];
150 char		BASE[] = "o u     x";
151 char		PROMPT[PROMPTSIZE] = "> ";
152 char		laststyle = '/';
153 char		lastpo = 'x';
154 short		input_pointer;
155 short		current_pathp;
156 short		stack_pathp;
157 short		input_pathp;
158 short		cmp_level;
159 short		nfiles;
160 short		type = NUMB;
161 short		dirslot;
162 short		fd;
163 short		c_count;
164 short		error;
165 short		paren;
166 short		trapped;
167 short		doing_cd;
168 short		doing_find;
169 short		find_by_name;
170 short		find_by_inode;
171 short		long_list;
172 short		recursive;
173 short		objsz = SHORT;
174 short		override = 0;
175 short		wrtflag;
176 short		base = HEX;
177 short		acting_on_inode;
178 short		acting_on_directory;
179 short		should_print = 1;
180 short		clear;
181 short		star;
182 long		addr;
183 long		bod_addr;
184 long		value;
185 long		erraddr;
186 long		errcur_bytes;
187 long		errino;
188 long		errinum;
189 long		cur_cgrp;
190 long		cur_ino;
191 long		cur_inum;
192 long		cur_dir;
193 long		cur_block;
194 long		cur_bytes;
195 long		find_ino;
196 long		filesize;
197 long		blocksize;
198 long		stringsize;
199 long		count = 1;
200 long		commands;
201 long		read_requests;
202 long		actual_disk_reads;
203 jmp_buf		env;
204 
205 extern char	*malloc(), *calloc();
206 char		getachar();
207 char		*getblk(), *fmtentry();
208 int		err();
209 long		get(), bmap(), expr(), term(), getnumb();
210 unsigned long	*print_check();
211 
212 /*
213  * main - lines are read up to the unprotected ('\') newline and
214  *	held in an input buffer.  Characters may be read from the
215  *	input buffer using getachar() and unread using ungetachar().
216  *	Reading the whole line ahead allows the use of debuggers
217  *	which would otherwise be impossible since the debugger
218  *	and fsdb could not share stdin.
219  */
220 
221 main(argc,argv)
222 	short			argc;
223 	char			**argv;
224 {
225 
226 	register char		c, *cptr;
227 	register short		i, j, *iptr;
228 	register struct direct	*dirp;
229 	register struct buf	*bp;
230 	struct filenames	*fn;
231 	char			*progname;
232 	short			colon, mode;
233 	long			temp;
234 	unsigned		block;
235 	int			ffcmp();
236 
237 	setbuf(stdin, NULL);
238 
239 	progname = argv[0];
240 	prompt = &PROMPT[0];
241 	/*
242 	 * Parse options.
243 	 */
244 	while (argc>1 && argv[1][0] == '-') {
245 		if (strcmp("-?", argv[1]) == 0)
246 			goto usage;
247 		if (strcmp("-o", argv[1]) == 0) {
248 			printf("error checking off\n");
249 			override = 1;
250 			argc--; argv++;
251 			continue;
252 		}
253 		if (strncmp("-p", argv[1],2) == 0) {
254 			prompt = &argv[1][2];
255 			argc--; argv++;
256 			continue;
257 		}
258 		if (strcmp("-w", argv[1]) == 0) {
259 			wrtflag = 2;		/* suitable for open */
260 			argc--; argv++;
261 			continue;
262 		}
263 	}
264 	if (argc!=2) {
265 usage:
266 		printf("usage:   %s [options] special\n", progname);
267 		printf("options:\n");
268 		printf("\t-?		display usage\n");
269 		printf("\t-o		override some error conditions\n");
270 		printf("\t-p\"string\"	set prompt to string\n");
271 		printf("\t-w		open for write\n");
272 		exit(1);
273 	}
274 	/*
275 	 * Attempt to open the special file.
276 	 */
277 	if ((fd = open(argv[1],wrtflag)) < 0) {
278 		perror(argv[1]);
279 		exit(1);
280 	}
281 	/*
282 	 * Read in the super block and validate (not too picky).
283 	 */
284 	if (lseek(fd, SBLOCK * DEV_BSIZE, 0) == -1) {
285 		perror(argv[1]);
286 		exit(1);
287 	}
288 	if (read(fd, &filesystem, sizeof filesystem) != sizeof filesystem) {
289 		printf("%s: cannot read superblock\n", argv[1]);
290 		exit(1);
291 	}
292 	fs = &filesystem;
293 	if (fs->fs_magic != FS_MAGIC) {
294 		printf("%s: Bad magic number in file system\n", argv[1]);
295 		exit(1);
296 	}
297 	printf("fsdb of %s %s -- last mounted on %s\n",
298 		argv[1], wrtflag ? "(Opened for write)" : "(Read only)",
299 		&fs->fs_fsmnt[0]);
300 	/*
301 	 * Malloc buffers and set up cache.
302 	 */
303 	buffers = malloc(NBUF * BLKSIZE);
304 	bhdr.fwd = bhdr.back = &bhdr;
305 	for (i=0; i<NBUF; i++) {
306 		bp = &buf[i];
307 		bp->blkaddr = buffers + (i * BLKSIZE);
308 		bp->valid = 0;
309 		insert(bp);
310 	}
311 	/*
312 	 * Malloc filenames structure.  The space for the actual filenames
313 	 * is allocated as it needs it.
314 	 */
315 	filenames = (struct filenames *)calloc(MAXFILES,
316 						sizeof (struct filenames));
317 	if (filenames == NULL) {
318 		printf("out of memory\n");
319 		exit(1);
320 	}
321 
322 	fn = filenames;
323 
324 	restore_inode(2);
325 	/*
326 	 * Malloc a few filenames (needed by pwd for example).
327 	 */
328 	for (i = 0; i < MAXPATHLEN; i++) {
329 		input_path[i] = calloc(1, MAXNAMLEN);
330 		stack_path[i] = calloc(1, MAXNAMLEN);
331 		current_path[i] = calloc(1, MAXNAMLEN);
332 		if (current_path[i] == NULL) {
333 			printf("out of memory\n");
334 			exit(1);
335 		}
336 	}
337 	current_pathp = -1;
338 
339 	signal(2,err);
340 	setjmp(env);
341 
342 	getnextinput();
343 	/*
344 	 * Main loop and case statement.  If an error condition occurs
345 	 * initialization and recovery is attempted.
346 	 */
347 	for (;;) {
348 		if (error) {
349 			freemem(filenames, nfiles);
350 			nfiles = 0;
351 			c_count = 0;
352 			count = 1;
353 			star = 0;
354 			error = 0;
355 			paren = 0;
356 			acting_on_inode = 0;
357 			acting_on_directory = 0;
358 			should_print = 1;
359 			addr = erraddr;
360 			cur_ino = errino;
361 			cur_inum = errinum;
362 			cur_bytes = errcur_bytes;
363 			printf("?\n");
364 			getnextinput();
365 			if (error)
366 				continue;
367 		}
368 		c_count++;
369 
370 		switch (c = getachar()) {
371 
372 		case '\n': /* command end */
373 			freemem(filenames, nfiles);
374 			nfiles = 0;
375 			if (should_print && laststyle == '=') {
376 				ungetachar(c);
377 				goto calc;
378 			}
379 			if (c_count == 1) {
380 				clear = 0;
381 				should_print = 1;
382 				erraddr = addr;
383 				errino = cur_ino;
384 				errinum = cur_inum;
385 				errcur_bytes = cur_bytes;
386 				switch (objsz) {
387 				case DIRECTORY:
388 					if ((addr =
389 					     getdirslot(dirslot+1)) == 0)
390 						should_print = 0;
391 					if (error) {
392 						ungetachar(c);
393 						continue;
394 					}
395 					break;
396 				case INODE:
397 					cur_inum++;
398 					addr = itob(cur_inum);
399 					if (!icheck(addr)) {
400 						cur_inum--;
401 						should_print = 0;
402 					}
403 					break;
404 				case CGRP:
405 				case SB:
406 					cur_cgrp++;
407 					if ((addr=cgrp_check(cur_cgrp)) == 0) {
408 					     cur_cgrp--;
409 					     continue;
410 					}
411 					break;
412 				default:
413 					addr += objsz;
414 					cur_bytes += objsz;
415 					if (valid_addr() == 0)
416 						continue;
417 				}
418 			}
419 			if (type == NUMB)
420 				trapped = 0;
421 			if (should_print)
422 				switch (objsz) {
423 				case DIRECTORY:
424 					fprnt('?', 'd');
425 					break;
426 				case INODE:
427 					fprnt('?', 'i');
428 					if (!error)
429 						cur_ino = addr;
430 					break;
431 				case CGRP:
432 					fprnt('?', 'c');
433 					break;
434 				case SB:
435 					fprnt('?', 's');
436 					break;
437 				case CHAR:
438 				case SHORT:
439 				case LONG:
440 					fprnt(laststyle, lastpo);
441 				}
442 			if (error) {
443 				ungetachar(c);
444 				continue;
445 			}
446 			c_count = colon = acting_on_inode = 0;
447 			acting_on_directory = 0;
448 			should_print = 1;
449 			getnextinput();
450 			if (error)
451 				continue;
452 			erraddr = addr;
453 			errino = cur_ino;
454 			errinum = cur_inum;
455 			errcur_bytes = cur_bytes;
456 			continue;
457 
458 		case '(': /* numeric expression or unknown command */
459 		default:
460 			colon = 0;
461 			if (digit(c) || c == '(') {
462 				ungetachar(c);
463 				addr = expr();
464 				type = NUMB;
465 				value = addr;
466 				continue;
467 			}
468 			printf("unknown command or bad syntax\n");
469 			error++;
470 			continue;
471 
472 		case '?': /* general print facilities */
473 		case '/':
474 			fprnt(c, getachar());
475 			continue;
476 
477 		case ';': /* command separator and . */
478 		case '\t':
479 		case ' ':
480 		case '.':
481 			continue;
482 
483 		case ':': /* command indicator */
484 			colon++;
485 			commands++;
486 			should_print = 0;
487 			stringsize = 0;
488 			trapped = 0;
489 			continue;
490 
491 		case ',': /* count indicator */
492 			colon = star = 0;
493 			if ((c = getachar()) == '*') {
494 				star = 1;
495 				count = BLKSIZE;
496 			} else {
497 				ungetachar(c);
498 				count = expr();
499 				if (error)
500 					continue;
501 				if (!count)
502 					count = 1;
503 			}
504 			clear = 0;
505 			continue;
506 
507 		case '+': /* address addition */
508 			colon = 0;
509 			c = getachar();
510 			ungetachar(c);
511 			if (c == '\n')
512 				temp = 1;
513 			else {
514 				temp = expr();
515 				if (error)
516 					continue;
517 			}
518 			erraddr = addr;
519 			errcur_bytes = cur_bytes;
520 			switch (objsz) {
521 			case DIRECTORY:
522 				addr = getdirslot(dirslot + temp);
523 				if (error)
524 					continue;
525 				break;
526 			case INODE:
527 				cur_inum += temp;
528 				addr = itob(cur_inum);
529 				if (!icheck(addr)) {
530 					cur_inum -= temp;
531 					continue;
532 				}
533 				break;
534 			case CGRP:
535 			case SB:
536 				cur_cgrp += temp;
537 				if ((addr = cgrp_check(cur_cgrp)) == 0) {
538 					cur_cgrp -= temp;
539 					continue;
540 				}
541 				break;
542 			default:
543 				laststyle = '/';
544 				addr += temp * objsz;
545 				cur_bytes += temp * objsz;
546 				if (valid_addr() == 0)
547 					continue;
548 			}
549 			value = get(objsz);
550 			continue;
551 
552 		case '-': /* address subtraction */
553 			colon = 0;
554 			c = getachar();
555 			ungetachar(c);
556 			if (c == '\n')
557 				temp = 1;
558 			else {
559 				temp = expr();
560 				if (error)
561 					continue;
562 			}
563 			erraddr = addr;
564 			errcur_bytes = cur_bytes;
565 			switch (objsz) {
566 			case DIRECTORY:
567 				addr = getdirslot(dirslot - temp);
568 				if (error)
569 					continue;
570 				break;
571 			case INODE:
572 				cur_inum -= temp;
573 				addr = itob(cur_inum);
574 				if (!icheck(addr)) {
575 					cur_inum += temp;
576 					continue;
577 				}
578 				break;
579 			case CGRP:
580 			case SB:
581 				cur_cgrp -= temp;
582 				if ((addr = cgrp_check(cur_cgrp)) == 0) {
583 					cur_cgrp += temp;
584 					continue;
585 				}
586 				break;
587 			default:
588 				laststyle = '/';
589 				addr -= temp * objsz;
590 				cur_bytes -= temp * objsz;
591 				if (valid_addr() == 0)
592 					continue;
593 			}
594 			value = get(objsz);
595 			continue;
596 
597 		case '*': /* address multiplication */
598 			colon = 0;
599 			temp = expr();
600 			if (error)
601 				continue;
602 			if (objsz != INODE && objsz != DIRECTORY)
603 				laststyle = '/';
604 			addr *= temp;
605 			value = get(objsz);
606 			continue;
607 
608 		case '%': /* address division */
609 			colon = 0;
610 			temp = expr();
611 			if (error)
612 				continue;
613 			if (!temp) {
614 				printf("divide by zero\n");
615 				error++;
616 				continue;
617 			}
618 			if (objsz != INODE && objsz != DIRECTORY)
619 				laststyle = '/';
620 			addr /= temp;
621 			value = get(objsz);
622 			continue;
623 
624 		case '=': { /* assignment operation */
625 			short tbase = base;
626 
627 calc:
628 			c = getachar();
629 			if (c == '\n') {
630 				ungetachar(c);
631 				c = lastpo;
632 				if (acting_on_inode == 1) {
633 					if (c != 'o' && c != 'd' && c != 'x' &&
634 					    c != 'O' && c != 'D' && c != 'X') {
635 						switch (objsz) {
636 						case LONG:
637 							c = lastpo = 'X';
638 							break;
639 						case SHORT:
640 							c = lastpo = 'x';
641 							break;
642 						case CHAR:
643 							c = lastpo = 'c';
644 						}
645 					}
646 				} else {
647 					if (acting_on_inode == 2)
648 						c = lastpo = 't';
649 				}
650 			} else if (acting_on_inode)
651 				lastpo = c;
652 			should_print = star = 0;
653 			count = 1;
654 			erraddr = addr;
655 			errcur_bytes = cur_bytes;
656 			switch (c) {
657 			case '"': /* character string */
658 				if (type == NUMB) {
659 					blocksize = BLKSIZE;
660 					filesize = BLKSIZE * 2;
661 					cur_bytes = blkoff(fs, addr);
662 					if (objsz==DIRECTORY || objsz==INODE)
663 						lastpo = 'X';
664 				}
665 				puta();
666 				continue;
667 			case '+': /* =+ operator */
668 				temp = expr();
669 				value = get(objsz);
670 				if (!error)
671 					put(value+temp,objsz);
672 				continue;
673 			case '-': /* =- operator */
674 				temp = expr();
675 				value = get(objsz);
676 				if (!error)
677 					put(value-temp,objsz);
678 				continue;
679 			case 'b':
680 			case 'c':
681 				if (objsz == CGRP)
682 					fprnt('?', c);
683 				else
684 					fprnt('/', c);
685 				continue;
686 			case 'i':
687 				addr = cur_ino;
688 				fprnt('?', 'i');
689 				continue;
690 			case 's':
691 				fprnt('?', 's');
692 				continue;
693 			case 't':
694 			case 'T':
695 				laststyle = '=';
696 				printf("\t\t");
697 				printf("%s", ctime(&value));
698 				continue;
699 			case 'o':
700 				base = OCTAL;
701 				goto otx;
702 			case 'd':
703 				if (objsz == DIRECTORY) {
704 					addr = cur_dir;
705 					fprnt('?', 'd');
706 					continue;
707 				}
708 				base = DECIMAL;
709 				goto otx;
710 			case 'x':
711 				base = HEX;
712 otx:
713 				laststyle = '=';
714 				printf("\t\t");
715 				if (acting_on_inode)
716 					print(value & 0177777L, 12, -8, 0);
717 				else
718 					print(addr & 0177777L, 12, -8, 0);
719 				printf("\n");
720 				base = tbase;
721 				continue;
722 			case 'O':
723 				base = OCTAL;
724 				goto OTX;
725 			case 'D':
726 				base = DECIMAL;
727 				goto OTX;
728 			case 'X':
729 				base = HEX;
730 OTX:
731 				laststyle = '=';
732 			 	printf("\t\t");
733 				if (acting_on_inode)
734 					print(value, 12, -8, 0);
735 				else
736 					print(addr, 12, -8, 0);
737 				printf("\n");
738 				base = tbase;
739 				continue;
740 			default: /* regular assignment */
741 				ungetachar(c);
742 				value = expr();
743 				if (error)
744 					printf("syntax error\n");
745 				else
746 					put(value,objsz);
747 				continue;
748 			}
749 		}
750 
751 		case '>': /* save current address */
752 			colon = 0;
753 			should_print = 0;
754 			c = getachar();
755 			if (!letter(c) && !digit(c)) {
756 				printf("invalid register specification, ");
757 				printf("must be letter or digit\n");
758 				error++;
759 				continue;
760 			}
761 			if (letter(c)) {
762 				if (c < 'a')
763 					c = uppertolower(c);
764 				c = hextodigit(c);
765 			} else
766 				c = numtodigit(c);
767 			regs[c].sv_addr = addr;
768 			regs[c].sv_value = value;
769 			regs[c].sv_objsz = objsz;
770 			continue;
771 
772 		case '<': /* restore saved address */
773 			colon = 0;
774 			should_print = 0;
775 			c = getachar();
776 			if (!letter(c) && !digit(c)) {
777 				printf("invalid register specification, ");
778 				printf("must be letter or digit\n");
779 				error++;
780 				continue;
781 			}
782 			if (letter(c)) {
783 				if (c < 'a')
784 					c = uppertolower(c);
785 				c = hextodigit(c);
786 			} else
787 				c = numtodigit(c);
788 			addr = regs[c].sv_addr;
789 			value = regs[c].sv_value;
790 			objsz = regs[c].sv_objsz;
791 			continue;
792 
793 		case 'a':
794 			if (colon)
795 				colon = 0;
796 			else
797 				goto no_colon;
798 			if (match("at", 2)) { 		/* access time */
799 				acting_on_inode = 2;
800 				should_print = 1;
801 				addr = (long)
802 					&((struct dinode *)cur_ino)->di_atime;
803 				value = get(LONG);
804 				type = NULL;
805 				continue;
806 			}
807 			goto bad_syntax;
808 
809 		case 'b':
810 			if (colon)
811 				colon = 0;
812 			else
813 				goto no_colon;
814 			if (match("block", 2)) { 	/* block conversion */
815 				if (type == NUMB) {
816 					value = addr;
817 					cur_bytes = 0;
818 					blocksize = BLKSIZE;
819 					filesize = BLKSIZE * 2;
820 				}
821 				addr = value << FRGSHIFT;
822 				bod_addr = addr;
823 				value = get(LONG);
824 				type = BLOCK;
825 				dirslot = 0;
826 				trapped++;
827 				continue;
828 			}
829 			if (match("bs", 2)) {		/* block size */
830 				acting_on_inode = 1;
831 				should_print = 1;
832 				if (icheck(cur_ino) == 0)
833 					continue;
834 				addr = (long)
835 					&((struct dinode *)cur_ino)->di_blocks;
836 				value = get(LONG);
837 				type = NULL;
838 				continue;
839 			}
840 			if (match("base", 2)) {		/* change/show base */
841 showbase:
842 				if ((c = getachar()) == '\n') {
843 					ungetachar(c);
844 					printf("base =\t\t");
845 					switch (base) {
846 					case OCTAL:
847 						printf("OCTAL\n");
848 						continue;
849 					case DECIMAL:
850 						printf("DECIMAL\n");
851 						continue;
852 					case HEX:
853 						printf("HEX\n");
854 						continue;
855 					}
856 				}
857 				if (c != '=') {
858 					printf("missing '='\n");
859 					error++;
860 					continue;
861 				}
862 				value = expr();
863 				switch (value) {
864 				default:
865 					printf("invalid base\n");
866 					error++;
867 					break;
868 				case OCTAL:
869 				case DECIMAL:
870 				case HEX:
871 					base = value;
872 				}
873 				goto showbase;
874 			}
875 			goto bad_syntax;
876 
877 		case 'c':
878 			if (colon)
879 				colon = 0;
880 			else
881 				goto no_colon;
882 			if (match("cd", 2)) {		/* change directory */
883 				top = filenames - 1;
884 				eat_spaces();
885 				if ((c = getachar()) == '\n') {
886 					ungetachar(c);
887 					current_pathp = -1;
888 					restore_inode(2);
889 					continue;
890 				}
891 				ungetachar(c);
892 				temp = cur_inum;
893 				doing_cd = 1;
894 				parse();
895 				doing_cd = 0;
896 				if (nfiles != 1) {
897 					restore_inode(temp);
898 					if (!error) {
899 						print_path(input_path,
900 								input_pathp);
901 						if (nfiles == 0)
902 							printf(" not found\n");
903 						else
904 							printf(" ambiguous\n");
905 						error++;
906 					}
907 					continue;
908 				}
909 				restore_inode(filenames->ino);
910 				if ((mode = icheck(addr)) == 0)
911 					continue;
912 				if ((mode & IFMT) != IFDIR) {
913 					restore_inode(temp);
914 					print_path(input_path, input_pathp);
915 					printf(" not a directory\n");
916 					error++;
917 					continue;
918 				}
919 				for (i = 0; i <= top->len; i++)
920 					strcpy(current_path[i],
921 						top->fname[i]);
922 				current_pathp = top->len;
923 				continue;
924 			}
925 			if (match("cg", 2)) {		/* cylinder group */
926 				if (type == NUMB)
927 					value = addr;
928 				if (value > fs->fs_ncg - 1) {
929 					printf("maximum cylinder group is ");
930 					print(fs->fs_ncg - 1, 8, -8, 0);
931 					printf("\n");
932 					error++;
933 					continue;
934 				}
935 				type = objsz = CGRP;
936 				cur_cgrp = value;
937 				addr = cgtod(fs, cur_cgrp) << FRGSHIFT;
938 				continue;
939 			}
940 			if (match("ct", 2)) {		/* creation time */
941 				acting_on_inode = 2;
942 				should_print = 1;
943 				addr = (long)
944 					&((struct dinode *)cur_ino)->di_ctime;
945 				value = get(LONG);
946 				type = NULL;
947 				continue;
948 			}
949 			goto bad_syntax;
950 
951 		case 'd':
952 			if (colon)
953 				colon = 0;
954 			else
955 				goto no_colon;
956 			if (match("directory", 2)) { 	/* directory offsets */
957 				if (type == NUMB)
958 					value = addr;
959 				objsz = DIRECTORY;
960 				type = DIRECTORY;
961 				addr = getdirslot(value);
962 				continue;
963 			}
964 			if (match("db", 2)) {		/* direct block */
965 				acting_on_inode = 1;
966 				should_print = 1;
967 				if (type == NUMB)
968 					value = addr;
969 				if (value >= NDADDR) {
970 					printf("direct blocks are 0 to ");
971 					print(NDADDR - 1, 0, 0, 0);
972 					printf("\n");
973 					error++;
974 					continue;
975 				}
976 				addr = cur_ino;
977 				if (!icheck(addr))
978 					continue;
979 				addr = (long)
980 				      &((struct dinode *)cur_ino)->di_db[value];
981 				bod_addr = addr;
982 				cur_bytes = (value) * BLKSIZE;
983 				cur_block = value;
984 				type = BLOCK;
985 				dirslot = 0;
986 				value = get(LONG);
987 				if (!value && !override) {
988 					printf("non existent block\n");
989 					error++;
990 				}
991 				continue;
992 			}
993 			goto bad_syntax;
994 
995 		case 'f':
996 			if (colon)
997 				colon = 0;
998 			else
999 				goto no_colon;
1000 			if (match("find", 3)) {		/* find command */
1001 				find();
1002 				continue;
1003 			}
1004 			if (match("fragment", 2)) {	/* fragment conv. */
1005 				if (type == NUMB) {
1006 					value = addr;
1007 					cur_bytes = 0;
1008 					blocksize = FRGSIZE;
1009 					filesize = FRGSIZE * 2;
1010 				}
1011 				if (min(blocksize, filesize) - cur_bytes >
1012 							FRGSIZE) {
1013 					blocksize = cur_bytes + FRGSIZE;
1014 					filesize = blocksize * 2;
1015 				}
1016 				addr = value << FRGSHIFT;
1017 				bod_addr = addr;
1018 				value = get(LONG);
1019 				type = FRAGMENT;
1020 				dirslot = 0;
1021 				trapped++;
1022 				continue;
1023 			}
1024 			if (match("file", 4)) {		/* access as file */
1025 				acting_on_inode = 1;
1026 				should_print = 1;
1027 				if (type == NUMB)
1028 					value = addr;
1029 				addr = cur_ino;
1030 				if ((mode = icheck(addr)) == 0)
1031 					continue;
1032 				if ((mode & IFCHR) && !override) {
1033 					printf("special device\n");
1034 					error++;
1035 					continue;
1036 				}
1037 				if ((addr = (bmap(value) << FRGSHIFT)) == 0)
1038 					continue;
1039 				cur_block = value;
1040 				bod_addr = addr;
1041 				type = BLOCK;
1042 				dirslot = 0;
1043 				continue;
1044 			}
1045 			if (match("fill", 4)) {		/* fill */
1046 				if (getachar() != '=') {
1047 					printf("missing '='\n");
1048 					error++;
1049 					continue;
1050 				}
1051 				if (objsz == INODE || objsz == DIRECTORY) {
1052 				      printf("can't fill inode or directory\n");
1053 				      error++;
1054 				      continue;
1055 				}
1056 				fill();
1057 				continue;
1058 			}
1059 			goto bad_syntax;
1060 
1061 		case 'g':
1062 			if (colon)
1063 				colon = 0;
1064 			else
1065 				goto no_colon;
1066 			if (match("gid", 1)) {		/* group id */
1067 				acting_on_inode = 1;
1068 				should_print = 1;
1069 				addr = (long)
1070 					&((struct dinode *)cur_ino)->di_gid;
1071 				value = get(SHORT);
1072 				type = NULL;
1073 				continue;
1074 			}
1075 			goto bad_syntax;
1076 
1077 		case 'i':
1078 			if (colon)
1079 				colon = 0;
1080 			else
1081 				goto no_colon;
1082 			if (match("inode", 2)) { /* i# to inode conversion */
1083 				if (c_count == 2) {
1084 					addr = cur_ino;
1085 					value = get(INODE);
1086 					type = NULL;
1087 					laststyle = '=';
1088 					lastpo = 'i';
1089 					should_print = 1;
1090 					continue;
1091 				}
1092 				if (type == NUMB)
1093 					value = addr;
1094 				addr = itob(value);
1095 				if (!icheck(addr))
1096 					continue;
1097 				cur_ino = addr;
1098 				cur_inum = value;
1099 				value = get(INODE);
1100 				type = NULL;
1101 				continue;
1102 			}
1103 			if (match("ib", 2)) {	/* indirect block */
1104 				acting_on_inode = 1;
1105 				should_print = 1;
1106 				if (type == NUMB)
1107 					value = addr;
1108 				if (value >= NIADDR) {
1109 					printf("indirect blocks are 0 to ");
1110 					print(NIADDR - 1, 0, 0, 0);
1111 					printf("\n");
1112 					error++;
1113 					continue;
1114 				}
1115 				addr = (long)
1116 				      &((struct dinode *)cur_ino)->di_ib[value];
1117 				cur_bytes = (NDADDR - 1) * BLKSIZE;
1118 				temp = 1;
1119 				for (i = 0; i < value; i++) {
1120 					temp *= NINDIR(fs) * BLKSIZE;
1121 					cur_bytes += temp;
1122 				}
1123 				type = BLOCK;
1124 				dirslot = 0;
1125 				value = get(LONG);
1126 				if (!value && !override) {
1127 					printf("non existent block\n");
1128 					error++;
1129 				}
1130 				continue;
1131 			}
1132 			goto bad_syntax;
1133 
1134 		case 'l':
1135 			if (colon)
1136 				colon = 0;
1137 			else
1138 				goto no_colon;
1139 			if (match("ls", 2)) {		/* ls command */
1140 				temp = cur_inum;
1141 				recursive = long_list = 0;
1142 				top = filenames - 1;
1143 				for (;;) {
1144 					eat_spaces();
1145 					if ((c = getachar()) == '-') {
1146 						if ((c = getachar()) == 'R') {
1147 						  recursive = 1;
1148 						  continue;
1149 						} else if (c == 'l') {
1150 						  long_list = 1;
1151 						} else {
1152 						  printf("unknown option ");
1153 						  printf("'%c'\n", c);
1154 						  error++;
1155 						  break;
1156 						}
1157 					} else
1158 						ungetachar(c);
1159 					if ((c = getachar()) == '\n') {
1160 						if (c_count != 2) {
1161 							ungetachar(c);
1162 							break;
1163 						}
1164 					}
1165 					c_count++;
1166 					ungetachar(c);
1167 					parse();
1168 					restore_inode(temp);
1169 					if (error)
1170 						break;
1171 				}
1172 				recursive = 0;
1173 				if (error || nfiles == 0) {
1174 					if (!error) {
1175 						print_path(input_path,
1176 								input_pathp);
1177 						printf(" not found\n");
1178 					}
1179 					continue;
1180 				}
1181 				if (nfiles) {
1182 				    cmp_level = 0;
1183 				    qsort((char *)filenames, nfiles,
1184 					sizeof (struct filenames), ffcmp);
1185 				    ls(filenames, filenames + (nfiles - 1), 0);
1186 				} else {
1187 				    printf("no match\n");
1188 				    error++;
1189 				}
1190 				restore_inode(temp);
1191 				continue;
1192 			}
1193 			if (match("ln", 2)) {		/* link count */
1194 				acting_on_inode = 1;
1195 				should_print = 1;
1196 				addr = (long)
1197 					&((struct dinode *)cur_ino)->di_nlink;
1198 				value = get(SHORT);
1199 				type = NULL;
1200 				continue;
1201 			}
1202 			goto bad_syntax;
1203 
1204 		case 'm':
1205 			if (colon)
1206 				colon = 0;
1207 			else
1208 				goto no_colon;
1209 			addr = cur_ino;
1210 			if ((mode = icheck(addr)) == 0)
1211 				continue;
1212 			if (match("mt", 2)) { 		/* modification time */
1213 				acting_on_inode = 2;
1214 				should_print = 1;
1215 				addr = (long)
1216 					&((struct dinode *)cur_ino)->di_mtime;
1217 				value = get(LONG);
1218 				type = NULL;
1219 				continue;
1220 			}
1221 			if (match("md", 2)) {		/* mode */
1222 				acting_on_inode = 1;
1223 				should_print = 1;
1224 				addr = (long)
1225 					&((struct dinode *)cur_ino)->di_mode;
1226 				value = get(SHORT);
1227 				type = NULL;
1228 				continue;
1229 			}
1230 			if (match("maj", 2)) {	/* major device number */
1231 				acting_on_inode = 1;
1232 				should_print = 1;
1233 				if (devcheck(mode))
1234 					continue;
1235 				addr = (long)
1236 					&((struct dinode *)cur_ino)->di_db[1];
1237 				value = get(LONG);
1238 				type = NULL;
1239 				continue;
1240 			}
1241 			if (match("min", 2)) {	/* minor device number */
1242 				acting_on_inode = 1;
1243 				should_print = 1;
1244 				if (devcheck(mode))
1245 					continue;
1246 				addr = (long)
1247 					&((struct dinode *)cur_ino)->di_db[0];
1248 				value = get(LONG);
1249 				type = NULL;
1250 				continue;
1251 			}
1252 			goto bad_syntax;
1253 
1254 		case 'n':
1255 			if (colon)
1256 				colon = 0;
1257 			else
1258 				goto no_colon;
1259 			if (match("nm", 1)) {		/* directory name */
1260 				objsz = DIRECTORY;
1261 				acting_on_directory = 1;
1262 				cur_dir = addr;
1263 				if ((cptr = getblk(addr)) == 0)
1264 					continue;
1265 				dirp = (struct direct *)(cptr+blkoff(fs, addr));
1266 				stringsize = (long)dirp->d_reclen -
1267 				  ((long)&dirp->d_name[0] - (long)&dirp->d_ino);
1268 				addr = (long)
1269 					&((struct direct *)addr)->d_name[0];
1270 				type = NULL;
1271 				continue;
1272 			}
1273 			goto bad_syntax;
1274 
1275 		case 'o':
1276 			if (colon)
1277 				colon = 0;
1278 			else
1279 				goto no_colon;
1280 			if (match("override", 1)) {	/* override flip flop */
1281 				if (override = !override)
1282 					printf("error checking off\n");
1283 				else
1284 					printf("error checking on\n");
1285 				continue;
1286 			}
1287 			goto bad_syntax;
1288 
1289 		case 'p':
1290 			if (colon)
1291 				colon = 0;
1292 			else
1293 				goto no_colon;
1294 			if (match("pwd", 2)) {		/* print working dir */
1295 				print_path(current_path, current_pathp);
1296 				printf("\n");
1297 				continue;
1298 			}
1299 			if (match("prompt", 2)) {	/* change prompt */
1300 				if ((c = getachar()) != '=') {
1301 					printf("missing '='\n");
1302 					error++;
1303 					continue;
1304 				}
1305 				if ((c = getachar()) != '"') {
1306 					printf("missing '\"'\n");
1307 					error++;
1308 					continue;
1309 				}
1310 				i = 0;
1311 				prompt = &prompt[0];
1312 				while ((c = getachar()) != '"' &&
1313 				       c != '\n') {
1314 					prompt[i++] = c;
1315 					if (i >= PROMPTSIZE) {
1316 						printf("string too long\n");
1317 						error++;
1318 						break;
1319 					}
1320 				}
1321 				prompt[i] = '\0';
1322 				continue;
1323 			}
1324 			goto bad_syntax;
1325 
1326 		case 'q':
1327 			if (!colon)
1328 				goto no_colon;
1329 			if (match("quit", 1)) {		/* quit */
1330 				if ((c = getachar()) != '\n') {
1331 					error++;
1332 					continue;
1333 				}
1334 				exit(0);
1335 			}
1336 			goto bad_syntax;
1337 
1338 		case 's':
1339 			if (colon)
1340 				colon = 0;
1341 			else
1342 				goto no_colon;
1343 			if (match("sb", 2)) {		/* super block */
1344 				if (c_count == 2) {
1345 					cur_cgrp = -1;
1346 					type = objsz = SB;
1347 					laststyle = '=';
1348 					lastpo = 's';
1349 					should_print = 1;
1350 					continue;
1351 				}
1352 				if (type == NUMB)
1353 					value = addr;
1354 				if (value > fs->fs_ncg - 1) {
1355 					printf("maximum super block is ");
1356 					print(fs->fs_ncg - 1, 8, -8, 0);
1357 					printf("\n");
1358 					error++;
1359 					continue;
1360 				}
1361 				type = objsz = SB;
1362 				cur_cgrp = value;
1363 				addr = cgsblock(fs, cur_cgrp) << FRGSHIFT;
1364 				continue;
1365 			}
1366 			if (match("sz", 2)) {		/* file size */
1367 				acting_on_inode = 1;
1368 				should_print = 1;
1369 				addr = (long)
1370 					&((struct dinode *)cur_ino)->di_size;
1371 				value = get(LONG);
1372 				type = NULL;
1373 				continue;
1374 			}
1375 			goto bad_syntax;
1376 
1377 		case 'u':
1378 			if (colon)
1379 				colon = 0;
1380 			else
1381 				goto no_colon;
1382 			if (match("uid", 1)) {		/* user id */
1383 				acting_on_inode = 1;
1384 				should_print = 1;
1385 				addr = (long)
1386 					&((struct dinode *)cur_ino)->di_uid;
1387 				value = get(SHORT);
1388 				type = NULL;
1389 				continue;
1390 			}
1391 			goto bad_syntax;
1392 
1393 		case 'F': /* buffer status (internal use only) */
1394 			if (colon)
1395 				colon = 0;
1396 			else
1397 				goto no_colon;
1398 			for (bp = bhdr.fwd; bp != &bhdr; bp = bp->fwd)
1399 				printf("%8x %d\n",bp->blkno,bp->valid);
1400 			printf("\n");
1401 			printf("# commands\t\t%d\n", commands);
1402 			printf("# read requests\t\t%d\n", read_requests);
1403 			printf("# actual disk reads\t%d\n", actual_disk_reads);
1404 			continue;
1405 no_colon:
1406 		printf("a colon should precede a command\n");
1407 		error++;
1408 		continue;
1409 bad_syntax:
1410 		printf("more letters needed to distinguish command\n");
1411 		error++;
1412 		continue;
1413 		}
1414 	}
1415 }
1416 
1417 /*
1418  * getachar - get next character from input buffer.
1419  */
1420 char
1421 getachar()
1422 {
1423 	return(input_buffer[input_pointer++]);
1424 }
1425 
1426 /*
1427  * ungetachar - return character to input buffer.
1428  */
1429 ungetachar(c)
1430 	register char	c;
1431 {
1432 	if (input_pointer == 0) {
1433 		printf("internal problem maintaining input buffer\n");
1434 		error++;
1435 		return;
1436 	}
1437 	input_buffer[--input_pointer] = c;
1438 }
1439 
1440 /*
1441  * getnextinput - display the prompt and read an input line.
1442  *	An input line is up to 128 characters terminated by the newline
1443  *	character.  Handle overflow, shell escape, and eof.
1444  */
1445 getnextinput()
1446 {
1447 	register int	i;
1448 	register char	c;
1449 	register short	pid, rpid;
1450 	int		retcode;
1451 
1452 newline:
1453 	i = 0;
1454 	printf("%s", prompt);
1455 ignore_eol:
1456 	while ((c = getc(stdin)) != '\n' && !(c == '!' && i == 0) &&
1457 	       !feof(stdin) && i <= INPUTBUFFER - 2)
1458 		input_buffer[i++] = c;
1459 	if (input_buffer[i - 1] == '\\') {
1460 		input_buffer[i++] = c;
1461 		goto ignore_eol;
1462 	}
1463 	if (feof(stdin)) {
1464 		printf("\n");
1465 		exit(0);
1466 	}
1467 	if (c == '!') {
1468 		if ((pid = fork()) == 0) {
1469 			execl("/bin/sh", "sh", "-t", 0);
1470 			error++;
1471 			return;
1472 		}
1473 		while ((rpid = wait(&retcode)) != pid && rpid != -1)
1474 			;
1475 		printf("!\n");
1476 		goto newline;
1477 	}
1478 	if (c != '\n')
1479 		printf("input truncated to 128 characters\n");
1480 	input_buffer[i] = '\n';
1481 	input_pointer = 0;
1482 }
1483 
1484 /*
1485  * eat_spaces - read extraneous spaces.
1486  */
1487 eat_spaces()
1488 {
1489 	register char	c;
1490 
1491 	while ((c = getachar()) == ' ')
1492 		;
1493 	ungetachar(c);
1494 }
1495 
1496 /*
1497  * restore_inode - set up all inode indicators so inum is now
1498  *	the current inode.
1499  */
1500 restore_inode(inum)
1501 	long		inum;
1502 {
1503 	errinum = cur_inum = inum;
1504 	addr = errino = cur_ino = itob(inum);
1505 }
1506 
1507 /*
1508  * match - return false if the input does not match string up to
1509  *	upto letters.   Then proceed to chew up extraneous letters.
1510  */
1511 match(string, upto)
1512 	register char	*string;
1513 	register int	upto;
1514 {
1515 	register int	i, length = strlen(string) - 1;
1516 	register char	c;
1517 	int		save_upto = upto;
1518 
1519 	while (--upto) {
1520 		string++;
1521 		if ((c = getachar()) != *string) {
1522 			for (i = save_upto - upto; i; i--) {
1523 				ungetachar(c);
1524 				c = *--string;
1525 			}
1526 			return(0);
1527 		}
1528 		length--;
1529 	}
1530 	while (length--) {
1531 		string++;
1532 		if ((c = getachar()) != *string) {
1533 			ungetachar(c);
1534 			return(1);
1535 		}
1536 	}
1537 	return(1);
1538 }
1539 
1540 /*
1541  * expr - expression evaluator.  Will evaluate expressions from
1542  *	left to right with no operator precedence.  Parentheses may
1543  *	be used.
1544  */
1545 long
1546 expr()
1547 {
1548 	register long	numb = 0, temp;
1549 	register char	c;
1550 
1551 	numb = term();
1552 	for (;;) {
1553 		if (error)
1554 			return;
1555 		c = getachar();
1556 		switch (c) {
1557 
1558 		case '+':
1559 			numb += term();
1560 			continue;
1561 
1562 		case '-':
1563 			numb -= term();
1564 			continue;
1565 
1566 		case '*':
1567 			numb *= term();
1568 			continue;
1569 
1570 		case '%':
1571 			temp = term();
1572 			if (!temp) {
1573 				printf("divide by zero\n");
1574 				error++;
1575 				return;
1576 			}
1577 			numb /= temp;
1578 			continue;
1579 
1580 		case ')':
1581 			paren--;
1582 			return(numb);
1583 
1584 		default:
1585 			ungetachar(c);
1586 			if (paren && !error) {
1587 				printf("missing ')'\n");
1588 				error++;
1589 			}
1590 			return(numb);
1591 		}
1592 	}
1593 }
1594 
1595 /*
1596  * term - used by expression evaluator to get an operand.
1597  */
1598 long
1599 term()
1600 {
1601 	register char	c;
1602 
1603 	switch (c = getachar()) {
1604 
1605 	default:
1606 		ungetachar(c);
1607 
1608 	case '+':
1609 		return(getnumb());
1610 
1611 	case '-':
1612 		return(-getnumb());
1613 
1614 	case '(':
1615 		paren++;
1616 		return(expr());
1617 	}
1618 }
1619 
1620 /*
1621  * getnumb - read a number from the input stream.  A leading
1622  *	zero signifies octal interpretation, a leading '0x'
1623  *	signifies hexadecimal, and a leading '0t' signifies
1624  *	decimal.  If the first character is a character,
1625  *	return an error.
1626  */
1627 long
1628 getnumb()
1629 {
1630 
1631 	register char	c, savec;
1632 	long		number = 0, tbase, num;
1633 	extern short	error;
1634 
1635 	c = getachar();
1636 	if (!digit(c)) {
1637 		error++;
1638 		ungetachar(c);
1639 		return(-1);
1640 	}
1641 	if (c == '0') {
1642 		tbase = OCTAL;
1643 		if ((c = getachar()) == 'x')
1644 			tbase = HEX;
1645 		else if (c == 't')
1646 			tbase = DECIMAL;
1647 		else ungetachar(c);
1648 	} else {
1649 		tbase = base;
1650 		ungetachar(c);
1651 	}
1652 	for (;;) {
1653 		num = tbase;
1654 		c = savec = getachar();
1655 		if (HEXLETTER(c))
1656 			c = uppertolower(c);
1657 		switch (tbase) {
1658 		case HEX:
1659 			if (hexletter(c)) {
1660 				num = hextodigit(c);
1661 				break;
1662 			}
1663 		case DECIMAL:
1664 			if (digit(c))
1665 				num = numtodigit(c);
1666 			break;
1667 		case OCTAL:
1668 			if (octaldigit(c))
1669 				num = numtodigit(c);
1670 		}
1671 		if (num == tbase)
1672 			break;
1673 		number = number * tbase + num;
1674 	}
1675 	ungetachar(savec);
1676 	return(number);
1677 }
1678 
1679 /*
1680  * find - the syntax is almost identical to the unix command.
1681  *		find dir [-name pattern] [-inum number]
1682  *	Note:  only one of -name or -inum may be used at a time.
1683  *	       Also, the -print is not needed (implied).
1684  */
1685 find()
1686 {
1687 	register struct filenames	*fn;
1688 	register char			c;
1689 	long				temp;
1690 	short				mode;
1691 
1692 	eat_spaces();
1693 	temp = cur_inum;
1694 	top = filenames - 1;
1695 	doing_cd = 1;
1696 	parse();
1697 	doing_cd = 0;
1698 	if (nfiles != 1) {
1699 		restore_inode(temp);
1700 		if (!error) {
1701 			print_path(input_path, input_pathp);
1702 			if (nfiles == 0)
1703 				printf(" not found\n");
1704 			else
1705 				printf(" ambiguous\n");
1706 			error++;
1707 			return;
1708 		}
1709 	}
1710 	restore_inode(filenames->ino);
1711 	freemem(filenames, nfiles);
1712 	nfiles = 0;
1713 	top = filenames - 1;
1714 	if ((mode = icheck(addr)) == 0)
1715 		return;
1716 	if ((mode & IFMT) != IFDIR) {
1717 		print_path(input_path, input_pathp);
1718 		printf(" not a directory\n");
1719 		error++;
1720 		return;
1721 	}
1722 	eat_spaces();
1723 	if ((c = getachar()) != '-') {
1724 		printf("missing '-'\n");
1725 		error++;
1726 		return;
1727 	}
1728 	find_by_name = find_by_inode = 0;
1729 	c = getachar();
1730 	if (match("name", 4)) {
1731 		eat_spaces();
1732 		find_by_name = 1;
1733 	} else if (match("inum", 4)) {
1734 		eat_spaces();
1735 		find_ino = expr();
1736 		if (error)
1737 			return;
1738 		while ((c = getachar()) != '\n')
1739 			;
1740 		ungetachar(c);
1741 		find_by_inode = 1;
1742 	} else {
1743 		printf("use -name or -inum with find\n");
1744 		error++;
1745 		return;
1746 	}
1747 	doing_find = 1;
1748 	parse();
1749 	doing_find = 0;
1750 	if (error) {
1751 		restore_inode(temp);
1752 		return;
1753 	}
1754 	for (fn = filenames; fn <= top; fn++) {
1755 		if (fn->find == 0)
1756 			continue;
1757 		printf("i#: ");
1758 		print(fn->ino, 12, -8, 0);
1759 		print_path(fn->fname, fn->len);
1760 		printf("\n");
1761 	}
1762 	restore_inode(temp);
1763 }
1764 
1765 /*
1766  * ls - do an ls.  Should behave exactly as ls(1).
1767  *	Only -R and -l is supported and -l gives different results.
1768  */
1769 ls(fn0, fnlast, level)
1770 	struct filenames		*fn0, *fnlast;
1771 	short				level;
1772 {
1773 	register struct filenames	*fn, *fnn;
1774 	register int			i;
1775 	int				fcmp();
1776 
1777 	fn = fn0;
1778 	for (;;) {
1779 		fn0 = fn;
1780 		if (fn0->len) {
1781 			cmp_level = level;
1782 			qsort((char *)fn0, fnlast - fn0 + 1,
1783 				sizeof (struct filenames), fcmp);
1784 		}
1785 		for (fnn = fn, fn++; fn <= fnlast; fnn = fn, fn++) {
1786 			if (fnn->len != fn->len && level == fnn->len - 1)
1787 				break;
1788 			if (fnn->len == 0)
1789 				continue;
1790 			if (strcmp(fn->fname[level], fnn->fname[level]))
1791 				break;
1792 		}
1793 		if (fn0->len && level != fn0->len - 1)
1794 			ls(fn0, fnn, level + 1);
1795 		else {
1796 			if (fn0 != filenames)
1797 				printf("\n");
1798 			print_path(fn0->fname, fn0->len - 1);
1799 			printf(":\n");
1800 			if (fn0->len == 0)
1801 				cmp_level = level;
1802 			else
1803 				cmp_level = level + 1;
1804 			qsort((char *)fn0, fnn - fn0 + 1,
1805 				sizeof (struct filenames), fcmp);
1806 			formatf(fn0, fnn);
1807 			nfiles -= fnn - fn0 + 1;
1808 		}
1809 		if (fn > fnlast)
1810 			return;
1811 	}
1812 }
1813 
1814 /*
1815  * formatf - code lifted from ls.
1816  */
1817 formatf(fn0, fnlast)
1818 	register struct filenames	*fn0, *fnlast;
1819 {
1820 	register struct filenames	*fn;
1821 	int				width = 0, w, nentry = fnlast - fn0 + 1;
1822 	int				i, j, columns, lines;
1823 	char				*cp;
1824 
1825 	if (long_list) {
1826 		columns = 1;
1827 	} else {
1828 		for (fn = fn0; fn <= fnlast; fn++) {
1829 			int len = strlen(fn->fname[cmp_level]) + 2;
1830 
1831 			if (len > width)
1832 				width = len;
1833 		}
1834 		width = (width + 8) &~ 7;
1835 		columns = 80 / width;
1836 		if (columns == 0)
1837 			columns = 1;
1838 	}
1839 	lines = (nentry + columns - 1) / columns;
1840 	for (i = 0; i < lines; i++) {
1841 		for (j = 0; j < columns; j++) {
1842 			fn = fn0 + j * lines + i;
1843 			if (long_list) {
1844 				printf("i#: ");
1845 				print(fn->ino, 12, -8, 0);
1846 			}
1847 			cp = fmtentry(fn);
1848 			printf("%s", cp);
1849 			if (fn + lines > fnlast) {
1850 				printf("\n");
1851 				break;
1852 			}
1853 			w = strlen(cp);
1854 			while (w < width) {
1855 				w = (w + 8) &~ 7;
1856 				putchar('\t');
1857 			}
1858 		}
1859 	}
1860 }
1861 
1862 /*
1863  * fmtentry - code lifted from ls.
1864  */
1865 char *
1866 fmtentry(fn)
1867 	register struct filenames	*fn;
1868 {
1869 	static char			fmtres[BUFSIZ];
1870 	register struct dinode		*ip;
1871 	register char			*cptr, *cp, *dp;
1872 
1873 	dp = &fmtres[0];
1874 	for (cp = fn->fname[cmp_level]; *cp; cp++) {
1875 		if (*cp < ' ' || *cp >= 0177)
1876 			*dp++ = '?';
1877 		else
1878 			*dp++ = *cp;
1879 	}
1880 	addr = itob(fn->ino);
1881 	if ((cptr = getblk(addr)) == 0)
1882 		return(NULL);
1883 	cptr += blkoff(fs, addr);
1884 	ip = (struct dinode *)cptr;
1885 	switch (ip->di_mode & IFMT) {
1886 	case IFDIR:
1887 		*dp++ = '/';
1888 		break;
1889 	case IFLNK:
1890 		*dp++ = '@';
1891 		break;
1892 	case IFSOCK:
1893 		*dp++ = '=';
1894 		break;
1895 /*		case IFIFO:
1896 		*dp++ = 'f';
1897 		break;		SYSTEM V only */
1898 	case IFCHR:
1899 	case IFBLK:
1900 	case IFREG:
1901 		if (ip->di_mode & 0111)
1902 			*dp++ = '*';
1903 		else
1904 			*dp++ = ' ';
1905 		break;
1906 	default:
1907 		*dp++ = '?';
1908 
1909 	}
1910 	*dp++ = 0;
1911 	return (fmtres);
1912 }
1913 
1914 /*
1915  * fcmp - routine used by qsort.  Will sort first by name, then
1916  *	then by pathname length if names are equal.  Uses global
1917  *	cmp_level to tell what component of the path name we are comparing.
1918  */
1919 fcmp(f1, f2)
1920 	register struct filenames	*f1, *f2;
1921 {
1922 	int 				value;
1923 
1924 	if ((value = strcmp(f1->fname[cmp_level], f2->fname[cmp_level])))
1925 		return(value);
1926 	return (f1->len - f2->len);
1927 }
1928 
1929 /*
1930  * ffcmp - routine used by qsort.  Sort only by pathname length.
1931  */
1932 ffcmp(f1, f2)
1933 	register struct filenames	*f1, *f2;
1934 {
1935 	return (f1->len - f2->len);
1936 }
1937 
1938 /*
1939  * parse - set up the call to follow_path.
1940  */
1941 parse()
1942 {
1943 	register int	i, j;
1944 	char		c;
1945 
1946 	stack_pathp = input_pathp = -1;
1947 	if ((c = getachar()) == '/') {
1948 		while ((c = getachar()) == '/')
1949 			;
1950 		ungetachar(c);
1951 		cur_inum = 2;
1952 		if ((c = getachar()) == '\n') {
1953 			ungetachar('\n');
1954 			if (doing_cd) {
1955 				top++;
1956 				top->ino = 2;
1957 				top->len = -1;
1958 				nfiles = 1;
1959 				return;
1960 			}
1961 		} else
1962 			ungetachar(c);
1963 	} else {
1964 		ungetachar(c);
1965 		stack_pathp = current_pathp;
1966 		if (!doing_find)
1967 			input_pathp = current_pathp;
1968 		for (i = 0; i <= current_pathp; i++) {
1969 			if (!doing_find)
1970 				strcpy(input_path[i], current_path[i]);
1971 			strcpy(stack_path[i], current_path[i]);
1972 		}
1973 	}
1974 	getname();
1975 	follow_path(stack_pathp + 1, cur_inum);
1976 }
1977 
1978 /*
1979  * follow_path - called by cd, find, and ls.
1980  *	input_path holds the name typed by the user.
1981  *	stack_path holds the name at the current depth.
1982  */
1983 follow_path(level, inum)
1984 	long			level, inum;
1985 {
1986 	register struct direct	*dirp;
1987 	register char		**ccptr, *cptr, c;
1988 	register int		i;
1989 	struct filenames	*tos, *bos, *fn, *fnn, *fnnn;
1990 	long			block;
1991 	short			mode;
1992 
1993 	tos = top + 1;
1994 	restore_inode(inum);
1995 	if ((mode = icheck(addr)) == 0)
1996 		return;
1997 	if ((mode & IFMT) != IFDIR)
1998 	    return;
1999 	block = cur_bytes = 0;
2000 	while (cur_bytes < filesize) {
2001 	    if (block == 0 || bcomp(addr)) {
2002 		error = 0;
2003 		if ((addr = (bmap(block++) << FRGSHIFT)) == 0)
2004 		    break;
2005 		if ((cptr = getblk(addr)) == 0)
2006 		    break;
2007 		cptr += blkoff(fs, addr);
2008 	    }
2009 	    dirp = (struct direct *)cptr;
2010 	    if (dirp->d_ino) {
2011 		if (level > input_pathp || doing_find ||
2012 			compare(input_path[level], &dirp->d_name[0], 1)) {
2013 		    if (++top - filenames >= MAXFILES) {
2014 			printf("too many files\n");
2015 			error++;
2016 			return;
2017 		    }
2018 		    top->fname = (char **)calloc(FIRST_DEPTH, sizeof (char **));
2019 		    top->flag = 0;
2020 		    if (top->fname == 0) {
2021 			printf("out of memory\n");
2022 			error++;
2023 			return;
2024 		    }
2025 		    nfiles++;
2026 		    top->ino = dirp->d_ino;
2027 		    top->len = stack_pathp;
2028 		    top->find = 0;
2029 		    if (doing_find) {
2030 			if (find_by_name) {
2031 			    if (compare(input_path[0], &dirp->d_name[0], 1))
2032 				top->find = 1;
2033 			} else if (find_by_inode)
2034 			    if (find_ino == dirp->d_ino)
2035 				top->find = 1;
2036 		    }
2037 		    if (top->len + 1 >= FIRST_DEPTH && top->flag == 0) {
2038 			ccptr = (char **)calloc(SECOND_DEPTH, sizeof (char **));
2039 			if (ccptr == 0) {
2040 			    printf("out of memory\n");
2041 			    error++;
2042 			    return;
2043 			}
2044 			for (i = 0; i < FIRST_DEPTH; i++)
2045 				ccptr[i] = top->fname[i];
2046 			free((char *)top->fname);
2047 			top->fname = ccptr;
2048 			top->flag = 1;
2049 		    }
2050 		    if (top->len >= SECOND_DEPTH) {
2051 			printf("maximum depth exceeded, try to cd lower\n");
2052 			error++;
2053 			return;
2054 		    }
2055 		    /*
2056 		     * Copy current depth.
2057 		     */
2058 		    for (i = 0; i <= stack_pathp; i++) {
2059 			top->fname[i]=calloc(1, strlen(stack_path[i])+1);
2060 			if (top->fname[i] == 0) {
2061 			    printf("out of memory\n");
2062 			    error++;
2063 			    return;
2064 			}
2065 			strcpy(top->fname[i], stack_path[i]);
2066 		    }
2067 		    /*
2068 		     * Check for '.' or '..' typed.
2069 		     */
2070 		    if ((level <= input_pathp) &&
2071 				       (strcmp(input_path[level], ".") == 0 ||
2072 					strcmp(input_path[level], "..") == 0)) {
2073 			if (strcmp(input_path[level],"..") == 0 &&
2074 							 top->len >= 0) {
2075 			    free(top->fname[top->len]);
2076 			    top->len -= 1;
2077 			}
2078 		    } else {
2079 			/*
2080 			 * Check for duplicates.
2081 			 */
2082 			if (!doing_cd && !doing_find) {
2083 			    for (fn = filenames; fn < top; fn++) {
2084 				if (fn->ino == dirp->d_ino &&
2085 					    fn->len == stack_pathp + 1) {
2086 				    for (i = 0; i < fn->len; i++)
2087 					if (strcmp(fn->fname[i], stack_path[i]))
2088 					    break;
2089 				    if (i != fn->len ||
2090 					    strcmp(fn->fname[i], dirp->d_name))
2091 					continue;
2092 				    freemem(top, 1);
2093 				    if (top == filenames)
2094 					top = NULL;
2095 				    else
2096 					top--;
2097 					nfiles--;
2098 					goto duplicate;
2099 				}
2100 			    }
2101 			}
2102 			top->len += 1;
2103 			top->fname[top->len] = calloc(1,
2104 						strlen(&dirp->d_name[0])+1);
2105 			if (top->fname[top->len] == 0) {
2106 			    printf("out of memory\n");
2107 			    error++;
2108 			    return;
2109 			}
2110 			strcpy(top->fname[top->len], &dirp->d_name[0]);
2111 		    }
2112 		}
2113 	    }
2114 duplicate:
2115 	    addr += dirp->d_reclen;
2116 	    cptr += dirp->d_reclen;
2117 	    cur_bytes += dirp->d_reclen;
2118 	}
2119 	if (top < filenames)
2120 	    return;
2121 	if ((doing_cd && level == input_pathp) ||
2122 		(!recursive && !doing_find && level > input_pathp))
2123 	    return;
2124 	bos = top;
2125 	/*
2126 	 * Check newly added entries to determine if further expansion
2127 	 * is required.
2128 	 */
2129 	for (fn = tos; fn <= bos; fn++) {
2130 	    /*
2131 	     * Avoid '.' and '..' if beyond input.
2132 	     */
2133 	    if ((recursive || doing_find) && (level > input_pathp) &&
2134 		(strcmp(fn->fname[fn->len], ".") == 0 ||
2135 		 strcmp(fn->fname[fn->len], "..") == 0))
2136 		 continue;
2137 	    restore_inode(fn->ino);
2138 	    if ((mode = icheck(cur_ino)) == 0)
2139 		return;
2140 	    if ((mode & IFMT) == IFDIR || level < input_pathp) {
2141 		/*
2142 		 * Set up current depth, remove current entry and
2143 		 * continue recursion.
2144 		 */
2145 		for (i = 0; i <= fn->len; i++)
2146 		    strcpy(stack_path[i], fn->fname[i]);
2147 		stack_pathp = fn->len;
2148 		if (!doing_find &&
2149 			(!recursive || (recursive && level <= input_pathp))) {
2150 		    /*
2151 		     * Remove current entry by moving others up.
2152 		     */
2153 		    freemem(fn, 1);
2154 		    fnn = fn;
2155 		    for (fnnn = fnn, fnn++; fnn <= top; fnnn = fnn, fnn++) {
2156 			fnnn->ino = fnn->ino;
2157 			fnnn->len = fnn->len;
2158 			if (fnnn->len + 1 < FIRST_DEPTH) {
2159 			    fnnn->fname = (char **)calloc(FIRST_DEPTH,
2160 							sizeof (char **));
2161 			    fnnn->flag = 0;
2162 			} else if (fnnn->len < SECOND_DEPTH) {
2163 			    fnnn->fname = (char **)calloc(SECOND_DEPTH,
2164 							sizeof (char **));
2165 			    fnnn->flag = 1;
2166 			} else {
2167 			    printf("maximum depth exceeded, ");
2168 			    printf("try to cd lower\n");
2169 			    error++;
2170 			    return;
2171 			}
2172 			for (i = 0; i <= fnn->len; i++)
2173 			    fnnn->fname[i] = fnn->fname[i];
2174 		    }
2175 		    if (fn == tos)
2176 			fn--;
2177 		    top--;
2178 		    bos--;
2179 		    nfiles--;
2180 		}
2181 		follow_path(level + 1, cur_inum);
2182 		if (error)
2183 			return;
2184 	    }
2185 	}
2186 }
2187 
2188 /*
2189  * getname - break up the pathname entered by the user into components.
2190  */
2191 getname()
2192 {
2193 	register int	i;
2194 	char		c;
2195 
2196 	if ((c = getachar()) == '\n') {
2197 	    ungetachar(c);
2198 	    return;
2199 	}
2200 	ungetachar(c);
2201 	input_pathp++;
2202 clear:
2203 	for (i = 0; i < MAXNAMLEN; i++)
2204 	    input_path[input_pathp][i] = '\0';
2205 	for (;;) {
2206 	    c = getachar();
2207 	    if (c == '\\') {
2208 		if (strlen(input_path[input_pathp]) + 1 >= MAXNAMLEN) {
2209 		    printf("maximum name length exceeded, ");
2210 		    printf("truncating\n");
2211 		    return;
2212 		}
2213 		input_path[input_pathp][strlen(input_path[input_pathp])] = c;
2214 		input_path[input_pathp][strlen(input_path[input_pathp])] =
2215 						getachar();
2216 		continue;
2217 	    }
2218 	    if (c == ' ' || c == '\n') {
2219 		ungetachar(c);
2220 		return;
2221 	    }
2222 	    if (!doing_find && c == '/') {
2223 		if (++input_pathp >= MAXPATHLEN) {
2224 		    printf("maximum path length exceeded, ");
2225 		    printf("truncating\n");
2226 		    input_pathp--;
2227 		    return;
2228 		}
2229 		goto clear;
2230 	    }
2231 	    if (strlen(input_path[input_pathp]) >= MAXNAMLEN) {
2232 		printf("maximum name length exceeded, truncating\n");
2233 		return;
2234 	    }
2235 	    input_path[input_pathp][strlen(input_path[input_pathp])] = c;
2236 	}
2237 }
2238 
2239 /*
2240  * compare - check if a filename matches the pattern entered by the user.
2241  *	Handles '*', '?', and '[]'.
2242  */
2243 compare(s1, s2, at_start)
2244 	char		*s1, *s2;
2245 	short		at_start;
2246 {
2247 	register char	c, *s;
2248 
2249 	s = s2;
2250 	while (c = *s1) {
2251 		if (c == '*') {
2252 			if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
2253 				return(0);
2254 			if (*++s1 == 0)
2255 				return(1);
2256 			while (*s2) {
2257 				if (compare(s1, s2, 0))
2258 					return(1);
2259 				if (error)
2260 					return(0);
2261 				s2++;
2262 			}
2263 		}
2264 		if (*s2 == 0)
2265 			return(0);
2266 		if (c == '\\') {
2267 			s1++;
2268 			goto compare_chars;
2269 		}
2270 		if (c == '?') {
2271 			if (at_start && s == s2 && !letter(*s2) && !digit(*s2))
2272 				return(0);
2273 			s1++;
2274 			s2++;
2275 			continue;
2276 		}
2277 		if (c == '[') {
2278 			s1++;
2279 			if (*s2 >= *s1++) {
2280 				if (*s1++ != '-') {
2281 					printf("missing '-'\n");
2282 					error++;
2283 					return(0);
2284 				}
2285 				if (*s2 <= *s1++) {
2286 					if (*s1++ != ']') {
2287 						printf("missing ']'");
2288 						error++;
2289 						return(0);
2290 					}
2291 					s2++;
2292 					continue;
2293 				}
2294 			}
2295 		}
2296 compare_chars:
2297 		if (*s1++ == *s2++)
2298 			continue;
2299 		else
2300 			return(0);
2301 	}
2302 	if (*s1 == *s2)
2303 		return(1);
2304 	return(0);
2305 }
2306 
2307 /*
2308  * freemem - free the memory allocated to the filenames structure.
2309  */
2310 freemem(p, numb)
2311 	struct filenames	*p;
2312 	int			numb;
2313 {
2314 	register int		i, j;
2315 
2316 	if (numb == 0)
2317 		return;
2318 	for (i = 0; i < numb; i++, p++) {
2319 		for (j = 0; j <= p->len; j++)
2320 			free(p->fname[j]);
2321 		free((char *)p->fname);
2322 	}
2323 }
2324 
2325 /*
2326  * print_path - print the pathname held in p.
2327  */
2328 print_path(p, pntr)
2329 	char		*p[];
2330 	short		pntr;
2331 {
2332 	register int	i;
2333 
2334 	printf("/");
2335 	if (pntr >= 0) {
2336 		for (i = 0; i < pntr; i++)
2337 			printf("%s/", p[i]);
2338 		printf("%s", p[pntr]);
2339 	}
2340 }
2341 
2342 /*
2343  * fill - fill a section with a value or string.
2344  *	addr,count:fill=[value, "string"].
2345  */
2346 fill()
2347 {
2348 	register char	*cptr;
2349 	register int	i;
2350 	short		eof_flag, end = 0, eof = 0;
2351 	long		temp, tcount, taddr;
2352 
2353 	if (!wrtflag) {
2354 		printf("not opened for write '-w'\n");
2355 		error++;
2356 		return;
2357 	}
2358 	temp = expr();
2359 	if (error)
2360 		return;
2361 	if ((cptr = getblk(addr)) == 0)
2362 		return;
2363 	if (type == NUMB)
2364 		eof_flag = 0;
2365 	else
2366 		eof_flag = 1;
2367 	taddr = addr;
2368 	switch (objsz) {
2369 	case LONG:
2370 		addr &= ~(LONG - 1);
2371 		break;
2372 	case SHORT:
2373 		addr &= ~(SHORT - 1);
2374 		temp &= 0177777L;
2375 		break;
2376 	case CHAR:
2377 		temp &= 0377;
2378 	}
2379 	cur_bytes -= taddr - addr;
2380 	cptr += blkoff(fs, addr);
2381 	tcount = check_addr(eof_flag, &end, &eof, 0);
2382 	for (i = 0; i < tcount; i++) {
2383 		switch (objsz) {
2384 		case LONG:
2385 			*(long *)cptr = temp;
2386 			break;
2387 		case SHORT:
2388 			*(short *)cptr = temp;
2389 			break;
2390 		case CHAR:
2391 			*cptr = temp;
2392 		}
2393 		cptr += objsz;
2394 	}
2395 	addr += (tcount - 1) * objsz;
2396 	cur_bytes += (tcount - 1) * objsz;
2397 	put(temp, objsz);
2398 	if (eof) {
2399 		printf("end of file\n");
2400 		error++;
2401 	} else if (end) {
2402 		printf("end of block\n");
2403 		error++;
2404 	}
2405 }
2406 
2407 /*
2408  * get - read a byte, short or long from the file system.
2409  *	The entire block containing the desired item is read
2410  *	and the appropriate data is extracted and returned.
2411  */
2412 long
2413 get(lngth)
2414 	short		lngth;
2415 {
2416 
2417 	register char	*bptr;
2418 	long		temp = addr;
2419 
2420 	objsz = lngth;
2421 	if (objsz == INODE || objsz == SHORT)
2422 		temp &= ~(SHORT - 1);
2423 	else if (objsz == DIRECTORY || objsz == LONG)
2424 		temp &= ~(LONG - 1);
2425 	if ((bptr = getblk(temp)) == 0)
2426 		return(-1);
2427 	bptr += blkoff(fs, temp);
2428 	switch (objsz) {
2429 	case CHAR:
2430 		return((long)*bptr);
2431 	case SHORT:
2432 	case INODE:
2433 		return((long)(*(short *)bptr));
2434 	case LONG:
2435 	case DIRECTORY:
2436 		return(*(long *)bptr);
2437 	}
2438 	return(0);
2439 }
2440 
2441 /*
2442  * cgrp_check - make sure that we don't bump the cylinder group
2443  *	beyond the total number of cylinder groups or before the start.
2444  */
2445 cgrp_check(cgrp)
2446 	long		cgrp;
2447 {
2448 	if (cgrp < 0) {
2449 		if (objsz == CGRP)
2450 			printf("beginning of cylinder groups\n");
2451 		else
2452 			printf("beginning of super blocks\n");
2453 		error++;
2454 		return(0);
2455 	}
2456 	if (cgrp >= fs->fs_ncg) {
2457 		if (objsz == CGRP)
2458 			printf("end of cylinder groups\n");
2459 		else
2460 			printf("end of super blocks\n");
2461 		error++;
2462 		return(0);
2463 	}
2464 	if (objsz == CGRP)
2465 		return(cgtod(fs, cgrp) << FRGSHIFT);
2466 	else
2467 		return(cgsblock(fs, cgrp) << FRGSHIFT);
2468 }
2469 
2470 /*
2471  * icheck -  make sure we can read the block containing the inode
2472  *	and determine the filesize (0 if inode not allocated).  Return
2473  *	0 if error otherwise return the mode.
2474  */
2475 icheck(address)
2476 	long			address;
2477 {
2478 	register char		*cptr;
2479 	register struct dinode	*ip;
2480 
2481 	if ((cptr = getblk(address)) == 0)
2482 		return(0);
2483 	cptr += blkoff(fs, address);
2484 	ip = (struct dinode *)cptr;
2485 	if ((ip->di_mode & IFMT) == 0) {
2486 		if (!override) {
2487 			printf("inode not allocated\n");
2488 			error++;
2489 			return(0);
2490 		}
2491 		blocksize = filesize = 0;
2492 	} else {
2493 		trapped++;
2494 		filesize = ip->di_size;
2495 		blocksize = filesize * 2;
2496 	}
2497 	return(ip->di_mode);
2498 }
2499 
2500 /*
2501  * getdirslot - get the address of the directory slot desired.
2502  */
2503 getdirslot(slot)
2504 	short			slot;
2505 {
2506 	register char		*cptr;
2507 	register struct direct	*dirp;
2508 	register short		i;
2509 	char			*string = &scratch[0];
2510 	short			bod = 0, mode, temp;
2511 
2512 	if (slot < 0) {
2513 		slot = 0;
2514 		bod++;
2515 	}
2516 	if (type != DIRECTORY) {
2517 		if (type == BLOCK)
2518 			string = "block";
2519 		else
2520 			string = "fragment";
2521 		addr = bod_addr;
2522 		if ((cptr = getblk(addr)) == 0)
2523 			return(0);
2524 		cptr += blkoff(fs, addr);
2525 		cur_bytes = 0;
2526 		dirp = (struct direct *)cptr;
2527 		for (dirslot = 0; dirslot < slot; dirslot++) {
2528 			dirp = (struct direct *)cptr;
2529 			if (blocksize > filesize) {
2530 				if (cur_bytes + dirp->d_reclen >= filesize) {
2531 					printf("end of file\n");
2532 					erraddr = addr;
2533 					errcur_bytes = cur_bytes;
2534 					stringsize = STRINGSIZE(dirp);
2535 					error++;
2536 					return(addr);
2537 				}
2538 			} else {
2539 				if (cur_bytes + dirp->d_reclen >= blocksize) {
2540 					printf("end of %s\n", string);
2541 					erraddr = addr;
2542 					errcur_bytes = cur_bytes;
2543 					stringsize = STRINGSIZE(dirp);
2544 					error++;
2545 					return(addr);
2546 				}
2547 			}
2548 			cptr += dirp->d_reclen;
2549 			addr += dirp->d_reclen;
2550 			cur_bytes += dirp->d_reclen;
2551 		}
2552 		if (bod) {
2553 			if (blocksize > filesize)
2554 				printf("beginning of file\n");
2555 			else
2556 				printf("beginning of %s\n", string);
2557 			erraddr = addr;
2558 			errcur_bytes = cur_bytes;
2559 			error++;
2560 		}
2561 		stringsize = STRINGSIZE(dirp);
2562 		return(addr);
2563 	} else {
2564 		addr = cur_ino;
2565 		if ((mode = icheck(addr)) == 0)
2566 			return(0);
2567 		if (!override && (mode & IFDIR) == 0) {
2568 			printf("inode is not a directory\n");
2569 			error++;
2570 			return(0);
2571 		}
2572 		temp = slot;
2573 		i = cur_bytes = 0;
2574 		for (;;) {
2575 			if (i == 0 || bcomp(addr)) {
2576 				error = 0;
2577 				if ((addr=(bmap(i++) << FRGSHIFT)) == 0)
2578 					break;
2579 				if ((cptr = getblk(addr)) == 0)
2580 					break;
2581 				cptr += blkoff(fs, addr);
2582 			}
2583 			dirp = (struct direct *)cptr;
2584 			value = dirp->d_ino;
2585 			if (!temp--)
2586 				break;
2587 			if (cur_bytes + dirp->d_reclen >= filesize) {
2588 				printf("end of file\n");
2589 				dirslot = slot - temp - 1;
2590 				objsz = DIRECTORY;
2591 				erraddr = addr;
2592 				errcur_bytes = cur_bytes;
2593 				stringsize = STRINGSIZE(dirp);
2594 				error++;
2595 				return(addr);
2596 			}
2597 			addr += dirp->d_reclen;
2598 			cptr += dirp->d_reclen;
2599 			cur_bytes += dirp->d_reclen;
2600 		}
2601 		dirslot = slot;
2602 		objsz = DIRECTORY;
2603 		if (bod) {
2604 			printf("beginning of file\n");
2605 			erraddr = addr;
2606 			errcur_bytes = cur_bytes;
2607 			error++;
2608 		}
2609 		stringsize = STRINGSIZE(dirp);
2610 		return(addr);
2611 	}
2612 }
2613 
2614 /*
2615  * putf - print a byte as an ascii character if possible.
2616  *	The exceptions are tabs, newlines, backslashes
2617  *	and nulls which are printed as the standard C
2618  *	language escapes. Characters which are not
2619  *	recognized are printed as \?.
2620  */
2621 putf(c)
2622 	register	char  c;
2623 {
2624 
2625 	if (c<=037 || c>=0177 || c=='\\') {
2626 		printf("\\");
2627 		switch (c) {
2628 		case '\\':
2629 			printf("\\");
2630 			break;
2631 		case '\t':
2632 			printf("t");
2633 			break;
2634 		case '\n':
2635 			printf("n");
2636 			break;
2637 		case '\0':
2638 			printf("0");
2639 			break;
2640 		default:
2641 			printf("?");
2642 		}
2643 	}
2644 	else {
2645 		printf("%c", c);
2646 		printf(" ");
2647 	}
2648 }
2649 
2650 /*
2651  * put - write an item into the buffer for the current address
2652  *	block.  The value is checked to make sure that it will
2653  *	fit in the size given without truncation.  If successful,
2654  *	the entire block is written back to the file system.
2655  */
2656 put(item,lngth)
2657 	long		item;
2658 	short		lngth;
2659 {
2660 
2661 	register char	*bptr, *sbptr;
2662 	register long	*vptr;
2663 	long		s_err,nbytes;
2664 	long		olditem;
2665 
2666 	if (!wrtflag) {
2667 		printf("not opened for write '-w'\n");
2668 		error++;
2669 		return;
2670 	}
2671 	objsz = lngth;
2672 	if ((sbptr = getblk(addr)) == 0)
2673 		return;
2674 	bptr = sbptr + blkoff(fs, addr);
2675 	switch (objsz) {
2676 	case LONG:
2677 	case DIRECTORY:
2678 		olditem = *(long *)bptr;
2679 		*(long *)bptr = item;
2680 		break;
2681 	case SHORT:
2682 	case INODE:
2683 		olditem = (long)*(short *)bptr;
2684 		item &= 0177777L;
2685 		*(short *)bptr = item;
2686 		break;
2687 	case CHAR:
2688 		olditem = (long)*bptr;
2689 		item &= 0377;
2690 		*bptr = lobyte(loword(item));
2691 		break;
2692 	default:
2693 		error++;
2694 		return;
2695 	}
2696 	if ((s_err = lseek(fd, addr & fs->fs_bmask, 0)) == -1) {
2697 		error++;
2698 		printf("seek error : %x\n",addr);
2699 		return(0);
2700 	}
2701 	if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
2702 		error++;
2703 		printf("write error : addr   = %x\n",addr);
2704 		printf("            : s_err  = %x\n",s_err);
2705 		printf("            : nbytes = %x\n",nbytes);
2706 		return(0);
2707 	}
2708 	if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
2709 		index(base);
2710 		print(olditem, 8, -8, 0);
2711 		printf("\t=\t");
2712 		print(item, 8, -8, 0);
2713 		printf("\n");
2714 	} else {
2715 		if (objsz == DIRECTORY) {
2716 			addr = cur_dir;
2717 			fprnt('?', 'd');
2718 		} else {
2719 			addr = cur_ino;
2720 			objsz = INODE;
2721 			fprnt('?', 'i');
2722 		}
2723 	}
2724 	return;
2725 }
2726 
2727 /*
2728  * getblk - check if the desired block is in the file system.
2729  *	Search the incore buffers to see if the block is already
2730  *	available. If successful, unlink the buffer control block
2731  *	from its position in the buffer list and re-insert it at
2732  *	the head of the list.  If failure, use the last buffer
2733  *	in the list for the desired block. Again, this control
2734  *	block is placed at the head of the list. This process
2735  *	will leave commonly requested blocks in the in-core buffers.
2736  *	Finally, a pointer to the buffer is returned.
2737  */
2738 char *
2739 getblk(address)
2740 	long			address;
2741 {
2742 
2743 	register struct buf	*bp;
2744 	long			s_err, nbytes;
2745 	unsigned long		block;
2746 
2747 	read_requests++;
2748 	block = lblkno(fs, address);
2749 	if (block >= fragstoblks(fs, fs->fs_size)) {
2750 		printf("block exceeds maximum block in file system\n");
2751 		error++;
2752 		return(0);
2753 	}
2754 	for (bp=bhdr.fwd; bp!= &bhdr; bp=bp->fwd)
2755 		if (bp->valid && bp->blkno==block)
2756 			goto xit;
2757 	actual_disk_reads++;
2758 	bp = bhdr.back;
2759 	bp->blkno = block;
2760 	bp->valid = 0;
2761 	if ((s_err = lseek(fd, address & fs->fs_bmask, 0)) == -1) {
2762 		error++;
2763 		printf("seek error : %x\n",address);
2764 		return(0);
2765 	}
2766 	if ((nbytes = read(fd, bp->blkaddr, BLKSIZE)) != BLKSIZE) {
2767 		error++;
2768 		printf("read error : addr   = %x\n",address);
2769 		printf("           : s_err  = %x\n",s_err);
2770 		printf("           : nbytes = %x\n",nbytes);
2771 		return(0);
2772 	}
2773 	bp->valid++;
2774 xit:	bp->back->fwd = bp->fwd;
2775 	bp->fwd->back = bp->back;
2776 	insert(bp);
2777 	return(bp->blkaddr);
2778 }
2779 
2780 /*
2781  * insert - place the designated buffer control block
2782  *	at the head of the linked list of buffers.
2783  */
2784 insert(bp)
2785 	register struct buf	*bp;
2786 {
2787 
2788 	bp->back = &bhdr;
2789 	bp->fwd = bhdr.fwd;
2790 	bhdr.fwd->back = bp;
2791 	bhdr.fwd = bp;
2792 }
2793 
2794 /*
2795  * err - called on interrupts.  Set the current address
2796  *	back to the last address stored in erraddr. Reset all
2797  *	appropriate flags.  A reset call is made to return
2798  *	to the main loop;
2799  */
2800 err()
2801 {
2802 	freemem(filenames, nfiles);
2803 	nfiles = 0;
2804 	signal(2,err);
2805 	addr = erraddr;
2806 	cur_ino = errino;
2807 	cur_inum = errinum;
2808 	cur_bytes = errcur_bytes;
2809 	error = 0;
2810 	c_count = 0;
2811 	printf("\n?\n");
2812 	fseek(stdin, 0L, 2);
2813 	longjmp(env,0);
2814 }
2815 
2816 /*
2817  * devcheck - check that the given mode represents a
2818  *	special device. The IFCHR bit is on for both
2819  *	character and block devices.
2820  */
2821 devcheck(md)
2822 	register	short md;
2823 {
2824 	if (override)
2825 		return(0);
2826 	if (md & IFCHR)
2827 		return(0);
2828 	printf("not character or block device\n");
2829 	error++;
2830 	return(1);
2831 }
2832 
2833 /*
2834  * nullblk - return error if address is zero.  This is done
2835  *	to prevent block 0 from being used as an indirect block
2836  *	for a large file or as a data block for a small file.
2837  */
2838 nullblk(bn)
2839 	long		bn;
2840 {
2841 	if (bn != 0)
2842 		return(0);
2843 	printf("non existent block\n");
2844 	error++;
2845 	return(1);
2846 }
2847 
2848 /*
2849  * puta - put ascii characters into a buffer.  The string
2850  *	terminates with a quote or newline.  The leading quote,
2851  *	which is optional for directory names, was stripped off
2852  *	by the assignment case in the main loop.
2853  */
2854 puta()
2855 {
2856 	register char		*cptr, c;
2857 	register int		i;
2858 	char			*sbptr;
2859 	short			terror = 0;
2860 	long			maxchars, s_err, nbytes, temp;
2861 	long			taddr = addr, tcount = 0, item, olditem = 0;
2862 
2863 	if (!wrtflag) {
2864 		printf("not opened for write '-w'\n");
2865 		error++;
2866 		return;
2867 	}
2868 	if ((sbptr = getblk(addr)) == 0)
2869 		return;
2870 	cptr = sbptr + blkoff(fs, addr);
2871 	if (objsz == DIRECTORY) {
2872 		if (acting_on_directory)
2873 			maxchars = stringsize - 1;
2874 		else
2875 			maxchars = LONG;
2876 	} else if (objsz == INODE)
2877 		maxchars = objsz - (addr - cur_ino);
2878 	else
2879 		maxchars = min(blocksize - cur_bytes, filesize - cur_bytes);
2880 	while ((c = getachar()) != '"') {
2881 		if (tcount >= maxchars) {
2882 			printf("string too long\n");
2883 			if (objsz == DIRECTORY)
2884 				addr = cur_dir;
2885 			else if (acting_on_inode || objsz == INODE)
2886 				addr = cur_ino;
2887 			else
2888 				addr = taddr;
2889 			erraddr = addr;
2890 			errcur_bytes = cur_bytes;
2891 			terror++;
2892 			break;
2893 		}
2894 		tcount++;
2895 		if (c == '\n') {
2896 			ungetachar(c);
2897 			break;
2898 		}
2899 		temp = (long)*cptr;
2900 		olditem <<= BITSPERCHAR;
2901 		olditem += temp & 0xff;
2902 		if (c == '\\') {
2903 			switch (c = getachar()) {
2904 			case 't':
2905 				*cptr++ = '\t';
2906 				break;
2907 			case 'n':
2908 				*cptr++ = '\n';
2909 				break;
2910 			case '0':
2911 				*cptr++ = '\0';
2912 				break;
2913 			default:
2914 				*cptr++ = c;
2915 				break;
2916 			}
2917 		}
2918 		else
2919 			*cptr++ = c;
2920 	}
2921 	if (objsz == DIRECTORY && acting_on_directory)
2922 		for (i = tcount; i <= maxchars; i++)
2923 			*cptr++ = '\0';
2924 	if ((s_err = lseek(fd, addr & fs->fs_bmask, 0)) == -1) {
2925 		error++;
2926 		printf("seek error : %x\n",addr);
2927 		return(0);
2928 	}
2929 	if ((nbytes = write(fd, sbptr, BLKSIZE)) != BLKSIZE) {
2930 		error++;
2931 		printf("write error : addr   = %x\n",addr);
2932 		printf("            : s_err  = %x\n",s_err);
2933 		printf("            : nbytes = %x\n",nbytes);
2934 		return(0);
2935 	}
2936 	if (!acting_on_inode && objsz != INODE && objsz != DIRECTORY) {
2937 		addr += tcount;
2938 		cur_bytes += tcount;
2939 		taddr = addr;
2940 		if (objsz != CHAR) {
2941 			addr &= ~(objsz - 1);
2942 			cur_bytes -= taddr - addr;
2943 		}
2944 		if (addr == taddr) {
2945 			addr -= objsz;
2946 			taddr = addr;
2947 		}
2948 		tcount = LONG - (taddr - addr);
2949 		index(base);
2950 		if ((cptr = getblk(addr)) == 0)
2951 			return;
2952 		cptr += blkoff(fs, addr);
2953 		switch (objsz) {
2954 		case LONG:
2955 			item = *(long *)cptr;
2956 			if (tcount < LONG) {
2957 				olditem <<= tcount * BITSPERCHAR;
2958 				temp = 1;
2959 				for (i = 0; i < (tcount*BITSPERCHAR); i++)
2960 					temp <<= 1;
2961 				olditem += item & (temp - 1);
2962 			}
2963 			break;
2964 		case SHORT:
2965 			item = (long)*(short *)cptr;
2966 			if (tcount < SHORT) {
2967 				olditem <<= tcount * BITSPERCHAR;
2968 				temp = 1;
2969 				for (i = 0; i < (tcount * BITSPERCHAR); i++)
2970 					temp <<= 1;
2971 				olditem += item & (temp - 1);
2972 			}
2973 			olditem &= 0177777L;
2974 			break;
2975 		case CHAR:
2976 			item = (long)*cptr;
2977 			olditem &= 0377;
2978 		}
2979 		print(olditem, 8, -8, 0);
2980 		printf("\t=\t");
2981 		print(item, 8, -8, 0);
2982 		printf("\n");
2983 	} else {
2984 		if (objsz == DIRECTORY) {
2985 			addr = cur_dir;
2986 			fprnt('?', 'd');
2987 		} else {
2988 			addr = cur_ino;
2989 			objsz = INODE;
2990 			fprnt('?', 'i');
2991 		}
2992 	}
2993 	if (terror)
2994 		error++;
2995 }
2996 
2997 /*
2998  * fprnt - print data.  'count' elements are printed where '*' will
2999  *	print an entire blocks worth or up to the eof, whichever
3000  *	occurs first.  An error will occur if crossing a block boundary
3001  *	is attempted since consecutive blocks don't usually have
3002  *	meaning.  Current print types:
3003  *		/		b   - print as bytes (base sensitive)
3004  *				c   - print as characters
3005  *				o O - print as octal shorts (longs)
3006  *				d D - print as decimal shorts (longs)
3007  *				x X - print as hexadecimal shorts (longs)
3008  *		?		c   - print as cylinder groups
3009  *				d   - print as directories
3010  *				i   - print as inodes
3011  *				s   - print as super blocks
3012  */
3013 fprnt(style, po)
3014 	register char		style, po;
3015 {
3016 	register int		i;
3017 	register struct fs	*sb;
3018 	register struct cg	*cg;
3019 	register struct direct	*dirp;
3020 	register struct dinode	*ip;
3021 	int			tbase;
3022 	char			c, *cptr, *p;
3023 	long			tinode, tcount, temp, taddr;
3024 	short			offset, mode, end = 0, eof = 0, eof_flag;
3025 	unsigned short		*sptr;
3026 	unsigned long		*lptr;
3027 
3028 	laststyle = style;
3029 	lastpo = po;
3030 	should_print = 0;
3031 	if (count != 1) {
3032 		if (clear) {
3033 			count = 1;
3034 			star = 0;
3035 			clear = 0;
3036 		} else
3037 			clear = 1;
3038 	}
3039 	tcount = count;
3040 	offset = blkoff(fs, addr);
3041 
3042 	if (style == '/') {
3043 		if (type == NUMB)
3044 			eof_flag = 0;
3045 		else
3046 			eof_flag = 1;
3047 		switch (po) {
3048 
3049 		case 'c': /* print as characters */
3050 		case 'b': /* or bytes */
3051 			if ((cptr = getblk(addr)) == 0)
3052 				return;
3053 			cptr += offset;
3054 			objsz = CHAR;
3055 			tcount = check_addr(eof_flag, &end, &eof, 0);
3056 			if (tcount) {
3057 				for (i=0; tcount--; i++) {
3058 					if (i % 16 == 0) {
3059 						if (i)
3060 							printf("\n");
3061 						index(base);
3062 					}
3063 					if (po == 'c') {
3064 						putf(*cptr++);
3065 						if ((i + 1) % 16)
3066 							printf("  ");
3067 					} else {
3068 						if ((i + 1) % 16 == 0)
3069 							print(*cptr++ & 0377,
3070 								2,-2,0);
3071 						else
3072 							print(*cptr++ & 0377,
3073 								4,-2,0);
3074 					}
3075 					addr += CHAR;
3076 					cur_bytes += CHAR;
3077 				}
3078 				printf("\n");
3079 			}
3080 			addr -= CHAR;
3081 			erraddr = addr;
3082 			cur_bytes -= CHAR;
3083 			errcur_bytes = cur_bytes;
3084 			if (eof) {
3085 				printf("end of file\n");
3086 				error++;
3087 			} else if (end) {
3088 				if (type == BLOCK)
3089 					printf("end of block\n");
3090 				else
3091 					printf("end of fragment\n");
3092 				error++;
3093 			}
3094 			return;
3095 
3096 		case 'o': /* print as octal shorts */
3097 			tbase = OCTAL;
3098 			goto otx;
3099 		case 'd': /* print as decimal shorts */
3100 			tbase = DECIMAL;
3101 			goto otx;
3102 		case 'x': /* print as hex shorts */
3103 			tbase = HEX;
3104 otx:
3105 			if ((cptr = getblk(addr)) == 0)
3106 				return;
3107 			taddr = addr;
3108 			addr &= ~(SHORT - 1);
3109 			cur_bytes -= taddr - addr;
3110 			cptr += blkoff(fs, addr);
3111 			sptr = (unsigned short *)cptr;
3112 			objsz = SHORT;
3113 			tcount = check_addr(eof_flag, &end, &eof, 0);
3114 			if (tcount) {
3115 				for (i=0; tcount--; i++) {
3116 					sptr = (unsigned short *)
3117 					   print_check(sptr, &tcount, tbase, i);
3118 					switch (po) {
3119 					case 'o':
3120 						printf("%06o ",*sptr++);
3121 						break;
3122 					case 'd':
3123 						printf("%05d  ",*sptr++);
3124 						break;
3125 					case 'x':
3126 						printf("%04x   ",*sptr++);
3127 					}
3128 					addr += SHORT;
3129 					cur_bytes += SHORT;
3130 				}
3131 				printf("\n");
3132 			}
3133 			addr -= SHORT;
3134 			erraddr = addr;
3135 			cur_bytes -= SHORT;
3136 			errcur_bytes = cur_bytes;
3137 			if (eof) {
3138 				printf("end of file\n");
3139 				error++;
3140 			} else if (end) {
3141 				if (type == BLOCK)
3142 					printf("end of block\n");
3143 				else
3144 					printf("end of fragment\n");
3145 				error++;
3146 			}
3147 			return;
3148 
3149 		case 'O': /* print as octal longs */
3150 			tbase = OCTAL;
3151 			goto OTX;
3152 		case 'D': /* print as decimal longs */
3153 			tbase = DECIMAL;
3154 			goto OTX;
3155 		case 'X': /* print as hex longs */
3156 			tbase = HEX;
3157 OTX:
3158 			if ((cptr = getblk(addr)) == 0)
3159 				return;
3160 			taddr = addr;
3161 			addr &= ~(LONG - 1);
3162 			cur_bytes -= taddr - addr;
3163 			cptr += blkoff(fs, addr);
3164 			lptr = (unsigned long *)cptr;
3165 			objsz = LONG;
3166 			tcount = check_addr(eof_flag, &end, &eof, 0);
3167 			if (tcount) {
3168 				for (i=0; tcount--; i++) {
3169 					lptr =
3170 					   print_check(lptr, &tcount, tbase, i);
3171 					switch (po) {
3172 					case 'O':
3173 						printf("%011o    ",*lptr++);
3174 						break;
3175 					case 'D':
3176 						printf("%010u     ",*lptr++);
3177 						break;
3178 					case 'X':
3179 						printf("%08x       ",*lptr++);
3180 					}
3181 					addr += LONG;
3182 					cur_bytes += LONG;
3183 				}
3184 				printf("\n");
3185 			}
3186 			addr -= LONG;
3187 			erraddr = addr;
3188 			cur_bytes -= LONG;
3189 			errcur_bytes = cur_bytes;
3190 			if (eof) {
3191 				printf("end of file\n");
3192 				error++;
3193 			} else if (end) {
3194 				if (type == BLOCK)
3195 					printf("end of block\n");
3196 				else
3197 					printf("end of fragment\n");
3198 				error++;
3199 			}
3200 			return;
3201 
3202 		default:
3203 			error++;
3204 			printf("no such print option\n");
3205 			return;
3206 		}
3207 	} else
3208 		switch (po) {
3209 
3210 		case 'c': /* print as cylinder group */
3211 			if (type != NUMB)
3212 				if (cur_cgrp + count > fs->fs_ncg) {
3213 					tcount = fs->fs_ncg - cur_cgrp;
3214 					if (!star)
3215 						end++;
3216 				}
3217 			addr &= ~(LONG - 1);
3218 			for (; tcount--;) {
3219 				erraddr = addr;
3220 				errcur_bytes = cur_bytes;
3221 				if (type != NUMB) {
3222 					addr = cgtod(fs, cur_cgrp)
3223 						<< FRGSHIFT;
3224 					cur_cgrp++;
3225 				}
3226 				if ((cptr = getblk(addr)) == 0) {
3227 					if (cur_cgrp)
3228 						cur_cgrp--;
3229 					return;
3230 				}
3231 				cptr += blkoff(fs, addr);
3232 				cg = (struct cg *)cptr;
3233 				if (type == NUMB) {
3234 					cur_cgrp = cg->cg_cgx + 1;
3235 					type = objsz = CGRP;
3236 					if (cur_cgrp + count - 1 > fs->fs_ncg) {
3237 						tcount = fs->fs_ncg - cur_cgrp;
3238 						if (!star)
3239 							end++;
3240 					}
3241 				}
3242 				if (!override && cg->cg_magic != CG_MAGIC) {
3243 					printf("invalid cylinder group ");
3244 					printf("magic word\n");
3245 					if (cur_cgrp)
3246 						cur_cgrp--;
3247 					error++;
3248 					return;
3249 				}
3250 				printf("\tcylinder group ");
3251 				print(cur_cgrp - 1, 0, 0, 0);
3252 				printf(":\n");
3253 				printf("cg_cgx    = ");
3254 				print(cg->cg_cgx, 12, -8, 0);
3255 				printf("cg_ncyl   = ");
3256 				print(cg->cg_ncyl, 12, -8, 0);
3257 				printf("\ncg_niblk  = ");
3258 				print(cg->cg_niblk, 12, -8, 0);
3259 				printf("cg_ndblk  = ");
3260 				print(cg->cg_ndblk, 12, -8, 0);
3261 				printf("\ncg_rotor  = ");
3262 				print(cg->cg_rotor, 12, -8, 0);
3263 				printf("cg_frotor = ");
3264 				print(cg->cg_frotor, 12, -8, 0);
3265 				printf("cg_irotor = ");
3266 				print(cg->cg_irotor, 12, -8, 0);
3267 				printf("\n\tcylinder group summary info:\n");
3268 				printf("cs_ndir   = ");
3269 				print(cg->cg_cs.cs_ndir, 12, -8, 0);
3270 				printf("cs_nbfree = ");
3271 				print(cg->cg_cs.cs_nbfree, 12, -8, 0);
3272 				printf("\ncs_nifree = ");
3273 				print(cg->cg_cs.cs_nifree, 12, -8, 0);
3274 				printf("cs_nffree = ");
3275 				print(cg->cg_cs.cs_nffree, 12, -8, 0);
3276 				printf("\n");
3277 				if (count == 1)
3278 					printf("\tmodified: %s",
3279 						ctime(&cg->cg_time));
3280 				if (tcount)
3281 					printf("\n");
3282 			}
3283 			cur_cgrp--;
3284 			if (end) {
3285 				printf("end of cylinder groups\n");
3286 				error++;
3287 			}
3288 			return;
3289 
3290 		case 'd': /* print as directories */
3291 			if ((cptr = getblk(addr)) == 0)
3292 				return;
3293 			if (type == NUMB) {
3294 				if (fragoff(fs, addr)) {
3295 					printf("address must be at the ");
3296 					printf("beginning of a fragment\n");
3297 					error++;
3298 					return;
3299 				}
3300 				bod_addr = addr;
3301 				type = FRAGMENT;
3302 				dirslot = 0;
3303 				cur_bytes = 0;
3304 				blocksize = FRGSIZE;
3305 				filesize = FRGSIZE * 2;
3306 			}
3307 			cptr += offset;
3308 			objsz = DIRECTORY;
3309 			while (tcount-- && cur_bytes < filesize &&
3310 			       cur_bytes < blocksize && !bcomp(addr)) {
3311 				dirp = (struct direct *)cptr;
3312 				tinode = dirp->d_ino;
3313 				printf("i#: ");
3314 				if (tinode == 0)
3315 					printf("free\t");
3316 				else
3317 					print(tinode, 12, -8, 0);
3318 				printf("%s\n",&dirp->d_name[0]);
3319 				erraddr = addr;
3320 				errcur_bytes = cur_bytes;
3321 				addr += dirp->d_reclen;
3322 				cptr += dirp->d_reclen;
3323 				cur_bytes += dirp->d_reclen;
3324 				dirslot++;
3325 			}
3326 			addr = erraddr;
3327 			cur_dir = addr;
3328 			cur_bytes = errcur_bytes;
3329 			stringsize = STRINGSIZE(dirp);
3330 			dirslot--;
3331 			if (tcount >= 0 && !star) {
3332 				switch (type) {
3333 				case FRAGMENT:
3334 					printf("end of fragment\n");
3335 					break;
3336 				case BLOCK:
3337 					printf("end of block\n");
3338 					break;
3339 				default:
3340 					printf("end of directory\n");
3341 				}
3342 				error++;
3343 			} else
3344 				error = 0;
3345 			return;
3346 
3347 		case 'i': /* print as inodes */
3348 			if ((ip = (struct dinode *)getblk(addr)) == 0)
3349 				return;
3350 			for (i=1; i < fs->fs_ncg; i++)
3351 				if (addr < (cgimin(fs,i) << FRGSHIFT))
3352 					break;
3353 			i--;
3354 			offset /= INODE;
3355 			temp = (addr - (cgimin(fs,i) << FRGSHIFT)) >> FRGSHIFT;
3356 			temp = (i * fs->fs_ipg) + fragstoblks(fs,temp) *
3357 							INOPB(fs) + offset;
3358 			if (count + offset > INOPB(fs)) {
3359 				tcount = INOPB(fs) - offset;
3360 				if (!star)
3361 					end++;
3362 			}
3363 			objsz = INODE;
3364 			ip += offset;
3365 			for (i=0; tcount--; ip++, temp++) {
3366 				if ((mode = icheck(addr)) == 0)
3367 					if (!override)
3368 						continue;
3369 				p = " ugtrwxrwxrwx";
3370 
3371 				switch (mode & IFMT) {
3372 				case IFDIR:
3373 					c = 'd';
3374 					break;
3375 				case IFCHR:
3376 					c = 'c';
3377 					break;
3378 				case IFBLK:
3379 					c = 'b';
3380 					break;
3381 				case IFREG:
3382 					c = '-';
3383 					break;
3384 /*				case IFIFO:
3385 					c = 'p';
3386 					break;	 	SYSTEM V only */
3387 				case IFLNK:
3388 					c = 'l';
3389 					break;
3390 				case IFSOCK:
3391 					c = 's';
3392 					break;
3393 				default:
3394 					c = '?';
3395 					if (!override)
3396 						goto empty;
3397 
3398 				}
3399 				printf("i#: ");
3400 				print(temp,12,-8,0);
3401 				printf("   md: ");
3402 				printf("%c", c);
3403 				for (mode = mode << 4; *++p; mode = mode << 1) {
3404 					if (mode & IFREG)
3405 						printf("%c", *p);
3406 					else
3407 						printf("-");
3408 				}
3409 				printf("  uid: ");
3410 				print(ip->di_uid,8,-4,0);
3411 				printf("      gid: ");
3412 				print(ip->di_gid,8,-4,0);
3413 				printf("\n");
3414 				printf("ln: ");
3415 				print(ip->di_nlink,8,-4,0);
3416 				printf("       bs: ");
3417 				print(ip->di_blocks,12,-8,0);
3418 				printf("   sz : ");
3419 				print(ip->di_size,12,-8,0);
3420 				printf("\n");
3421 				if (ip->di_mode & IFCHR) {
3422 					printf("maj: ");
3423 					print(ip->di_db[1] & 0377,4,-2,0);
3424 					printf("  min: ");
3425 					print(ip->di_db[0] & 0377,4,-2,0);
3426 					printf("\n");
3427 				} else {
3428 					for (i = 0; i < NDADDR; ) {
3429 						if (ip->di_db[i] == 0)
3430 							break;
3431 						printf("db#%x: ",i);
3432 						print(ip->di_db[i],11,-8,0);
3433 						if (++i % 4 == 0)
3434 							printf("\n");
3435 						else
3436 							printf("  ");
3437 					}
3438 					if (i % 4)
3439 						printf("\n");
3440 					for (i = 0; i < NIADDR; i++) {
3441 						if (ip->di_ib[i] == 0)
3442 							break;
3443 						printf("ib#%x: ",i);
3444 						print(ip->di_ib[i],11,-8,0);
3445 						printf("  ");
3446 					}
3447 					if (i)
3448 						printf("\n");
3449 				}
3450 				if (count == 1) {
3451 					printf("\taccessed: %s",
3452 						ctime(&ip->di_atime));
3453 					printf("\tmodified: %s",
3454 						ctime(&ip->di_mtime));
3455 					printf("\tcreated : %s",
3456 						ctime(&ip->di_ctime));
3457 				}
3458 				if (tcount)
3459 					printf("\n");
3460 empty:
3461 				if (c == '?' && !override) {
3462 					printf("i#: ");
3463 					print(temp, 12, -8, 0);
3464 					printf("  is unallocated\n");
3465 					if (count != 1)
3466 						printf("\n");
3467 				}
3468 				cur_ino = erraddr = addr;
3469 				errcur_bytes = cur_bytes;
3470 				cur_inum++;
3471 				addr = addr + INODE;
3472 			}
3473 			addr = erraddr;
3474 			cur_bytes = errcur_bytes;
3475 			cur_inum--;
3476 			if (end) {
3477 				printf("end of block\n");
3478 				error++;
3479 			}
3480 			return;
3481 
3482 		case 's': /* print as super block */
3483 			if (cur_cgrp == -1) {
3484 				addr = SBLOCK * DEV_BSIZE;
3485 				type = NUMB;
3486 			}
3487 			addr &= ~(LONG - 1);
3488 			if (type != NUMB)
3489 				if (cur_cgrp + count > fs->fs_ncg) {
3490 					tcount = fs->fs_ncg - cur_cgrp;
3491 					if (!star)
3492 						end++;
3493 				}
3494 			for (; tcount--;) {
3495 				erraddr = addr;
3496 				cur_bytes = errcur_bytes;
3497 				if (type != NUMB) {
3498 					addr = cgsblock(fs, cur_cgrp)
3499 							<< FRGSHIFT;
3500 					cur_cgrp++;
3501 				}
3502 				if ((cptr = getblk(addr)) == 0) {
3503 					if (cur_cgrp)
3504 						cur_cgrp--;
3505 					return;
3506 				}
3507 				cptr += blkoff(fs, addr);
3508 				sb = (struct fs *)cptr;
3509 				if (type == NUMB) {
3510 					for (i = 0; i < fs->fs_ncg; i++)
3511 						if (addr == cgsblock(fs, i) <<
3512 								FRGSHIFT)
3513 							break;
3514 					if (i == fs->fs_ncg)
3515 						cur_cgrp = 0;
3516 					else
3517 						cur_cgrp = i + 1;
3518 					type = objsz = SB;
3519 					if (cur_cgrp + count - 1 > fs->fs_ncg) {
3520 						tcount = fs->fs_ncg - cur_cgrp;
3521 						if (!star)
3522 							end++;
3523 					}
3524 				}
3525 				if (sb->fs_magic != FS_MAGIC) {
3526 					cur_cgrp = 0;
3527 					if (!override) {
3528 						printf("invalid super block ");
3529 						printf("magic word\n");
3530 						cur_cgrp--;
3531 						error++;
3532 						return;
3533 					}
3534 				}
3535 				if (cur_cgrp == 0)
3536 					printf("\tsuper block:\n");
3537 				else {
3538 					printf("\tsuper block in cylinder ");
3539 					printf("group ");
3540 					print(cur_cgrp - 1, 0, 0, 0);
3541 					printf(":\n");
3542 				}
3543 				printf("fs_sblkno    = ");
3544 				print(fs->fs_sblkno, 12, -8, 0);
3545 				printf("fs_cblkno    = ");
3546 				print(fs->fs_cblkno, 12, -8, 0);
3547 				printf("fs_iblkno = ");
3548 				print(fs->fs_iblkno, 12, -8, 0);
3549 				printf("\nfs_dblkno    = ");
3550 				print(fs->fs_dblkno, 12, -8, 0);
3551 				printf("fs_cgoffset  = ");
3552 				print(fs->fs_cgoffset, 12, -8, 0);
3553 				printf("fs_cgmask = ");
3554 				print(fs->fs_cgmask, 12, -8, 0);
3555 				printf("\nfs_size      = ");
3556 				print(fs->fs_size, 12, -8, 0);
3557 				printf("fs_dsize     = ");
3558 				print(fs->fs_dsize, 12, -8, 0);
3559 				printf("fs_ncg    = ");
3560 				print(fs->fs_ncg, 12, -8, 0);
3561 				printf("\nfs_bsize     = ");
3562 				print(fs->fs_bsize, 12, -8, 0);
3563 				printf("fs_fsize     = ");
3564 				print(fs->fs_fsize, 12, -8, 0);
3565 				printf("fs_frag   = ");
3566 				print(fs->fs_frag, 12, -8, 0);
3567 				printf("\nfs_minfree   = ");
3568 				print(fs->fs_minfree, 12, -8, 0);
3569 				printf("fs_rotdelay  = ");
3570 				print(fs->fs_rotdelay, 12, -8, 0);
3571 				printf("fs_rps    = ");
3572 				print(fs->fs_rps, 12, -8, 0);
3573 				printf("\nfs_bmask     = ");
3574 				print(fs->fs_bmask, 12, -8, 0);
3575 				printf("fs_fmask     = ");
3576 				print(fs->fs_fmask, 12, -8, 0);
3577 				printf("fs_bshift = ");
3578 				print(fs->fs_bshift, 12, -8, 0);
3579 				printf("\nfs_fshift    = ");
3580 				print(fs->fs_fshift, 12, -8, 0);
3581 				printf("fs_maxcontig = ");
3582 				print(fs->fs_maxcontig, 12, -8, 0);
3583 				printf("fs_maxbpg = ");
3584 				print(fs->fs_maxbpg, 12, -8, 0);
3585 				printf("\nfs_fragshift = ");
3586 				print(fs->fs_fragshift, 12, -8, 0);
3587 				printf("fs_fsbtodb   = ");
3588 				print(fs->fs_fsbtodb, 12, -8, 0);
3589 				printf("fs_sbsize = ");
3590 				print(fs->fs_sbsize, 12, -8, 0);
3591 				printf("\nfs_csmask    = ");
3592 				print(fs->fs_csmask, 12, -8, 0);
3593 				printf("fs_csshift   = ");
3594 				print(fs->fs_csshift, 12, -8, 0);
3595 				printf("fs_nindir = ");
3596 				print(fs->fs_nindir, 12, -8, 0);
3597 				printf("\nfs_inopb     = ");
3598 				print(fs->fs_inopb, 12, -8, 0);
3599 				printf("fs_nspf      = ");
3600 				print(fs->fs_nspf, 12, -8, 0);
3601 				printf("fs_csaddr = ");
3602 				print(fs->fs_csaddr, 12, -8, 0);
3603 				printf("\nfs_cssize    = ");
3604 				print(fs->fs_cssize, 12, -8, 0);
3605 				printf("fs_cgsize    = ");
3606 				print(fs->fs_cgsize, 12, -8, 0);
3607 				printf("fs_ntrak  = ");
3608 				print(fs->fs_ntrak, 12, -8, 0);
3609 				printf("\nfs_nsect     = ");
3610 				print(fs->fs_nsect, 12, -8, 0);
3611 				printf("fs_spc       = ");
3612 				print(fs->fs_spc, 12, -8, 0);
3613 				printf("fs_ncyl   = ");
3614 				print(fs->fs_ncyl, 12, -8, 0);
3615 				printf("\nfs_cpg       = ");
3616 				print(fs->fs_cpg, 12, -8, 0);
3617 				printf("fs_ipg       = ");
3618 				print(fs->fs_ipg, 12, -8, 0);
3619 				printf("fs_fpg    = ");
3620 				print(fs->fs_fpg, 12, -8, 0);
3621 				printf("\nfs_fmod      = ");
3622 				print(fs->fs_fmod, 12, -8, 0);
3623 				printf("fs_clean     = ");
3624 				print(fs->fs_clean, 12, -8, 0);
3625 				printf("fs_ronly  = ");
3626 				print(fs->fs_ronly, 12, -8, 0);
3627 				printf("\nfs_flags     = ");
3628 				print(fs->fs_flags, 12, -8, 0);
3629 				printf("fs_cgrotor   = ");
3630 				print(fs->fs_cgrotor, 12, -8, 0);
3631 				printf("fs_cpc    = ");
3632 				print(fs->fs_cpc, 12, -8, 0);
3633 				printf("\nfs_magic     = ");
3634 				print(fs->fs_magic, 12, -8, 0);
3635 				printf("\n");
3636 				printf("\ttotal cylinder group summary ");
3637 				printf("info:\n");
3638 				printf("cs_ndir      = ");
3639 				print(fs->fs_cstotal.cs_ndir, 12, -8, 0);
3640 				printf("cs_nbfree    = ");
3641 				print(fs->fs_cstotal.cs_nbfree, 12, -8, 0);
3642 				printf("\ncs_nifree    = ");
3643 				print(fs->fs_cstotal.cs_nifree, 12, -8, 0);
3644 				printf("cs_nffree    = ");
3645 				print(fs->fs_cstotal.cs_nffree, 12, -8, 0);
3646 				printf("\n");
3647 				if (count == 1)
3648 					printf("\tmodified: %s",
3649 						ctime(&fs->fs_time));
3650 				if (tcount)
3651 					printf("\n");
3652 			}
3653 			cur_cgrp--;
3654 			if (end) {
3655 				printf("end of super blocks\n");
3656 				error++;
3657 			}
3658 			return;
3659 		default:
3660 			error++;
3661 			printf("no such print option\n");
3662 			return;
3663 		}
3664 }
3665 
3666 /*
3667  * valid_addr - call check_addr to validate the current address.
3668  */
3669 valid_addr()
3670 {
3671 	short	eof_flag, end = 0, eof = 0;
3672 	long	tcount = count;
3673 
3674 	if (!trapped)
3675 		return(1);
3676 	if (cur_bytes < 0) {
3677 		cur_bytes = 0;
3678 		if (blocksize > filesize) {
3679 			printf("beginning of file\n");
3680 		} else {
3681 			if (type == BLOCK)
3682 				printf("beginning of block\n");
3683 			else
3684 				printf("beginning of fragment\n");
3685 		}
3686 		error++;
3687 		return(0);
3688 	}
3689 	count = 1;
3690 	check_addr(1, &end, &eof, (filesize < blocksize));
3691 	count = tcount;
3692 	if (eof) {
3693 		printf("end of file\n");
3694 		error++;
3695 		return(0);
3696 	}
3697 	if (end == 2) {
3698 		if (erraddr > addr) {
3699 			if (type == BLOCK)
3700 				printf("beginning of block\n");
3701 			else
3702 				printf("beginning of fragment\n");
3703 			error++;
3704 			return(0);
3705 		}
3706 	}
3707 	if (end) {
3708 		if (type == BLOCK)
3709 			printf("end of block\n");
3710 		else
3711 			printf("end of fragment\n");
3712 		error++;
3713 		return(0);
3714 	}
3715 	return(1);
3716 }
3717 
3718 /*
3719  * check_addr - check if the address crosses the end of block or
3720  *	end of file.  Return the proper count.
3721  */
3722 check_addr(eof_flag, end, eof, keep_on)
3723 	short	eof_flag, *end, *eof, keep_on;
3724 {
3725 	long	temp, tcount = count, taddr = addr, tcur_bytes = cur_bytes;
3726 
3727 	if (bcomp(addr + count * objsz - 1) ||
3728 	    (keep_on && taddr < (bmap(cur_block) << FRGSHIFT))) {
3729 		error = 0;
3730 		addr = taddr;
3731 		cur_bytes = tcur_bytes;
3732 		if (keep_on) {
3733 			if (addr < erraddr) {
3734 				if (cur_bytes < 0) {
3735 					(*end) = 2;
3736 					return;
3737 				}
3738 				temp = cur_block - lblkno(fs, cur_bytes);
3739 				cur_block -= temp;
3740 				if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
3741 					cur_block += temp;
3742 					return;
3743 				}
3744 				temp = tcur_bytes - cur_bytes;
3745 				addr += temp;
3746 				cur_bytes += temp;
3747 				return;
3748 			} else {
3749 				if (cur_bytes >= filesize) {
3750 					(*eof)++;
3751 					return;
3752 				}
3753 				temp = lblkno(fs, cur_bytes) - cur_block;
3754 				cur_block += temp;
3755 				if ((addr = bmap(cur_block) << FRGSHIFT) == 0) {
3756 					cur_block -= temp;
3757 					return;
3758 				}
3759 				temp = tcur_bytes - cur_bytes;
3760 				addr += temp;
3761 				cur_bytes += temp;
3762 				return;
3763 			}
3764 		}
3765 		tcount = (blkroundup(fs, addr+1)-addr) / objsz;
3766 		if (!star)
3767 			(*end) = 2;
3768 	}
3769 	addr = taddr;
3770 	cur_bytes = tcur_bytes;
3771 	if (eof_flag) {
3772 		if (blocksize > filesize) {
3773 			if (cur_bytes >= filesize) {
3774 				tcount = 0;
3775 				(*eof)++;
3776 			} else if (tcount > (filesize - cur_bytes) / objsz) {
3777 				tcount = (filesize - cur_bytes) / objsz;
3778 				if (!star || tcount == 0)
3779 					(*eof)++;
3780 			}
3781 		} else {
3782 			if (cur_bytes >= blocksize) {
3783 				tcount = 0;
3784 				(*end)++;
3785 			} else if (tcount > (blocksize - cur_bytes) / objsz) {
3786 				tcount = (blocksize - cur_bytes) / objsz;
3787 				if (!star || tcount == 0)
3788 					(*end)++;
3789 			}
3790 		}
3791 	}
3792 	return(tcount);
3793 }
3794 
3795 /*
3796  * print_check - check if the index needs to be printed and delete
3797  *	rows of zeros from the output.
3798  */
3799 unsigned long *
3800 print_check(lptr, tcount, tbase, i)
3801 	unsigned long	*lptr;
3802 	long		*tcount;
3803 	short		tbase;
3804 	register int	i;
3805 {
3806 	register int	j, k, temp = BYTESPERLINE / objsz;
3807 	short		first_time = 0;
3808 	unsigned long	*tlptr;
3809 	unsigned short	*tsptr, *sptr;
3810 
3811 	sptr = (unsigned short *)lptr;
3812 	if (i == 0)
3813 		first_time = 1;
3814 	if (i % temp == 0) {
3815 		if (*tcount >= temp - 1) {
3816 			if (objsz == SHORT)
3817 				tsptr = sptr;
3818 			else
3819 				tlptr = lptr;
3820 			k = *tcount - 1;
3821 			for (j = i; k--; j++)
3822 				if (objsz == SHORT) {
3823 					if (*tsptr++ != 0)
3824 						break;
3825 				} else {
3826 					if (*tlptr++ != 0)
3827 						break;
3828 				}
3829 			if (j > (i + temp - 1)) {
3830 				j = (j - i) / temp;
3831 				while (j-- > 0) {
3832 					if (objsz == SHORT)
3833 						sptr += temp;
3834 					else
3835 						lptr += temp;
3836 					*tcount -= temp;
3837 					i += temp;
3838 					addr += BYTESPERLINE;
3839 					cur_bytes += BYTESPERLINE;
3840 				}
3841 				if (first_time)
3842 					printf("*");
3843 				else
3844 					printf("\n*");
3845 			}
3846 			if (i)
3847 				printf("\n");
3848 			index(tbase);
3849 		} else {
3850 			if (i)
3851 				printf("\n");
3852 			index(tbase);
3853 		}
3854 	}
3855 	if(objsz == SHORT)
3856 		return((unsigned long *)sptr);
3857 	else
3858 		return(lptr);
3859 }
3860 
3861 /*
3862  * index - print a byte index for the printout in base b
3863  *	with leading zeros.
3864  */
3865 index(b)
3866 	int	b;
3867 {
3868 	int	tbase = base;
3869 
3870 	base = b;
3871 	print(addr, 8, 8, 1);
3872 	printf(":\t");
3873 	base = tbase;
3874 }
3875 
3876 /*
3877  * print - print out the value to digits places with/without
3878  *	leading zeros and right/left justified in the current base.
3879  */
3880 print(value, fieldsz, digits, lead)
3881 	int		value, fieldsz, digits, lead;
3882 {
3883 	register int	i, left = 0;
3884 	char		mode = BASE[base - OCTAL];
3885 	char		*string = &scratch[0];
3886 
3887 	if (digits < 0) {
3888 		left = 1;
3889 		digits *= -1;
3890 	}
3891 	if (base != HEX)
3892 		if (digits)
3893 			digits = digits + (digits - 1)/((base >> 1) - 1) + 1;
3894 		else
3895 			digits = 1;
3896 	if (lead) {
3897 		if (left)
3898 			sprintf(string, "%%%c%d%d.%d%c",
3899 				'-', 0, digits, lead, mode);
3900 		else
3901 			sprintf(string, "%%%d%d.%d%c", 0, digits, lead, mode);
3902 	} else {
3903 		if (left)
3904 			sprintf(string, "%%%c%d%c", '-', digits, mode);
3905 		else
3906 			sprintf(string, "%%%d%c", digits, mode);
3907 	}
3908 	printf(string, value);
3909 	for (i = 0; i < fieldsz - digits; i++)
3910 		printf(" ");
3911 }
3912 
3913 /*
3914  * bcomp - used to check for block over/under flows when stepping through
3915  *	a file system.
3916  */
3917 bcomp(addr)
3918 	long	addr;
3919 {
3920 	if (override)
3921 		return(0);
3922 	if (lblkno(fs, addr) == (bhdr.fwd)->blkno)
3923 		return(0);
3924 	error++;
3925 	return(1);
3926 }
3927 
3928 /*
3929  * bmap - maps the logical block number of a file into
3930  *	the corresponding physical block on the file
3931  *	system.
3932  */
3933 long
3934 bmap(bn)
3935 	long			bn;
3936 {
3937 	register int		i, j;
3938 	register struct dinode	*ip;
3939 	int			sh;
3940 	long			nb;
3941 
3942 	ip = (struct dinode *)cur_ino;
3943 	if (bn < NDADDR) {
3944 		addr = (long)&ip->di_db[bn];
3945 		cur_bytes = bn * BLKSIZE;
3946 		return(nullblk(nb=get(LONG)) ? 0L : nb);
3947 	}
3948 
3949 	sh = 1;
3950 	bn -= NDADDR;
3951 	for (j = NIADDR; j > 0; j--) {
3952 		sh *= NINDIR(fs);
3953 		if (bn < sh)
3954 			break;
3955 		bn -= sh;
3956 	}
3957 	if (j == 0) {
3958 		printf("file too big\n");
3959 		error++;
3960 		return(0L);
3961 	}
3962 	addr = (long)&ip->di_ib[NIADDR - j];
3963 	nb = get(LONG);
3964 	if (nb == 0)
3965 		return(0L);
3966 	for (; j <= NIADDR; j++) {
3967 		sh /= NINDIR(fs);
3968 		addr = (nb << FRGSHIFT) + ((bn / sh) % NINDIR(fs)) * LONG;
3969 		if (nullblk(nb = get(LONG)))
3970 			return(0L);
3971 	}
3972 	return(nb);
3973 }
3974