home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / c_spec / sources / sub.c < prev    next >
C/C++ Source or Header  |  1986-02-20  |  7KB  |  267 lines

  1. #include <stdio.h>
  2. #include <ctype.h>
  3.  
  4. /*    A very stupid sed (ie. it only supports the substitiute
  5.  *    command. Usage is:
  6.  *
  7.  *        set <cmd> [<file list>]
  8.  *
  9.  *    where <cmd> is:
  10.  *
  11.  *        [s]/<pat>/<string>[/g]
  12.  *
  13.  *    The regular expression <pat> is replaced with <string> everywhere
  14.  *    it's found in the file. If the /g is present it's replaced
  15.  *    everywhere on the line, else only the first ocurance on the line
  16.  *    is replaced. In the replacement <string>:
  17.  *
  18.  *        \e is mapped to ESC
  19.  *        \t is mapped to HT
  20.  *        \s is mapped to ' '
  21.  *        \n is mapped to LF
  22.  *        \b is mapped to BS
  23.  *        \& is mapped to &
  24.  *        \\ is mapped to \
  25.  *        &  is mapped to the string that matched <pat>
  26.  *        \xYY          is mapped to   the hex number YY.
  27.  *        \<anything else> is mapped to   <anything else>
  28.  */
  29.  
  30. /*    HEX_L(c) converts c (a hex digit in ASCII) to a number.
  31.  *    HEX_H(c) does the same but puts the result in the high nibble.
  32.  */
  33.  
  34. #define BSIZE    256    /* Max input line size    */
  35. typedef char    TOKEN;
  36. #define HEX_L(c)    ( isdigit(c) ?  (c)-'0' : ((toupper(c))-'A')+10)
  37. #define HEX_H(c)    ( HEX_L(c) << 4 )
  38. #define E(x)        fprintf(stderr, "%s\n", x);
  39.  
  40.  
  41. /*------------------------------------------------------------------*/
  42.  
  43. extern TOKEN    *makepat( char *, int        );
  44. extern char    *matchs ( char *, TOKEN *, int    );
  45.  
  46. /*------------------------------------------------------------------*/
  47.  
  48. TOKEN    *getpat( pp, isglobal )
  49. char    **pp;
  50. int    *isglobal;
  51. {
  52.     /*    Return a pointer to the pattern template for string.
  53.      *    Update isglobal to true if /g is found at the
  54.      *    end of the string. Update *pp to point at the replacement
  55.      *    string.
  56.      */
  57.  
  58.     register int    delim;
  59.     register char    *p = *pp ;
  60.     TOKEN        *pat ;
  61.  
  62.     if( *p == 's' )        /* Extract the expression:        */
  63.         p++;        /* Skip the 's' if it's there        */
  64.     delim = *p++ ;        /* Get the delimiter and skip past it    */
  65.  
  66.  
  67.     if( !(pat = makepat(p, delim)) )
  68.     {
  69.         fprintf(stderr, "Sub: Illegal pattern\n");
  70.         exit( 1 );
  71.     }
  72.  
  73.  
  74.     while( *p && *p != delim )    /* Skip past the pattern */
  75.         p++;
  76.     if( *p )            /* And the delimiter     */
  77.         p++;
  78.  
  79.     *pp = p;            /* remember the replacement str */
  80.  
  81.     while( *p && *p != delim )    /* get rid of the second delim  */
  82.         p++;
  83.  
  84.     if( *p )
  85.         *p++ = '\0';        /* by replacing it with a null    */
  86.     else
  87.         *p   = '\0';
  88.  
  89.     *isglobal = *p == 'g' ;
  90.     return pat;
  91. }
  92.  
  93. /*------------------------------------------------------------------*/
  94.  
  95. int    subst( src, pat, replacement, global )
  96. TOKEN    *pat;
  97. char    *src, *replacement;
  98. {
  99.     /*    Print the string with any replacement made. Substitute
  100.      *    & in the replacement string with the src pattern.
  101.      *    Expand:
  102.      *        \& in the replacement string to '&'
  103.      *        \s in the replacement string to a space
  104.      *        \t in the replacement string to a tab
  105.      *        \n in the replacement string to a newline.
  106.      *
  107.      *    If global is true the routine is applied recursively
  108.      *    to the part of src that follows a matched pattern. 
  109.      */
  110.  
  111.     register char    *p, *start, *end;
  112.  
  113.     p = src + (strlen(src)-1);  /* Remove \n from the end of the    */
  114.     if( *p == '\n' )        /* the line if one's there.        */
  115.         *p = '\0';
  116.  
  117.     if( !(start = matchs(src, pat, 0)) )
  118.     {
  119.         puts( src );        /* No match found    */
  120.         return( 0 );        /* Just print string    */
  121.     }
  122.  
  123.     end = matchs(start, pat, 1);
  124.                 
  125.     while( src < start )
  126.         fputchar( *src++ );
  127.  
  128.     print_replacement( replacement, start, end );
  129.  
  130.  
  131.     if( *end )    /* Increment end to point at the start of the    */
  132.         end++;    /* tail rather than the end of the match string */
  133.  
  134.     if( *end && global )
  135.         return( 1 + subst(end, pat, replacement, global) );
  136.  
  137.     puts( end );
  138.     return( 1 );
  139. }
  140.  
  141. /*----------------------------------------------------------------------*/
  142.  
  143.  
  144. print_replacement( rep, start, end )
  145. char    *rep, *start, *end;
  146. {
  147.     /*    Print the replacement string. start and end are the
  148.      *    boundarys of the string inserted instead of & in the
  149.      *    replacement string. In addition:
  150.      *        \e is mapped to ESC
  151.      *        \t is mapped to HT
  152.      *        \s is mapped to ' '
  153.      *        \n is mapped to LF
  154.      *        \b is mapped to BS
  155.      *        \xYY is mapped to the hex number YY.
  156.      */
  157.  
  158.     register char    *p;
  159.     register int    i;
  160.  
  161.     for( ; *rep ; rep++ )
  162.     {
  163.         if( *rep == '\\' )
  164.         {
  165.             switch( *++rep )
  166.             {
  167.             case 'b':    fputchar('\b'); break;
  168.             case 'e':    fputchar(0x1b); break;
  169.             case 'n':    fputchar('\n');    break;
  170.             case 's':    fputchar(' ' );    break;
  171.             case 't':    fputchar('\t');    break;
  172.             default:    fputchar(*rep);    break;
  173.             case 'x':    rep++;
  174.                     i  = HEX_H( *rep );
  175.                     rep++;
  176.                     i |= HEX_L( *rep );
  177.                     fputchar(i);
  178.                     break;
  179.             }
  180.         }
  181.         else if( *rep == '&' )
  182.             for( p = start; *p && p <= end ; fputchar(*p++) )
  183.                 ;
  184.         else
  185.             fputchar( *rep );
  186.     }
  187. }
  188.  
  189. /*----------------------------------------------------------------------*/
  190.  
  191. usage()
  192. {
  193.     E("Usage:            sub <command> [<file list>]\n")
  194.     E("Scan through the file list (or standard input if no");
  195.     E("files are listed, substituting all matches of a regular");
  196.     E("expression specified in <command> with an alternate pattern");
  197.     E("The <command> syntax is:");
  198.     E("                         [s]/<pat>/<str>/[g]\n");
  199.     E("The leading s, if present, is ignored. If the trailing g is there,");
  200.     E("all (rather than just the first) possible substitutions are made");
  201.     E("on a line. The delimiter (/ above) can be any character. <pat> is");
  202.     E("a grep-like regular expression (type \"grep --\" for more details");
  203.     E("about regular expressions).  <str> is the string that will replace");
  204.     E("<pat> in the outpt file. It can be any ASCII string but the ");
  205.     E("following are treated specially when found in a <str>:");
  206.     E("");
  207.     E("\\e   is mapped to ESC  \\t is mapped to HT   \\s is mapped to ' '");
  208.      E("\\n   is mapped to LF   \\b is mapped to BS   \\& is mapped to &");
  209.      E("\\xDD is mapped to the hex number DD.");
  210.      E("\\<anything else> is mapped to <anything else>");
  211.     E("&  is mapped to the string that matched <pat>");
  212.  
  213.     exit( 1 );
  214. }
  215.  
  216. /*------------------------------------------------------------------*/
  217.  
  218. main( argc, argv )
  219. char    **argv;
  220. {
  221.     register int    numsubst = 0;
  222.     char        *p        ;
  223.     int        isglobal = 0;
  224.     TOKEN        *pat        ;
  225.     int        delim        ;
  226.     FILE        *fp = stdin ;
  227.     static     char    buf[BSIZE]  ;
  228.     static   int    use_stdin   ;
  229.  
  230.     ctlc();
  231.     reargv( &argc, &argv );
  232.  
  233.     E("SUB: Copyright (c) 1986, Allen I. Holub. All rights reserved\n");
  234.  
  235.  
  236.     if( argc < 2 || (argc > 1 && argv[1][0] == '-') )
  237.         usage();
  238.  
  239.     use_stdin = (--argc==1);  /* Use standard input as the input    */
  240.                   /* stream if argc == 1.        */
  241.  
  242.     p = *++argv;
  243.     pat = getpat( &p, &isglobal );
  244.  
  245.  
  246.     if( use_stdin )
  247.         while( fgets(buf, BSIZE, stdin) )
  248.             numsubst += subst(buf, pat, p, isglobal );
  249.  
  250.     else for( ++argv; --argc > 0  ; argv++ )
  251.     {
  252.         if( !(fp = fopen(*argv, "r")) )
  253.         {
  254.             fprintf(stderr,"Can't open %s\n", *argv );
  255.             exit( 1 );
  256.         }
  257.  
  258.         while( fgets(buf, BSIZE, fp) )
  259.             numsubst += subst(buf, pat, p, isglobal );
  260.  
  261.         fclose( fp );
  262.     }
  263.  
  264.  
  265.     fprintf(stderr, "\n%d substitutions made\n", numsubst );
  266. }
  267.