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
 74static void
 75summary(void)
 76{
 77  fprintf(stderr, "%" PRIdMAX "+%" PRIdMAX " records in\n", (intmax_t)ifull, (intmax_t)ipart);
 78  fprintf(stderr, "%" PRIdMAX "+%" PRIdMAX " records out\n", (intmax_t)ofull, (intmax_t)opart);
 79}
 80
 81// ?man dd: convert and copy a file
 82// ?man arguments: operand...
 83// ?man copy a file while performing conversions
 84int
 85main(int argc, char *argv[])
 86{
 87  enum {
 88    LCASE   = 1 << 0,
 89    UCASE   = 1 << 1,
 90    SWAB    = 1 << 2,
 91    NOERROR = 1 << 3,
 92    NOTRUNC = 1 << 4,
 93    SYNC    = 1 << 5,
 94  } conv = 0;
 95  char          *arg, *val, *end;
 96  const char    *iname = "-", *oname = "-";
 97  int            ifd = 0, ofd = 1, eof = 0;
 98  size_t         len, bs = 0, ibs = 512, obs = 512, ipos = 0, opos = 0;
 99  off_t          skip = 0, seek = 0, count = -1;
100  ssize_t        ret;
101  unsigned char *buf;
102
103  argv0 = argc ? (argc--, *argv++) : "dd";
104  for (; argc > 0; argc--, argv++) {
105    arg = *argv;
106    val = strchr(arg, '=');
107    if (!val)
108      usage();
109    *val++ = '\0';
110    if (strcmp(arg, "if") == 0) {
111      iname = val;
112    } else if (strcmp(arg, "of") == 0) {
113      oname = val;
114    } else if (strcmp(arg, "ibs") == 0) {
115      ibs = parsesize(val);
116    } else if (strcmp(arg, "obs") == 0) {
117      obs = parsesize(val);
118    } else if (strcmp(arg, "bs") == 0) {
119      bs = parsesize(val);
120    } else if (strcmp(arg, "skip") == 0) {
121      skip = estrtonum(val, 0, LLONG_MAX);
122    } else if (strcmp(arg, "seek") == 0) {
123      seek = estrtonum(val, 0, LLONG_MAX);
124    } else if (strcmp(arg, "count") == 0) {
125      count = estrtonum(val, 0, LLONG_MAX);
126    } else if (strcmp(arg, "conv") == 0) {
127      do {
128        end = strchr(val, ',');
129        if (end)
130          *end++ = '\0';
131        if (strcmp(val, "lcase") == 0)
132          conv |= LCASE;
133        else if (strcmp(val, "ucase") == 0)
134          conv |= UCASE;
135        else if (strcmp(val, "swab") == 0)
136          conv |= SWAB;
137        else if (strcmp(val, "noerror") == 0)
138          conv |= NOERROR;
139        else if (strcmp(val, "notrunc") == 0)
140          conv |= NOTRUNC;
141        else if (strcmp(val, "sync") == 0)
142          conv |= SYNC;
143        else
144          eprintf("unknown conv flag '%s'\n", val);
145        val = end;
146      } while (val);
147    } else {
148      weprintf("unknown operand '%s'\n", arg);
149      usage();
150    }
151  }
152
153  if (bs)
154    ibs = obs = bs;
155  if (strcmp(iname, "-") != 0) {
156    ifd = open(iname, O_RDONLY);
157    if (ifd < 0)
158      eprintf("open %s:", iname);
159  }
160  if (strcmp(oname, "-") != 0) {
161    ofd = open(oname, O_WRONLY | O_CREAT | (conv & NOTRUNC || seek ? 0 : O_TRUNC), 0666);
162    if (ofd < 0)
163      eprintf("open %s:", oname);
164  }
165
166  len = MAX(ibs, obs) + ibs;
167  buf = emalloc(len);
168  if (skip && lseek(ifd, skip * ibs, SEEK_SET) < 0) {
169    while (skip--) {
170      ret = read(ifd, buf, ibs);
171      if (ret < 0)
172        eprintf("read:");
173      if (ret == 0) {
174        eof = 1;
175        break;
176      }
177    }
178  }
179  if (seek) {
180    if (!(conv & NOTRUNC) && ftruncate(ofd, seek * ibs) != 0)
181      eprintf("ftruncate:");
182    if (lseek(ofd, seek * ibs, SEEK_SET) < 0)
183      eprintf("lseek:");
184    /* XXX: handle non-seekable files */
185  }
186  while (!eof) {
187    while (ipos - opos < obs) {
188      if (ifull + ipart == count) {
189        eof = 1;
190        break;
191      }
192      ret = read(ifd, buf + ipos, ibs);
193      if (ret == 0) {
194        eof = 1;
195        break;
196      }
197      if (ret < 0) {
198        weprintf("read:");
199        if (!(conv & NOERROR))
200          return 1;
201        summary();
202        if (!(conv & SYNC))
203          continue;
204        ret = 0;
205      }
206      if ((size_t)ret < ibs) {
207        ipart++;
208        if (conv & SYNC) {
209          memset(buf + ipos + ret, 0, ibs - ret);
210          ret = ibs;
211        }
212      } else {
213        ifull++;
214      }
215      if (conv & SWAB)
216        bswap(buf + ipos, ret);
217      if (conv & LCASE)
218        lcase(buf + ipos, ret);
219      if (conv & UCASE)
220        ucase(buf + ipos, ret);
221      ipos += ret;
222      if (bs && !(conv & (SWAB | LCASE | UCASE)))
223        break;
224    }
225    if (ipos == opos)
226      break;
227    do {
228      ret = write(ofd, buf + opos, MIN(obs, ipos - opos));
229      if (ret < 0)
230        eprintf("write:");
231      if (ret == 0)
232        eprintf("write returned 0\n");
233      if ((size_t)ret < obs)
234        opart++;
235      else
236        ofull++;
237      opos += ret;
238    } while (ipos - opos >= (eof ? 1 : obs));
239    if (opos < ipos)
240      memmove(buf, buf + opos, ipos - opos);
241    ipos -= opos;
242    opos = 0;
243  }
244  summary();
245
246  return 0;
247}