1/*
2 * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like.  Any license provided herein, whether implied or
15 * otherwise, applies only to this software file.  Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA  94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31 *
32 */
33/* $Header: /cvsroot/ltp/ltp/testcases/kernel/ipc/pipeio/pipeio.c,v 1.18 2009/03/19 07:10:02 subrata_modak Exp $ */
34/*
35 *  This tool can be used to beat on system or named pipes.
36 *  See the help() function below for user information.
37 */
38#include <stdio.h>
39#include <fcntl.h>
40#include <stdlib.h>
41#include <unistd.h>
42#include <sys/types.h>
43#include <sys/param.h>
44#include <sys/wait.h>
45#include <time.h>
46#include <errno.h>
47#include <string.h>
48#include <signal.h>
49#include <sys/stat.h>
50#include <sys/sem.h>
51
52#include "tlibio.h"
53
54#include "test.h"
55#include "safe_macros.h"
56#include "lapi/semun.h"
57
58char *TCID = "pipeio";
59int TST_TOTAL = 1;
60
61#define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } }
62
63#if defined(__linux__)
64#define NBPW sizeof(int)
65#endif
66
67#define OCTAL	'o'
68#define HEX	'x'
69#define DECIMAL	'd'
70#define ASCII	'a'
71#define NO_OUT	'n'
72
73#define PIPE_NAMED	"named pipe,"
74#define PIPE_UNNAMED	"sys pipe,"
75
76#define BLOCKING_IO	"blking,"
77#define NON_BLOCKING_IO	"non-blking,"
78#define UNNAMED_IO	""
79
80#define MAX_ERRS 16
81#define MAX_EMPTY 256
82
83static int parse_options(int argc, char *argv[]);
84static void setup(int argc, char *argv[]);
85static void cleanup(void);
86
87static void do_child(void);
88static void do_parent(void);
89
90static void help(void), usage(void), prt_examples(void);
91static void prt_buf(char **addr, char *buf, int length, int format);
92static void sig_child(int sig);
93static int check_rw_buf(void);
94
95static volatile sig_atomic_t nchildcompleted;
96
97/* variables may be modified in setup() */
98static int num_writers = 1;	/* number of writers */
99static int num_writes = 1;	/* number of writes per child */
100static int loop;		/* loop indefinitely */
101static int exit_error = 1;	/* exit on error #, zero means no exit */
102static int size = 327;		/* default size */
103static int unpipe;		/* un-named pipe if non-zero */
104static int verbose;		/* verbose mode if set */
105static int quiet;		/* quiet mode if set */
106static int num_rpt;		/* ping number, how often to print message */
107static int chld_wait;	/* max time to wait between writes, 1 == no wait */
108static int parent_wait;	/* max time to wait between reads, 1 == no wait */
109static int ndelay = O_NDELAY;	/* additional flag to open */
110static char *writebuf;
111static char *readbuf;
112static char pname[PATH_MAX];	/* contains the name of the named pipe */
113static char *blk_type = NON_BLOCKING_IO; /* blocking i/o or not */
114static char *pipe_type;		/* type of pipe under test */
115static int format = HEX;
116static int format_size = -1;
117static int iotype;		/* sync io */
118
119/* variables will be modified in running */
120static int error;
121static int count;
122static int read_fd;
123static int write_fd;
124static int empty_read;
125static int sem_id;
126
127static union semun u;
128
129int main(int ac, char *av[])
130{
131	int i;
132	unsigned int j;
133	unsigned int uwait_iter = 1000, uwait_total = 5000000;
134	pid_t child;
135
136	setup(ac, av);
137
138	for (i = num_writers; i > 0; --i) {
139
140		child = tst_fork();
141		switch (child) {
142		case -1:
143			tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
144		case 0:
145			do_child();
146			exit(0);
147		default:
148			break;
149		}
150	}
151
152	do_parent();
153
154	if (empty_read)
155		tst_resm(TWARN, "%d empty reads", empty_read);
156
157	if (error) {
158		tst_resm(TFAIL, "%d data errors on pipe, read size = %d, %s %s",
159			 error, size, pipe_type, blk_type);
160	} else if (!quiet) {
161		tst_resm(TPASS, "%d pipe reads complete, read size = %d, %s %s",
162			 count + 1, size, pipe_type, blk_type);
163	}
164
165	/*
166	 * wait for all children to finish, timeout after uwait_total
167	 * semtimedop might not be available everywhere
168	 */
169	for (j = 0; j < uwait_total; j += uwait_iter) {
170		if (semctl(sem_id, 1, GETVAL) == 0)
171			break;
172		usleep(uwait_iter);
173	}
174
175	if (j >= uwait_total) {
176		tst_resm(TWARN,
177			 "Timed out waiting for child processes to exit");
178	}
179
180	cleanup();
181	tst_exit();
182}
183
184static int parse_options(int argc, char *argv[])
185{
186	char *cp;
187	int c;
188	int ret = 0;
189	static double d;
190
191	while ((c = getopt(argc, argv, "T:bc:D:he:Ef:i:I:ln:p:qs:uvW:w:"))
192	       != -1) {
193		switch (c) {
194		case 'T':
195			TCID = optarg;
196			break;
197		case 'h':
198			help();
199			ret = 1;
200			break;
201		case 'D':	/* pipe name */
202			strcpy(pname, optarg);
203			break;
204		case 'b':	/* blocked */
205			ndelay = 0;
206			blk_type = BLOCKING_IO;
207			break;
208		case 'c':	/* number childern */
209			if (sscanf(optarg, "%d", &num_writers) != 1) {
210				fprintf(stderr,
211					"%s: --c option invalid arg '%s'.\n",
212					TCID, optarg);
213				ret = 1;
214			} else if (num_writers <= 0) {
215				fprintf(stderr, "%s: --c option must be "
216					"greater than zero.\n", TCID);
217				ret = 1;
218			}
219			break;
220		case 'e':	/* exit on error # */
221			if (sscanf(optarg, "%d", &exit_error) != 1) {
222				fprintf(stderr,
223					"%s: --e option invalid arg '%s'.\n",
224					TCID, optarg);
225				ret = 1;
226			} else if (exit_error < 0) {
227				fprintf(stderr, "%s: --e option must be "
228					"greater than zero.\n", TCID);
229				ret = 1;
230			}
231			break;
232		case 'E':
233			prt_examples();
234			ret = 1;
235			break;
236		case 'f':	/* format of buffer on error */
237			switch (optarg[0]) {
238			case 'x':
239			case 'X':
240				format = HEX;
241				break;
242			case 'o':
243			case 'O':
244				format = OCTAL;
245				break;
246			case 'd':
247			case 'D':
248				format = DECIMAL;
249				break;
250			case 'a':
251			case 'A':
252				format = ASCII;
253				break;
254			case 'n':	/* not output */
255			case 'N':
256				format = NO_OUT;
257				break;
258
259			default:
260				fprintf(stderr,
261					"%s: --f option invalid arg '%s'.\n",
262					TCID, optarg);
263				fprintf(stderr, "\tIt must be x(hex), o(octal),"
264					"d(decimal), a(ascii) or n(none) with "
265					"opt sz\n");
266				ret = 1;
267				break;
268			}
269			cp = optarg;
270			cp++;
271			if (*cp) {
272				if (sscanf(cp, "%i", &format_size) != 1) {
273					fprintf(stderr, "%s: --f option invalid"
274						"arg '%s'.\n", TCID, optarg);
275					fprintf(stderr, "\tIt must be x(hex),"
276						"o(octal), d(decimal), a(ascii)"
277						" or n(none) with opt sz\n");
278					ret = 1;
279					break;
280				}
281			}
282			break;
283
284		case 'I':
285			iotype = lio_parse_io_arg1(optarg);
286			if (iotype == -1) {
287				fprintf(stderr, "%s: --I arg is invalid, "
288					"must be s, p, f, a, l, L or r.\n",
289					TCID);
290				ret = 1;
291			}
292			break;
293
294		case 'l':	/* loop forever */
295			++loop;
296			break;
297
298		case 'i':
299		case 'n':	/* number writes per child */
300			if (sscanf(optarg, "%d", &num_writes) != 1) {
301				fprintf(stderr, "%s: --i/n option invalid "
302					"arg '%s'.\n", TCID, optarg);
303				ret = 1;
304			} else if (num_writes < 0) {
305				fprintf(stderr, "%s: --i/n option must be "
306					"greater than equal to zero.\n",
307					TCID);
308				ret = 1;
309			}
310
311			if (num_writes == 0)	/* loop forever */
312				++loop;
313			break;
314		case 'p':	/* ping */
315			if (sscanf(optarg, "%d", &num_rpt) != 1) {
316				fprintf(stderr,
317					"%s: --p option invalid arg '%s'.\n",
318					TCID, optarg);
319				ret = 1;
320			} else if (num_rpt < 0) {
321				fprintf(stderr, "%s: --p option must be greater"
322					" than equal to zero.\n", TCID);
323				ret = 1;
324			}
325			break;
326		case 'q':	/* Quiet - NOPASS */
327			quiet = 1;
328			break;
329		case 's':	/* size */
330			if (sscanf(optarg, "%d", &size) != 1) {
331				fprintf(stderr,
332					"%s: --s option invalid arg '%s'.\n",
333					TCID, optarg);
334				ret = 1;
335			} else if (size <= 0) {
336				fprintf(stderr, "%s: --s option must be greater"
337					" than zero.\n", TCID);
338				ret = 1;
339			}
340			break;
341		case 'u':
342			unpipe = 1;	/* un-named pipe */
343			break;
344		case 'v':	/* verbose */
345			verbose = 1;
346			break;
347		case 'W':	/* max wait time between reads */
348			d = strtod(optarg, &cp);
349			if (*cp != '\0') {
350				fprintf(stderr,
351					"%s: --w option invalid arg '%s'.\n",
352					TCID, optarg);
353				ret = 1;
354			} else if (d < 0) {
355				fprintf(stderr, "%s: --w option must be greater"
356					" than zero.\n", TCID);
357				ret = 1;
358			}
359			parent_wait = (int)(d * 1000000.0);
360			break;
361		case 'w':	/* max wait time between writes */
362			d = strtod(optarg, &cp);
363			if (*cp != '\0') {
364				fprintf(stderr,
365					"%s: --w option invalid arg '%s'.\n",
366					TCID, optarg);
367				ret = 1;
368			} else if (d < 0) {
369				fprintf(stderr, "%s: --w option must be greater"
370					" than zero.\n", TCID);
371				ret = 1;
372			}
373			chld_wait = (int)(d * 1000000.0);
374			break;
375		case '?':
376			ret = 1;
377			break;
378		}
379
380		if (ret == 1) {
381			usage();
382			return ret;
383		}
384	}
385
386	return ret;
387}
388
389static void setup(int argc, char *argv[])
390{
391	int ret;
392	char *toutput;
393	int fds[2];
394
395	tst_sig(FORK, DEF_HANDLER, cleanup);
396
397	TEST_PAUSE;
398
399	tst_tmpdir();
400
401	if (signal(SIGCHLD, sig_child) == SIG_ERR) {
402		tst_brkm(TBROK | TERRNO, cleanup,
403			 "set signal handler for SIGCHLD failed");
404	}
405
406	toutput = getenv("TOUTPUT");
407	if (toutput != NULL && strcmp(toutput, "NOPASS") == 0)
408		quiet = 1;
409
410	sprintf(pname, "%s", "tpipe");
411
412	ret = parse_options(argc, argv);
413	if (ret == 1)
414		tst_brkm(TBROK, cleanup, "options parse error");
415
416	if (format_size == -1)
417		format_size = size;
418
419	/*
420	 * If there is more than one writer, all writes and reads
421	 * must be the same size.  Only writes of a size <= PIPE_BUF
422	 * are atomic.  T
423	 * Therefore, if size is greater than PIPE_BUF, we will break
424	 * the writes into PIPE_BUF chunks.  We will also increase the
425	 * number of writes to ensure the same (or more) amount of
426	 * data is written.  This is the same as erroring and telling
427	 * the user the new cmd line to do the same thing.
428	 * Example:
429	 *      pipeio -s 5000 -n 10 -c 5
430	 *      (each child will write at least 50000 bytes, since all
431	 *      writes have to be in 4096 chuncks or 13*4096 (53248)
432	 *      bytes will be written.)  This is the same as:
433	 *      pipeio -s 4096 -n 13 -c 5
434	 */
435	if (size > PIPE_BUF && num_writers > 1) {
436		if (!loop) {
437			/*
438			 * we must set num_writes*num_writers
439			 * doesn't overflow later
440			 */
441			num_writes = MIN(((long long)num_writes * size +
442					 PIPE_BUF - 1) / PIPE_BUF,
443					 INT_MAX / num_writers);
444			tst_resm(TINFO, "adjusting i/o size to %d, and # of "
445				 "writes to %d", PIPE_BUF, num_writes);
446		} else {
447			tst_resm(TINFO, "adjusting i/o size to %d", PIPE_BUF);
448		}
449		size = PIPE_BUF;
450	}
451
452	writebuf = SAFE_MALLOC(cleanup, size);
453	readbuf = SAFE_MALLOC(cleanup, size);
454
455	memset(writebuf, 'Z', size);
456	writebuf[size - 1] = 'A';
457
458	sem_id = semget(IPC_PRIVATE, 2, IPC_CREAT | S_IRWXU);
459	if (sem_id == -1) {
460		tst_brkm(TBROK | TERRNO, cleanup,
461			 "Couldn't allocate semaphore");
462	}
463
464	if (semctl(sem_id, 0, SETVAL, u) == -1) {
465		tst_brkm(TBROK | TERRNO, cleanup,
466			 "Couldn't initialize semaphore 0 value");
467	}
468
469	if (semctl(sem_id, 1, SETVAL, u) == -1) {
470		tst_brkm(TBROK | TERRNO, cleanup,
471			 "Couldn't initialize semaphore 1 value");
472	}
473
474	if (unpipe) {
475		SAFE_PIPE(cleanup, fds);
476		read_fd = fds[0];
477		write_fd = fds[1];
478		pipe_type = PIPE_UNNAMED;
479		blk_type = UNNAMED_IO;
480	} else {
481		SAFE_MKFIFO(cleanup, pname, 0777);
482		pipe_type = PIPE_NAMED;
483	}
484}
485
486static void cleanup(void)
487{
488	SAFE_FREE(writebuf);
489	SAFE_FREE(readbuf);
490
491	semctl(sem_id, 0, IPC_RMID);
492
493	if (!unpipe)
494		unlink(pname);
495
496	tst_rmdir();
497}
498
499static void do_child(void)
500{
501	int *count_word;        /* holds address where to write writers count */
502	int *pid_word;          /* holds address where to write writers pid */
503	int nb, j;
504	long clock;
505	char *cp;
506	long int n;
507	struct sembuf sem_op;
508	pid_t self_pid =  getpid();
509
510	if (!unpipe) {
511		write_fd = open(pname, O_WRONLY);
512		if (write_fd == -1) {
513			fprintf(stderr, "child pipe open(%s, %#o) failed",
514				pname, O_WRONLY | ndelay);
515			exit(1);
516		}
517		if (ndelay && fcntl(write_fd, F_SETFL, O_NONBLOCK) == -1) {
518			fprintf(stderr, "Failed setting the pipe to "
519				"nonblocking mode");
520			exit(1);
521		}
522	} else {
523		close(read_fd);
524	}
525
526	sem_op = (struct sembuf) {
527		 .sem_num = 0, .sem_op = 1, .sem_flg = 0};
528
529	if (semop(sem_id, &sem_op, 1) == -1) {
530		fprintf(stderr, "child: %d couldn't raise the semaphore 0",
531			self_pid);
532		exit(1);
533	}
534
535	pid_word = (int *)&writebuf[0];
536	count_word = (int *)&writebuf[NBPW];
537
538	for (j = 0; j < num_writes || loop; ++j) {
539		/*
540		 * writes are only in one unit when the size of the write
541		 * is <= PIPE_BUF.
542		 * Therefore, if size is greater than PIPE_BUF, we will break
543		 * the writes into PIPE_BUF chunks.
544		 * All writes and read need to be same size.
545		 */
546
547		/*
548		 * write pid and count in first two
549		 * words of buffer
550		 */
551		*count_word = j;
552		*pid_word = self_pid;
553
554		nb = lio_write_buffer(write_fd, iotype, writebuf, size,
555				      SIGUSR1, &cp, 0);
556		if (nb < 0) {
557			/*
558			 * If lio_write_buffer returns a negative number,
559			 * the return will be -errno.
560			 */
561			fprintf(stderr, "pass %d: lio_write_buffer(%s) failed;"
562				" it returned %d: %s",
563				j, cp, nb, strerror(-nb));
564				exit(1);
565		} else if (nb != size) {
566			fprintf(stderr, "pass %d: lio_write_buffer(%s) failed,"
567				" write count %d, but expected to write %d",
568				j, cp, nb, size);
569		}
570		if (verbose) {
571			fprintf(stderr, "pass %d: pid %d: wrote %d bytes,"
572				"expected %d bytes",
573				j, self_pid, nb, size);
574		}
575
576		if (chld_wait) {
577			clock = time(0);
578			srand48(clock);
579			n = lrand48() % chld_wait;
580			usleep(n);
581		}
582		fflush(stderr);
583	}
584
585	/* child waits until parent completes open() */
586	sem_op = (struct sembuf) {
587		  .sem_num = 1, .sem_op = -1, .sem_flg = 0};
588	if (semop(sem_id, &sem_op, 1) == -1)
589		fprintf(stderr, "Couldn't lower the semaphore 1");
590
591	exit(0);
592}
593
594static int check_rw_buf(void)
595{
596	int i;
597
598	for (i = 2 * NBPW; i < size; ++i) {
599		if (writebuf[i] != readbuf[i]) {
600			++error;
601			tst_resm(TFAIL,
602				 "FAIL data error on byte %d; rd# %d, sz= %d, "
603				 "%s %s empty_reads= %d, err= %d",
604				 i, count, size, pipe_type, blk_type,
605				 empty_read, error);
606			prt_buf(&readbuf, readbuf, format_size, format);
607			fflush(stdout);
608			return 1;
609		}
610	}
611
612	return 0;
613}
614
615static void do_parent(void)
616{
617	int i, nb;
618	long clock;
619	time_t start_time, current_time, diff_time;
620	char *cp;
621	long int n;
622	struct sembuf sem_op;
623
624	start_time = time(0);
625	if (!unpipe) {
626		read_fd = SAFE_OPEN(cleanup, pname, O_RDONLY);
627		if (ndelay && fcntl(read_fd, F_SETFL, O_NONBLOCK) == -1) {
628			tst_brkm(TBROK | TERRNO, cleanup,
629				 "Failed setting the pipe to nonblocking mode");
630		}
631	} else {
632		SAFE_CLOSE(cleanup, write_fd);
633	}
634
635	/* raise semaphore so children can exit */
636	sem_op = (struct sembuf) {
637		  .sem_num = 1, .sem_op = num_writers, .sem_flg = 0};
638	if (semop(sem_id, &sem_op, 1) == -1) {
639		tst_brkm(TBROK | TERRNO, cleanup,
640			 "Couldn't raise the semaphore 1");
641	}
642
643	sem_op = (struct sembuf) {
644		  .sem_num = 0, .sem_op = -num_writers, .sem_flg = 0};
645
646	while (nchildcompleted < num_writers
647	       && semop(sem_id, &sem_op, 1) == -1) {
648		if (errno == EINTR)
649			continue;
650		tst_brkm(TBROK | TERRNO, cleanup,
651			 "Couldn't wait on semaphore 0");
652	}
653
654	/* parent start to read pipe */
655	for (i = num_writers * num_writes; i > 0 || loop; --i) {
656		if (error >= MAX_ERRS || empty_read >= MAX_EMPTY)
657			break;
658		if (parent_wait) {
659			clock = time(0);
660			srand48(clock);
661			n = lrand48() % parent_wait;
662			usleep(n);
663		}
664		++count;
665		nb = lio_read_buffer(read_fd, iotype, readbuf, size,
666				     SIGUSR1, &cp, 0);
667		if (nb < 0) {
668			/*
669			 * If lio_read_buffer returns a negative number,
670			 * the return will be -errno.
671			 */
672			tst_resm(TFAIL, "pass %d: lio_read_buffer(%s) failed; "
673				 "returned %d: %s", i, cp, nb, strerror(-nb));
674			++i;
675			count--;
676			error++;
677			continue;
678		} else {
679			if (nb == 0) {
680				if (nchildcompleted >= num_writers && !loop) {
681					tst_resm(TWARN, "The children have "
682						 "died prematurely");
683					break;	/* All children have died */
684				}
685				empty_read++;
686				++i;
687				count--;
688				continue;
689			} else if (nb < size && size <= PIPE_BUF) {
690				tst_resm(TFAIL, "pass %d: partial read from the"
691					" pipe: read %d bytes, expected %d, "
692					"read count %d", i, nb, size, count);
693				++error;
694			} else if (nb == size) {
695				check_rw_buf();
696				if (exit_error && exit_error == error)
697					return;
698			}
699
700			if (verbose || (num_rpt && !(count % num_rpt))) {
701				current_time = time(0);
702				diff_time = current_time - start_time;
703				tst_resm(TFAIL,
704					 "(%d) rd# %d, sz= %d, %s %s "
705					 "empty_reads= %d, err= %d\n",
706					 (int)diff_time, count, size,
707					 pipe_type, blk_type,
708					 empty_read, error);
709				fflush(stdout);
710			}
711		}
712	}
713
714	SAFE_CLOSE(cleanup, read_fd);
715}
716
717static void usage(void)
718{
719	fprintf(stderr, "Usage: %s [-bEv][-c #writers][-D pname][-h]"
720		"[-e exit_num][-f fmt][-l][-i #writes][-n #writes][-p num_rpt]"
721		"\n\t[-s size][-W max_wait][-w max_wait][-u]\n", TCID);
722	fflush(stderr);
723}
724
725static void help(void)
726{
727	usage();
728
729	printf(" -b    - blocking reads and writes. default non-block\n\
730  -c #writers  - number of writers (childern)\n\
731  -D pname     - name of fifo (def tpipe<pid>)\n\
732  -h           - print this help message\n\
733  -e exit_num  - exit on error exit_num, 0 is ignore errors, 1 is default.\n\
734  -E           - print cmd line examples and exit\n\
735  -f format    - define format of bad buffer: h(hex), o(octal)\n\
736                 d(decimal), a(ascii), n (none). hex is default\n\
737	         option size can be added to control output\n\
738  -i #writes   - number write per child, zero means forever.\n\
739  -I io_type   - Specifies io type: s - sync, p - polled async, a - async (def s)\n\
740                 l - listio sync, L - listio async, r - random\n\
741  -l           - loop forever (implied by -n 0).\n\
742  -n #writes   - same as -i (for compatability).\n\
743  -p num_rpt   - number of reads before a report\n\
744  -q           - quiet mode, no PASS results are printed\n\
745  -s size      - size of read and write (def 327)\n\
746                 if size >= 4096, i/o will be in 4096 chuncks\n\
747  -w max_wait  - max time (seconds) for sleep between writes.\n\
748                 max_wait is interpreted as a double with ms accuracy.\n\
749  -W max_wait  - max time (seconds) for sleep between reads\n\
750                 max_wait is interpreted as a double with ms accuracy.\n\
751  -u           - un-named pipe instead of named pipe\n\
752  -v           - verbose mode, all writes/reads resutlts printed\n");
753
754	fflush(stdout);
755}
756
757static void prt_buf(char **addr, char *buf, int length, int format)
758{
759	int i;
760	int num_words = length / NBPW;	/* given length in bytes, get length in words */
761	int width;		/* number of columns */
762	int extra_words = 0;	/* odd or even number of words */
763	char *a = buf;
764	char b[NBPW];
765	char c[NBPW * 2];
766	char *p;
767	long *word;
768
769	if (format == NO_OUT)	/* if no output wanted, return */
770		return;
771
772	if (length % NBPW)
773		++num_words;	/* is length in full words? */
774	if (format == ASCII) {
775		width = 3;
776	} else {
777		width = 2;
778		/* do we have an odd number of words? */
779		extra_words = num_words % width;
780	}
781	for (i = 0; i < num_words; ++i, a += NBPW, addr++) {
782		word = (long *)a;
783		if (!(i % width)) {
784			if (i > 0 && format != ASCII) {
785				/*
786				 * print the ascii equivalent of the data
787				 * before beginning the next line of output.
788				 */
789				memset(c, 0x00, width * NBPW);
790				/*
791				 * get the last 2 words printed
792				 */
793				memcpy(c, a - (width * NBPW), width * NBPW);
794				for (p = c; (p - c) < (int)(width*NBPW); ++p) {
795					if (*p < '!' || *p > '~')
796						*p = '.';
797				}
798				printf("\t%16.16s", c);
799			}
800			printf("\n%p: ", addr);
801			/***printf("\n%7o (%d): ",addr,i);***/
802		}
803
804		switch (format) {
805		case HEX:
806			printf("%16.16lx ", *word);
807			break;
808		case DECIMAL:
809			printf("%10.10ld ", *word);
810			break;
811		case ASCII:
812			memcpy(b, a, NBPW);
813			for (p = b; (p - b) < (int)NBPW; ++p) {
814				if (*p < '!' || *p > '~')
815					*p = '.';
816			}
817			printf("%8.8s ", b);
818			break;
819		default:
820			printf("%22.22lo ", *word);
821			break;
822		}
823	}
824	if (format != ASCII) {
825		/*
826		 * print the ascii equivalent of the last words in the buffer
827		 * before returning.
828		 */
829		memset(c, 0x00, width * NBPW);
830		if (extra_words)
831			width = extra_words;	/* odd number of words */
832		memcpy(c, a - (width * NBPW), width * NBPW);
833		for (p = c; (p - c) < (int)(width * NBPW); ++p) {
834			if (*p < '!' || *p > '~')
835				*p = '.';
836		}
837		if (width == 2)
838			printf("\t%16.16s", c);
839		else
840			printf("\t\t%16.8s", c);
841	}
842	printf("\n");
843	fflush(stdout);
844}
845
846static void prt_examples(void)
847{
848	printf("%s -c 5 -i 0 -s 4090 -b\n", TCID);
849	printf("%s -c 5 -i 0 -s 4090 -b -u \n", TCID);
850	printf("%s -c 5 -i 0 -s 4090 -b -W 3 -w 3 \n", TCID);
851}
852
853static void sig_child(int sig)
854{
855	int status;
856
857	nchildcompleted++;
858#if DEBUG
859	#define STR	"parent: received SIGCHLD\n"
860	write(STDOUT_FILENO, str, strlen(STR));
861#endif
862	waitpid(-1, &status, WNOHANG);
863}
864