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