/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1989-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* posix dd
*/
static const char usage1[] =
"[-1p0?\n@(#)$Id: dd (AT&T Research) 2011-04-21 $\n]"
"[+NAME?dd - convert and copy a file]"
"[+DESCRIPTION?\bdd\b copies an input file to an output file with optional"
" conversions. The standard input and output are used by default."
" Input and output block sizes can be specified to take advantage of"
" physical io limitations. Options are of the form \aname=value\a"
" and may be specified with 0, 1, or 2 leading `-' characters.]"
"[+?A \anumber\a argument may be a scaled number with optional trailing"
" multipliers of the form `* \anumber\a', `x \anumber\a' or `X \anumber\a'."
" Scale suffixes may be one of:]{"
" [+b|B?512]"
" [+k|K?1000]"
" [+ki|Ki?1024]"
" [+m|M?1000000]"
" [+mi|Mi?1048576]"
" [+g|G?1000000000]"
" [+gi|Gi?1073741824]"
" [+t|T?1000000000000]"
" [+ti|Ti?1099511627776]"
" [+p|P?1000000000000000]"
" [+pi|Pi?1125899906842624]"
" [+e|E?1000000000000000000]"
" [+ei|Ei?1152921504606846976]"
"}"
;
static const char usage2[] =
"[+SEE ALSO?\bcp\b(1), \biconv\b(1), \bpax\b(1), \btr\b(1), \bseek\b(2)]"
;
#include <ast.h>
#include <ctype.h>
#include <ccode.h>
#include <error.h>
#include <iconv.h>
#include <ls.h>
#include <sig.h>
#include <swap.h>
#define CODE 0
typedef struct
{
const char* name;
const char* help;
} Desc_t;
typedef struct
{
char* string;
} Value_t;
typedef struct
{
const char* name;
long type;
const char* help;
} Operand_t;
typedef struct
{
int special;
} Io_t;
typedef struct
{
char* buffer;
int pad;
} State_t;
{
{
"bs",
"Input and output block size.",
},
{
"cbs",
"Conversion buffer size (logical record length)."
},
{
"conv",
CONV,
"Convert input.",
},
{
"count",
"Copy only \anumber\a input blocks.",
},
{
"from",
CODE,
"Convert from \acodeset\a to the \bto\b=\acodeset\a"
" or the local default. \acodeset\a names are matched"
" by left-anchored case-insensitive \bksh\b(1) patterns.",
},
{
"ibs",
"Input block size.",
0, BS,
},
{
"if",
"The input file name; standard input is the default.",
},
{
"iseek",
"Seek \anumber\a blocks from the beginning of the input"
" file before copying.",
},
{
"obs",
"Output block size.",
0, BS,
},
{
"of",
"The output file name; standard output is the default.",
},
{
"oseek|seek",
"Seek \anumber\a blocks from the beginning of the output"
" file before copying.",
},
{
"silent",
FLAG,
"\bsilent\b does not print the total number of io blocks"
" on exit.",
},
{
"skip",
"Skip \anumber\a blocks before reading. Seek is used if"
" possible, otherwise the blocks are read and discarded.",
},
{
"swap",
"Swap bytes acording to the inclusive or of: 1-byte,"
" 2-short, 4-long, 8-quad, etc.",
},
{
"to",
CODE,
"Convert to \acodeset\a from the \bfrom\b=\acodeset\a"
" or the local default. See \bfrom\b for \acodeset\a names.",
},
{
"a2e",
A2E,
"ascii to ebcdic",
},
{
"a2i",
A2I,
"ascii to ibm",
},
{
"a2n",
A2N,
"ascii to native",
},
{
"a2o",
A2O,
"ascii to open edition ebcdic",
},
{
"ascii",
E2A,
"ebcdic to ascii",
},
{
"block",
"newline-terminated ascii to fixed record length",
},
{
"e2a",
E2A,
"ebcdic to ascii",
},
{
"ebcdic",
A2E,
"equivalent to \ba2e\b",
},
{
"i2a",
I2A,
"ibm to ascii",
},
{
"ibm",
A2I,
"ascii to ibm ebcdic",
},
{
"ignerror",
"continue processing after errors",
},
{
"ispecial",
"currently ignored",
},
{
"lcase",
"to lower case",
},
{
"n2a",
N2A,
"native to ascii",
},
{
"noerror",
"stop processing only after 5 consecutive errors",
},
{
"notrunc",
"do not truncate pre-existing output files",
},
{
"o2a",
O2A,
"open edition ibm to ascii",
},
{
"ospecial",
"currently ignored"
},
{
"swab",
SWAP,
"swap byte pairs",
},
{
"sync",
SYNC,
"Pad each input block to \bibs\b. Pad with spaces if"
" \bconv=block\b or \bconv=unblock\b, otherwise pad"
" with nulls.",
},
{
"ucase",
"to upper case",
},
{
"unblock",
"fixed-length records to newline-terminated records",
},
};
{
"codeset", "",
"conversion", "",
0, 0,
"number", 0,
"file", 0,
};
static void
{
else
{
sfprintf(sfstderr, "%I*u+%I*u records in\n", sizeof(Sfulong_t), (Sfulong_t)state.in.complete, sizeof(Sfulong_t), (Sfulong_t)(state.in.partial + (state.in.remains > 0)));
sfprintf(sfstderr, "%I*u+%I*u records out\n", sizeof(Sfulong_t), (Sfulong_t)state.out.complete, sizeof(Sfulong_t), (Sfulong_t)(state.out.partial + (state.out.remains > 0)));
sfprintf(sfstderr, "%I*u truncated record%s\n", sizeof(Sfulong_t), (Sfulong_t)state.in.truncated, state.in.truncated == 1 ? "" : "s");
}
}
static void
{
}
static ssize_t
{
register ssize_t r;
register size_t x;
{
{
{
}
}
}
return r;
}
int
{
register char* s;
register char* v;
register char* b;
register int f;
char* usage;
char* e;
int i;
char* cb;
Sflong_t c;
Sflong_t d;
Sflong_t m;
Sflong_t n;
Sflong_t r;
{
i = ']';
{
{
else
{
i = '}';
}
{
{
}
i = '}';
}
}
else
}
}
{
if (f > 0)
{
if (f == '?')
if (f = ':')
continue;
}
{
case CODE:
case STRING:
break;
case CONV:
do
{
if (e = strchr(v, ','))
*e = 0;
vp = (Operand_t*)strsearch(&state.conv_begin, &state.conv_end - &state.conv_begin + 1, sizeof(Operand_t), stracmp, v, NiL);
if (e)
*e++ = ',';
if (!vp)
} while (v = e);
break;
case FLAG:
break;
case NUMBER:
c = 0;
for (;;)
{
if (n < 0)
if (!c)
{
c = 1;
}
else
for (v = e; isspace(*v); v++);
if (*v != 'x' && *v != 'X' && *v != '*')
break;
while (isspace(*++v));
if (!*v)
{
v = e;
break;
}
}
if (*v)
break;
}
}
if (error_info.errors)
{
case 0:
break;
case A2E:
break;
case A2I:
break;
case A2N:
break;
case A2O:
break;
case E2A:
break;
case I2A:
break;
case N2A:
break;
case O2A:
break;
default:
error(3, "only one of %s={%s,%s,%s} may be specified", state.conv.name, state.ascii.value.string, state.ebcdic.value.string, state.ibm.value.string);
}
error(3, "cannot convert from %s to %s", *state.from.value.string ? state.from.value.string : ccmapname(CC_NATIVE), *state.to.value.string ? state.to.value.string : ccmapname(CC_NATIVE));
error(3, "only one of %s=%s and %s=%s may be specified", state.conv.name, state.block.value.string, state.conv.name, state.unblock.value.string);
{
error(1, "%s=%s ignored for %s=%s", state.conv.name, state.sync.value.string, state.conv.name, state.unblock.value.string);
}
{
}
error(3, "only one of %s=%s and %s=%s may be specified", state.conv.name, state.lcase.value.string, state.conv.name, state.ucase.value.string);
{
error(3, "%s must be specified for %s=%s", state.cbs.name, state.conv.name, (state.conv.value.number & BLOCK) ? state.block.value.string : state.unblock.value.string);
{
{
}
}
}
{
}
{
}
{
#if O_NONBLOCK
#endif
}
{
}
else if (!(state.out.fp = sfopen(NiL, s, (state.conv.value.number & NOTRUNC) ? "a+b" : state.oseek.value.number ? "w+b" : "wb")))
if ((state.conv.value.number & ISPECIAL) || fstat(sffileno(state.in.fp), &st) || !S_ISREG(st.st_mode))
if ((state.conv.value.number & OSPECIAL) || fstat(sffileno(state.out.fp), &st) || !S_ISREG(st.st_mode))
{
{
do
{
if (!sfreserve(state.in.fp, state.in.special && state.ibs.value.number < n ? state.ibs.value.number : n, 0))
{
break;
}
if (n > 0)
}
}
{
{
do
{
if (!sfreserve(state.out.fp, state.out.special && state.obs.value.number < n ? state.obs.value.number : n, 0))
{
break;
}
while (n > 0 && (c = sfwrite(state.out.fp, state.buffer, state.out.special && state.obs.value.number < n ? state.obs.value.number : n)) > 0)
n -= c;
if (c < 0)
}
}
{
else
}
else
{
{
if (n > c)
{
}
else
{
n = c - n;
}
}
}
else
{
c = BS;
f &= ~NOERROR;
r = -1;
partial = 0;
{
if (!b)
{
if (!m)
break;
}
{
s = b;
/*
* m is the amount actually read
* c is the specified (or default) block size
*/
if (m >= c)
{
/*
* the read gave us at least a complete block
*/
/*
* set n (the # of bytes to write) to the block size
*/
n = c;
/*
* if we've hit the block count, check to see if the
* previous write was in the middle of an output block, and
* adjust the # of bytes to write accordingly; don't bother
* adjusting the remains, since we're done
*/
}
else
{
/*
* read gave us less than a complete block
*/
n = m;
/*
* see if writing the amount read in will cross an (output) block boundry
*/
{
/*
* see above comment on block count, but also adjust
* partial block byte count (remains)
*/
{
}
else
{
partial++;
}
}
else
if (f & SYNC)
{
n = c;
}
}
if (f & SWAP)
{
cb = s;
cc = n;
d = 0;
m -= n - d;
{
m -= n;
n = 0;
}
}
{
case LCASE:
for (v = (e = s) + n; s < v; s++)
if (isupper(*s))
*s = tolower(*s);
s = e;
break;
case UCASE:
for (v = (e = s) + n; s < v; s++)
if (islower(*s))
*s = toupper(*s);
s = e;
break;
}
if (f & UNBLOCK)
{
for (v = s + n; v > s && *(v - 1) == ' '; v--);
}
if ((m -= n) <= 0)
break;
b += n;
}
}
{
}
}
}