home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume40 / bin2ascii / part01 / bin2ascii.c next >
Encoding:
C/C++ Source or Header  |  1993-11-01  |  12.7 KB  |  529 lines

  1. /*
  2.  * bin2ascii:
  3.  *    Convert an arbitrary binary file into vi-editable text, and back
  4.  *    again. Tries to do a "human-readable" job: well-known control
  5.  *    characters are by default represented using their standard backslash
  6.  *    sequences. Newlines are by default left as newlines, but they can
  7.  *    also be rendered as \n. (Newlines created by the program are preceded
  8.  *    by a backslash, and thus look like a dangling backslash at the end of
  9.  *    a line like you might find in a Makefile or csh script.) Text can
  10.  *    either be printed free-form to follow the original (with a maximum
  11.  *    line length imposed if desired), or broken into regular columns
  12.  *    separated by spaces. If the latter, _real_ spaces are indicated by
  13.  *    the backslash sequence \w, although "\ " works too. The sequence \E
  14.  *    is used to mark the end of the file, to get around vi's habit of
  15.  *    appending a spurious carriage-return on the end of anything it edits.
  16.  *    You can also use \E to prematurely stop processing before the end of
  17.  *    a long file. See self-doc for a complete list of options.
  18.  *
  19.  *    Joe Dellinger
  20.  *    U. Hawai`i Manoa
  21.  *    Oct 10, 1993
  22.  */
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <fcntl.h>
  26. #include <sys/types.h>
  27. #include <unistd.h>
  28.  
  29. /* How many bytes to read in at a time */
  30. #define BUFFERLENGTH    1024
  31.  
  32. #ifndef lint
  33. char            copyright[] =
  34. "@(#) Joe Dellinger, Oct 10, 1993, U. Hawaii Manoa\n";
  35. #endif            /* not lint */
  36.  
  37. #define YES    1
  38. #define NO    0
  39.  
  40. static int      return_ascii = YES;
  41. static int      columns = 0;
  42. static int      all_hex = 0;
  43. static int      spaces = NO;
  44. static int      label = NO;
  45. static unsigned int offset = 0;
  46. static unsigned int endpoint = 0;
  47. static int      invert = NO;
  48.  
  49. static int      buffer_len = BUFFERLENGTH;
  50. static char     inbuf[BUFFERLENGTH];
  51. static int      fdin;
  52. static int      alldone = NO;
  53. static int      newline;
  54.  
  55. main (argc, argv)
  56.     int             argc;
  57.     char          **argv;
  58. {
  59. extern char    *optarg;
  60. extern int      optind;
  61. int             ch;
  62. int             nread;
  63. int             ii, count;
  64. unsigned int    lencount;
  65. char            sp4[5];
  66. char            sp3[4];
  67. char            sp1[2];
  68. extern int      getachar ();
  69. extern void     putachar ();
  70. unsigned int    hexno;
  71. char            hexstring[10];
  72.  
  73.     if (argc == 1 && isatty (fileno (stdin)))
  74.     {
  75. /*
  76.  * SELF DOC
  77.  */
  78.     printf ("bin2ascii: Convert an arbitrary binary file into a readable ASCII format,\n");
  79.     printf ("           and back again.\n\n");
  80.     printf ("Usage: bin2ascii [options] binary_input > ascii_output\n");
  81.     printf ("       bin2ascii [options] < binary_input > ascii_output\n");
  82.     printf ("       bin2ascii -i [options] bin2ascii_output > binary_input\n");
  83.     printf ("       bin2ascii -i [options] < bin2ascii_output > binary_input\n");
  84.     printf ("              HEX looks like \\xDD where DD is a two-digit\n");
  85.     printf ("              hexadecimal number. The second digit can be\n");
  86.     printf ("              a space, but it should be present.\n");
  87.     printf ("              A \"\\E\" marks the end of the file.\n");
  88.     printf ("\n");
  89.     printf ("\tOptions: -c #columns -e #bytes -h -H -i -n -o #bytes -r -s\n");
  90.     printf ("\t-c  #columns (or merely max line length unless -r).\n");
  91.     printf ("\t-e  #bytes of the input file to process before exiting.\n");
  92.     printf ("\t-h  means print ALL nonprintables as hex, even \\0 \\a \\b \\f \\n \\r and \\t.\n");
  93.     printf ("\t-H  means treat EVERYTHING as hex.\n");
  94.     printf ("\t-i  means do the reverse: go from ASCII back to BINARY.\n");
  95.     printf ("\t    (For this to work, options should be same as before.)\n");
  96.     printf ("\t-n  means label byte offset at start of each line.\n");
  97.     printf ("\t-o  #bytes to offset into the input file before\n");
  98.     printf ("\t    beginning. Only works when input is a file!\n");
  99.     printf ("\t-r  means treat carriage returns like any other nonprintable.\n");
  100.     printf ("\t-s  put spaces between characters. WARNING!\n");
  101.     printf ("\t    In this case \"\\w\" is used to identify a legitimate space.\n");
  102.     exit (0);
  103.     }
  104.  
  105.  
  106.     while ((ch = getopt (argc, argv, "c:e:hHino:rs")) != EOF)
  107.     switch (ch)
  108.     {
  109.     case 'c':        /* Number of columns to print.  */
  110.         sscanf (optarg, "%d", &columns);
  111.         if (columns <= 0)
  112.         columns = 0;
  113.         break;
  114.     case 'e':        /* number of bytes to process */
  115.         sscanf (optarg, "%u", &endpoint);
  116.         break;
  117.     case 'h':        /* use \xHEX or things like \r, \t, etc? */
  118.         all_hex = 1;
  119.         break;
  120.     case 'H':        /* use \xHEX for EVERYTHING? */
  121.         all_hex = 2;
  122.         break;
  123.     case 'i':        /* The inverse program */
  124.         invert = YES;
  125.         break;
  126.     case 'n':        /* label offset */
  127.         label = YES;
  128.         break;
  129.     case 'o':        /* offset to seek */
  130.         sscanf (optarg, "%u", &offset);
  131.         break;
  132.     case 'r':        /* Should a carriage return be left alone? */
  133.         return_ascii = NO;
  134.         break;
  135.     case 's':        /* Put in spaces to keep columns regular? */
  136.         spaces = YES;
  137.         break;
  138.     default:
  139.         fprintf (stderr, "Type \"bin2hex\" without arguments to get self-doc!\n");
  140.         exit (1);
  141.         break;
  142.     }
  143.     argc -= optind;
  144.     argv += optind;
  145.  
  146.     if (((offset != 0) || (endpoint != 0)) && invert)
  147.     {
  148.     fprintf (stderr, "Warning: offset and endpoint options ignored in inverse mode.\n");
  149.     offset = 0;
  150.     endpoint = 0;
  151.     }
  152.  
  153.     /* Find input */
  154.     if (argc > 0)
  155.     {
  156.     /* Input file name */
  157.     fdin = open (argv[0], O_RDONLY);
  158.     if (fdin < 0)
  159.     {
  160.         fprintf (stderr, "Could not open input file \"%s\".\n", argv[1]);
  161.         exit (1);
  162.     }
  163.     if (offset > 0)
  164.     {
  165.         if (lseek (fdin, (off_t) offset, SEEK_SET) < 0)
  166.         {
  167.         fprintf (stderr, "The lseek didn't work.\n");
  168.         exit (1);
  169.         }
  170.     }
  171.     else if (offset < 0)
  172.     {
  173.         fprintf (stderr, "Negative offset?! It's unsigned; this should NOT be able to happen!\n");
  174.         exit (1);
  175.     }
  176.     }
  177.     else
  178.     {
  179.     fdin = fileno (stdin);
  180.     if (offset != 0)
  181.     {
  182.         fprintf (stderr, "Sorry, can't seek on a stream.\n");
  183.         exit (1);
  184.     }
  185.     }
  186.  
  187.  
  188. /* Forward direction */
  189.     if (!invert)
  190.     {
  191.     if (spaces)
  192.     {
  193.         strcpy (sp4, "    ");
  194.         strcpy (sp3, "   ");
  195.         strcpy (sp1, " ");
  196.     }
  197.     else
  198.     {
  199.         strcpy (sp4, "");
  200.         strcpy (sp3, "");
  201.         strcpy (sp1, "");
  202.     }
  203.  
  204.     count = 0;
  205.     lencount = offset;
  206.  
  207.     while (
  208.            ((endpoint == 0) || (lencount < offset + endpoint))
  209.            &&
  210.            ((nread = read (fdin, inbuf, buffer_len)) > 0)
  211.      )
  212.     {
  213.         for (ii = 0;
  214.          (ii < nread) && ((endpoint == 0) || (lencount < offset + endpoint));
  215.          ii++, count++, lencount++)
  216.         {
  217.         /* Handle newlines that are inserted automatically */
  218.         if (columns > 0 && count % columns == 0 && count > 0)
  219.         {
  220.             printf ("\\\n");
  221.             count = 0;
  222.         }
  223.  
  224.         /* Handle byte count at start of line */
  225.         if (label && count == 0)
  226.         {
  227.             printf ("%10u: ", lencount);
  228.         }
  229.  
  230.         ch = 0xFF & (unsigned int) inbuf[ii];
  231.  
  232.         if (all_hex < 2)
  233.         {
  234.             /*
  235.              * Have to catch backslashes, because we use it to mark
  236.              * special characters.
  237.              */
  238.             if (ch == '\\')
  239.             {
  240.             printf ("\\\\%s", sp3);
  241.             continue;
  242.             }
  243.             else if (ch == ' ')
  244.             {
  245.             if (spaces)
  246.                 printf ("\\w%s", sp3);
  247.             else
  248.                 printf ("%c", (char) ch);
  249.  
  250.             continue;
  251.             }
  252.             else if (isprint (ch))
  253.             {
  254.             printf ("%c%s", (char) ch, sp4);
  255.             continue;
  256.             }
  257.             else if (return_ascii && ch == '\n')
  258.             {
  259.             printf ("\n");
  260.             count = -1;
  261.             continue;
  262.             }
  263.             else if (all_hex == 0)
  264.             {
  265.             switch (ch)
  266.             {
  267.             case '\0':
  268.                 printf ("\\0%s", sp3);
  269.                 continue;
  270.                 break;
  271.             case '\007':    /* Some compilers don't know '\a' */
  272.                 printf ("\\a%s", sp3);
  273.                 continue;
  274.                 break;
  275.             case '\b':
  276.                 printf ("\\b%s", sp3);
  277.                 continue;
  278.                 break;
  279.             case '\f':
  280.                 printf ("\\f%s", sp3);
  281.                 continue;
  282.                 break;
  283.             case '\n':
  284.                 printf ("\\n%s", sp3);
  285.                 continue;
  286.                 break;
  287.             case '\r':
  288.                 printf ("\\r%s", sp3);
  289.                 continue;
  290.                 break;
  291.             case '\t':
  292.                 printf ("\\t%s", sp3);
  293.                 continue;
  294.                 break;
  295.             case '\v':
  296.                 printf ("\\v%s", sp3);
  297.                 continue;
  298.                 break;
  299.             default:
  300.                 break;
  301.             }
  302.             }
  303.         }
  304.         /*
  305.          * Last case left: it's a random garbage thing we have to do
  306.          * as hex. Force it to have a leading zero.
  307.          */
  308.         sprintf (hexstring, "%2X", (unsigned int) ch);
  309.         if (hexstring[2] != '\0')
  310.         {
  311.             fprintf (stderr, "uh oh! Something is badly wrong with the hex conversion!\n");
  312.             exit (2);
  313.         }
  314.         /* Zero pad, don't blank-pad */
  315.         if (hexstring[0] == ' ')
  316.             hexstring[0] = '0';
  317.         printf ("\\x%s%s", hexstring, sp1);
  318.         /*
  319.          * Does nothing, but all the other possibilities ended with
  320.          * continue, so do it that way here too.
  321.          */
  322.         continue;
  323.         }
  324.     }
  325.     /*
  326.      * vi's going to insert a carriage return at the end anyway!
  327.      */
  328.     if (lencount == offset + endpoint)
  329.         printf ("\\End of dumping after %u bytes.\n", endpoint);
  330.     else
  331.         printf ("\\EOF\n");
  332.     }
  333.     else
  334.     {
  335. /* Go the other direction! */
  336.  
  337.     newline = YES;
  338.  
  339.     while (ch = getachar (), !alldone)
  340.     {
  341.         /*
  342.          * If lines are labeled, then skip past the number and ":" and
  343.          * blank.
  344.          */
  345.         if (newline && label)
  346.         {
  347.         if (ch != ':')
  348.             continue;
  349.  
  350.         /*
  351.          * We've made it past the initial number and ":"
  352.          */
  353.         newline = NO;
  354.  
  355.         ch = getachar ();
  356.         if (ch != ' ')
  357.         {
  358.             fprintf (stderr, "Warning! Expected to find a number then a : then a space at start of a line.\n");
  359.             fprintf (stderr, "The space is missing!\n");
  360.         }
  361.         else
  362.             ch = getachar ();
  363.         }
  364.  
  365.         /*
  366.          * If automatic spaces is present, then ignore all spaces.
  367.          */
  368.         if (spaces && ch == ' ')
  369.         continue;
  370.  
  371.         /*
  372.          * Check for the start of a backslash sequence.
  373.          */
  374.         if (ch == '\\')
  375.         {
  376.         /* Backslash what? */
  377.         ch = getachar ();
  378.  
  379.         switch (ch)
  380.         {
  381.         case 'x':    /* Aha! Hex */
  382.             ch = getachar ();
  383.             if (
  384.             (ch >= '0' && ch <= '9') ||
  385.             (ch >= 'a' && ch <= 'f') ||
  386.             (ch >= 'A' && ch <= 'F')
  387.              )
  388.             {
  389.             if (ch >= '0' && ch <= '9')
  390.                 hexno = 0xFF & (unsigned int) (ch - '0');
  391.             else
  392.                 hexno = 0xFF & (unsigned int) (tolower (ch) - 'a' + 10);
  393.             }
  394.             else
  395.             {
  396.             fprintf (stderr, "Warning! Unknown backslash sequence \\x%c.\n", (char) ch);
  397.             putachar ();
  398.             continue;
  399.             }
  400.  
  401.             ch = getachar ();
  402.             if (
  403.             (ch >= '0' && ch <= '9') ||
  404.             (ch >= 'a' && ch <= 'f') ||
  405.             (ch >= 'A' && ch <= 'F')
  406.              )
  407.             {
  408.             if (ch >= '0' && ch <= '9')
  409.                 hexno = (hexno << 4) | (0xFF & (unsigned int) (ch - '0'));
  410.             else
  411.                 hexno = (hexno << 4) | (0xFF & ((unsigned int) tolower (ch) - 'a' + 10));
  412.             }
  413.             else if (ch != ' ')
  414.             {
  415.             fprintf (stderr, "Warning! Prematurely truncated backslash sequence \\x?%c.\n", (char) ch);
  416.             putachar ();
  417.             }
  418.             printf ("%c", (char) (hexno));
  419.             break;
  420.         case '\\':    /* Hey, it really *was* a backslash */
  421.             printf ("%c", (char) ch);
  422.             break;
  423.         case '\n':    /* A newline inserted by the program: ignore
  424.                  * it! */
  425.             newline = YES;    /* Start ignoring the label for the
  426.                      * next line */
  427.             break;
  428.         case 'E':    /* the END OF THE FILE! */
  429.             alldone = YES;
  430.             break;
  431.         case 'w':    /* A space denoted by \w */
  432.         case ' ':    /* A space made visible by backslashing it */
  433.             printf ("%c", (char) ' ');
  434.             continue;
  435.             break;
  436.         case '0':    /* A null */
  437.             printf ("%c", (char) '\0');
  438.             continue;
  439.             break;
  440.         case 'a':    /* Bell */
  441.             printf ("%c", (char) '\007');
  442.             continue;
  443.             break;
  444.         case 'b':    /* backspace */
  445.             printf ("%c", (char) '\b');
  446.             continue;
  447.             break;
  448.         case 'f':    /* form feed */
  449.             printf ("%c", (char) '\f');
  450.             continue;
  451.             break;
  452.         case 'n':    /* A bona-fide newline made visible */
  453.             printf ("%c", (char) '\n');
  454.             continue;
  455.             break;
  456.         case 'r':    /* carriage return */
  457.             printf ("%c", (char) '\r');
  458.             continue;
  459.             break;
  460.         case 't':    /* tab */
  461.             printf ("%c", (char) '\t');
  462.             continue;
  463.             break;
  464.         case 'v':    /* vertical tab */
  465.             printf ("%c", (char) '\v');
  466.             continue;
  467.             break;
  468.         default:
  469.             fprintf (stderr, "Warning! Unknown backslash sequence \\%c.\n", (char) ch);
  470.             printf ("%c", (char) ch);
  471.             break;
  472.         }
  473.         continue;
  474.         }
  475.  
  476.         /*
  477.          * Otherwise, just pass it on through.
  478.          */
  479.         printf ("%c", (char) ch);
  480.  
  481.         if (ch == '\n')
  482.         newline = YES;    /* Start ignoring the label for the next line */
  483.     }
  484.     }
  485.  
  486.     return 0;
  487. }
  488.  
  489. static int      left_in_queue = 0;
  490. void
  491. putachar ()
  492. {
  493.     left_in_queue++;
  494. }
  495.  
  496. /*
  497.  * On most UNIX systems reading from input a byte at a time is horribly
  498.  * Slow and inefficient, so buffer the intput reading ourselves.
  499.  */
  500. int
  501. getachar ()
  502. {
  503. static int      nread = 0;
  504. int             output;
  505.  
  506.     if (left_in_queue == 0)
  507.     {
  508.     if (alldone || ((nread = read (fdin, inbuf, buffer_len)) == 0))
  509.     {
  510. /* We're done! Let them know. */
  511.         alldone = YES;
  512. /*
  513.  * Just in case something is wrong and they try to use this value,
  514.  * go ahead and give them something.
  515.  */
  516.         output = '\0';
  517.         return output;
  518.     }
  519.     else
  520.     {
  521.         left_in_queue = nread;
  522.     }
  523.     }
  524.     output = 0xFF & (unsigned int) inbuf[nread - left_in_queue];
  525.     left_in_queue--;
  526.  
  527.     return output;
  528. }
  529.