master xplshn/aruu / cmd / posix / dd.c
  1/* see LICENSE file for copyright and license details */
  2
  3#include <ctype.h>
  4#include <fcntl.h>
  5#include <inttypes.h>
  6#include <stdint.h>
  7#include <string.h>
  8#include <unistd.h>
  9
 10#include "util.h"
 11
 12static off_t ifull, ofull, ipart, opart;
 13
 14static void
 15usage(void)
 16{
 17  eprintf("usage: %s [operand...]\n", argv0);
 18}
 19
 20static size_t
 21parsesize(char *expr)
 22{
 23  char  *s = expr;
 24  size_t n = 1;
 25
 26  for (;;) {
 27    n *= strtoumax(s, &s, 10);
 28    switch (*s) {
 29      case 'k':
 30        n <<= 10;
 31        s++;
 32        break;
 33      case 'b':
 34        n <<= 9;
 35        s++;
 36        break;
 37    }
 38    if (*s != 'x' || !s[1])
 39      break;
 40    s++;
 41  }
 42  if (*s || n == 0)
 43    eprintf("invalid block size expression '%s'\n", expr);
 44
 45  return n;
 46}
 47
 48static void
 49bswap(unsigned char *buf, size_t len)
 50{
 51  int c;
 52
 53  for (len &= ~1; len > 0; buf += 2, len -= 2) {
 54    c      = buf[0];
 55    buf[0] = buf[1];
 56    buf[1] = c;
 57  }
 58}
 59
 60static void
 61lcase(unsigned char *buf, size_t len)
 62{
 63  for (; len > 0; buf++, len--)
 64    buf[0] = tolower(buf[0]);
 65}
 66
 67static void
 68ucase(unsigned char *buf, size_t len)
 69{
 70  for (; len > 0; buf++, len--)
 71    buf[0] = toupper(buf[0]);
 72}
 73
 74enum Status {
 75  STATUS_DEFAULT,
 76  STATUS_NOXFER,
 77  STATUS_NONE,
 78};
 79
 80static void
 81summary(enum Status status)
 82{
 83  if (status == STATUS_NONE)
 84    return;
 85  fprintf(stderr, "%" PRIdMAX "+%" PRIdMAX " records in\n", (intmax_t)ifull, (intmax_t)ipart);
 86  fprintf(stderr, "%" PRIdMAX "+%" PRIdMAX " records out\n", (intmax_t)ofull, (intmax_t)opart);
 87}
 88
 89// ?man dd: convert and copy a file
 90// ?man arguments: operand...
 91// ?man copy a file while performing conversions
 92int
 93main(int argc, char *argv[])
 94{
 95  enum {
 96    LCASE   = 1 << 0,
 97    UCASE   = 1 << 1,
 98    SWAB    = 1 << 2,
 99    NOERROR = 1 << 3,
100    NOTRUNC = 1 << 4,
101    SYNC    = 1 << 5,
102  } conv = 0;
103  enum Status    status = STATUS_DEFAULT;
104  char          *arg, *val, *end;
105  const char    *iname = "-", *oname = "-";
106  int            ifd = 0, ofd = 1, eof = 0;
107  size_t         len, bs = 0, ibs = 512, obs = 512, ipos = 0, opos = 0;
108  off_t          skip = 0, seek = 0, count = -1;
109  ssize_t        ret;
110  unsigned char *buf;
111
112  argv0 = argc ? (argc--, *argv++) : "dd";
113  for (; argc > 0; argc--, argv++) {
114    arg = *argv;
115    val = strchr(arg, '=');
116    if (!val)
117      usage();
118    *val++ = '\0';
119    if (strcmp(arg, "if") == 0) {
120      iname = val;
121    } else if (strcmp(arg, "of") == 0) {
122      oname = val;
123    } else if (strcmp(arg, "ibs") == 0) {
124      ibs = parsesize(val);
125    } else if (strcmp(arg, "obs") == 0) {
126      obs = parsesize(val);
127    } else if (strcmp(arg, "bs") == 0) {
128      bs = parsesize(val);
129    } else if (strcmp(arg, "skip") == 0) {
130      skip = estrtonum(val, 0, LLONG_MAX);
131    } else if (strcmp(arg, "seek") == 0) {
132      seek = estrtonum(val, 0, LLONG_MAX);
133    } else if (strcmp(arg, "count") == 0) {
134      count = estrtonum(val, 0, LLONG_MAX);
135    } else if (strcmp(arg, "status") == 0) {
136      if (strcmp(val, "none") == 0)
137        status = STATUS_NONE;
138      else if (strcmp(val, "noxfer") == 0)
139        status = STATUS_NOXFER;
140      else
141        eprintf("unknown status level '%s'\n", val);
142    } else if (strcmp(arg, "conv") == 0) {
143      do {
144        end = strchr(val, ',');
145        if (end)
146          *end++ = '\0';
147        if (strcmp(val, "lcase") == 0)
148          conv |= LCASE;
149        else if (strcmp(val, "ucase") == 0)
150          conv |= UCASE;
151        else if (strcmp(val, "swab") == 0)
152          conv |= SWAB;
153        else if (strcmp(val, "noerror") == 0)
154          conv |= NOERROR;
155        else if (strcmp(val, "notrunc") == 0)
156          conv |= NOTRUNC;
157        else if (strcmp(val, "sync") == 0)
158          conv |= SYNC;
159        else
160          eprintf("unknown conv flag '%s'\n", val);
161        val = end;
162      } while (val);
163    } else {
164      weprintf("unknown operand '%s'\n", arg);
165      usage();
166    }
167  }
168
169  if (bs)
170    ibs = obs = bs;
171  if (strcmp(iname, "-") != 0) {
172    ifd = open(iname, O_RDONLY);
173    if (ifd < 0)
174      eprintf("open %s:", iname);
175  }
176  if (strcmp(oname, "-") != 0) {
177    ofd = open(oname, O_WRONLY | O_CREAT | (conv & NOTRUNC || seek ? 0 : O_TRUNC), 0666);
178    if (ofd < 0)
179      eprintf("open %s:", oname);
180  }
181
182  len = MAX(ibs, obs) + ibs;
183  buf = emalloc(len);
184  if (skip && lseek(ifd, skip * ibs, SEEK_SET) < 0) {
185    while (skip--) {
186      ret = read(ifd, buf, ibs);
187      if (ret < 0)
188        eprintf("read:");
189      if (ret == 0) {
190        eof = 1;
191        break;
192      }
193    }
194  }
195  if (seek) {
196    if (!(conv & NOTRUNC) && ftruncate(ofd, seek * ibs) != 0)
197      eprintf("ftruncate:");
198    if (lseek(ofd, seek * ibs, SEEK_SET) < 0)
199      eprintf("lseek:");
200    /* XXX: handle non-seekable files */
201  }
202  while (!eof) {
203    while (ipos - opos < obs) {
204      if (ifull + ipart == count) {
205        eof = 1;
206        break;
207      }
208      ret = read(ifd, buf + ipos, ibs);
209      if (ret == 0) {
210        eof = 1;
211        break;
212      }
213      if (ret < 0) {
214        weprintf("read:");
215        if (!(conv & NOERROR))
216          return 1;
217        summary(status);
218        if (!(conv & SYNC))
219          continue;
220        ret = 0;
221      }
222      if ((size_t)ret < ibs) {
223        ipart++;
224        if (conv & SYNC) {
225          memset(buf + ipos + ret, 0, ibs - ret);
226          ret = ibs;
227        }
228      } else {
229        ifull++;
230      }
231      if (conv & SWAB)
232        bswap(buf + ipos, ret);
233      if (conv & LCASE)
234        lcase(buf + ipos, ret);
235      if (conv & UCASE)
236        ucase(buf + ipos, ret);
237      ipos += ret;
238      if (bs && !(conv & (SWAB | LCASE | UCASE)))
239        break;
240    }
241    if (ipos == opos)
242      break;
243    do {
244      ret = write(ofd, buf + opos, MIN(obs, ipos - opos));
245      if (ret < 0)
246        eprintf("write:");
247      if (ret == 0)
248        eprintf("write returned 0\n");
249      if ((size_t)ret < obs)
250        opart++;
251      else
252        ofull++;
253      opos += ret;
254    } while (ipos - opos >= (eof ? 1 : obs));
255    if (opos < ipos)
256      memmove(buf, buf + opos, ipos - opos);
257    ipos -= opos;
258    opos = 0;
259  }
260  summary(status);
261
262  return 0;
263}