/* * dd - convert and copy * * Gunnar Ritter, Freiburg i. Br., Germany, January 2003. */ /* * Copyright (c) 2003 Gunnar Ritter * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. */ #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4 #define USED __attribute__ ((used)) #elif defined __GNUC__ #define USED __attribute__ ((unused)) #else #define USED #endif static const char sccsid[] USED = "@(#)dd.sl 1.30 (gritter) 1/22/06"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sigset.h" #include #include #include #include #if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \ defined (__hpux) || defined (_AIX) || defined (__NetBSD__) || \ defined (__OpenBSD__) || defined (__DragonFly__) || defined (__APPLE__) #include #else /* SVR4.2MP */ #include #include #endif /* SVR4.2MP */ #include "atoll.h" #include "memalign.h" #include "mbtowi.h" /* * For 'conv=ascii'. */ static const unsigned char c_ascii[] = { 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,0325,0056,0074,0050,0053,0174, 0046,0251,0252,0253,0254,0255,0256,0257,0260,0261,0041,0044,0052,0051,0073,0176, 0055,0057,0262,0263,0264,0265,0266,0267,0270,0271,0313,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,0136,0314,0315,0316,0317,0320, 0321,0345,0163,0164,0165,0166,0167,0170,0171,0172,0322,0323,0324,0133,0326,0327, 0330,0331,0332,0333,0334,0335,0336,0337,0340,0341,0342,0343,0344,0135,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 }; /* * For 'conv=ibm'. */ static const unsigned char c_ibm[] = { 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 }; /* * For 'conv=ebcdic'. */ static const unsigned char c_ebcdic[] = { 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,0232,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,0137,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,0152,0233,0234,0235,0236, 0237,0240,0252,0253,0254,0112,0256,0257,0260,0261,0262,0263,0264,0265,0266,0267, 0270,0271,0272,0273,0274,0241,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 }; static char *progname; /* argv[0] to main() */ typedef long long d_type; static char *iblok; /* input buffer */ static char *oblok; /* output buffer */ static char *cblok; /* conversion buffer */ static char mblok[MB_LEN_MAX+1]; /* tow{upper|lower} buffer */ static char *mbp; /* points to remaining chars in mblok */ static int mbrest; /* number of remaining chars in mblok */ static const char *iffile; /* input file name */ static int iffd; /* input file descriptor */ static const char *offile; /* output file name */ static int offd; /* output file descriptor */ static struct stat istat; /* stat of input */ static struct stat ostat; /* stat of output */ static d_type ibs = 512; /* input block size */ static d_type obs = 512; /* output block size */ static d_type bs; /* size for both buffers */ static d_type oflow; /* remaining bytes in output buffer */ static d_type cbs; /* conversion block size */ static d_type cflow; /* remaining bytes in conv. buffer */ static int ctrunc; /* truncate current data (conv=block) */ static d_type skip; /* skip these blocks on input */ static d_type count = -1; /* no more than count blocks of input */ static int files = 1; /* read EOF this many times */ static d_type iseek; /* seek these blocks on input */ static d_type oseek; /* seek these blocks on output */ static int mb_cur_max; /* MB_CUR_MAX acceleration */ static d_type iwhole; /* statistics */ static d_type ipartial; static d_type owhole; static d_type opartial; static d_type truncated; static enum charconv { CHAR_NONE = 0, CHAR_ASCII = 1, CHAR_EBCDIC = 2, CHAR_IBM = 3 } chars = CHAR_NONE; static enum conversion { CONV_NONE = 0, CONV_BLOCK = 01, CONV_UNBLOCK = 02, CONV_LCASE = 04, CONV_UCASE = 010, CONV_SWAB = 020, CONV_NOERROR = 040, CONV_NOTRUNC = 0100, CONV_IDIRECT = 0200, CONV_ODIRECT = 0400, CONV_DIRECT = 0600, CONV_SYNC = 01000 } convs = CONV_NONE; static struct { const char *c_name; enum conversion c_conv; enum charconv c_char; } convtab[] = { { "ascii", CONV_UNBLOCK, CHAR_ASCII }, { "ebcdic", CONV_BLOCK, CHAR_EBCDIC }, { "ibm", CONV_BLOCK, CHAR_IBM }, { "block", CONV_BLOCK, CHAR_NONE }, { "unblock", CONV_UNBLOCK, CHAR_NONE }, { "lcase", CONV_LCASE, CHAR_NONE }, { "ucase", CONV_UCASE, CHAR_NONE }, { "swab", CONV_SWAB, CHAR_NONE }, { "noerror", CONV_NOERROR, CHAR_NONE }, { "notrunc", CONV_NOTRUNC, CHAR_NONE }, #ifdef O_DIRECT { "idirect", CONV_IDIRECT, CHAR_NONE }, { "odirect", CONV_ODIRECT, CHAR_NONE }, #endif /* O_DIRECT */ { "sync", CONV_SYNC, CHAR_NONE }, { NULL, CONV_NONE, CHAR_NONE } }; static void * bmalloc(size_t nbytes) { static long pagesize; void *vp; if (pagesize == 0) if ((pagesize = sysconf(_SC_PAGESIZE)) < 0) pagesize = 4096; if ((vp = memalign(pagesize, nbytes)) == NULL) { fprintf(stderr, "%s: not enough memory\n", progname); fprintf(stderr, "Please use a smaller buffer size\n"); exit(077); } return vp; } /************************** ARGUMENT SCANNING ***************************/ static void badarg(const char *arg) { fprintf(stderr, "%s: bad arg: \"%s\"\n", progname, arg); exit(2); } static void badnumeric(const char *arg) { fprintf(stderr, "%s: bad numeric arg: \"%s\"\n", progname, arg); exit(2); } static void nozeroblok(void) { fprintf(stderr, "%s: buffer sizes cannot be zero\n", progname); exit(2); } /* * Get the value of a numeric argument. */ static d_type expr(const char *ap) { d_type val; char *x; int c; if (*ap == '-' || *ap == '+') badnumeric(ap); val = strtoull(ap, &x, 10); while ((c = *x++) != '\0') { switch (c) { case 'k': val *= 1024; break; case 'b': val *= 512; break; case 'w': val *= 2; break; case 'x': case '*': return val * expr(x); default: badnumeric(ap); } } return val; } static void setin(const char *ap) { iffile = ap; } static void setof(const char *ap) { offile = ap; } static void setibs(const char *ap) { ibs = expr(ap); if (ibs == 0) nozeroblok(); } static void setobs(const char *ap) { obs = expr(ap); if (obs == 0) nozeroblok(); } static void setbs(const char *ap) { bs = expr(ap); } static void setcbs(const char *ap) { cbs = expr(ap); } static void setskip(const char *ap) { skip = expr(ap); } static void setcount(const char *ap) { count = expr(ap); } static void setconv(const char *ap) { const char *cp, *cq; int i; for (;;) { while (*ap == ',') ap++; if (*ap == '\0') break; for (i = 0; convtab[i].c_name; i++) { for (cp = convtab[i].c_name, cq = ap; *cp && (*cp == *cq); cp++, cq++); if (*cp == '\0' && (*cq == ',' || *cq == '\0')) { convs |= convtab[i].c_conv; if (convtab[i].c_char != CHAR_NONE) chars = convtab[i].c_char; ap = cq; goto next; } } badarg(ap); next:; } } static void setfiles(const char *ap) { files = expr(ap); } static void setiseek(const char *ap) { iseek = expr(ap); } static void setoseek(const char *ap) { oseek = expr(ap); } static struct { const char *a_name; void (*a_func)(const char *); } argtab[] = { { "if=", setin }, { "of=", setof }, { "ibs=", setibs }, { "obs=", setobs }, { "bs=", setbs }, { "cbs=", setcbs }, { "skip=", setskip }, { "seek=", setoseek }, { "count=", setcount }, { "conv=", setconv }, { "files=", setfiles }, { "iseek=", setiseek }, { "oseek=", setoseek }, { NULL, NULL } }; static const char * thisarg(const char *sp, const char *ap) { do { if (*sp != *ap) return NULL; if (*sp == '=') return &sp[1]; } while (*sp++ && *ap++); return NULL; } /******************************* EXECUTION ********************************/ static void stats(void) { fprintf(stderr, "%llu+%llu records in\n", (unsigned long long)iwhole, (unsigned long long)ipartial); fprintf(stderr, "%llu+%llu records out\n", (unsigned long long)owhole, (unsigned long long)opartial); if (truncated) { fprintf(stderr, "%llu truncated record%s\n", (unsigned long long)truncated, truncated > 1 ? "s" : ""); } } static void charconv(char *data, size_t size); static void bflush(void); static void cflush(void); static void uflush(void); static void quit(int status) { if (mbp) charconv(NULL, 0); cflush(); uflush(); bflush(); stats(); exit(status); } static void onint(int sig) { stats(); exit(sig | 0200); } static int ontape(void) { static int yes = -1; if (yes == -1) { #if defined (__linux__) || defined (__FreeBSD__) || defined (__hpux) || \ defined (_AIX) || defined (__NetBSD__) || defined (__OpenBSD__) || \ defined (__DragonFly__) || defined (__APPLE__) struct mtget mg; yes = (istat.st_mode&S_IFMT) == S_IFCHR && ioctl(iffd, MTIOCGET, &mg) == 0; #elif defined (__sun) struct mtdrivetype_request mr; struct mtdrivetype md; mr.size = sizeof md; mr.mtdtp = &md; yes = (istat.st_mode&S_IFMT) == S_IFCHR && ioctl(iffd, MTIOCGETDRIVETYPE, &mr) == 0; #else /* SVR4.2MP */ struct blklen bl; yes = (istat.st_mode&S_IFMT) == S_IFCHR && ioctl(iffd, T_RDBLKLEN, &bl) == 0; #endif /* SVR4.2MP */ } return yes; } static void seekconv(d_type count) { ssize_t sz; off_t offs; if (lseek(offd, 0, SEEK_CUR) != (off_t)-1) { do { if ((offs = lseek(offd, obs, SEEK_CUR)) == (off_t)-1) { err: fprintf(stderr, "%s: output seek error: %s\n", progname, strerror(errno)); exit(3); } } while (--count); if ((convs & CONV_NOTRUNC) == 0 && (ostat.st_mode&S_IFMT) == S_IFREG) ftruncate(offd, offs); return; } while (count) { if ((sz = read(offd, oblok, obs)) == 0) break; if (sz < 0) goto err; count--; } if (count) { memset(oblok, 0, obs); do { if ((sz = write(offd, oblok, obs)) < 0) goto err; } while (--count); } } static void skipconv(int canseek, d_type count) { ssize_t rd = 0; if (canseek && lseek(iffd, 0, SEEK_CUR) == (off_t)-1) canseek = 0; while (count--) { if (canseek) { if (lseek(iffd, ibs, SEEK_CUR) != (off_t)-1) rd = ibs; else if (errno == EINVAL) rd = 0; else { fprintf(stderr, "%s: input seek error: %s\n", progname, strerror(errno)); exit(3); } } else { if ((rd = read(iffd, iblok, ibs)) < 0) { fprintf(stderr, "%s: read error during skip: %s\n", progname, strerror(errno)); exit(3); } } if (rd == 0 && files-- <= 1) { fprintf(stderr, "%s: cannot skip past end-of-file\n", progname); exit(3); } } } static void prepare(void) { int flags; if (bs) ibs = obs = bs; iblok = bmalloc(ibs); if (!(bs && chars == CHAR_NONE && (convs|CONV_SYNC|CONV_NOERROR|CONV_NOTRUNC|CONV_DIRECT) == (CONV_SYNC|CONV_NOERROR|CONV_NOTRUNC|CONV_DIRECT))) oblok = bmalloc(obs); if (cbs > 0) { if ((convs & (CONV_BLOCK|CONV_UNBLOCK)) == 0) { fprintf(stderr, "%s: cbs must be zero if no block conversion requested\n", progname); exit(2); } cblok = bmalloc(cbs + 1); } else convs &= ~(CONV_BLOCK|CONV_UNBLOCK); if ((iffd = iffile ? open(iffile, O_RDONLY) : dup(0)) < 0) { fprintf(stderr, "%s: cannot open %s: %s\n", progname, iffile ? iffile : "", strerror(errno)); exit(1); } fstat(iffd, &istat); #ifdef O_DIRECT if (convs & CONV_IDIRECT) { int flags; flags = fcntl(iffd, F_GETFL); fcntl(iffd, F_SETFL, flags | O_DIRECT); } #endif /* O_DIRECT */ if (skip) skipconv(0, skip); else if (iseek) skipconv(1, iseek); flags = O_RDWR | O_CREAT; if ((convs & CONV_NOTRUNC) == 0 && oseek == 0) flags |= O_TRUNC; if ((offd = offile ? open(offile, flags, 0666) : dup(1)) < 0) { fprintf(stderr, "%s: cannot %s %s: %s\n", progname, flags & O_TRUNC ? "create" : "open", offile ? offile : "", strerror(errno)); exit(1); } fstat(offd, &ostat); #ifdef O_DIRECT if (convs & CONV_ODIRECT) { int flags; flags = fcntl(offd, F_GETFL); fcntl(offd, F_SETFL, flags | O_DIRECT); } #endif /* O_DIRECT */ if (oseek) seekconv(oseek); } static void swabconv(char *data, size_t size) { char c; while (size > 1) { c = data[0]; data[0] = data[1]; data[1] = c; size -= 2; data += 2; } } static void ascconv(char *data, size_t size) { while (size--) { *data = c_ascii[*data & 0377]; data++; } } static ssize_t swrite(const char *data, size_t size) { ssize_t wt; for (;;) { if ((wt = write(offd, data, size)) <= 0) { if (errno == EINTR) continue; fprintf(stderr, "%s: write error: %s\n", progname, strerror(errno)); oflow = 0; offd = -1; quit(1); } break; } return wt; } /* * Write without output buffering (if bs= was specified). */ static void dwrite(const char *data, size_t size) { ssize_t wrt; do { wrt = swrite(data, size); if (wrt == obs) owhole++; else opartial++; data += wrt; size -= wrt; } while (size > 0); } /* * Write to output buffer. On short write, remaining data is kept within * the buffer and written next time again. Might a warning be useful in * this case? */ static void bwrite(const char *data, size_t size) { ssize_t wrt; size_t di; while (oflow + size > obs) { di = obs - oflow; size -= di; if (oflow) { memcpy(&oblok[oflow], data, di); wrt = swrite(oblok, obs); } else wrt = swrite(data, obs); if (wrt != obs) { memcpy(oblok, &(oflow ? oblok : data)[wrt], obs - wrt); opartial++; } else owhole++; oflow = obs - wrt; data += di; } if (size == obs) { if ((wrt = swrite(data, obs)) == obs) owhole++; else opartial++; size -= wrt; data += wrt; } if (size) { memcpy(&oblok[oflow], data, size); oflow += size; } } static void bflush(void) { ssize_t wrt; if (offd >= 0) { while (oflow) { if ((wrt = swrite(oblok, oflow)) != oflow) memcpy(oblok, &oblok[wrt], obs - wrt); oflow -= wrt; opartial++; } if (close(offd) < 0) { fprintf(stderr, "%s: write error: %s\n", progname, strerror(errno)); offd = -1; quit(1); } offd = -1; } } /* * Handle conversions to EBCDIC. */ static void ewrite(char *data, size_t size) { char *dt = data; size_t sz = size; if (chars == CHAR_EBCDIC) { while (sz--) { *dt = c_ebcdic[*dt & 0377]; dt++; } } else if (chars == CHAR_IBM) { while (sz--) { *dt = c_ibm[*dt & 0377]; dt++; } } bwrite(data, size); } /* * Handle 'conv=block'. */ static void cflush(void) { if (convs & CONV_BLOCK && cflow) { while (cflow < cbs) cblok[cflow++] = ' '; ewrite(cblok, cbs); cflow = 0; } } static void cwrite(const char *data, size_t size) { while (size) { if (ctrunc == 0) { cblok[cflow] = *data++; if (cblok[cflow] == '\n') { if (cflow == 0) cblok[cflow++] = ' '; cflush(); } else if (++cflow == cbs) { cflush(); ctrunc = 1; } } else { if (*data++ == '\n') ctrunc = 0; else if (ctrunc == 1) { truncated++; ctrunc = 2; } } size--; } } /* * Handle 'conv=unblock'. */ static void uflush(void) { char *cp; if (cflow) { for (cp = &cblok[cflow-1]; cp >= cblok && *cp == ' '; cp--); cp[1] = '\n'; bwrite(cblok, cp - cblok + 2); cflow = 0; } } static void uwrite(const char *data, size_t size) { while (size) { while (cflow < cbs) { cblok[cflow++] = *data++; if (--size == 0) return; } uflush(); } } static void blokconv(char *data, size_t size) { switch (chars) { case CHAR_EBCDIC: case CHAR_IBM: if ((convs & (CONV_BLOCK|CONV_UNBLOCK)) == 0) { ewrite(data, size); break; } /*FALLTHRU*/ default: if (convs & CONV_BLOCK) cwrite(data, size); else if (convs & CONV_UNBLOCK) uwrite(data, size); else bwrite(data, size); break; } } static void charconv(char *data, size_t size) { if (convs & (CONV_LCASE|CONV_UCASE)) { if (mb_cur_max > 1) { /* * Multibyte case conversion is somewhat ugly * with dd as there is no guarantee that a * character fits in an input block. We need * another intermediate therefore to store * incomplete multibyte sequences. */ int i, n, len; wint_t wc; int flush = size == 0; while (size > 0 || (flush && mbrest)) { i = 0; if (mbrest && mbp && mbp > mblok) { do mblok[i] = mbp[i]; while (i++, --mbrest); } else if (mbp == mblok) { i = mbrest; mbrest = 0; } if (i == 0 && size) { mblok[i++] = *data++; size--; } if (mblok[0] & 0200) { while (i < mb_cur_max && size) { mblok[i++] = *data++; size--; } if (!flush && i < mb_cur_max) { mbp = mblok; mbrest = i; return; } if ((n = mbtowi(&wc, mblok, i)) < 0) { len = 1; wc = WEOF; } else if (n == 0) len = 1; else len = n; } else { wc = mblok[0]; len = n = 1; } if (i > 0) { mbrest = i - len; mbp = &mblok[len]; } else { mbrest = 0; mbp = NULL; } if (wc != WEOF) { char new[MB_LEN_MAX + 1]; if (convs & CONV_LCASE) wc = wc & ~(wchar_t)0177 ? towlower(wc) : tolower(wc); if (convs & CONV_UCASE) wc = wc & ~(wchar_t)0177 ? towupper(wc) : toupper(wc); if ((n = wctomb(new, wc)) > 0) blokconv(new, n); else goto inv; } else inv: blokconv(mblok, len); } return; } else { char *dp = data; size_t sz = size; while (sz--) { if (convs & CONV_LCASE) *dp = tolower(*dp & 0377); if (convs & CONV_UCASE) *dp = toupper(*dp & 0377); dp++; } } } blokconv(data, size); } static void dd(void) { ssize_t rd; while (count == -1 || count > 0) { if ((rd = read(iffd, iblok, ibs)) < ibs) { if (rd < 0) { fprintf(stderr, "%s: read error: %s\n", progname, strerror(errno)); if (convs & CONV_NOERROR) { stats(); if (!ontape()) lseek(iffd, ibs, SEEK_CUR); if (convs & CONV_SYNC) rd = 0; else continue; } else quit(1); } else if (rd == 0) { if (files-- <= 1) break; continue; } else if (rd > 0) ipartial++; if (convs & CONV_SYNC) { int c; c = convs&(CONV_BLOCK|CONV_UNBLOCK) ? ' ' : 0; memset(&iblok[rd], c, ibs - rd); rd = ibs; } } else iwhole++; if (count > 0) count--; if (bs && chars == CHAR_NONE && (convs|CONV_SYNC|CONV_NOERROR|CONV_NOTRUNC|CONV_DIRECT) == (CONV_SYNC|CONV_NOERROR|CONV_NOTRUNC|CONV_DIRECT)) dwrite(iblok, rd); else { if (convs & CONV_SWAB) swabconv(iblok, rd); if (chars == CHAR_ASCII) ascconv(iblok, rd); charconv(iblok, rd); } } } int main(int argc, char **argv) { const char *cp; int o, i; progname = basename(argv[0]); setlocale(LC_CTYPE, ""); mb_cur_max = MB_CUR_MAX; if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '-' && argv[1][2] == '\0') o = 2; else o = 1; while (o < argc) { for (i = 0; argtab[i].a_name; i++) { if ((cp = thisarg(argv[o], argtab[i].a_name)) != 0) { argtab[i].a_func(cp); break; } } if (argtab[i].a_name == NULL) badarg(argv[o]); o++; } if ((sigset(SIGINT, SIG_IGN)) != SIG_IGN) sigset(SIGINT, onint); prepare(); dd(); quit(0); /*NOTREACHED*/ return 0; }