📄 dd.c
字号:
voidblock_close(void){ /* * Copy any remaining data into the output buffer and pad to a record. * Don't worry about truncation or translation, the input buffer is * always empty when truncating, and no characters have been added for * translation. The bottom line is that anything left in the input * buffer is a truncated record. Anything left in the output buffer * just wasn't big enough. */ if (in.dbcnt) { ++st.trunc; (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); (void)memset(out.dbp + in.dbcnt, ctab ? ctab[' '] : ' ', cbsz - in.dbcnt); out.dbcnt += cbsz; }}/* * Convert fixed length (cbsz) records to variable length. Deletes any * trailing blanks and appends a newline. * * max in buffer: MAX(ibs, cbsz) + cbsz * max out buffer: obs + cbsz */voidunblock(void){ uint64_t cnt; u_char *inp; const u_char *t; /* Translation and case conversion. */ if ((t = ctab) != NULL) for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--) *inp = t[*inp]; /* * Copy records (max cbsz size chunks) into the output buffer. The * translation has to already be done or we might not recognize the * spaces. */ for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t); if (t >= inp) { cnt = t - inp + 1; (void)memmove(out.dbp, inp, cnt); out.dbp += cnt; out.dbcnt += cnt; } ++out.dbcnt; *out.dbp++ = '\n'; if (out.dbcnt >= out.dbsz) dd_out(0); } if (in.dbcnt) (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); in.dbp = in.db + in.dbcnt;}voidunblock_close(void){ uint64_t cnt; u_char *t; if (in.dbcnt) { warnx("%s: short input record", in.name); for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t); if (t >= in.db) { cnt = t - in.db + 1; (void)memmove(out.dbp, in.db, cnt); out.dbp += cnt; out.dbcnt += cnt; } ++out.dbcnt; *out.dbp++ = '\n'; }}#endif /* NO_CONV */#define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)voidsummary(void){ char buf[100]; int64_t mS; struct timeval tv; if (progress) (void)write(STDERR_FILENO, "\n", 1); (void)gettimeofday(&tv, NULL); mS = tv2mS(tv) - tv2mS(st.start); if (mS == 0) mS = 1; /* Use snprintf(3) so that we don't reenter stdio(3). */ (void)snprintf(buf, sizeof(buf), "%llu+%llu records in\n%llu+%llu records out\n", (unsigned long long)st.in_full, (unsigned long long)st.in_part, (unsigned long long)st.out_full, (unsigned long long)st.out_part); (void)write(STDERR_FILENO, buf, strlen(buf)); if (st.swab) { (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n", (unsigned long long)st.swab, (st.swab == 1) ? "block" : "blocks"); (void)write(STDERR_FILENO, buf, strlen(buf)); } if (st.trunc) { (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n", (unsigned long long)st.trunc, (st.trunc == 1) ? "block" : "blocks"); (void)write(STDERR_FILENO, buf, strlen(buf)); } if (st.sparse) { (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n", (unsigned long long)st.sparse, (st.sparse == 1) ? "block" : "blocks"); (void)write(STDERR_FILENO, buf, strlen(buf)); } (void)snprintf(buf, sizeof(buf), "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n", (unsigned long long) st.bytes, (long) (mS / 1000), (int) (mS % 1000), (unsigned long long) (st.bytes * 1000LL / mS)); (void)write(STDERR_FILENO, buf, strlen(buf));}voidterminate(int notused){ exit(0); /* NOTREACHED */}static int c_arg(const void *, const void *);#ifndef NO_CONVstatic int c_conv(const void *, const void *);#endifstatic void f_bs(char *);static void f_cbs(char *);static void f_conv(char *);static void f_count(char *);static void f_files(char *);static void f_ibs(char *);static void f_if(char *);static void f_obs(char *);static void f_of(char *);static void f_seek(char *);static void f_skip(char *);static void f_progress(char *);static const struct arg { const char *name; void (*f)(char *); u_int set, noset;} args[] = { /* the array needs to be sorted by the first column so bsearch() can be used to find commands quickly */ { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC }, { "cbs", f_cbs, C_CBS, C_CBS }, { "conv", f_conv, 0, 0 }, { "count", f_count, C_COUNT, C_COUNT }, { "files", f_files, C_FILES, C_FILES }, { "ibs", f_ibs, C_IBS, C_BS|C_IBS }, { "if", f_if, C_IF, C_IF }, { "obs", f_obs, C_OBS, C_BS|C_OBS }, { "of", f_of, C_OF, C_OF }, { "progress", f_progress, 0, 0 }, { "seek", f_seek, C_SEEK, C_SEEK }, { "skip", f_skip, C_SKIP, C_SKIP },};/* * args -- parse JCL syntax of dd. */voidjcl(char **argv){ struct arg *ap, tmp; char *oper, *arg; in.dbsz = out.dbsz = 512; while ((oper = *++argv) != NULL) { if ((arg = strchr(oper, '=')) == NULL) { fprintf(stderr, "unknown operand %s\n", oper); exit(1); /* NOTREACHED */ } *arg++ = '\0'; if (!*arg) { fprintf(stderr, "no value specified for %s\n", oper); exit(1); /* NOTREACHED */ } tmp.name = oper; if (!(ap = (struct arg *)bsearch(&tmp, args, sizeof(args)/sizeof(struct arg), sizeof(struct arg), c_arg))) { fprintf(stderr, "unknown operand %s\n", tmp.name); exit(1); /* NOTREACHED */ } if (ddflags & ap->noset) { fprintf(stderr, "%s: illegal argument combination or already set\n", tmp.name); exit(1); /* NOTREACHED */ } ddflags |= ap->set; ap->f(arg); } /* Final sanity checks. */ if (ddflags & C_BS) { /* * Bs is turned off by any conversion -- we assume the user * just wanted to set both the input and output block sizes * and didn't want the bs semantics, so we don't warn. */ if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE | C_UNBLOCK | C_OSYNC | C_ASCII | C_EBCDIC | C_SPARSE)) { ddflags &= ~C_BS; ddflags |= C_IBS|C_OBS; } /* Bs supersedes ibs and obs. */ if (ddflags & C_BS && ddflags & (C_IBS|C_OBS)) fprintf(stderr, "bs supersedes ibs and obs\n"); } /* * Ascii/ebcdic and cbs implies block/unblock. * Block/unblock requires cbs and vice-versa. */ if (ddflags & (C_BLOCK|C_UNBLOCK)) { if (!(ddflags & C_CBS)) { fprintf(stderr, "record operations require cbs\n"); exit(1); /* NOTREACHED */ } cfunc = ddflags & C_BLOCK ? block : unblock; } else if (ddflags & C_CBS) { if (ddflags & (C_ASCII|C_EBCDIC)) { if (ddflags & C_ASCII) { ddflags |= C_UNBLOCK; cfunc = unblock; } else { ddflags |= C_BLOCK; cfunc = block; } } else { fprintf(stderr, "cbs meaningless if not doing record operations\n"); exit(1); /* NOTREACHED */ } } else cfunc = def; /* Read, write and seek calls take off_t as arguments. * * The following check is not done because an off_t is a quad * for current NetBSD implementations. * * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz) * errx(1, "seek offsets cannot be larger than %d", INT_MAX); */}static intc_arg(const void *a, const void *b){ return (strcmp(((const struct arg *)a)->name, ((const struct arg *)b)->name));}static long long strsuftoll(const char* name, const char* arg, int def, unsigned int max){ long long result; if (sscanf(arg, "%lld", &result) == 0) result = def; return result;}static voidf_bs(char *arg){ in.dbsz = out.dbsz = strsuftoll("block size", arg, 1, UINT_MAX);}static voidf_cbs(char *arg){ cbsz = strsuftoll("conversion record size", arg, 1, UINT_MAX);}static voidf_count(char *arg){ cpy_cnt = strsuftoll("block count", arg, 0, LLONG_MAX); if (!cpy_cnt) terminate(0);}static voidf_files(char *arg){ files_cnt = (u_int)strsuftoll("file count", arg, 0, UINT_MAX); if (!files_cnt) terminate(0);}static voidf_ibs(char *arg){ if (!(ddflags & C_BS)) in.dbsz = strsuftoll("input block size", arg, 1, UINT_MAX);}static voidf_if(char *arg){ in.name = arg;}static voidf_obs(char *arg){ if (!(ddflags & C_BS)) out.dbsz = strsuftoll("output block size", arg, 1, UINT_MAX);}static voidf_of(char *arg){ out.name = arg;}static voidf_seek(char *arg){ out.offset = strsuftoll("seek blocks", arg, 0, LLONG_MAX);}static voidf_skip(char *arg){ in.offset = strsuftoll("skip blocks", arg, 0, LLONG_MAX);}static voidf_progress(char *arg){ if (*arg != '0') progress = 1;}#ifdef NO_CONV/* Build a small version (i.e. for a ramdisk root) */static voidf_conv(char *arg){ fprintf(stderr, "conv option disabled\n"); exit(1); /* NOTREACHED */}#else /* NO_CONV */static const struct conv { const char *name; u_int set, noset; const u_char *ctab;} clist[] = { { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, { "block", C_BLOCK, C_UNBLOCK, NULL }, { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, { "lcase", C_LCASE, C_UCASE, NULL }, { "noerror", C_NOERROR, 0, NULL }, { "notrunc", C_NOTRUNC, 0, NULL }, { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, { "osync", C_OSYNC, C_BS, NULL }, { "sparse", C_SPARSE, 0, NULL }, { "swab", C_SWAB, 0, NULL }, { "sync", C_SYNC, 0, NULL }, { "ucase", C_UCASE, C_LCASE, NULL }, { "unblock", C_UNBLOCK, C_BLOCK, NULL }, /* If you add items to this table, be sure to add the * conversions to the C_BS check in the jcl routine above. */};static voidf_conv(char *arg){ struct conv *cp, tmp; while (arg != NULL) { tmp.name = strsep(&arg, ","); if (!(cp = (struct conv *)bsearch(&tmp, clist, sizeof(clist)/sizeof(struct conv), sizeof(struct conv), c_conv))) { errx(EXIT_FAILURE, "unknown conversion %s", tmp.name); /* NOTREACHED */ } if (ddflags & cp->noset) { errx(EXIT_FAILURE, "%s: illegal conversion combination", tmp.name); /* NOTREACHED */ } ddflags |= cp->set; if (cp->ctab) ctab = cp->ctab; }}static intc_conv(const void *a, const void *b){ return (strcmp(((const struct conv *)a)->name, ((const struct conv *)b)->name));}#endif /* NO_CONV */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -