home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume7 / nstrings.bsd / strings.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-06-03  |  13.3 KB  |  557 lines

  1. static char * sccsid = "@(#)  strings.c  (v1.6.9 5/23/89)";
  2. /*
  3.  * strings
  4.  * =======
  5.  *
  6.  * Find and output the printable strings with a certain minimal length or
  7.  * more in any files.
  8.  *
  9.  * This is a rewrite of 4BSD strings, which had some errors:
  10.  * - ^L is a printable character (4.3BSD, SUN OS)
  11.  * - 0x80 is a printable character (4.3BSD, SUN OS)
  12.  * - on some systems (MX2) it does not get the segment of initialized data
  13.  *   correctly
  14.  *
  15.  * A printable string is any string of printable characters. A printable
  16.  * character is any obvious one, plus blank. If flag -t was specified TAB
  17.  * is considered a printable character too. If flag -c was specified
  18.  * on the command line, only strings are output, which are followed
  19.  * by LF or NUL, just like the original BSD manual entry claimed.
  20.  *
  21.  */
  22.  
  23. # include "strings.h"
  24.  
  25. # include <ctype.h>
  26.  
  27. char isp [256];
  28. char * isp_mid;
  29. # define IS_PRINTABLE(c)    (isp_mid[c])
  30.  
  31. int fd;
  32.  
  33. CHAR_TYPE buf [IN_BUF_LEN];
  34.  
  35. int ind_whole    = 0;        /* Shall we examine whole file ? */
  36. int ind_offset    = 0;        /* Offsets required ? */
  37. int min_str_len = 4;        /* If a string has more characters, it is output */
  38. int ind_file    = 0;        /* # of filenames in command */
  39. int ind_c        = 0;        /* Only strings ending with NUL or LF ? */
  40. int ind_tab        = 0;        /* Is TAB a printable character ? */
  41. int ind_prefix  = 0;        /* should the filename be added before string ? */
  42. int ind_version = 0;        /* print version information ? */
  43.  
  44. LSEEK_TYPE offset;
  45.  
  46. char * cur_file_name;
  47.  
  48. extern CHAR_TYPE * out_buf, * level;
  49. extern int num_out_buf;
  50. extern int saved;
  51.  
  52. # define SEARCH        1
  53. # define DECIDE        2
  54. # define TRY         3
  55.  
  56. usage (n)
  57. int n;
  58. {
  59.     out ("usage: strings [options] [file ...]\n");
  60. # ifdef I_SPECIAL
  61.     out ("-a : look in whole file. Default : only look in initialized data\n");
  62.     out ("-  : the same as -a\n");
  63. # endif
  64.     out ("-o : output offset in decimal before each string.\n");
  65.     out ("-N : only output sequences of length >= N; N is a number > 0.\n");
  66.     out ("-c : only output C strings; terminated by \\0 or \\n\n");
  67.     out ("-e : the next word is taken as a filename, even if it starts with '-'.\n");
  68.     out ("-t : TAB is considered a printable character too.\n");
  69.     out ("-p : the name of the input file is output before each string.\n");
  70.     out ("-v : only print version information, examine no files.\n");
  71. # ifdef I_SPECIAL
  72.     out ("Options can be combined like '-a20ot'.\n");
  73. # else
  74.     out ("Options can be combined like '-20o'.\n");
  75.     out ("The whole file is scanned.\n");
  76. # endif
  77.     exit (n);
  78. }
  79.  
  80. out_int (n)
  81. register int n;
  82. {
  83.     char s [20];
  84.     register int i;
  85.     s [19] = '\0';
  86.     i = 18;
  87.     if (n == 0) {
  88.         s [18] = '0';
  89.         i = 17;
  90.     }
  91.     while (n != 0) {
  92.         s[i--] = '0' + (n % 10);
  93.         n /= 10;
  94.     }
  95.     out (s+i+1);
  96. }
  97.  
  98. print_version()
  99. {
  100.     out (sccsid); out ("\n\n");
  101.     out ("Compilation flags:\n");
  102.     out ("FOUND = \""); out (FOUND); out ("\"\n");
  103. # ifdef FCNTL
  104.     out ("FCNTL = "); out_int (FCNTL); out ("\n");
  105. # else
  106.     out ("FCNTL is not defined.\n");
  107. # endif FCNTL
  108. # ifdef FAST_COPY
  109.     out ("FAST_COPY is defined.\n");
  110. # else
  111.     out ("FAST_COPY is not defined.\n");
  112. # endif FAST_COPY
  113. # ifdef WHAT_LSEEK
  114.     out ("WHAT_LSEEK = "); out_int (WHAT_LSEEK); out ("\n");
  115. # else
  116.     out ("WHAT_LSEEK is not defined.\n");
  117. # endif WHAT_LSEEK
  118. # ifdef I_SPECIAL
  119.     out ("I_SPECIAL is defined.\n");
  120. # else
  121.     out ("I_SPECIAL is not defined.\n");
  122. # endif I_SPECIAL
  123.     out ("IN_BUFLEN  = "); out_int (IN_BUF_LEN); out ("\n");
  124.     out ("OUT_BUFLEN = "); out_int (OUT_BUF_LEN); out ("\n");
  125.     out ("THRESHOLD  = "); out_int (THRESHOLD); out ("\n");
  126. }
  127.  
  128. options (ac, av, f)
  129. int ac;
  130. char * av [];
  131. char ** f;
  132. {
  133.     register int i, j;
  134.     int take_file = 0;                /* is the next word a file ? */
  135.     int had_whole, had_offset, had_min_str_len;
  136.     int had_c, had_tab, had_prefix, had_version;
  137.  
  138.     had_whole = had_offset = had_min_str_len = had_c = had_tab = 0;
  139.     had_prefix = had_version = 0;
  140.     for (i = 1; i < ac; i++) {
  141.         if (take_file == 0 && av[i][0] == '-') {
  142.             if (av[i][1] == '\0') {
  143.                 if (had_whole == 1)
  144.                     usage (5);
  145.                 had_whole = 1;
  146.                 ind_whole = 1;
  147.             } else {
  148.                 for (j = 1; av[i][j] != '\0'; j++) {
  149.                     if ('0' <= av[i][j] && av[i][j] <= '9') {
  150.                         if (had_min_str_len == 1)
  151.                             usage (3);
  152.                         for (min_str_len = 0; '0' <= av[i][j]  && av[i][j] <= '9'; j++)
  153.                             min_str_len = min_str_len * 10 + av[i][j] - '0';
  154.                         j--;    /* So we don't lose a character */
  155.                         had_min_str_len = 1;
  156.                     } else
  157.                         switch (av[i][j]) {
  158. # ifdef I_SPECIAL
  159.                             case 'a':
  160.                             case '-':
  161.                                 if (had_whole == 1)
  162.                                     usage (5);
  163.                                 had_whole = 1;
  164.                                 ind_whole = 1;
  165.                                 break;
  166. # endif
  167.                             case 'o':
  168.                                 if (had_offset == 1)
  169.                                     usage (6);
  170.                                 had_offset = ind_offset = 1;
  171.                                 break;
  172.                             case 'c':
  173.                                 if (had_c == 1)
  174.                                     usage (7);
  175.                                 had_c = ind_c = 1;
  176.                                 break;
  177.                             case 'f':
  178.                                 take_file = 1;
  179.                                 break;
  180.                             case 't':
  181.                                 if (had_tab == 1)
  182.                                     usage (8);
  183.                                 had_tab = ind_tab = 1;
  184.                                 break;
  185.                             case 'p':
  186.                                 if (had_prefix == 1)
  187.                                     usage (9);
  188.                                 had_prefix = ind_prefix = 1;
  189.                                 break;
  190.                             case 'v':
  191.                                 if (had_version == 1)
  192.                                     usage (10);
  193.                                 had_version = ind_version = 1;
  194.                                 break;
  195.                             default:
  196.                                 usage (8);
  197.                                 break;
  198.                         }
  199.                 }
  200.             }
  201.         } else {
  202.             f [ind_file++] = av[i];
  203.             take_file = 0;
  204.         }
  205.     }
  206.     if (min_str_len <= 0)
  207.         usage (4);
  208. # ifdef DEBUG
  209.     fprintf (prot, "ind_offset  = %3d\n", ind_offset);
  210.     fprintf (prot, "ind_whole   = %3d\n", ind_whole);
  211.     fprintf (prot, "ind_file    = %3d\n", ind_file);
  212.     fprintf (prot, "ind_c       = %3d\n", ind_c);
  213.     fprintf (prot, "min_str_len = %3d\n", min_str_len);
  214.     fprintf (prot, "ind_tab     = %3d\n", ind_tab);
  215.     fprintf (prot, "ind_prefix  = %3d\n", ind_prefix);
  216.     fprintf (prot, "ind_version = %3d\n", ind_version);
  217.     if (ind_file == 0)
  218.         fprintf (prot, "had no files on command line\n");
  219.     else {
  220.         fprintf (prot, "had %1d files on command line\n", ind_file);
  221.         for (i = 0; i < ind_file; i++)
  222.             fprintf (prot, "%s\n", f[i]);
  223.     }
  224. # endif
  225. }
  226.  
  227. init ()
  228. {
  229.     register int i;
  230.     int min;
  231.     char c;
  232.  
  233.     min = 0;
  234.     for (i = 0; i < 256; i++) {
  235.         c = i;
  236.         if (c < min)
  237.             min = c;
  238.     }
  239.     isp_mid = isp - min;
  240.     for (i = 0; i < 256; i++) {
  241.         c = i;
  242.         isp_mid [c] = isascii(c) && isprint(c);
  243.     }
  244.     if (ind_tab)
  245.         isp_mid['\t'] = 1;
  246.  
  247.     init_output ();
  248. }
  249.  
  250. main (argc, argv)
  251. int argc;
  252. char * argv[];
  253. {
  254.     register int i;
  255.     char ** f;
  256.  
  257. # ifdef DEBUG
  258.     if ((prot = fopen ("prot", "w")) == NULL) {
  259.         fprintf (stderr, "could not open prot\n");
  260.         exit (1);
  261.     }
  262. # endif
  263.     f = (char **) malloc ((unsigned)(sizeof (char *) * argc));
  264.     options (argc, argv, f);
  265.     if (ind_version) {
  266.         print_version ();
  267.         exit (0);
  268.     }
  269.     init ();
  270.  
  271.     if (ind_file == 0)
  272.         strings ((char*)NULL);
  273.     else
  274.         for (i = 0; i < ind_file; i++) {
  275.             if (ind_file != 1)
  276.                 out_name (f[i]);
  277.             strings (f[i]);
  278.         }
  279.     exit (0);
  280. }
  281.  
  282. out_name (b)
  283. register CHAR_TYPE * b;
  284. {
  285.     CHAR_TYPE s [45];
  286.     CHAR_TYPE * s2 = (CHAR_TYPE*) "  ";
  287.     register int n, i;
  288.  
  289.     for (i = 0; i < 45; i++)
  290.         s [i] = '-';
  291.     n = strlen (s);
  292.     i = strlen (b);
  293. # ifdef DEBUG
  294.     fprintf (prot, "out_name :: n = %d, i = %d\n", n, i);
  295. # endif
  296.     if (n*2 + (i+2) > 80)
  297.         n = (80 - (i+2)) / 2;
  298. # ifdef DEBUG
  299.     fprintf (prot, "out_name :: first string is %d long.\n", n);
  300. # endif
  301.     (void) append (s, s+n, 0);
  302.  
  303.     (void) append (s2, s2+1, 0);
  304.     (void) append (b, b+i, 0);
  305.     (void) append (s2, s2+1, 0);
  306.     
  307.     if (2*n + (i+2) < 80)
  308.         n++;
  309. # ifdef DEBUG
  310.     fprintf (prot, "out_name :: second string is %d long.\n", n);
  311. # endif
  312.     (void) append (s, s+n, 1);
  313. }
  314.  
  315. int
  316. examine (state, n)
  317. register int state;
  318. int n;
  319. /*
  320.  * Find strings of printable characters in buf and append them to
  321.  * the output buffer, if they meet certain conditions.
  322.  *
  323.  * The main part of this routine is a DFA (deterministic finite automaton) with
  324.  * three states.
  325.  * These states are
  326.  * SEARCH : search for a printable character by examining characters in
  327.  *            distance min_str_len. If found, set b1 to the start
  328.  *            of the sequence and enter state TRY.
  329.  * TRY    : We have found a printable character. Set b2 to the first character
  330.  *            after the end of the sequence by single stepping. Set state to
  331.  *            DECIDE.
  332.  * DECIDE : We have found a sequence of printable characters. If the first
  333.  *            character after the sequence is in the buffer, then we can decide
  334.  *            what to do with the sequence (even if flag -c was not specified).
  335.  *            If not then the stuff is buffered, state set to TRY, and returned
  336.  *            to the caller to read a new block of input.
  337.  *            It is tested whether the sequence meets the requirements.
  338.  *            Either it is output by placing it permanently into the output
  339.  *            buffer, or it is forgotten.
  340.  */
  341. {
  342.     register CHAR_TYPE * b, * b1, * b2, * end;
  343.  
  344.     end = buf + n;
  345.  
  346.     b = b1 = b2 = buf;
  347.     for (;b < end; b = b2+1) {
  348. # ifdef DEBUG
  349.         fprintf (prot, "state = %s; b at %d\n",
  350.             state == SEARCH ? "SEARCH" : (state == DECIDE ? "DECIDE" : "TRY"),
  351.             (int)(b - buf));
  352. # endif
  353.         b1 = b;
  354.         switch (state) {
  355.             case SEARCH:
  356.                 /*
  357.                  * Search a character which might be in a sequence of
  358.                  * printable characters. Note that it suffices to examine
  359.                  * characters in distance min_str_len.
  360.                  */
  361.                 for (;b2 < end && !IS_PRINTABLE(*b2); b2 += min_str_len);
  362.                 /*
  363.                  * If we have stepped outside the buffer, we must examine
  364.                  * the end of the buffer yet.
  365.                  */
  366.                 if (b2 >= end)
  367.                     b2 = end;
  368.                 b1 = b2-1;
  369.                 /*
  370.                  * Find the start of the current sequence.
  371.                  */
  372.                 while (b1 >= buf && IS_PRINTABLE(*b1))
  373.                     b1--;
  374.                 b1++;
  375.                 if (b1 >= end)
  376.                     return (SEARCH);
  377.                 /* FALL THROUGH */
  378.         case TRY:
  379.                 /*
  380.                  * Find  the end of the current sequence. Set b2 one beyond.
  381.                  */
  382.                 while (b2 < end && IS_PRINTABLE(*b2))
  383.                     b2++;
  384. # ifdef DEBUG
  385.                 fprintf (prot, "found seq between %1d and %1d -->",
  386.                     (int)(b1-buf), (int)(b2-buf));
  387.                 { CHAR_TYPE * tmp;
  388.                     for (tmp = b1; tmp < b2; tmp++)
  389.                         if (IS_PRINTABLE(*tmp))
  390.                             fputc (*tmp, prot);
  391.                         else
  392.                             fputc ('.', prot);
  393.                 }
  394.                 fprintf (prot, "<--\n");
  395. # endif
  396.                 /*
  397.                  * Should set state to DECIDE; but we don't need it.
  398.                  * state will be reset anyway.
  399.                  */
  400.                 /* FALL THROUGH */
  401.             case DECIDE:
  402.                 /*
  403.                  * Can we decide what to do with the sequence which
  404.                  * we have found? We cannot, if we are at the end of
  405.                  * the block, because we need just one more character.
  406.                  */
  407.                 if (b2 >= end) {
  408. # ifdef DEBUG
  409.                     fprintf (prot, "I cannot decide. Must read a new block.\n");
  410. # endif
  411.                     (void) append (b1, b2, 0);
  412.                     return (TRY);
  413.                 }
  414. # ifdef DEBUG
  415.                 fprintf (prot, "I can decide.\n");
  416.                 if (ind_c) {
  417.                     if (*b2 == '\0' || *b2 == '\n')
  418.                         fprintf (prot, "String is a C string; followed by %s\n",
  419.                             *b2 == '\0'?"NUL":"\\n");
  420.                 }
  421. # endif
  422.                 if (((int)(b2-b1)+saved >= min_str_len) &&
  423.                     (!ind_c || (*b2 == '\0' || *b2 == '\n'))) {
  424.                     /*
  425.                      * String is accepted. Copy it to the output buffer.
  426.                      */
  427. # ifdef DEBUG
  428.                     fprintf (prot, "Accept string.\n");
  429. # endif
  430.                     (void) append (b1, b2, 1);
  431.                 } else {
  432.                     /*
  433.                      * String is refused. Forget any temporarily buffered
  434.                      * stuff in output buffer.
  435.                      */
  436. # ifdef DEBUG
  437.                     fprintf (prot, "String refused.\n");
  438. # endif
  439.                     level = out_buf + num_out_buf;
  440.                     saved = 0;
  441.                 }
  442.                 state = SEARCH;
  443.         }    /* switch */
  444.     }    /* for (;b < end; ... */
  445.     return (state);
  446. }
  447.  
  448. strings (name)
  449. char * name;
  450. /*
  451.  * Find strings in a file or an input stream.
  452.  * This routine sets the limits to handle a file, either to the
  453.  * whole file, or to the initialized data only.
  454.  * In a loop it reads blocks from the file and calls the DFA ('examine').
  455.  * Examine returns its state, so that it can be reentered at the
  456.  * right place.
  457.  */
  458. {
  459.     register int n, state;
  460.     LSEEK_TYPE l, first, last;
  461.  
  462.     if (name == NULL) {
  463.         fd = 0;
  464.     } else
  465.         if ((fd = open (name, O_RDONLY, 0)) == -1) {
  466.             perror (name);
  467.             return;
  468.         }
  469.     if (name == NULL || ind_whole == 1) {
  470.         first = (LSEEK_TYPE)0;
  471.         last = (LSEEK_TYPE)(-1);         /* --> no limit */
  472.     } else {
  473. # ifdef I_SPECIAL
  474.         /*
  475.          * Get the limits for reading.
  476.          * If the file is not an object, then we look at whole file.
  477.          */
  478.         get_limits (fd, &first, &last);
  479. # ifdef DEBUG
  480.         fprintf (prot, "lseek to %ld; last = %ld\n", (long)first, (long)last);
  481. # endif
  482.         if (lseek (fd, first, 0) != first) {
  483.             perror ("lseek");
  484.             return;
  485.         }
  486. # else I_SPECIAL
  487.         first = (LSEEK_TYPE)0;
  488.         last = (LSEEK_TYPE)(-1);         /* --> no limit */
  489. # endif I_SPECIAL
  490.     }
  491.  
  492.     cur_file_name = name;
  493.     offset = first;
  494.     state = SEARCH;
  495.     for (;;) {
  496.         /*
  497.          * Do we really have to read a block ?
  498.          * How much should we read? The difficult thing here
  499.          * is to watch out not to read beyond the limits of
  500.          * initialized data.
  501.          */
  502.         if (last != (LSEEK_TYPE)(-1)) {
  503.             l = last - offset;
  504.             if (l <= 0)
  505.                 break;
  506.             if (l > IN_BUF_LEN)
  507.                 l = IN_BUF_LEN;
  508.         } else
  509.                 l = IN_BUF_LEN;
  510. # ifdef DEBUG
  511.         fprintf (prot, "reading %1ld chars\n", l);
  512. # endif
  513.         if ((n = read (fd, buf, (int)l)) <= 0)
  514.             break;
  515. # ifdef DEBUG
  516.         fprintf (prot, "read %1d characters\n", n);
  517.         fflush (prot);
  518. # endif
  519.  
  520.         state = examine (state, n);
  521.  
  522.         offset += n;
  523.     }
  524.     if (n == -1)
  525.         perror ("read");
  526.     /*
  527.      * If the piece of the file ended with a string of printable characters,
  528.      * we must check whether this string is valid.
  529.      * We need not peek at the first character after the strings, as we know
  530.      * that it cannot be \0 or \n.
  531.      */
  532.     if (saved > 0 && !ind_c)
  533.         if (saved >= min_str_len)
  534.             (void) append (buf, buf, 1);
  535.     /*
  536.      * We must flush the output buffer.
  537.      */
  538.     flush_output ();
  539.     if (name != 0)
  540.         (void)close (fd);
  541. }
  542.  
  543. # ifndef FAST_COPY
  544. char *
  545. FAST_COPY (from, to, count)
  546. register char * from, * to;
  547. register int count;
  548. {
  549.     register char * tmp;
  550.  
  551.     tmp = to;
  552.     while (count--)
  553.         *to++ = *from++;
  554.     return (tmp);
  555. }
  556. # endif FAST_COPY
  557.