BSD 4_4_Lite1 release
[unix-history] / usr / src / bin / dd / dd.c
index 6a1c11d..05ac7a9 100644 (file)
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
 #ifndef lint
 #ifndef lint
-static char *sccsid = "@(#)dd.c        4.5 (Berkeley) %G%";
-#endif
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
 
 
-#include <sys/file.h>
-#include <stdio.h>
+#ifndef lint
+static char sccsid[] = "@(#)dd.c       8.5 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <signal.h>
-
-#define        BIG     2147483647
-#define        LCASE   01
-#define        UCASE   02
-#define        SWAB    04
-#define NERR   010
-#define SYNC   020
-int    cflag;
-int    fflag;
-int    skip;
-int    seekn;
-int    count;
-int    files   = 1;
-char   *string;
-char   *ifile;
-char   *ofile;
-char   *ibuf;
-char   *obuf;
-char   *sbrk();
-int    ibs     = 512;
-int    obs     = 512;
-int    bs;
-int    cbs;
-int    ibc;
-int    obc;
-int    cbc;
-int    nifr;
-int    nipr;
-int    nofr;
-int    nopr;
-int    ntrunc;
-int    ibf;
-int    obf;
-char   *op;
-int    nspace;
-char   etoa[] = {
-       0000,0001,0002,0003,0234,0011,0206,0177,
-       0227,0215,0216,0013,0014,0015,0016,0017,
-       0020,0021,0022,0023,0235,0205,0010,0207,
-       0030,0031,0222,0217,0034,0035,0036,0037,
-       0200,0201,0202,0203,0204,0012,0027,0033,
-       0210,0211,0212,0213,0214,0005,0006,0007,
-       0220,0221,0026,0223,0224,0225,0226,0004,
-       0230,0231,0232,0233,0024,0025,0236,0032,
-       0040,0240,0241,0242,0243,0244,0245,0246,
-       0247,0250,0133,0056,0074,0050,0053,0041,
-       0046,0251,0252,0253,0254,0255,0256,0257,
-       0260,0261,0135,0044,0052,0051,0073,0136,
-       0055,0057,0262,0263,0264,0265,0266,0267,
-       0270,0271,0174,0054,0045,0137,0076,0077,
-       0272,0273,0274,0275,0276,0277,0300,0301,
-       0302,0140,0072,0043,0100,0047,0075,0042,
-       0303,0141,0142,0143,0144,0145,0146,0147,
-       0150,0151,0304,0305,0306,0307,0310,0311,
-       0312,0152,0153,0154,0155,0156,0157,0160,
-       0161,0162,0313,0314,0315,0316,0317,0320,
-       0321,0176,0163,0164,0165,0166,0167,0170,
-       0171,0172,0322,0323,0324,0325,0326,0327,
-       0330,0331,0332,0333,0334,0335,0336,0337,
-       0340,0341,0342,0343,0344,0345,0346,0347,
-       0173,0101,0102,0103,0104,0105,0106,0107,
-       0110,0111,0350,0351,0352,0353,0354,0355,
-       0175,0112,0113,0114,0115,0116,0117,0120,
-       0121,0122,0356,0357,0360,0361,0362,0363,
-       0134,0237,0123,0124,0125,0126,0127,0130,
-       0131,0132,0364,0365,0366,0367,0370,0371,
-       0060,0061,0062,0063,0064,0065,0066,0067,
-       0070,0071,0372,0373,0374,0375,0376,0377,
-};
-char   atoe[] = {
-       0000,0001,0002,0003,0067,0055,0056,0057,
-       0026,0005,0045,0013,0014,0015,0016,0017,
-       0020,0021,0022,0023,0074,0075,0062,0046,
-       0030,0031,0077,0047,0034,0035,0036,0037,
-       0100,0117,0177,0173,0133,0154,0120,0175,
-       0115,0135,0134,0116,0153,0140,0113,0141,
-       0360,0361,0362,0363,0364,0365,0366,0367,
-       0370,0371,0172,0136,0114,0176,0156,0157,
-       0174,0301,0302,0303,0304,0305,0306,0307,
-       0310,0311,0321,0322,0323,0324,0325,0326,
-       0327,0330,0331,0342,0343,0344,0345,0346,
-       0347,0350,0351,0112,0340,0132,0137,0155,
-       0171,0201,0202,0203,0204,0205,0206,0207,
-       0210,0211,0221,0222,0223,0224,0225,0226,
-       0227,0230,0231,0242,0243,0244,0245,0246,
-       0247,0250,0251,0300,0152,0320,0241,0007,
-       0040,0041,0042,0043,0044,0025,0006,0027,
-       0050,0051,0052,0053,0054,0011,0012,0033,
-       0060,0061,0032,0063,0064,0065,0066,0010,
-       0070,0071,0072,0073,0004,0024,0076,0341,
-       0101,0102,0103,0104,0105,0106,0107,0110,
-       0111,0121,0122,0123,0124,0125,0126,0127,
-       0130,0131,0142,0143,0144,0145,0146,0147,
-       0150,0151,0160,0161,0162,0163,0164,0165,
-       0166,0167,0170,0200,0212,0213,0214,0215,
-       0216,0217,0220,0232,0233,0234,0235,0236,
-       0237,0240,0252,0253,0254,0255,0256,0257,
-       0260,0261,0262,0263,0264,0265,0266,0267,
-       0270,0271,0272,0273,0274,0275,0276,0277,
-       0312,0313,0314,0315,0316,0317,0332,0333,
-       0334,0335,0336,0337,0352,0353,0354,0355,
-       0356,0357,0372,0373,0374,0375,0376,0377,
-};
-char   atoibm[] =
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dd.h"
+#include "extern.h"
+
+static void dd_close __P((void));
+static void dd_in __P((void));
+static void getfdtype __P((IO *));
+static void setup __P((void));
+
+IO     in, out;                /* input/output state */
+STAT   st;                     /* statistics */
+void   (*cfunc) __P((void));   /* conversion function */
+u_long cpy_cnt;                /* # of blocks to copy */
+u_int  ddflags;                /* conversion options */
+u_int  cbsz;                   /* conversion block size */
+u_int  files_cnt = 1;          /* # of files to copy */
+u_char *ctab;                  /* conversion table */
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
 {
 {
-       0000,0001,0002,0003,0067,0055,0056,0057,
-       0026,0005,0045,0013,0014,0015,0016,0017,
-       0020,0021,0022,0023,0074,0075,0062,0046,
-       0030,0031,0077,0047,0034,0035,0036,0037,
-       0100,0132,0177,0173,0133,0154,0120,0175,
-       0115,0135,0134,0116,0153,0140,0113,0141,
-       0360,0361,0362,0363,0364,0365,0366,0367,
-       0370,0371,0172,0136,0114,0176,0156,0157,
-       0174,0301,0302,0303,0304,0305,0306,0307,
-       0310,0311,0321,0322,0323,0324,0325,0326,
-       0327,0330,0331,0342,0343,0344,0345,0346,
-       0347,0350,0351,0255,0340,0275,0137,0155,
-       0171,0201,0202,0203,0204,0205,0206,0207,
-       0210,0211,0221,0222,0223,0224,0225,0226,
-       0227,0230,0231,0242,0243,0244,0245,0246,
-       0247,0250,0251,0300,0117,0320,0241,0007,
-       0040,0041,0042,0043,0044,0025,0006,0027,
-       0050,0051,0052,0053,0054,0011,0012,0033,
-       0060,0061,0032,0063,0064,0065,0066,0010,
-       0070,0071,0072,0073,0004,0024,0076,0341,
-       0101,0102,0103,0104,0105,0106,0107,0110,
-       0111,0121,0122,0123,0124,0125,0126,0127,
-       0130,0131,0142,0143,0144,0145,0146,0147,
-       0150,0151,0160,0161,0162,0163,0164,0165,
-       0166,0167,0170,0200,0212,0213,0214,0215,
-       0216,0217,0220,0232,0233,0234,0235,0236,
-       0237,0240,0252,0253,0254,0255,0256,0257,
-       0260,0261,0262,0263,0264,0265,0266,0267,
-       0270,0271,0272,0273,0274,0275,0276,0277,
-       0312,0313,0314,0315,0316,0317,0332,0333,
-       0334,0335,0336,0337,0352,0353,0354,0355,
-       0356,0357,0372,0373,0374,0375,0376,0377,
-};
+       jcl(argv);
+       setup();
 
 
+       (void)signal(SIGINFO, summaryx);
+       (void)signal(SIGINT, terminate);
 
 
-main(argc, argv)
-int    argc;
-char   **argv;
-{
-       int (*conv)();
-       register char *ip;
-       register c;
-       int ebcdic(), ibm(), ascii(), null(), cnull(), term(), block(), unblock();
-       int a;
-
-       conv = null;
-       for(c=1; c<argc; c++) {
-               string = argv[c];
-               if(match("ibs=")) {
-                       ibs = number(BIG);
-                       continue;
-               }
-               if(match("obs=")) {
-                       obs = number(BIG);
-                       continue;
-               }
-               if(match("cbs=")) {
-                       cbs = number(BIG);
-                       continue;
-               }
-               if (match("bs=")) {
-                       bs = number(BIG);
-                       continue;
-               }
-               if(match("if=")) {
-                       ifile = string;
-                       continue;
-               }
-               if(match("of=")) {
-                       ofile = string;
-                       continue;
-               }
-               if(match("skip=")) {
-                       skip = number(BIG);
-                       continue;
-               }
-               if(match("seek=")) {
-                       seekn = number(BIG);
-                       continue;
-               }
-               if(match("count=")) {
-                       count = number(BIG);
-                       continue;
-               }
-               if(match("files=")) {
-                       files = number(BIG);
-                       continue;
-               }
-               if(match("conv=")) {
-               cloop:
-                       if(match(","))
-                               goto cloop;
-                       if(*string == '\0')
-                               continue;
-                       if(match("ebcdic")) {
-                               conv = ebcdic;
-                               goto cloop;
-                       }
-                       if(match("ibm")) {
-                               conv = ibm;
-                               goto cloop;
-                       }
-                       if(match("ascii")) {
-                               conv = ascii;
-                               goto cloop;
-                       }
-                       if(match("block")) {
-                               conv = block;
-                               goto cloop;
-                       }
-                       if(match("unblock")) {
-                               conv = unblock;
-                               goto cloop;
-                       }
-                       if(match("lcase")) {
-                               cflag |= LCASE;
-                               goto cloop;
-                       }
-                       if(match("ucase")) {
-                               cflag |= UCASE;
-                               goto cloop;
-                       }
-                       if(match("swab")) {
-                               cflag |= SWAB;
-                               goto cloop;
-                       }
-                       if(match("noerror")) {
-                               cflag |= NERR;
-                               goto cloop;
-                       }
-                       if(match("sync")) {
-                               cflag |= SYNC;
-                               goto cloop;
-                       }
-               }
-               fprintf(stderr,"bad arg: %s\n", string);
-               exit(1);
-       }
-       if(conv == null && cflag&(LCASE|UCASE))
-               conv = cnull;
-       if (ifile)
-               ibf = open(ifile, 0);
-       else
-               ibf = dup(0);
-       if(ibf < 0) {
-               perror(ifile);
-               exit(1);
-       }
-       obf = ofile ? open(ofile, O_WRONLY|O_CREAT, 0666) : dup(1);
-       if(obf < 0) {
-               fprintf(stderr,"cannot create: %s\n", ofile);
-               exit(1);
-       }
-       if (bs) {
-               ibs = obs = bs;
-               if (conv == null)
-                       fflag++;
-       }
-       if(ibs == 0 || obs == 0) {
-               fprintf(stderr,"counts: cannot be zero\n");
-               exit(1);
-       }
-       ibuf = sbrk(ibs);
-       if (fflag)
-               obuf = ibuf;
-       else
-               obuf = sbrk(obs);
-       sbrk(64);       /* For good measure */
-       if(ibuf == (char *)-1 || obuf == (char *)-1) {
-               fprintf(stderr, "not enough memory\n");
-               exit(1);
-       }
-       ibc = 0;
-       obc = 0;
-       cbc = 0;
-       op = obuf;
-
-       if (signal(SIGINT, SIG_IGN) != SIG_IGN)
-               signal(SIGINT, term);
-       while(skip) {
-               read(ibf, ibuf, ibs);
-               skip--;
-       }
-       while(seekn) {
-               lseek(obf, (long)obs, 1);
-               seekn--;
-       }
+       atexit(summary);
 
 
-loop:
-       if(ibc-- == 0) {
-               ibc = 0;
-               if(count==0 || nifr+nipr!=count) {
-                       if(cflag&(NERR|SYNC))
-                       for(ip=ibuf+ibs; ip>ibuf;)
-                               *--ip = 0;
-                       ibc = read(ibf, ibuf, ibs);
-               }
-               if(ibc == -1) {
-                       perror("read");
-                       if((cflag&NERR) == 0) {
-                               flsh();
-                               term();
-                       }
-                       ibc = 0;
-                       for(c=0; c<ibs; c++)
-                               if(ibuf[c] != 0)
-                                       ibc = c;
-                       stats();
-               }
-               if(ibc == 0 && --files<=0) {
-                       flsh();
-                       term();
-               }
-               if(ibc != ibs) {
-                       nipr++;
-                       if(cflag&SYNC)
-                               ibc = ibs;
-               } else
-                       nifr++;
-               ip = ibuf;
-               c = ibc >> 1;
-               if(cflag&SWAB && c)
-               do {
-                       a = *ip++;
-                       ip[-1] = *ip;
-                       *ip++ = a;
-               } while(--c);
-               ip = ibuf;
-               if (fflag) {
-                       obc = ibc;
-                       flsh();
-                       ibc = 0;
-               }
-               goto loop;
-       }
-       c = 0;
-       c |= *ip++;
-       c &= 0377;
-       (*conv)(c);
-       goto loop;
-}
+       while (files_cnt--)
+               dd_in();
 
 
-flsh()
-{
-       register c;
-
-       if(obc) {
-               if(obc == obs)
-                       nofr++; else
-                       nopr++;
-               c = write(obf, obuf, obc);
-               if(c != obc) {
-                       perror("write");
-                       term();
-               }
-               obc = 0;
-       }
+       dd_close();
+       exit(0);
 }
 
 }
 
-match(s)
-char *s;
+static void
+setup()
 {
 {
-       register char *cs;
-
-       cs = string;
-       while(*cs++ == *s)
-               if(*s++ == '\0')
-                       goto true;
-       if(*s != '\0')
-               return(0);
-
-true:
-       cs--;
-       string = cs;
-       return(1);
-}
+       u_int cnt;
+
+       if (in.name == NULL) {
+               in.name = "stdin";
+               in.fd = STDIN_FILENO;
+       } else {
+               in.fd = open(in.name, O_RDONLY, 0);
+               if (in.fd < 0)
+                       err(1, "%s", in.name);
+       }
 
 
-number(big)
-{
-       register char *cs;
-       long n;
-
-       cs = string;
-       n = 0;
-       while(*cs >= '0' && *cs <= '9')
-               n = n*10 + *cs++ - '0';
-       for(;;)
-       switch(*cs++) {
-
-       case 'k':
-               n *= 1024;
-               continue;
-
-       case 'w':
-               n *= sizeof(int);
-               continue;
-
-       case 'b':
-               n *= 512;
-               continue;
-
-       case '*':
-       case 'x':
-               string = cs;
-               n *= number(BIG);
-
-       case '\0':
-               if (n>=big || n<0) {
-                       fprintf(stderr, "dd: argument %D out of range\n", n);
-                       exit(1);
+       getfdtype(&in);
+
+       if (files_cnt > 1 && !(in.flags & ISTAPE))
+               errx(1, "files is not supported for non-tape devices");
+
+       if (out.name == NULL) {
+               /* No way to check for read access here. */
+               out.fd = STDOUT_FILENO;
+               out.name = "stdout";
+       } else {
+#define        OFLAGS \
+    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
+               out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
+               /*
+                * May not have read access, so try again with write only.
+                * Without read we may have a problem if output also does
+                * not support seeks.
+                */
+               if (out.fd < 0) {
+                       out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
+                       out.flags |= NOREAD;
                }
                }
-               return(n);
+               if (out.fd < 0)
+                       err(1, "%s", out.name);
        }
        }
-       /* never gets here */
-}
 
 
-cnull(cc)
-{
-       register c;
-
-       c = cc;
-       if(cflag&UCASE && c>='a' && c<='z')
-               c += 'A'-'a';
-       if(cflag&LCASE && c>='A' && c<='Z')
-               c += 'a'-'A';
-       null(c);
+       getfdtype(&out);
+
+       /*
+        * Allocate space for the input and output buffers.  If not doing
+        * record oriented I/O, only need a single buffer.
+        */
+       if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
+               if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
+                       err(1, NULL);
+               out.db = in.db;
+       } else if ((in.db =
+           malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
+           (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL)
+               err(1, NULL);
+       in.dbp = in.db;
+       out.dbp = out.db;
+
+       /* Position the input/output streams. */
+       if (in.offset)
+               pos_in();
+       if (out.offset)
+               pos_out();
+
+       /*
+        * Truncate the output file; ignore errors because it fails on some
+        * kinds of output files, tapes, for example.
+        */
+       if (ddflags & (C_OF | C_SEEK | C_NOTRUNC) == (C_OF | C_SEEK))
+               (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
+
+       /*
+        * If converting case at the same time as another conversion, build a
+        * table that does both at once.  If just converting case, use the
+        * built-in tables.
+        */
+       if (ddflags & (C_LCASE|C_UCASE))
+               if (ddflags & C_ASCII)
+                       if (ddflags & C_LCASE) {
+                               for (cnt = 0; cnt < 0377; ++cnt)
+                                       if (isupper(ctab[cnt]))
+                                               ctab[cnt] = tolower(ctab[cnt]);
+                       } else {
+                               for (cnt = 0; cnt < 0377; ++cnt)
+                                       if (islower(ctab[cnt]))
+                                               ctab[cnt] = toupper(ctab[cnt]);
+                       }
+               else if (ddflags & C_EBCDIC)
+                       if (ddflags & C_LCASE) {
+                               for (cnt = 0; cnt < 0377; ++cnt)
+                                       if (isupper(cnt))
+                                               ctab[cnt] = ctab[tolower(cnt)];
+                       } else {
+                               for (cnt = 0; cnt < 0377; ++cnt)
+                                       if (islower(cnt))
+                                               ctab[cnt] = ctab[toupper(cnt)];
+                       }
+               else
+                       ctab = ddflags & C_LCASE ? u2l : l2u;
+       (void)time(&st.start);                  /* Statistics timestamp. */
 }
 
 }
 
-null(c)
+static void
+getfdtype(io)
+       IO *io;
 {
 {
-
-       *op = c;
-       op++;
-       if(++obc >= obs) {
-               flsh();
-               op = obuf;
-       }
+       struct mtget mt;
+       struct stat sb;
+
+       if (fstat(io->fd, &sb))
+               err(1, "%s", io->name);
+       if (S_ISCHR(sb.st_mode))
+               io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE;
+       else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
+               io->flags |= ISPIPE;            /* XXX fixed in 4.4BSD */
 }
 
 }
 
-ascii(cc)
+static void
+dd_in()
 {
 {
-       register c;
-
-       c = etoa[cc] & 0377;
-       if(cbs == 0) {
-               cnull(c);
-               return;
-       }
-       if(c == ' ') {
-               nspace++;
-               goto out;
-       }
-       while(nspace > 0) {
-               null(' ');
-               nspace--;
-       }
-       cnull(c);
-
-out:
-       if(++cbc >= cbs) {
-               null('\n');
-               cbc = 0;
-               nspace = 0;
-       }
-}
+       int flags, n;
+
+       for (flags = ddflags;;) {
+               if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
+                       return;
+
+               /*
+                * Zero the buffer first if trying to recover from errors so
+                * lose the minimum amount of data.  If doing block operations
+                * use spaces.
+                */
+               if ((flags & (C_NOERROR|C_SYNC)) == (C_NOERROR|C_SYNC))
+                       if (flags & (C_BLOCK|C_UNBLOCK))
+                               memset(in.dbp, ' ', in.dbsz);
+                       else
+                               memset(in.dbp, 0, in.dbsz);
+
+               n = read(in.fd, in.dbp, in.dbsz);
+               if (n == 0) {
+                       in.dbrcnt = 0;
+                       return;
+               }
 
 
-unblock(cc)
-{
-       register c;
+               /* Read error. */
+               if (n < 0) {
+                       /*
+                        * If noerror not specified, die.  POSIX requires that
+                        * the warning message be followed by an I/O display.
+                        */
+                       if (!(flags & C_NOERROR))
+                               err(1, "%s", in.name);
+                       warn("%s", in.name);
+                       summary();
+
+                       /*
+                        * If it's not a tape drive or a pipe, seek past the
+                        * error.  If your OS doesn't do the right thing for
+                        * raw disks this section should be modified to re-read
+                        * in sector size chunks.
+                        */
+                       if (!(in.flags & (ISPIPE|ISTAPE)) &&
+                           lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
+                               warn("%s", in.name);
+
+                       /* If sync not specified, omit block and continue. */
+                       if (!(ddflags & C_SYNC))
+                               continue;
 
 
-       c = cc & 0377;
-       if(cbs == 0) {
-               cnull(c);
-               return;
-       }
-       if(c == ' ') {
-               nspace++;
-               goto out;
-       }
-       while(nspace > 0) {
-               null(' ');
-               nspace--;
-       }
-       cnull(c);
+                       /* Read errors count as full blocks. */
+                       in.dbcnt += in.dbrcnt = in.dbsz;
+                       ++st.in_full;
+
+               /* Handle full input blocks. */
+               } else if (n == in.dbsz) {
+                       in.dbcnt += in.dbrcnt = n;
+                       ++st.in_full;
+
+               /* Handle partial input blocks. */
+               } else {
+                       /* If sync, use the entire block. */
+                       if (ddflags & C_SYNC)
+                               in.dbcnt += in.dbrcnt = in.dbsz;
+                       else
+                               in.dbcnt += in.dbrcnt = n;
+                       ++st.in_part;
+               }
 
 
-out:
-       if(++cbc >= cbs) {
-               null('\n');
-               cbc = 0;
-               nspace = 0;
-       }
-}
+               /*
+                * POSIX states that if bs is set and no other conversions
+                * than noerror, notrunc or sync are specified, the block
+                * is output without buffering as it is read.
+                */
+               if (ddflags & C_BS) {
+                       out.dbcnt = in.dbcnt;
+                       dd_out(1);
+                       in.dbcnt = 0;
+                       continue;
+               }
 
 
-ebcdic(cc)
-{
-       register c;
-
-       c = cc;
-       if(cflag&UCASE && c>='a' && c<='z')
-               c += 'A'-'a';
-       if(cflag&LCASE && c>='A' && c<='Z')
-               c += 'a'-'A';
-       c = atoe[c] & 0377;
-       if(cbs == 0) {
-               null(c);
-               return;
-       }
-       if(cc == '\n') {
-               while(cbc < cbs) {
-                       null(atoe[' ']);
-                       cbc++;
+               if (ddflags & C_SWAB) {
+                       if ((n = in.dbcnt) & 1) {
+                               ++st.swab;
+                               --n;
+                       }
+                       swab(in.dbp, in.dbp, n);
                }
                }
-               cbc = 0;
-               return;
+
+               in.dbp += in.dbrcnt;
+               (*cfunc)();
        }
        }
-       if(cbc == cbs)
-               ntrunc++;
-       cbc++;
-       if(cbc <= cbs)
-               null(c);
 }
 
 }
 
-ibm(cc)
+/*
+ * Cleanup any remaining I/O and flush output.  If necesssary, output file
+ * is truncated.
+ */
+static void
+dd_close()
 {
 {
-       register c;
-
-       c = cc;
-       if(cflag&UCASE && c>='a' && c<='z')
-               c += 'A'-'a';
-       if(cflag&LCASE && c>='A' && c<='Z')
-               c += 'a'-'A';
-       c = atoibm[c] & 0377;
-       if(cbs == 0) {
-               null(c);
-               return;
-       }
-       if(cc == '\n') {
-               while(cbc < cbs) {
-                       null(atoibm[' ']);
-                       cbc++;
-               }
-               cbc = 0;
-               return;
+       if (cfunc == def)
+               def_close();
+       else if (cfunc == block)
+               block_close();
+       else if (cfunc == unblock)
+               unblock_close();
+       if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
+               memset(out.dbp, 0, out.dbsz - out.dbcnt);
+               out.dbcnt = out.dbsz;
        }
        }
-       if(cbc == cbs)
-               ntrunc++;
-       cbc++;
-       if(cbc <= cbs)
-               null(c);
+       if (out.dbcnt)
+               dd_out(1);
 }
 
 }
 
-block(cc)
+void
+dd_out(force)
+       int force;
 {
 {
-       register c;
-
-       c = cc;
-       if(cflag&UCASE && c>='a' && c<='z')
-               c += 'A'-'a';
-       if(cflag&LCASE && c>='A' && c<='Z')
-               c += 'a'-'A';
-       c &= 0377;
-       if(cbs == 0) {
-               null(c);
-               return;
-       }
-       if(cc == '\n') {
-               while(cbc < cbs) {
-                       null(' ');
-                       cbc++;
+       static int warned;
+       int cnt, n, nw;
+       u_char *outp;
+
+       /*
+        * Write one or more blocks out.  The common case is writing a full
+        * output block in a single write; increment the full block stats.
+        * Otherwise, we're into partial block writes.  If a partial write,
+        * and it's a character device, just warn.  If a tape device, quit.
+        *
+        * The partial writes represent two cases.  1: Where the input block
+        * was less than expected so the output block was less than expected.
+        * 2: Where the input block was the right size but we were forced to
+        * write the block in multiple chunks.  The original versions of dd(1)
+        * never wrote a block in more than a single write, so the latter case
+        * never happened.
+        *
+        * One special case is if we're forced to do the write -- in that case
+        * we play games with the buffer size, and it's usually a partial write.
+        */
+       outp = out.db;
+       for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
+               for (cnt = n;; cnt -= nw) {
+                       nw = write(out.fd, outp, cnt);
+                       if (nw <= 0) {
+                               if (nw == 0)
+                                       errx(1, "%s: end of device", out.name);
+                               if (errno != EINTR)
+                                       err(1, "%s", out.name);
+                               nw = 0;
+                       }
+                       outp += nw;
+                       st.bytes += nw;
+                       if (nw == n) {
+                               if (n != out.dbsz)
+                                       ++st.out_part;
+                               else
+                                       ++st.out_full;
+                               break;
+                       }
+                       ++st.out_part;
+                       if (nw == cnt)
+                               break;
+                       if (out.flags & ISCHR && !warned) {
+                               warned = 1;
+                               warnx("%s: short write on character device",
+                                   out.name);
+                       }
+                       if (out.flags & ISTAPE)
+                               errx(1, "%s: short write on tape device", out.name);
                }
                }
-               cbc = 0;
-               return;
+               if ((out.dbcnt -= n) < out.dbsz)
+                       break;
        }
        }
-       if(cbc == cbs)
-               ntrunc++;
-       cbc++;
-       if(cbc <= cbs)
-               null(c);
-}
-
-term()
-{
-
-       stats();
-       exit(0);
-}
-
-stats()
-{
 
 
-       fprintf(stderr,"%u+%u records in\n", nifr, nipr);
-       fprintf(stderr,"%u+%u records out\n", nofr, nopr);
-       if(ntrunc)
-               fprintf(stderr,"%u truncated records\n", ntrunc);
+       /* Reassemble the output block. */
+       if (out.dbcnt)
+               memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
+       out.dbp = out.db + out.dbcnt;
 }
 }