home *** CD-ROM | disk | FTP | other *** search
- /*
- * bin2ascii:
- * Convert an arbitrary binary file into vi-editable text, and back
- * again. Tries to do a "human-readable" job: well-known control
- * characters are by default represented using their standard backslash
- * sequences. Newlines are by default left as newlines, but they can
- * also be rendered as \n. (Newlines created by the program are preceded
- * by a backslash, and thus look like a dangling backslash at the end of
- * a line like you might find in a Makefile or csh script.) Text can
- * either be printed free-form to follow the original (with a maximum
- * line length imposed if desired), or broken into regular columns
- * separated by spaces. If the latter, _real_ spaces are indicated by
- * the backslash sequence \w, although "\ " works too. The sequence \E
- * is used to mark the end of the file, to get around vi's habit of
- * appending a spurious carriage-return on the end of anything it edits.
- * You can also use \E to prematurely stop processing before the end of
- * a long file. See self-doc for a complete list of options.
- *
- * Joe Dellinger
- * U. Hawai`i Manoa
- * Oct 10, 1993
- */
- #include <stdio.h>
- #include <string.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <unistd.h>
-
- /* How many bytes to read in at a time */
- #define BUFFERLENGTH 1024
-
- #ifndef lint
- char copyright[] =
- "@(#) Joe Dellinger, Oct 10, 1993, U. Hawaii Manoa\n";
- #endif /* not lint */
-
- #define YES 1
- #define NO 0
-
- static int return_ascii = YES;
- static int columns = 0;
- static int all_hex = 0;
- static int spaces = NO;
- static int label = NO;
- static unsigned int offset = 0;
- static unsigned int endpoint = 0;
- static int invert = NO;
-
- static int buffer_len = BUFFERLENGTH;
- static char inbuf[BUFFERLENGTH];
- static int fdin;
- static int alldone = NO;
- static int newline;
-
- main (argc, argv)
- int argc;
- char **argv;
- {
- extern char *optarg;
- extern int optind;
- int ch;
- int nread;
- int ii, count;
- unsigned int lencount;
- char sp4[5];
- char sp3[4];
- char sp1[2];
- extern int getachar ();
- extern void putachar ();
- unsigned int hexno;
- char hexstring[10];
-
- if (argc == 1 && isatty (fileno (stdin)))
- {
- /*
- * SELF DOC
- */
- printf ("bin2ascii: Convert an arbitrary binary file into a readable ASCII format,\n");
- printf (" and back again.\n\n");
- printf ("Usage: bin2ascii [options] binary_input > ascii_output\n");
- printf (" bin2ascii [options] < binary_input > ascii_output\n");
- printf (" bin2ascii -i [options] bin2ascii_output > binary_input\n");
- printf (" bin2ascii -i [options] < bin2ascii_output > binary_input\n");
- printf (" HEX looks like \\xDD where DD is a two-digit\n");
- printf (" hexadecimal number. The second digit can be\n");
- printf (" a space, but it should be present.\n");
- printf (" A \"\\E\" marks the end of the file.\n");
- printf ("\n");
- printf ("\tOptions: -c #columns -e #bytes -h -H -i -n -o #bytes -r -s\n");
- printf ("\t-c #columns (or merely max line length unless -r).\n");
- printf ("\t-e #bytes of the input file to process before exiting.\n");
- printf ("\t-h means print ALL nonprintables as hex, even \\0 \\a \\b \\f \\n \\r and \\t.\n");
- printf ("\t-H means treat EVERYTHING as hex.\n");
- printf ("\t-i means do the reverse: go from ASCII back to BINARY.\n");
- printf ("\t (For this to work, options should be same as before.)\n");
- printf ("\t-n means label byte offset at start of each line.\n");
- printf ("\t-o #bytes to offset into the input file before\n");
- printf ("\t beginning. Only works when input is a file!\n");
- printf ("\t-r means treat carriage returns like any other nonprintable.\n");
- printf ("\t-s put spaces between characters. WARNING!\n");
- printf ("\t In this case \"\\w\" is used to identify a legitimate space.\n");
- exit (0);
- }
-
-
- while ((ch = getopt (argc, argv, "c:e:hHino:rs")) != EOF)
- switch (ch)
- {
- case 'c': /* Number of columns to print. */
- sscanf (optarg, "%d", &columns);
- if (columns <= 0)
- columns = 0;
- break;
- case 'e': /* number of bytes to process */
- sscanf (optarg, "%u", &endpoint);
- break;
- case 'h': /* use \xHEX or things like \r, \t, etc? */
- all_hex = 1;
- break;
- case 'H': /* use \xHEX for EVERYTHING? */
- all_hex = 2;
- break;
- case 'i': /* The inverse program */
- invert = YES;
- break;
- case 'n': /* label offset */
- label = YES;
- break;
- case 'o': /* offset to seek */
- sscanf (optarg, "%u", &offset);
- break;
- case 'r': /* Should a carriage return be left alone? */
- return_ascii = NO;
- break;
- case 's': /* Put in spaces to keep columns regular? */
- spaces = YES;
- break;
- default:
- fprintf (stderr, "Type \"bin2hex\" without arguments to get self-doc!\n");
- exit (1);
- break;
- }
- argc -= optind;
- argv += optind;
-
- if (((offset != 0) || (endpoint != 0)) && invert)
- {
- fprintf (stderr, "Warning: offset and endpoint options ignored in inverse mode.\n");
- offset = 0;
- endpoint = 0;
- }
-
- /* Find input */
- if (argc > 0)
- {
- /* Input file name */
- fdin = open (argv[0], O_RDONLY);
- if (fdin < 0)
- {
- fprintf (stderr, "Could not open input file \"%s\".\n", argv[1]);
- exit (1);
- }
- if (offset > 0)
- {
- if (lseek (fdin, (off_t) offset, SEEK_SET) < 0)
- {
- fprintf (stderr, "The lseek didn't work.\n");
- exit (1);
- }
- }
- else if (offset < 0)
- {
- fprintf (stderr, "Negative offset?! It's unsigned; this should NOT be able to happen!\n");
- exit (1);
- }
- }
- else
- {
- fdin = fileno (stdin);
- if (offset != 0)
- {
- fprintf (stderr, "Sorry, can't seek on a stream.\n");
- exit (1);
- }
- }
-
-
- /* Forward direction */
- if (!invert)
- {
- if (spaces)
- {
- strcpy (sp4, " ");
- strcpy (sp3, " ");
- strcpy (sp1, " ");
- }
- else
- {
- strcpy (sp4, "");
- strcpy (sp3, "");
- strcpy (sp1, "");
- }
-
- count = 0;
- lencount = offset;
-
- while (
- ((endpoint == 0) || (lencount < offset + endpoint))
- &&
- ((nread = read (fdin, inbuf, buffer_len)) > 0)
- )
- {
- for (ii = 0;
- (ii < nread) && ((endpoint == 0) || (lencount < offset + endpoint));
- ii++, count++, lencount++)
- {
- /* Handle newlines that are inserted automatically */
- if (columns > 0 && count % columns == 0 && count > 0)
- {
- printf ("\\\n");
- count = 0;
- }
-
- /* Handle byte count at start of line */
- if (label && count == 0)
- {
- printf ("%10u: ", lencount);
- }
-
- ch = 0xFF & (unsigned int) inbuf[ii];
-
- if (all_hex < 2)
- {
- /*
- * Have to catch backslashes, because we use it to mark
- * special characters.
- */
- if (ch == '\\')
- {
- printf ("\\\\%s", sp3);
- continue;
- }
- else if (ch == ' ')
- {
- if (spaces)
- printf ("\\w%s", sp3);
- else
- printf ("%c", (char) ch);
-
- continue;
- }
- else if (isprint (ch))
- {
- printf ("%c%s", (char) ch, sp4);
- continue;
- }
- else if (return_ascii && ch == '\n')
- {
- printf ("\n");
- count = -1;
- continue;
- }
- else if (all_hex == 0)
- {
- switch (ch)
- {
- case '\0':
- printf ("\\0%s", sp3);
- continue;
- break;
- case '\007': /* Some compilers don't know '\a' */
- printf ("\\a%s", sp3);
- continue;
- break;
- case '\b':
- printf ("\\b%s", sp3);
- continue;
- break;
- case '\f':
- printf ("\\f%s", sp3);
- continue;
- break;
- case '\n':
- printf ("\\n%s", sp3);
- continue;
- break;
- case '\r':
- printf ("\\r%s", sp3);
- continue;
- break;
- case '\t':
- printf ("\\t%s", sp3);
- continue;
- break;
- case '\v':
- printf ("\\v%s", sp3);
- continue;
- break;
- default:
- break;
- }
- }
- }
- /*
- * Last case left: it's a random garbage thing we have to do
- * as hex. Force it to have a leading zero.
- */
- sprintf (hexstring, "%2X", (unsigned int) ch);
- if (hexstring[2] != '\0')
- {
- fprintf (stderr, "uh oh! Something is badly wrong with the hex conversion!\n");
- exit (2);
- }
- /* Zero pad, don't blank-pad */
- if (hexstring[0] == ' ')
- hexstring[0] = '0';
- printf ("\\x%s%s", hexstring, sp1);
- /*
- * Does nothing, but all the other possibilities ended with
- * continue, so do it that way here too.
- */
- continue;
- }
- }
- /*
- * vi's going to insert a carriage return at the end anyway!
- */
- if (lencount == offset + endpoint)
- printf ("\\End of dumping after %u bytes.\n", endpoint);
- else
- printf ("\\EOF\n");
- }
- else
- {
- /* Go the other direction! */
-
- newline = YES;
-
- while (ch = getachar (), !alldone)
- {
- /*
- * If lines are labeled, then skip past the number and ":" and
- * blank.
- */
- if (newline && label)
- {
- if (ch != ':')
- continue;
-
- /*
- * We've made it past the initial number and ":"
- */
- newline = NO;
-
- ch = getachar ();
- if (ch != ' ')
- {
- fprintf (stderr, "Warning! Expected to find a number then a : then a space at start of a line.\n");
- fprintf (stderr, "The space is missing!\n");
- }
- else
- ch = getachar ();
- }
-
- /*
- * If automatic spaces is present, then ignore all spaces.
- */
- if (spaces && ch == ' ')
- continue;
-
- /*
- * Check for the start of a backslash sequence.
- */
- if (ch == '\\')
- {
- /* Backslash what? */
- ch = getachar ();
-
- switch (ch)
- {
- case 'x': /* Aha! Hex */
- ch = getachar ();
- if (
- (ch >= '0' && ch <= '9') ||
- (ch >= 'a' && ch <= 'f') ||
- (ch >= 'A' && ch <= 'F')
- )
- {
- if (ch >= '0' && ch <= '9')
- hexno = 0xFF & (unsigned int) (ch - '0');
- else
- hexno = 0xFF & (unsigned int) (tolower (ch) - 'a' + 10);
- }
- else
- {
- fprintf (stderr, "Warning! Unknown backslash sequence \\x%c.\n", (char) ch);
- putachar ();
- continue;
- }
-
- ch = getachar ();
- if (
- (ch >= '0' && ch <= '9') ||
- (ch >= 'a' && ch <= 'f') ||
- (ch >= 'A' && ch <= 'F')
- )
- {
- if (ch >= '0' && ch <= '9')
- hexno = (hexno << 4) | (0xFF & (unsigned int) (ch - '0'));
- else
- hexno = (hexno << 4) | (0xFF & ((unsigned int) tolower (ch) - 'a' + 10));
- }
- else if (ch != ' ')
- {
- fprintf (stderr, "Warning! Prematurely truncated backslash sequence \\x?%c.\n", (char) ch);
- putachar ();
- }
- printf ("%c", (char) (hexno));
- break;
- case '\\': /* Hey, it really *was* a backslash */
- printf ("%c", (char) ch);
- break;
- case '\n': /* A newline inserted by the program: ignore
- * it! */
- newline = YES; /* Start ignoring the label for the
- * next line */
- break;
- case 'E': /* the END OF THE FILE! */
- alldone = YES;
- break;
- case 'w': /* A space denoted by \w */
- case ' ': /* A space made visible by backslashing it */
- printf ("%c", (char) ' ');
- continue;
- break;
- case '0': /* A null */
- printf ("%c", (char) '\0');
- continue;
- break;
- case 'a': /* Bell */
- printf ("%c", (char) '\007');
- continue;
- break;
- case 'b': /* backspace */
- printf ("%c", (char) '\b');
- continue;
- break;
- case 'f': /* form feed */
- printf ("%c", (char) '\f');
- continue;
- break;
- case 'n': /* A bona-fide newline made visible */
- printf ("%c", (char) '\n');
- continue;
- break;
- case 'r': /* carriage return */
- printf ("%c", (char) '\r');
- continue;
- break;
- case 't': /* tab */
- printf ("%c", (char) '\t');
- continue;
- break;
- case 'v': /* vertical tab */
- printf ("%c", (char) '\v');
- continue;
- break;
- default:
- fprintf (stderr, "Warning! Unknown backslash sequence \\%c.\n", (char) ch);
- printf ("%c", (char) ch);
- break;
- }
- continue;
- }
-
- /*
- * Otherwise, just pass it on through.
- */
- printf ("%c", (char) ch);
-
- if (ch == '\n')
- newline = YES; /* Start ignoring the label for the next line */
- }
- }
-
- return 0;
- }
-
- static int left_in_queue = 0;
- void
- putachar ()
- {
- left_in_queue++;
- }
-
- /*
- * On most UNIX systems reading from input a byte at a time is horribly
- * Slow and inefficient, so buffer the intput reading ourselves.
- */
- int
- getachar ()
- {
- static int nread = 0;
- int output;
-
- if (left_in_queue == 0)
- {
- if (alldone || ((nread = read (fdin, inbuf, buffer_len)) == 0))
- {
- /* We're done! Let them know. */
- alldone = YES;
- /*
- * Just in case something is wrong and they try to use this value,
- * go ahead and give them something.
- */
- output = '\0';
- return output;
- }
- else
- {
- left_in_queue = nread;
- }
- }
- output = 0xFF & (unsigned int) inbuf[nread - left_in_queue];
- left_in_queue--;
-
- return output;
- }
-