home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume15 / ddd / ddd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-09-22  |  10.8 KB  |  389 lines

  1. /*
  2.  * ddd.c - double dd (version 2)
  3.  *
  4.  * Copyright 1988 Helsinki University of Technology.
  5.  * All rights reserved.
  6.  *
  7.  * Permission granted to distribute, use and modify
  8.  * this code for uncommercial purposes, provided
  9.  * that this copyright notice is not changed.
  10.  *
  11.  * Author: Tapani Lindgren (nispa@cs.hut.fi)
  12.  *
  13.  * Ddd is a dd clone that operates as two processes;
  14.  * one process reads while the other one writes and vice versa.
  15.  * This way the throughput may be up to twice as good as that of dd,
  16.  * especially with slow devices such as tape drives.
  17.  *
  18.  * ***** WARNING ***** (For U.S. residents & citizens only)
  19.  *
  20.  * Do not use this program!  Get rid of it as soon as you can!
  21.  * It will probably corrupt all your data, break down your computer
  22.  * and cause severe injury to the operators.
  23.  * Even reading the source code may give you a headache.
  24.  * I warned you!  I will take no responsibility whatsoever!
  25.  */
  26.  
  27. /* declarations common to all unix versions */
  28. #include <stdio.h>     /* for fprintf() and stderr() */
  29. #include <signal.h>    /* for SIGTERM */
  30. extern char *malloc();
  31.  
  32. /* version dependent declarations */
  33.  
  34. #ifdef BSD
  35. #include <sys/wait.h>  /* for union wait */
  36. #include <sys/file.h>  /* for O_RDONLY and O_WRONLY */
  37. extern char *sprintf();
  38. #endif
  39.  
  40. #ifdef SYSV
  41. #include <fcntl.h>     /* for O_RDONLY and O_WRONLY */
  42. void exit();
  43. void perror();
  44. #endif
  45.  
  46.  
  47.  
  48. /* macros to find min or max of two values */
  49. #define min(a,b) ((a)<(b)? (a): (b))
  50. #define max(a,b) ((a)>(b)? (a): (b))
  51.  
  52. /* inherited file descriptors */
  53. #define STDIN 0
  54. #define STDOUT 1
  55.  
  56. /* boolean values */
  57. #define FALSE 0
  58. #define TRUE 1
  59.  
  60. /* pipes have a read end and a write end */
  61. #define P_REND 0
  62. #define P_WEND 1
  63.  
  64. /* there are two pipes; one for read tokens and one for write tokens */
  65. #define RTOK_P 0
  66. #define WTOK_P 1
  67.  
  68. /* token bytes passed along pipes */
  69. #define TOK_CONT 0     /* go ahead */
  70. #define TOK_DONE 1     /* end of data */
  71. #define TOK_ERROR 2    /* something's wrong, you've better stop now */
  72.  
  73. /* input/output full/short record counters are in a table;
  74.  indexes defined below */
  75. #define FULLIN 0
  76. #define SHORTIN 1
  77. #define FULLOUT 2
  78. #define SHORTOUT 3
  79.  
  80. /* defaults */
  81. #define DEFBS 512      /* default block size */
  82.  
  83. /* forward declarations */
  84. int doerror();
  85.  
  86. /* global variables */
  87. static int
  88.   ifd, ofd,    /* input/output file descriptors */
  89.   ibs, obs,    /* input/output block sizes */
  90.   cbs, /* "conversion" buffer size */
  91.   pid, /* pid of child (in parent) or 0 (in child) */
  92.   eof = FALSE, /* have we encountered end-of-file */
  93.   pipefd[2][2],        /* read/write fd's for 2 pipes */
  94.   counters[4] = {0, 0, 0, 0},  /* input/output full/short record counters */
  95.   buflen;      /* count of characters read into buffer */
  96. static char
  97.   *buffer;     /* address of buffer */
  98.  
  99.  
  100. main(argc, argv)
  101. int argc;
  102. char *argv[];
  103. {
  104.   (void) catchsignals();       /* prepare for interrupts etc */
  105.   (void) setdefaults();        /* set default values for parameters */
  106.   (void) parsearguments(argc, argv);   /* parse arguments */
  107.   (void) initbuffer(); /* initialize buffer */
  108.   (void) inittokens(); /* create one READ and one WRITE token */
  109.   (void) dofork();     /* 1 will be 2 */
  110.   while (!eof) {       /* enter main loop */
  111.     (void) gettoken(RTOK_P);   /* compete for first/next read turn */
  112.     (void) readbuffer();       /* fill buffer with input */
  113.     (void) gettoken(WTOK_P);   /* make sure we also get the next write turn */
  114.     /* now others may read if they wish (and if there's any data left */
  115.     (void) passtoken(RTOK_P, eof? TOK_DONE: TOK_CONT);
  116.     (void) writebuffer();      /* ... while we write to output */
  117.     /* this cycle is done now */
  118.     if (!eof) (void) passtoken(WTOK_P, TOK_CONT);
  119.   }    /* end of main loop */
  120.   (void) passcounters(RTOK_P); /* send record counters to our partner */
  121.   (void) terminate(0); /* and exit (no errors) */
  122.   /* NOTREACHED */
  123. }      /* end of main() */
  124.  
  125.  
  126. catchsignals()
  127. /* arrange for some signals to be catched, so that statistics can be printed */
  128. {
  129.   static int siglist[] = {
  130.     SIGINT, SIGQUIT, SIGILL, SIGFPE,
  131.     SIGBUS, SIGSEGV, SIGSYS, SIGPIPE,
  132.     SIGALRM, SIGTERM, 0
  133.     };
  134.   int *sigp;
  135.  
  136.   for (sigp = siglist; *sigp != 0; sigp++)
  137.     (void) signal(*sigp, doerror);
  138. }      /* end of catchsignals() */
  139.  
  140. doerror()
  141. /* what we do if we get an error or catch a signal */
  142. {
  143.   /* send error token to both pipes */
  144.   (void) passtoken(RTOK_P, TOK_ERROR);
  145.   (void) passtoken(WTOK_P, TOK_ERROR);
  146.   /* also send i/o record counters */
  147.   (void) passcounters(RTOK_P);
  148.   (void) passcounters(RTOK_P);
  149.   /* terminate with error status */
  150.   (void) terminate(1);
  151. }
  152.  
  153. terminate(stat)
  154. int stat;
  155. {
  156.   /* parent will try to wait for child */
  157. #ifdef BSD
  158.   if (pid) (void) wait((union wait *) 0);
  159. #endif
  160. #ifdef SYSV
  161.   if (pid) (void) wait((int *) 0);
  162. #endif
  163.  
  164.   exit(stat);
  165. }
  166.  
  167. setdefaults()
  168. /* set default values */
  169. {
  170.   ifd = STDIN;
  171.   ofd = STDOUT;
  172.   ibs = obs = DEFBS;   /* block sizes */
  173.   cbs = 0;     /* initially; will be set to max(ibs, obs, cbs) */
  174. }
  175.  
  176. parsearguments(argc, argv)
  177. int argc;
  178. char *argv[];
  179.   /* parse arguments */
  180. {
  181.   /* constant strings "array" for recognizing options */
  182.   static struct {
  183.     char *IF, *OF, *CBS, *IBS, *OBS, *BS, *NOTHING;
  184.   } consts = {
  185.     "if=", "of=", "cbs=", "ibs=", "obs=", "bs=", ""
  186.     };
  187.   char **constp;       /* const structure pointer */
  188.  
  189.   for (argc--, argv++; argc > 0; argc--, argv++) {
  190.     constp = (char **) &consts;
  191.     while (**constp && strncmp(*argv, *constp, strlen(*constp)))
  192.       constp++;
  193.     /* constp now points to one of the pointers in consts structure */
  194.     *argv += strlen(*constp);  /* skip the constant part of the argument */
  195.     if (constp == &consts.IF) {        /* open another file for input */
  196.       ifd = open(*argv, O_RDONLY);
  197.       if (ifd < 0) perror (*argv);
  198.     } else if (constp == &consts.OF) {
  199.       ofd = open(*argv, O_WRONLY | O_CREAT);   /* open file for output */
  200.       if (ofd < 0) perror (*argv);
  201.     } else if (constp == &consts.CBS) {        /* set buffer size */
  202.       cbs = evalopt(*argv);
  203.     } else if (constp == &consts.IBS) {        /* set input block size */
  204.       ibs = evalopt(*argv);
  205.     } else if (constp == &consts.OBS) {        /* set output block size */
  206.       obs = evalopt(*argv);
  207.     } else if (constp == &consts.BS) { /* set input and output block sizes */
  208.       ibs = obs = evalopt(*argv);
  209.     } else {
  210.       (void) fprintf(stderr,
  211.                     "usage: ddd [if=name] [of=name] [bs=n] [ibs=n obs=n]\n");
  212.       exit(1);
  213.     }
  214.   } /* end of for loop */
  215. } /* end of parsearguments() */
  216.  
  217. evalopt(p) /* return numerical value of string */
  218. char *p;
  219. {
  220.   int temp = 0;
  221.  
  222.   for ( ; *p >= '0' && *p <= '9'; p++)
  223.     temp = temp * 10 + *p - '0';
  224.   if (temp < 1) {
  225.     (void) fprintf(stderr, "ddd: illegal size option\n");
  226.     exit(1);
  227.   }
  228.   switch (*p) {
  229.   case '\0':
  230.     return(temp);
  231.   case 'w':
  232.   case 'W':
  233.     return(temp << 2); /* 4-byte words */
  234.   case 'b':
  235.   case 'B':
  236.     return(temp << 9); /* 512-byte blocks */
  237.   case 'k':
  238.   case 'K':
  239.     return(temp << 10);        /* kilobytes */
  240.   default:
  241.     (void) fprintf(stderr, "ddd: bad size option\n");
  242.     exit(1);
  243.   }
  244.   /* NOTREACHED */
  245. }      /* end of evalopt() */
  246.  
  247. initbuffer()
  248. /* initialize buffer */
  249. {
  250.   cbs = max(cbs, max(ibs, obs));       /* determine buffer size */
  251.   if (cbs % ibs || cbs % obs) {
  252.     (void) fprintf(stderr, "ddd: warning: incompatible block/buffer sizes\n");
  253.   }
  254.   buffer = malloc((unsigned) cbs);
  255.   if (buffer == NULL) {
  256.     (void) perror("ddd: cannot allocate buffer");
  257.     exit(1);
  258.   }
  259. }      /* end of initbuffer() */
  260.  
  261. inittokens()
  262. /* initialize token passing system with 2 pipes */
  263. {
  264.   if(pipe(pipefd[RTOK_P]) < 0 || pipe(pipefd[WTOK_P]) < 0) {
  265.     (void) perror("ddd: cannot create token pipes");
  266.     exit(1);
  267.   }
  268.   /* create initial tokens */
  269.   (void) passtoken(RTOK_P, TOK_CONT);
  270.   (void) passtoken(WTOK_P, TOK_CONT);
  271. }      /* end of inittokens() */
  272.  
  273. passtoken(pipenum, token)
  274. int pipenum;
  275. char token;
  276. /* pass a token to a pipe */
  277. {
  278.   if (write(pipefd[pipenum][P_WEND], &token, 1) < 1) {
  279.     (void) perror("ddd: cannot write token to pipe");
  280.     exit(1);
  281.   }
  282. }      /* end of passtoken() */
  283.  
  284. gettoken(pipenum)
  285. int pipenum;
  286. /* wait to read a token from the pipe; also see if we should stop */
  287. {
  288.   char tokenbuf;
  289.  
  290.   if (read(pipefd[pipenum][P_REND], &tokenbuf, 1) < 1) {
  291.     (void) perror("ddd: cannot read token from pipe");
  292.     exit(1);
  293.   }
  294.   if (tokenbuf != TOK_CONT) {  /* we did not get what we wanted */
  295.     (void) getcounters(pipenum);       /* report record counters */
  296.     terminate(tokenbuf == TOK_DONE);   /* TOK_DONE means no error */
  297.   }
  298. }      /* end of gettoken() */
  299.  
  300. passcounters(pipenum)
  301. int pipenum;
  302. /* pass read/write counters to the other process */
  303. {
  304.   if (write(pipefd[pipenum][P_WEND], (char *) counters,
  305.            sizeof(counters)) < sizeof(counters)) {
  306.     (void) perror("ddd: cannot write counters to pipe");
  307.     exit(1);
  308.   }
  309. }
  310.  
  311. getcounters(pipenum)
  312. int pipenum;
  313. /* report input/output record counts */
  314. {
  315.   int hiscounters[4];
  316.  
  317.   if (read(pipefd[pipenum][P_REND], (char *) hiscounters,
  318.           sizeof(hiscounters)) < sizeof(hiscounters)) {
  319.     (void) perror("ddd: cannot read counters from pipe");
  320.     exit(1);
  321.   }
  322.   (void) fprintf(stderr,
  323.                 "%d+%d records in\n%d+%d records out\n",
  324.                 counters[FULLIN] + hiscounters[FULLIN],
  325.                 counters[SHORTIN] + hiscounters[SHORTIN],
  326.                 counters[FULLOUT] + hiscounters[FULLOUT],
  327.                 counters[SHORTOUT] + hiscounters[SHORTOUT]
  328.                 );
  329. }      /* end of printcounters() */
  330.  
  331. dofork()
  332. /* fork into 2 processes */
  333. {
  334.   if ((pid = fork()) < 0) {
  335.     (void) perror("ddd: warning: cannot fork");
  336.     /* But continue and do our job anyway, as regular dd */
  337.   }
  338. }
  339.  
  340. readbuffer()
  341. /* read buffer from input */
  342. {
  343.   int iolen, ioresult;
  344.  
  345.   buflen = 0;
  346.   while (buflen < cbs && !eof) {
  347.     iolen = min(ibs, cbs - buflen);
  348. #ifdef BSD
  349.     ioresult = read(ifd, &buffer[buflen], iolen);
  350. #endif
  351. #ifdef SYSV
  352.     ioresult = read(ifd, &buffer[buflen], (unsigned) iolen);
  353. #endif
  354.     if (ioresult == 0) {       /* end of file */
  355.       eof = TRUE;
  356.     } else if (ioresult < 0) {
  357.       (void) perror("ddd: read error");
  358.       (void) doerror();
  359.     }
  360.     buflen += ioresult;        /* update current count of chars in buffer */
  361.     /* if we got any data, update appropriate input record count */
  362.     if (ioresult > 0) counters[(ioresult == ibs)? FULLIN: SHORTIN]++;
  363.   }
  364. }      /* end of readbuffer() */
  365.  
  366. writebuffer()
  367. /* writing phase */
  368. {
  369.   int ocount, iolen, ioresult;
  370.  
  371.   ocount = 0;  /* count of chars written */
  372.   while (ocount < buflen) {
  373.     iolen = min(obs, buflen - ocount);
  374. #ifdef BSD
  375.     ioresult = write(ofd, &buffer[ocount], iolen);
  376. #endif
  377. #ifdef SYSV
  378.     ioresult = write(ofd, &buffer[ocount], (unsigned) iolen);
  379. #endif
  380.     if (ioresult < iolen) {
  381.       perror("ddd: write error");
  382.       (void) doerror();
  383.     }
  384.     ocount += ioresult;
  385.     /* count output records */
  386.     counters[(ioresult == obs)? FULLOUT: SHORTOUT]++;
  387.   }
  388. }      /* end of writebuffer() */
  389.