diff options
author | Jim Meyering <jim@meyering.net> | 1992-10-31 20:42:48 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1992-10-31 20:42:48 +0000 |
commit | 14fd34b78818660e05806b6eda178e3f846c5c21 (patch) | |
tree | b40038aa2684b5cd95ae2d5fcef564bcf8e05cc3 /src/dd.c | |
download | coreutils-14fd34b78818660e05806b6eda178e3f846c5c21.tar.xz |
Initial revision
Diffstat (limited to 'src/dd.c')
-rw-r--r-- | src/dd.c | 1020 |
1 files changed, 1020 insertions, 0 deletions
diff --git a/src/dd.c b/src/dd.c new file mode 100644 index 000000000..dd5606815 --- /dev/null +++ b/src/dd.c @@ -0,0 +1,1020 @@ +/* dd -- convert a file while copying it. + Copyright (C) 1985, 1990, 1991 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Written by Paul Rubin, David MacKenzie, and Stuart Kemp. */ + +/* Options: + + Numbers can be followed by a multiplier: + b=512, k=1024, w=2, xm=number m + + if=FILE Read from FILE instead of stdin. + of=FILE Write to FILE instead of stdout; don't + truncate FILE. + ibs=BYTES Read BYTES bytes at a time. + obs=BYTES Write BYTES bytes at a time. + bs=BYTES Override ibs and obs. + cbs=BYTES Convert BYTES bytes at a time. + skip=BLOCKS Skip BLOCKS ibs-sized blocks at + start of input. + seek=BLOCKS Skip BLOCKS obs-sized blocks at + start of output. + count=BLOCKS Copy only BLOCKS input blocks. + conv=CONVERSION[,CONVERSION...] + + Conversions: + ascii Convert EBCDIC to ASCII. + ebcdic Convert ASCII to EBCDIC. + ibm Convert ASCII to alternate EBCDIC. + block Pad newline-terminated records to size of + cbs, replacing newline with trailing spaces. + unblock Replace trailing spaces in cbs-sized block + with newline. + lcase Change uppercase characters to lowercase. + ucase Change lowercase characters to uppercase. + swab Swap every pair of input bytes. + Unlike the Unix dd, this works when an odd + number of bytes are read. + noerror Continue after read errors. + sync Pad every input block to size of ibs with + trailing NULs. */ + +#include <stdio.h> +#include <ctype.h> +#ifdef STDC_HEADERS +#define ISLOWER islower +#define ISUPPER isupper +#else +#define ISLOWER(c) (isascii ((c)) && islower ((c))) +#define ISUPPER(c) (isascii ((c)) && isupper ((c))) +#endif +#include <sys/types.h> +#include <signal.h> +#include "system.h" + +#define equal(p, q) (strcmp ((p),(q)) == 0) +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define output_char(c) \ + do { \ + obuf[oc++] = (c); if (oc >= output_blocksize) write_output (); \ + } while (0) + +/* Default input and output blocksize. */ +#define DEFAULT_BLOCKSIZE 512 + +/* Conversions bit masks. */ +#define C_ASCII 01 +#define C_EBCDIC 02 +#define C_IBM 04 +#define C_BLOCK 010 +#define C_UNBLOCK 020 +#define C_LCASE 040 +#define C_UCASE 0100 +#define C_SWAB 0200 +#define C_NOERROR 0400 +#define C_NOTRUNC 01000 +#define C_SYNC 02000 +/* Use separate input and output buffers, and combine partial input blocks. */ +#define C_TWOBUFS 04000 + +char *xmalloc (); +RETSIGTYPE interrupt_handler (); +int bit_count (); +int parse_integer (); +void apply_translations (); +void copy (); +void copy_simple (); +void copy_with_block (); +void copy_with_unblock (); +void error (); +void parse_conversion (); +void print_stats (); +void translate_charset (); +void quit (); +void scanargs (); +void skip (); +void usage (); +void write_output (); + +/* The name this program was run with. */ +char *program_name; + +/* The name of the input file, or NULL for the standard input. */ +char *input_file = NULL; + +/* The input file descriptor. */ +int input_fd = 0; + +/* The name of the output file, or NULL for the standard output. */ +char *output_file = NULL; + +/* The output file descriptor. */ +int output_fd = 1; + +/* The number of bytes in which atomic reads are done. */ +long input_blocksize = -1; + +/* The number of bytes in which atomic writes are done. */ +long output_blocksize = -1; + +/* Conversion buffer size, in bytes. 0 prevents conversions. */ +long conversion_blocksize = 0; + +/* Skip this many records of `input_blocksize' bytes before input. */ +long skip_records = 0; + +/* Skip this many records of `output_blocksize' bytes before output. */ +long seek_record = 0; + +/* Copy only this many records. <0 means no limit. */ +int max_records = -1; + +/* Bit vector of conversions to apply. */ +int conversions_mask = 0; + +/* If nonzero, filter characters through the translation table. */ +int translation_needed = 0; + +/* Number of partial blocks written. */ +unsigned w_partial = 0; + +/* Number of full blocks written. */ +unsigned w_full = 0; + +/* Number of partial blocks read. */ +unsigned r_partial = 0; + +/* Number of full blocks read. */ +unsigned r_full = 0; + +/* Records truncated by conv=block. */ +unsigned r_truncate = 0; + +/* Output representation of newline and space characters. + They change if we're converting to EBCDIC. */ +unsigned char newline_character = '\n'; +unsigned char space_character = ' '; + +struct conversion +{ + char *convname; + int conversion; +}; + +struct conversion conversions[] = +{ + "ascii", C_ASCII | C_TWOBUFS, /* EBCDIC to ASCII. */ + "ebcdic", C_EBCDIC | C_TWOBUFS, /* ASCII to EBCDIC. */ + "ibm", C_IBM | C_TWOBUFS, /* Slightly different ASCII to EBCDIC. */ + "block", C_BLOCK | C_TWOBUFS, /* Variable to fixed length records. */ + "unblock", C_UNBLOCK | C_TWOBUFS, /* Fixed to variable length records. */ + "lcase", C_LCASE | C_TWOBUFS, /* Translate upper to lower case. */ + "ucase", C_UCASE | C_TWOBUFS, /* Translate lower to upper case. */ + "swab", C_SWAB | C_TWOBUFS, /* Swap bytes of input. */ + "noerror", C_NOERROR, /* Ignore i/o errors. */ + "notrunc", C_NOTRUNC, /* Do not truncate output file. */ + "sync", C_SYNC, /* Pad input records to ibs with NULs. */ + NULL, 0 +}; + +/* Translation table formed by applying successive transformations. */ +unsigned char trans_table[256]; + +unsigned char ascii_to_ebcdic[] = +{ + 0, 01, 02, 03, 067, 055, 056, 057, + 026, 05, 045, 013, 014, 015, 016, 017, + 020, 021, 022, 023, 074, 075, 062, 046, + 030, 031, 077, 047, 034, 035, 036, 037, + 0100, 0117, 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, 0112, 0340, 0132, 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, 0152, 0320, 0241, 07, + 040, 041, 042, 043, 044, 025, 06, 027, + 050, 051, 052, 053, 054, 011, 012, 033, + 060, 061, 032, 063, 064, 065, 066, 010, + 070, 071, 072, 073, 04, 024, 076, 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 +}; + +unsigned char ascii_to_ibm[] = +{ + 0, 01, 02, 03, 067, 055, 056, 057, + 026, 05, 045, 013, 014, 015, 016, 017, + 020, 021, 022, 023, 074, 075, 062, 046, + 030, 031, 077, 047, 034, 035, 036, 037, + 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, 07, + 040, 041, 042, 043, 044, 025, 06, 027, + 050, 051, 052, 053, 054, 011, 012, 033, + 060, 061, 032, 063, 064, 065, 066, 010, + 070, 071, 072, 073, 04, 024, 076, 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 +}; + +unsigned char ebcdic_to_ascii[] = +{ + 0, 01, 02, 03, 0234, 011, 0206, 0177, + 0227, 0215, 0216, 013, 014, 015, 016, 017, + 020, 021, 022, 023, 0235, 0205, 010, 0207, + 030, 031, 0222, 0217, 034, 035, 036, 037, + 0200, 0201, 0202, 0203, 0204, 012, 027, 033, + 0210, 0211, 0212, 0213, 0214, 05, 06, 07, + 0220, 0221, 026, 0223, 0224, 0225, 0226, 04, + 0230, 0231, 0232, 0233, 024, 025, 0236, 032, + 040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, + 0247, 0250, 0133, 056, 074, 050, 053, 041, + 046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, + 0260, 0261, 0135, 044, 052, 051, 073, 0136, + 055, 057, 0262, 0263, 0264, 0265, 0266, 0267, + 0270, 0271, 0174, 054, 045, 0137, 076, 077, + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, + 0302, 0140, 072, 043, 0100, 047, 075, 042, + 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, 0313, 0314, 0315, 0316, 0317, 0320, + 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, + 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, + 0340, 0341, 0342, 0343, 0344, 0345, 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, + 060, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 0372, 0373, 0374, 0375, 0376, 0377 +}; + +void +main (argc, argv) + int argc; + char **argv; +{ +#ifdef _POSIX_VERSION + struct sigaction sigact; +#endif /* _POSIX_VERSION */ + int i; + + program_name = argv[0]; + + /* Initialize translation table to identity translation. */ + for (i = 0; i < 256; i++) + trans_table[i] = i; + + /* Decode arguments. */ + scanargs (argc, argv); + apply_translations (); + + if (input_file != NULL) + { + input_fd = open (input_file, O_RDONLY); + if (input_fd < 0) + error (1, errno, "%s", input_file); + } + else + input_file = "standard input"; + + if (input_fd == output_fd) + error (1, 0, "standard %s is closed", input_fd == 0 ? "input" : "output"); + + if (output_file != NULL) + { + int omode = O_RDWR | O_CREAT; + + if (seek_record == 0 && !(conversions_mask & C_NOTRUNC)) + omode |= O_TRUNC; + output_fd = open (output_file, omode, 0666); + if (output_fd < 0) + error (1, errno, "%s", output_file); +#ifdef HAVE_FTRUNCATE + if (seek_record > 0 && !(conversions_mask & C_NOTRUNC)) + { + if (ftruncate (output_fd, seek_record * output_blocksize) < 0) + error (0, errno, "%s", output_file); + } +#endif + } + else + output_file = "standard output"; + +#ifdef _POSIX_VERSION + sigaction (SIGINT, NULL, &sigact); + if (sigact.sa_handler != SIG_IGN) + { + sigact.sa_handler = interrupt_handler; + sigemptyset (&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction (SIGINT, &sigact, NULL); + } +#else /* !_POSIX_VERSION */ + if (signal (SIGINT, SIG_IGN) != SIG_IGN) + signal (SIGINT, interrupt_handler); +#endif /* !_POSIX_VERSION */ + copy (); +} + +/* Throw away RECORDS blocks of BLOCKSIZE bytes on file descriptor FDESC, + which is open with read permission for FILE. Store up to BLOCKSIZE + bytes of the data at a time in BUF, if necessary. */ + +void +skip (fdesc, file, records, blocksize, buf) + int fdesc; + char *file; + long records; + long blocksize; + char *buf; +{ + struct stat stats; + + /* Use fstat instead of checking for errno == ESPIPE because + lseek doesn't work on some special files but doesn't return an + error, either. */ + if (fstat (fdesc, &stats)) + { + error (0, errno, "%s", file); + quit (1); + } + + if (S_ISREG (stats.st_mode)) + { + if (lseek (fdesc, records * blocksize, SEEK_SET) < 0) + { + error (0, errno, "%s", file); + quit (1); + } + } + else + { + while (records-- > 0) + { + if (read (fdesc, buf, blocksize) < 0) + { + error (0, errno, "%s", file); + quit (1); + } + /* FIXME If fewer bytes were read than requested, meaning that + EOF was reached, POSIX wants the output file padded with NULs. */ + } + } +} + +/* Apply the character-set translations specified by the user + to the NREAD bytes in BUF. */ + +void +translate_buffer (buf, nread) + unsigned char *buf; + int nread; +{ + register unsigned char *cp; + register int i; + + for (i = nread, cp = buf; i; i--, cp++) + *cp = trans_table[*cp]; +} + +/* If nonnzero, the last char from the previous call to `swab_buffer' + is saved in `saved_char'. */ +int char_is_saved = 0; + +/* Odd char from previous call. */ +unsigned char saved_char; + +/* Swap NREAD bytes in BUF, plus possibly an initial char from the + previous call. If NREAD is odd, save the last char for the + next call. Return the new start of the BUF buffer. */ + +unsigned char * +swab_buffer (buf, nread) + unsigned char *buf; + int *nread; +{ + unsigned char *bufstart = buf; + register unsigned char *cp; + register int i; + + /* Is a char left from last time? */ + if (char_is_saved) + { + *--bufstart = saved_char; + *nread++; + char_is_saved = 0; + } + + if (*nread & 1) + { + /* An odd number of chars are in the buffer. */ + saved_char = bufstart[--*nread]; + char_is_saved = 1; + } + + /* Do the byte-swapping by moving every second character two + positions toward the end, working from the end of the buffer + toward the beginning. This way we only move half of the data. */ + + cp = bufstart + *nread; /* Start one char past the last. */ + for (i = *nread / 2; i; i--, cp -= 2) + *cp = *(cp - 2); + + return ++bufstart; +} + +/* Output buffer. */ +unsigned char *obuf; + +/* Current index into `obuf'. */ +int oc = 0; + +/* Index into current line, for `conv=block' and `conv=unblock'. */ +int col = 0; + +/* The main loop. */ + +void +copy () +{ + unsigned char *ibuf, *bufstart; /* Input buffer. */ + int nread; /* Bytes read in the current block. */ + int exit_status = 0; + + /* Leave an extra byte at the beginning and end of `ibuf' for conv=swab. */ + ibuf = (unsigned char *) xmalloc (input_blocksize + 2) + 1; + if (conversions_mask & C_TWOBUFS) + obuf = (unsigned char *) xmalloc (output_blocksize); + else + obuf = ibuf; + + if (skip_records > 0) + skip (input_fd, input_file, skip_records, input_blocksize, ibuf); + + if (seek_record > 0) + skip (output_fd, output_file, seek_record, output_blocksize, obuf); + + if (max_records == 0) + quit (exit_status); + + while (1) + { + if (max_records >= 0 && r_partial + r_full >= max_records) + break; + + /* Zero the buffer before reading, so that if we get a read error, + whatever data we are able to read is followed by zeros. + This minimizes data loss. */ + if ((conversions_mask & C_SYNC) && (conversions_mask & C_NOERROR)) + bzero (ibuf, input_blocksize); + + nread = read (input_fd, ibuf, input_blocksize); + + if (nread == 0) + break; /* EOF. */ + + if (nread < 0) + { + error (0, errno, "%s", input_file); + if (conversions_mask & C_NOERROR) + { + print_stats (); + /* Seek past the bad block if possible. */ + lseek (input_fd, input_blocksize, SEEK_CUR); + if (conversions_mask & C_SYNC) + /* Replace the missing input with null bytes and + proceed normally. */ + nread = 0; + else + continue; + } + else + { + /* Write any partial block. */ + exit_status = 2; + break; + } + } + + if (nread < input_blocksize) + { + r_partial++; + if (conversions_mask & C_SYNC) + { + if (!(conversions_mask & C_NOERROR)) + /* If C_NOERROR, we zeroed the block before reading. */ + bzero (ibuf + nread, input_blocksize - nread); + nread = input_blocksize; + } + } + else + r_full++; + + if (ibuf == obuf) /* If not C_TWOBUFS. */ + { + int nwritten = write (output_fd, obuf, nread); + if (nwritten != nread) + { + error (0, errno, "%s", output_file); + if (nwritten > 0) + w_partial++; + quit (1); + } + else if (nread == input_blocksize) + w_full++; + else + w_partial++; + continue; + } + + /* Do any translations on the whole buffer at once. */ + + if (translation_needed) + translate_buffer (ibuf, nread); + + if (conversions_mask & C_SWAB) + bufstart = swab_buffer (ibuf, &nread); + else + bufstart = ibuf; + + if (conversions_mask & C_BLOCK) + copy_with_block (bufstart, nread); + else if (conversions_mask & C_UNBLOCK) + copy_with_unblock (bufstart, nread); + else + copy_simple (bufstart, nread); + } + + /* If we have a char left as a result of conv=swab, output it. */ + if (char_is_saved) + { + if (conversions_mask & C_BLOCK) + copy_with_block (&saved_char, 1); + else if (conversions_mask & C_UNBLOCK) + copy_with_unblock (&saved_char, 1); + else + output_char (saved_char); + } + + if ((conversions_mask & C_BLOCK) && col > 0) + { + /* If the final input line didn't end with a '\n', pad + the output block to `conversion_blocksize' chars. */ + int pending_spaces = max (0, conversion_blocksize - col); + while (pending_spaces--) + output_char (space_character); + } + + if ((conversions_mask & C_UNBLOCK) && col == conversion_blocksize) + /* Add a final '\n' if there are exactly `conversion_blocksize' + characters in the final record. */ + output_char (newline_character); + + /* Write out the last block. */ + if (oc > 0) + { + int nwritten = write (output_fd, obuf, oc); + if (nwritten > 0) + w_partial++; + if (nwritten != oc) + { + error (0, errno, "%s", output_file); + quit (1); + } + } + + free (ibuf - 1); + if (obuf != ibuf) + free (obuf); + + quit (exit_status); +} + +/* Copy NREAD bytes of BUF, with no conversions. */ + +void +copy_simple (buf, nread) + unsigned char *buf; + int nread; +{ + int nfree; /* Number of unused bytes in `obuf'. */ + unsigned char *start = buf; /* First uncopied char in BUF. */ + + do + { + nfree = output_blocksize - oc; + if (nfree > nread) + nfree = nread; + + bcopy (start, obuf + oc, nfree); + + nread -= nfree; /* Update the number of bytes left to copy. */ + start += nfree; + oc += nfree; + if (oc >= output_blocksize) + write_output (); + } + while (nread > 0); +} + +/* Copy NREAD bytes of BUF, doing conv=block + (pad newline-terminated records to `conversion_blocksize', + replacing the newline with trailing spaces). */ + +void +copy_with_block (buf, nread) + unsigned char *buf; + int nread; +{ + register int i; + + for (i = nread; i; i--, buf++) + { + if (*buf == newline_character) + { + int pending_spaces = max (0, conversion_blocksize - col); + while (pending_spaces--) + output_char (space_character); + col = 0; + } + else + { + if (col == conversion_blocksize) + r_truncate++; + else if (col < conversion_blocksize) + output_char (*buf); + col++; + } + } +} + +/* Copy NREAD bytes of BUF, doing conv=unblock + (replace trailing spaces in `conversion_blocksize'-sized records + with a newline). */ + +void +copy_with_unblock (buf, nread) + unsigned char *buf; + int nread; +{ + register int i; + register unsigned char c; + static int pending_spaces = 0; + + for (i = 0; i < nread; i++) + { + c = buf[i]; + + if (col++ >= conversion_blocksize) + { + col = pending_spaces = 0; /* Wipe out any pending spaces. */ + i--; /* Push the char back; get it later. */ + output_char (newline_character); + } + else if (c == space_character) + pending_spaces++; + else + { + if (pending_spaces) + { + /* `c' is the character after a run of spaces that were not + at the end of the conversion buffer. Output them. */ + while (pending_spaces--) + output_char (space_character); + } + output_char (c); + } + } +} + +/* Write, then empty, the output buffer `obuf'. */ + +void +write_output () +{ + int nwritten = write (output_fd, obuf, output_blocksize); + if (nwritten != output_blocksize) + { + error (0, errno, "%s", output_file); + if (nwritten > 0) + w_partial++; + quit (1); + } + else + w_full++; + oc = 0; +} + +void +scanargs (argc, argv) + int argc; + char **argv; +{ + int i, n; + + for (i = 1; i < argc; i++) + { + char *name, *val; + + name = argv[i]; + val = index (name, '='); + if (val == NULL) + usage ("unrecognized option `%s'", name); + *val++ = '\0'; + + if (equal (name, "if")) + input_file = val; + else if (equal (name, "of")) + output_file = val; + else if (equal (name, "conv")) + parse_conversion (val); + else + { + n = parse_integer (val); + if (n < 0) + error (1, 0, "invalid number `%s'", val); + + if (equal (name, "ibs")) + { + input_blocksize = n; + conversions_mask |= C_TWOBUFS; + } + else if (equal (name, "obs")) + { + output_blocksize = n; + conversions_mask |= C_TWOBUFS; + } + else if (equal (name, "bs")) + output_blocksize = input_blocksize = n; + else if (equal (name, "cbs")) + conversion_blocksize = n; + else if (equal (name, "skip")) + skip_records = n; + else if (equal (name, "seek")) + seek_record = n; + else if (equal (name, "count")) + max_records = n; + else + usage ("unrecognized option `%s=%s'", name, val); + } + } + + /* If bs= was given, both `input_blocksize' and `output_blocksize' will + have been set to non-negative values. If either has not been set, + bs= was not given, so make sure two buffers are used. */ + if (input_blocksize == -1 || output_blocksize == -1) + conversions_mask |= C_TWOBUFS; + if (input_blocksize == -1) + input_blocksize = DEFAULT_BLOCKSIZE; + if (output_blocksize == -1) + output_blocksize = DEFAULT_BLOCKSIZE; + if (conversion_blocksize == 0) + conversions_mask &= ~(C_BLOCK | C_UNBLOCK); +} + +/* Return the value of STR, interpreted as a non-negative decimal integer, + optionally multiplied by various values. + Return -1 if STR does not represent a number in this format. */ + +int +parse_integer (str) + char *str; +{ + register int n = 0; + register int temp; + register char *p = str; + + while (isdigit (*p)) + { + n = n * 10 + *p - '0'; + p++; + } +loop: + switch (*p++) + { + case '\0': + return n; + case 'b': + n *= 512; + goto loop; + case 'k': + n *= 1024; + goto loop; + case 'w': + n *= 2; + goto loop; + case 'x': + temp = parse_integer (p); + if (temp == -1) + return -1; + n *= temp; + break; + default: + return -1; + } + return n; +} + +/* Interpret one "conv=..." option. */ + +void +parse_conversion (str) + char *str; +{ + char *new; + int i; + + do + { + new = index (str, ','); + if (new != NULL) + *new++ = '\0'; + for (i = 0; conversions[i].convname != NULL; i++) + if (equal (conversions[i].convname, str)) + { + conversions_mask |= conversions[i].conversion; + break; + } + if (conversions[i].convname == NULL) + { + usage ("%s: invalid conversion", str); + exit (1); + } + str = new; + } while (new != NULL); +} + +/* Fix up translation table. */ + +void +apply_translations () +{ + int i; + +#define MX(a) (bit_count (conversions_mask & (a))) + if ((MX (C_ASCII | C_EBCDIC | C_IBM) > 1) + || (MX (C_BLOCK | C_UNBLOCK) > 1) + || (MX (C_LCASE | C_UCASE) > 1) + || (MX (C_UNBLOCK | C_SYNC) > 1)) + { + error (1, 0, "\ +only one conv in {ascii,ebcdic,ibm}, {lcase,ucase}, {block,unblock}, {unblock,sync}"); + } +#undef MX + + if (conversions_mask & C_ASCII) + translate_charset (ebcdic_to_ascii); + + if (conversions_mask & C_UCASE) + { + for (i = 0; i < 256; i++) + if (ISLOWER (trans_table[i])) + trans_table[i] = toupper (trans_table[i]); + translation_needed = 1; + } + else if (conversions_mask & C_LCASE) + { + for (i = 0; i < 256; i++) + if (ISUPPER (trans_table[i])) + trans_table[i] = tolower (trans_table[i]); + translation_needed = 1; + } + + if (conversions_mask & C_EBCDIC) + { + translate_charset (ascii_to_ebcdic); + newline_character = ascii_to_ebcdic['\n']; + space_character = ascii_to_ebcdic[' ']; + } + else if (conversions_mask & C_IBM) + { + translate_charset (ascii_to_ibm); + newline_character = ascii_to_ibm['\n']; + space_character = ascii_to_ibm[' ']; + } +} + +void +translate_charset (new_trans) + unsigned char *new_trans; +{ + int i; + + for (i = 0; i < 256; i++) + trans_table[i] = new_trans[trans_table[i]]; + translation_needed = 1; +} + +/* Return the number of 1 bits in `i'. */ + +int +bit_count (i) + register unsigned int i; +{ + register int set_bits; + + for (set_bits = 0; i != 0; set_bits++) + i &= i - 1; + return set_bits; +} + +void +print_stats () +{ + fprintf (stderr, "%u+%u records in\n", r_full, r_partial); + fprintf (stderr, "%u+%u records out\n", w_full, w_partial); + if (r_truncate > 0) + fprintf (stderr, "%u truncated block%s\n", r_truncate, + r_truncate == 1 ? "" : "s"); +} + +void +quit (code) + int code; +{ + int errcode = code ? code : 1; + print_stats (); + if (close (input_fd) < 0) + error (errcode, errno, "%s", input_file); + if (close (output_fd) < 0) + error (errcode, errno, "%s", output_file); + exit (code); +} + +RETSIGTYPE +interrupt_handler () +{ + quit (1); +} + +void +usage (string, arg0, arg1) + char *string, *arg0, *arg1; +{ + fprintf (stderr, "%s: ", program_name); + fprintf (stderr, string, arg0, arg1); + fprintf (stderr, "\n"); + fprintf (stderr, "\ +Usage: %s [if=file] [of=file] [ibs=bytes] [obs=bytes] [bs=bytes] [cbs=bytes]\n\ + [skip=blocks] [seek=blocks] [count=blocks]\n\ + [conv={ascii,ebcdic,ibm,block,unblock,lcase,ucase,swab,noerror,notrunc,\n\ + sync}]\n\ +Numbers can be followed by a multiplier:\n\ +b=512, k=1024, w=2, xm=number m\n", + program_name); + exit (1); +} |