home *** CD-ROM | disk | FTP | other *** search
- /*
- * ddd.c - double dd (version 2)
- *
- * Copyright 1988 Helsinki University of Technology.
- * All rights reserved.
- *
- * Permission granted to distribute, use and modify
- * this code for uncommercial purposes, provided
- * that this copyright notice is not changed.
- *
- * Author: Tapani Lindgren (nispa@cs.hut.fi)
- *
- * Ddd is a dd clone that operates as two processes;
- * one process reads while the other one writes and vice versa.
- * This way the throughput may be up to twice as good as that of dd,
- * especially with slow devices such as tape drives.
- *
- * ***** WARNING ***** (For U.S. residents & citizens only)
- *
- * Do not use this program! Get rid of it as soon as you can!
- * It will probably corrupt all your data, break down your computer
- * and cause severe injury to the operators.
- * Even reading the source code may give you a headache.
- * I warned you! I will take no responsibility whatsoever!
- */
-
- /* declarations common to all unix versions */
- #include <stdio.h> /* for fprintf() and stderr() */
- #include <signal.h> /* for SIGTERM */
- extern char *malloc();
-
- /* version dependent declarations */
-
- #ifdef BSD
- #include <sys/wait.h> /* for union wait */
- #include <sys/file.h> /* for O_RDONLY and O_WRONLY */
- extern char *sprintf();
- #endif
-
- #ifdef SYSV
- #include <fcntl.h> /* for O_RDONLY and O_WRONLY */
- void exit();
- void perror();
- #endif
-
-
-
- /* macros to find min or max of two values */
- #define min(a,b) ((a)<(b)? (a): (b))
- #define max(a,b) ((a)>(b)? (a): (b))
-
- /* inherited file descriptors */
- #define STDIN 0
- #define STDOUT 1
-
- /* boolean values */
- #define FALSE 0
- #define TRUE 1
-
- /* pipes have a read end and a write end */
- #define P_REND 0
- #define P_WEND 1
-
- /* there are two pipes; one for read tokens and one for write tokens */
- #define RTOK_P 0
- #define WTOK_P 1
-
- /* token bytes passed along pipes */
- #define TOK_CONT 0 /* go ahead */
- #define TOK_DONE 1 /* end of data */
- #define TOK_ERROR 2 /* something's wrong, you've better stop now */
-
- /* input/output full/short record counters are in a table;
- indexes defined below */
- #define FULLIN 0
- #define SHORTIN 1
- #define FULLOUT 2
- #define SHORTOUT 3
-
- /* defaults */
- #define DEFBS 512 /* default block size */
-
- /* forward declarations */
- int doerror();
-
- /* global variables */
- static int
- ifd, ofd, /* input/output file descriptors */
- ibs, obs, /* input/output block sizes */
- cbs, /* "conversion" buffer size */
- pid, /* pid of child (in parent) or 0 (in child) */
- eof = FALSE, /* have we encountered end-of-file */
- pipefd[2][2], /* read/write fd's for 2 pipes */
- counters[4] = {0, 0, 0, 0}, /* input/output full/short record counters */
- buflen; /* count of characters read into buffer */
- static char
- *buffer; /* address of buffer */
-
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- (void) catchsignals(); /* prepare for interrupts etc */
- (void) setdefaults(); /* set default values for parameters */
- (void) parsearguments(argc, argv); /* parse arguments */
- (void) initbuffer(); /* initialize buffer */
- (void) inittokens(); /* create one READ and one WRITE token */
- (void) dofork(); /* 1 will be 2 */
- while (!eof) { /* enter main loop */
- (void) gettoken(RTOK_P); /* compete for first/next read turn */
- (void) readbuffer(); /* fill buffer with input */
- (void) gettoken(WTOK_P); /* make sure we also get the next write turn */
- /* now others may read if they wish (and if there's any data left */
- (void) passtoken(RTOK_P, eof? TOK_DONE: TOK_CONT);
- (void) writebuffer(); /* ... while we write to output */
- /* this cycle is done now */
- if (!eof) (void) passtoken(WTOK_P, TOK_CONT);
- } /* end of main loop */
- (void) passcounters(RTOK_P); /* send record counters to our partner */
- (void) terminate(0); /* and exit (no errors) */
- /* NOTREACHED */
- } /* end of main() */
-
-
- catchsignals()
- /* arrange for some signals to be catched, so that statistics can be printed */
- {
- static int siglist[] = {
- SIGINT, SIGQUIT, SIGILL, SIGFPE,
- SIGBUS, SIGSEGV, SIGSYS, SIGPIPE,
- SIGALRM, SIGTERM, 0
- };
- int *sigp;
-
- for (sigp = siglist; *sigp != 0; sigp++)
- (void) signal(*sigp, doerror);
- } /* end of catchsignals() */
-
- doerror()
- /* what we do if we get an error or catch a signal */
- {
- /* send error token to both pipes */
- (void) passtoken(RTOK_P, TOK_ERROR);
- (void) passtoken(WTOK_P, TOK_ERROR);
- /* also send i/o record counters */
- (void) passcounters(RTOK_P);
- (void) passcounters(RTOK_P);
- /* terminate with error status */
- (void) terminate(1);
- }
-
- terminate(stat)
- int stat;
- {
- /* parent will try to wait for child */
- #ifdef BSD
- if (pid) (void) wait((union wait *) 0);
- #endif
- #ifdef SYSV
- if (pid) (void) wait((int *) 0);
- #endif
-
- exit(stat);
- }
-
- setdefaults()
- /* set default values */
- {
- ifd = STDIN;
- ofd = STDOUT;
- ibs = obs = DEFBS; /* block sizes */
- cbs = 0; /* initially; will be set to max(ibs, obs, cbs) */
- }
-
- parsearguments(argc, argv)
- int argc;
- char *argv[];
- /* parse arguments */
- {
- /* constant strings "array" for recognizing options */
- static struct {
- char *IF, *OF, *CBS, *IBS, *OBS, *BS, *NOTHING;
- } consts = {
- "if=", "of=", "cbs=", "ibs=", "obs=", "bs=", ""
- };
- char **constp; /* const structure pointer */
-
- for (argc--, argv++; argc > 0; argc--, argv++) {
- constp = (char **) &consts;
- while (**constp && strncmp(*argv, *constp, strlen(*constp)))
- constp++;
- /* constp now points to one of the pointers in consts structure */
- *argv += strlen(*constp); /* skip the constant part of the argument */
- if (constp == &consts.IF) { /* open another file for input */
- ifd = open(*argv, O_RDONLY);
- if (ifd < 0) perror (*argv);
- } else if (constp == &consts.OF) {
- ofd = open(*argv, O_WRONLY | O_CREAT); /* open file for output */
- if (ofd < 0) perror (*argv);
- } else if (constp == &consts.CBS) { /* set buffer size */
- cbs = evalopt(*argv);
- } else if (constp == &consts.IBS) { /* set input block size */
- ibs = evalopt(*argv);
- } else if (constp == &consts.OBS) { /* set output block size */
- obs = evalopt(*argv);
- } else if (constp == &consts.BS) { /* set input and output block sizes */
- ibs = obs = evalopt(*argv);
- } else {
- (void) fprintf(stderr,
- "usage: ddd [if=name] [of=name] [bs=n] [ibs=n obs=n]\n");
- exit(1);
- }
- } /* end of for loop */
- } /* end of parsearguments() */
-
- evalopt(p) /* return numerical value of string */
- char *p;
- {
- int temp = 0;
-
- for ( ; *p >= '0' && *p <= '9'; p++)
- temp = temp * 10 + *p - '0';
- if (temp < 1) {
- (void) fprintf(stderr, "ddd: illegal size option\n");
- exit(1);
- }
- switch (*p) {
- case '\0':
- return(temp);
- case 'w':
- case 'W':
- return(temp << 2); /* 4-byte words */
- case 'b':
- case 'B':
- return(temp << 9); /* 512-byte blocks */
- case 'k':
- case 'K':
- return(temp << 10); /* kilobytes */
- default:
- (void) fprintf(stderr, "ddd: bad size option\n");
- exit(1);
- }
- /* NOTREACHED */
- } /* end of evalopt() */
-
- initbuffer()
- /* initialize buffer */
- {
- cbs = max(cbs, max(ibs, obs)); /* determine buffer size */
- if (cbs % ibs || cbs % obs) {
- (void) fprintf(stderr, "ddd: warning: incompatible block/buffer sizes\n");
- }
- buffer = malloc((unsigned) cbs);
- if (buffer == NULL) {
- (void) perror("ddd: cannot allocate buffer");
- exit(1);
- }
- } /* end of initbuffer() */
-
- inittokens()
- /* initialize token passing system with 2 pipes */
- {
- if(pipe(pipefd[RTOK_P]) < 0 || pipe(pipefd[WTOK_P]) < 0) {
- (void) perror("ddd: cannot create token pipes");
- exit(1);
- }
- /* create initial tokens */
- (void) passtoken(RTOK_P, TOK_CONT);
- (void) passtoken(WTOK_P, TOK_CONT);
- } /* end of inittokens() */
-
- passtoken(pipenum, token)
- int pipenum;
- char token;
- /* pass a token to a pipe */
- {
- if (write(pipefd[pipenum][P_WEND], &token, 1) < 1) {
- (void) perror("ddd: cannot write token to pipe");
- exit(1);
- }
- } /* end of passtoken() */
-
- gettoken(pipenum)
- int pipenum;
- /* wait to read a token from the pipe; also see if we should stop */
- {
- char tokenbuf;
-
- if (read(pipefd[pipenum][P_REND], &tokenbuf, 1) < 1) {
- (void) perror("ddd: cannot read token from pipe");
- exit(1);
- }
- if (tokenbuf != TOK_CONT) { /* we did not get what we wanted */
- (void) getcounters(pipenum); /* report record counters */
- terminate(tokenbuf == TOK_DONE); /* TOK_DONE means no error */
- }
- } /* end of gettoken() */
-
- passcounters(pipenum)
- int pipenum;
- /* pass read/write counters to the other process */
- {
- if (write(pipefd[pipenum][P_WEND], (char *) counters,
- sizeof(counters)) < sizeof(counters)) {
- (void) perror("ddd: cannot write counters to pipe");
- exit(1);
- }
- }
-
- getcounters(pipenum)
- int pipenum;
- /* report input/output record counts */
- {
- int hiscounters[4];
-
- if (read(pipefd[pipenum][P_REND], (char *) hiscounters,
- sizeof(hiscounters)) < sizeof(hiscounters)) {
- (void) perror("ddd: cannot read counters from pipe");
- exit(1);
- }
- (void) fprintf(stderr,
- "%d+%d records in\n%d+%d records out\n",
- counters[FULLIN] + hiscounters[FULLIN],
- counters[SHORTIN] + hiscounters[SHORTIN],
- counters[FULLOUT] + hiscounters[FULLOUT],
- counters[SHORTOUT] + hiscounters[SHORTOUT]
- );
- } /* end of printcounters() */
-
- dofork()
- /* fork into 2 processes */
- {
- if ((pid = fork()) < 0) {
- (void) perror("ddd: warning: cannot fork");
- /* But continue and do our job anyway, as regular dd */
- }
- }
-
- readbuffer()
- /* read buffer from input */
- {
- int iolen, ioresult;
-
- buflen = 0;
- while (buflen < cbs && !eof) {
- iolen = min(ibs, cbs - buflen);
- #ifdef BSD
- ioresult = read(ifd, &buffer[buflen], iolen);
- #endif
- #ifdef SYSV
- ioresult = read(ifd, &buffer[buflen], (unsigned) iolen);
- #endif
- if (ioresult == 0) { /* end of file */
- eof = TRUE;
- } else if (ioresult < 0) {
- (void) perror("ddd: read error");
- (void) doerror();
- }
- buflen += ioresult; /* update current count of chars in buffer */
- /* if we got any data, update appropriate input record count */
- if (ioresult > 0) counters[(ioresult == ibs)? FULLIN: SHORTIN]++;
- }
- } /* end of readbuffer() */
-
- writebuffer()
- /* writing phase */
- {
- int ocount, iolen, ioresult;
-
- ocount = 0; /* count of chars written */
- while (ocount < buflen) {
- iolen = min(obs, buflen - ocount);
- #ifdef BSD
- ioresult = write(ofd, &buffer[ocount], iolen);
- #endif
- #ifdef SYSV
- ioresult = write(ofd, &buffer[ocount], (unsigned) iolen);
- #endif
- if (ioresult < iolen) {
- perror("ddd: write error");
- (void) doerror();
- }
- ocount += ioresult;
- /* count output records */
- counters[(ioresult == obs)? FULLOUT: SHORTOUT]++;
- }
- } /* end of writebuffer() */
-