home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / contrib / smail / smail-3.1 / smail-3 / smail-3.1.28 / util / gleem.c < prev    next >
C/C++ Source or Header  |  1992-07-11  |  15KB  |  606 lines

  1. /* @(#)util/gleem.c    1.4 7/11/92 11:39:57 */
  2. /*
  3.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  4.  *    Copyright (C) 1992  Ronald S. Karr
  5.  * 
  6.  * See the file COPYING, distributed with smail, for restriction
  7.  * and warranty information.
  8.  */
  9.  
  10. /*
  11.  * gleem - gleem useful map info from standard input
  12.  *
  13.  * usage:  
  14.  *    gleem [-n name] [-c] [-f [-d dir]] [-p] [-s] [-F] file ...
  15.  *
  16.  *    -n name        name mode, convert to:  name { ... } form, 
  17.  *                imples -c, ignores -s
  18.  *    -c        strip most comments
  19.  *    -f        print:  file { filename }  before reading each file
  20.  *    -d dir        add dir to filenames not starting with '/'
  21.  *    -p        print:  private {}  after reading each file
  22.  *    -s        convert delete & adjust directives to whitespace + error
  23.  *    -F        convert file directives to spaces + error
  24.  *    file ...    files to read, - means stdin & -f shows [stdin]
  25.  *
  26.  *
  27.  * If name mode:  (-n name)
  28.  *
  29.  *    Since -c is implied,  comments and blank lines are discarded.  
  30.  *    If the entire line is discarded, no further processing is done.
  31.  *
  32.  *    Next, the <line> is processed as follows:  The whitespace 
  33.  *    within ()'s is removed.  Whitespace between a token and a 
  34.  *    '(' is removed.  All tokens are separated by commas and all 
  35.  *    whitespace is removed.  The final comma and newline, if
  36.  *    any, is removed.
  37.  *
  38.  *     Last, the string:  "name {<line>}" is printed.
  39.  *
  40.  * In not name mode: (no -n)
  41.  *
  42.  *    Print the contents of each file, striping comments if -c was given.
  43.  *
  44.  * A comment begins with a # and ends with a newline.  A token is a set of
  45.  * chars that does not include whitespace, commas and ()'s.  A whitespace
  46.  * char is a tab newline.  Blank lines are lines containing zero or more
  47.  * whitespace chars.
  48.  *
  49.  * Note: Comment lines are not completely stripped.  Doing so would alter the
  50.  *       line counts and throw off error reporting.  Also pathalias allows
  51.  *     comments to exist in the middle of a line.  If a comment is found
  52.  *     in the middle of the line, it is replaced by a single '#'.  If
  53.  *     the single '#' would start the line, a space is placed in before
  54.  *     the single '#'.
  55.  */
  56.  
  57. #include <stdio.h>
  58. #include "defs.h"
  59.  
  60. #define BUFFER_SIZE 4096        /* length of longest line */
  61.  
  62. void pack();                /* pack a line in second stage */
  63. void strip_com();            /* strip comments from a string */
  64. void cat_file();            /* cat a file in a special way */
  65. void usage();                /* print a usage message and exit */
  66. void safemap();                /* perform safemap striping */
  67. void file_2_space();            /* convert file {...} to spaces */
  68. char *program;                /* our name */
  69. int errors = 0;                /* 1 ==> errors were encountered */
  70.  
  71. main(argc, argv)
  72.     int argc;                /* arg count */
  73.     char *argv[];            /* args */
  74. {
  75.     int c;                /* operand */
  76.     int file_to_space = 0;        /* 1 ==> convert file to spaces */
  77.     int strip_comments = 0;        /* 1 ==> strip input comments */
  78.     int safemap_mode = 0;        /* 1 ==> remove delete, adjust lines */
  79.     int print_private = 0;        /* 1 ==> print private {} */
  80.     int print_filename = 0;        /* 1 ==> print file { filename } */
  81.     char *dirname = NULL;        /* dirname for non / based files */
  82.     char *name = NULL;            /* non-NULL ==> name mode */
  83.     char *filename;            /* the filename we are reading */
  84.     extern int optind;            /* operand index */
  85.     extern char *optarg;        /* operand argument */
  86.  
  87.     /*
  88.      * parse args
  89.      */
  90.     program = argv[0];
  91.     while ((c = getopt(argc, argv, "n:cfd:psF")) != EOF) {
  92.     switch (c) {
  93.     case 'n':        /* token name, token mode */
  94.         name = optarg;
  95.         break;
  96.     case 'c':        /* strip comments */
  97.         strip_comments = 1;
  98.         break;
  99.     case 'f':        /* print leading file { filename } names */
  100.         print_filename = 1;
  101.         break;
  102.     case 'd':        /* directory base for non / based files */
  103.         dirname = optarg;
  104.         break;
  105.     case 'p':        /* print trailing private {} */
  106.         print_private = 1;
  107.         break;
  108.     case 's':        /* strip delete and adjust directives */
  109.         safemap_mode = 1;
  110.         break;
  111.     case 'F':        /* convert file directives to spaces */
  112.         file_to_space = 1;
  113.         break;
  114.     default:
  115.         usage();
  116.         break;
  117.     }
  118.     }
  119.     /* be sure we have filenames */
  120.     if (optind >= argc) {
  121.     usage();
  122.     }
  123.     /* name mode -n imples -c, disables -s */
  124.     if ( name != NULL ) {
  125.     strip_comments = 1;        /* -c implied */
  126.     safemap_mode = 0;        /* -s disabled */
  127.     }
  128.  
  129.     /*
  130.      * process each file
  131.      */
  132.     for (filename=argv[optind]; optind < argc; ++optind,filename=argv[optind]) {
  133.  
  134.     /*
  135.      * if -f, print a file directive
  136.      */
  137.     if ( print_filename ) {
  138.  
  139.         /* catch the stdin case */
  140.         if (strcmp(filename, "-") == 0) {
  141.         puts("file {[stdin]}");
  142.  
  143.         /* catch the /-root based filename, or if we have no dirname */
  144.         } else if (filename[0] == '/' || dirname == NULL) {
  145.         printf("file {%s}\n", filename);
  146.  
  147.         /* non /-root based file, append dirname */
  148.         } else {
  149.         printf("file {%s/%s}\n", dirname, filename);
  150.         }
  151.     }
  152.  
  153.     /*
  154.      * proces the file
  155.      */
  156.     cat_file(filename, name, strip_comments, safemap_mode, file_to_space);
  157.  
  158.     /*
  159.      * print a private directive if needed
  160.      */
  161.     if ( print_private ) {
  162.         puts("private {}");
  163.     }
  164.     }
  165.  
  166.     /*
  167.      * exit noting if errors happened
  168.      */
  169.     exit(errors);
  170. }
  171.  
  172. /*
  173.  * pack - pack a line for name mode
  174.  *
  175.  *     The whitespace within ()'s is removed.  Whitespace between a token
  176.  *     and a '(' is removed.  All tokens are separated by commas and
  177.  *     all whitespace is removed.  The final comma and newline, if
  178.  *     any, is removed.
  179.  */
  180. void
  181. pack( line )
  182.     char *line;            /* the line to pack */
  183. {
  184.     int in_paren;        /* 1 ==> inside ()'s */
  185.     int space;            /* 1 ==> last char was a space */
  186.     int comma;            /* 1 ==> last char was a comma */
  187.     char *p;            /* pointer */
  188.     char *q;            /* pointer */
  189.     char buf[BUFFER_SIZE+3];    /* I/O buffer */
  190.     char *malloc();        /* stroage allocator */
  191.  
  192.     /*
  193.      * remove whitespace within ()'s
  194.      */
  195.     q = line;        /* from */
  196.     p = buf;        /* to */
  197.     in_paren = 0;    /* not inside ()'s */
  198.     while ( *q ) {
  199.     /* detect open and close ()'s */
  200.     switch (*q) {
  201.     case '(':
  202.         in_paren = 1;
  203.         *p++ = *q++;
  204.         break;
  205.     case ')':
  206.         in_paren = 0;
  207.         *p++ = *q++;
  208.         break;
  209.     case ' ':
  210.     case '\t':
  211.         if (in_paren == 0) {
  212.         *p++ = *q++;
  213.         } else {
  214.         ++q;    /* skip whitespace insize a () */
  215.         }
  216.         break;
  217.     case '\n':
  218.         ++q;    /* skip newline */
  219.         break;
  220.     default:
  221.         *p++ = *q++;
  222.         break;
  223.     }
  224.     }
  225.     *p = '\0';
  226.  
  227.     /*
  228.      * remove leading whitespace, convert multiple spaces into a
  229.      * comma, except when before a ( or a comma
  230.      */
  231.     q = buf;        /* from */
  232.     p = line;        /* to */
  233.     space = 0;        /* have not seen spaces */
  234.     comma = 0;        /* have not seen commas */
  235.     while (*q == ' ' || *q == '\t') {      /* skip leading whitespace */
  236.     ++q;
  237.     }
  238.     while ( *q ) {
  239.     switch (*q) {
  240.     case ' ':
  241.     case '\t':
  242.         ++q;        /* skip whitespace until later, maybe */
  243.         space = 1;
  244.         break;
  245.     case '(':
  246.         *p++ = *q++;    /* no comma before ( or comma */
  247.         space = 0;
  248.         comma = 0;
  249.         break;
  250.     case ',':
  251.         if (comma == 0) {
  252.         *p++ = *q++;    /* copy only one comma */
  253.         comma = 1;
  254.         } else {
  255.         ++q;        /* skip multiple commas */
  256.         }
  257.         break;
  258.     default:
  259.         if (space == 1 && comma == 0) {
  260.         *p++ = ',';    /* replace multi whitespace with a comma */
  261.         }
  262.         comma = 0;
  263.         *p++ = *q++;
  264.         space = 0;
  265.         break;
  266.     }
  267.     }
  268.  
  269.     /*
  270.      * remove any trailing commas
  271.      */
  272.     while (--p > line && *p == ',') {
  273.     }
  274.     if (*p == ',') {
  275.     *p = '\0';
  276.     } else {
  277.     *(p+1) = '\0';
  278.     }
  279. }
  280.  
  281. /*
  282.  * strip_com - perform -c comment striping
  283.  *
  284.  * This routine removes most comments and all trailing whitespace from 
  285.  * the string passed to it.  The trailing newline is always removed.
  286.  *
  287.  * A comment begins with a '#' and ends with a newline. A whitespace
  288.  * char is a tab newline.  Blank lines are lines containing zero or 
  289.  * more whitespace chars.
  290.  *
  291.  * Note: Comment lines are not completely stripped.  Doing so would alter the
  292.  *       line counts and throw off error reporting.  Also pathalias allows
  293.  *     comments to exist in the middle of a line.  If a comment is found
  294.  *     in the middle of the line, it is replaced by a single '#'.  If
  295.  *     the single '#' would start the line, a space is placed in before
  296.  *     the single '#'.
  297.  */
  298. void
  299. strip_com( buf )
  300.     char *buf;            /* string to strip */
  301. {
  302.     register char *p;        /* index */
  303.     int found_comment=0;    /* 1 => we removed a #comment */
  304.  
  305.     /*
  306.      * remove a comment
  307.      */
  308.     p = index(buf, '#');
  309.     if (p != NULL) {
  310.  
  311.     /* strip */
  312.     *p = '\0';    
  313.  
  314.     /* if entire lne was a comment, remove it all and return */
  315.     if (p == buf) {
  316.         return;
  317.     }
  318.  
  319.     /* note mid line comment found */
  320.     found_comment = 1;
  321.     }
  322.  
  323.     /*
  324.      * scan over any trailing whitespace
  325.      */
  326.     p = buf + strlen(buf) - 1;
  327.     while (p >= buf && (*p == ' ' || *p == '\t' || *p == '\n')) {
  328.     --p;
  329.     }
  330.  
  331.     /*
  332.      * clear out any whitespace
  333.      *
  334.      * If we removed a comment, put the '#' back.
  335.      */
  336.     if (found_comment) {
  337.     /* if the '#' would start a line, blank fill first */
  338.     if (p < buf) {
  339.         *++p = ' ';
  340.     }
  341.     /* minimal comment */
  342.     *++p = '#';    
  343.     }
  344.     *++p = '\0';    /* strip */
  345.     return;
  346. }
  347.  
  348. /*
  349.  * cat_file - output a file
  350.  *
  351.  * opens a filename, writes it contents to stadard output.  Strips comments, 
  352.  * removes problem lines and pack for name mode if needed.  Problems lines
  353.  * are lines that begin with the token "delete" or "adjust" or "file".
  354.  */
  355. void
  356. cat_file( filename, name, strip_comments, safemap_mode, file_to_space )
  357.     char *filename;        /* the name of the file to output */
  358.     char *name;            /* non-NULL ==> name mode */
  359.     int strip_comments;        /* 1 ==> strip input comments */
  360.     int safemap_mode;        /* 1 ==> remove delete, adjust lines */
  361.     int file_to_space;        /* 1 ==> convert file to spaces */
  362. {
  363.     int linenum;        /* current line number */
  364.     char buf[BUFFER_SIZE+3];    /* I/O buffer */
  365.     FILE *stream;        /* file stream to read */
  366.  
  367.     /*
  368.      * try to open the file
  369.      */
  370.     if (strcmp(filename, "-") == 0) {
  371.     stream = stdin;        /* - ==> read from stdin */
  372.     filename = "[stdin]";
  373.     } else {
  374.     stream = fopen(filename, "r");
  375.     }
  376.     if (stream == NULL) {
  377.     /* unable to open, object and return */
  378.     fprintf(stderr, "%s: unable to open file: %s\n", program, filename);
  379.     errors = 1;
  380.     return;
  381.     }
  382.  
  383.     /*
  384.      * process each line
  385.      *
  386.      * We allow lines to be up to BUFFER_SIZE chars long.  But we
  387.      * read BUFFER_SIZE+1 chars (i.e., fgets value of BUFFER_SZIE+2)
  388.      * chars to detect a longer line.  If the line were BUFFER_SIZE+1
  389.      * chars, then the BUFFER_SIZE+1-th char would not be '\0'.
  390.      */
  391.     clearerr(stream);    /* no errors yet */
  392.     linenum = 0;    /* no lines read yet */
  393.     buf[BUFFER_SIZE] = '\0';    /* used to detect line long lines */
  394.     while (fgets(buf, BUFFER_SIZE+2, stream) != NULL) {
  395.  
  396.     /*
  397.      * count the line
  398.      */
  399.     ++linenum;
  400.  
  401.     /*
  402.      * firewall - see if the line is too long
  403.      */
  404.     if (buf[BUFFER_SIZE] != '\0') {
  405.         fprintf(stderr, "%s: file: %s line: %d longer than %d chars\n",
  406.         program, filename, linenum, BUFFER_SIZE);
  407.         errors = 1;
  408.         break;
  409.     }
  410.  
  411.     /*
  412.      * strip comments if needed
  413.      */
  414.     if (name != NULL || strip_comments) {
  415.         strip_com(buf);
  416.     }
  417.  
  418.     /*
  419.      * convert file directives to spaces if needed
  420.      */
  421.     if (file_to_space) {
  422.         file_2_space(buf);
  423.     }
  424.  
  425.     /*
  426.      * pack the line, if name mode
  427.      */
  428.     if (name != NULL) {
  429.         pack(buf);
  430.     }
  431.  
  432.     /*
  433.      * perform safemap processing if needed
  434.      */
  435.     if (safemap_mode && name == NULL) {
  436.         safemap(filename, linenum, buf);
  437.     }
  438.  
  439.     /*
  440.      * print the newly formed line
  441.      */
  442.     if (name != NULL) {
  443.         /* output in name mode style */
  444.         if (buf[0] != '\0') {
  445.         printf("%s {%s}\n", name, buf);
  446.         }
  447.     } else {
  448.         /* output line, being sure to write a newline */
  449.         if ( strip_comments ) {
  450.         puts(buf);        /* replace the stripped newline */
  451.         } else {
  452.         fputs(buf, stdout);    /* newline is already there */
  453.         }
  454.         }
  455.     }
  456.  
  457.     /*
  458.      * examine stop reason
  459.      */
  460.     if ( ferror(stream) ) {
  461.     fprintf(stderr, "%s: error in reading file: %s line: %d\n", 
  462.         program, filename, linenum);
  463.     errors = 1;
  464.     }
  465.  
  466.     /*
  467.      * flush & close if not stdin
  468.      */
  469.     fflush(stdout);
  470.     if (stream != stdin) {
  471.     (void) fclose(stream);
  472.     }
  473. }
  474.  
  475. /*
  476.  * usage - print usage message and exit
  477.  */
  478. void
  479. usage()
  480. {
  481.     fprintf(stderr, 
  482.     "usage: %s [-n name] [-f [-d dir]] [-c] [-p] [-s] file ...\n",
  483.     program);
  484.     exit(2);
  485. }
  486.  
  487. /*
  488.  * safemap - avoid all delete, adjust and file directives
  489.  *
  490.  * Any lines of the form:
  491.  *
  492.  *    ^delete[ \t]*{
  493.  *    ^adjust[ \t]*{
  494.  *
  495.  * are removed.  The 'delete' and 'adjust' result in errors.
  496.  */
  497. void
  498. safemap( filename, linenum, buf )
  499.     char *filename;        /* filename or [stdin] being read */
  500.     int linenum;        /* current line number */
  501.     char *buf;            /* the buffer to check for unsafe words */
  502. {
  503.     register char *p;        /* pointer */
  504.  
  505.     /*
  506.      * rule out good lines quickly
  507.      */
  508.     switch (buf[0]) {
  509.     case 'd':
  510.     case 'a':
  511.     break;
  512.     default:
  513.     return;        /* nothing unsafe about this line */
  514.     }
  515.  
  516.     /* 
  517.      * begins with an unsafe letter, is it really an unsafe word? 
  518.      */
  519.     if (strncmp(buf, "delete", 6) == 0) {
  520.  
  521.     /* leading chars are ok, but is if followed by space/tab? */
  522.     for (p = buf+6; *p; ++p) {
  523.  
  524.         /* look for start of {} group after word */
  525.         if (*p == '{' /*}*/) {
  526.         /* we found end of word, start of {} group, unsafe! */
  527.         buf[0] = '\0';
  528.         fprintf(stderr,
  529.             "%s: bogus delete directive in file: %s line: %d\n",
  530.             program, filename, linenum);
  531.         errors = 1;
  532.         break;
  533.         }
  534.  
  535.         /* must be between words then */
  536.         if (*p != ' ' && *p != '\t') {
  537.         break;        /* not an unsafe word */
  538.         }
  539.     }
  540.  
  541.     } else if (strncmp(buf, "adjust", 6) == 0) {
  542.  
  543.     /* leading chars are ok, but is if followed by space/tab? */
  544.     for (p = buf+6; *p; ++p) {
  545.  
  546.         /* look for start of {} group after word */
  547.         if (*p == '{' /*}*/) {
  548.         /* we found end of word, start of {} group, unsafe! */
  549.         buf[0] = '\0';
  550.         fprintf(stderr,
  551.             "%s: bogus adjust directive in file: %s line: %d\n",
  552.             program, filename, linenum);
  553.         errors = 1;
  554.         break;
  555.         }
  556.  
  557.         /* must be between words then */
  558.         if (*p != ' ' && *p != '\t') {
  559.         break;        /* not an unsafe word */
  560.         }
  561.     }
  562.     }
  563.     return;
  564. }
  565.  
  566. /*
  567.  * file_2_space - convert file {...} lines to spaces
  568.  *
  569.  * This allows one to strip file directives from uuwho input without changing
  570.  * the position within the file.
  571.  */
  572. void
  573. file_2_space( buf )
  574.     char *buf;            /* the string to examine */
  575. {
  576.     char *p;            /* pointer */
  577.  
  578.     /*
  579.      * look for a file directive
  580.      */
  581.     if (strncmp(buf, "file", 4) == 0) {
  582.  
  583.     /* leading chars are ok, but is if followed by space/tab? */
  584.     for (p = buf+4; *p; ++p) {
  585.  
  586.         /* look for start of {} group after word */
  587.         if (*p == '{' /*}*/) {
  588.  
  589.         /*
  590.          * convert a file directive into spaces
  591.          */
  592.         for (p=buf; *p && *p != '\n'; ++p) {
  593.             *p = ' ';
  594.         }
  595.         return;
  596.         }
  597.         
  598.         /* must be between words then */
  599.         if (*p != ' ' && *p != '\t') {
  600.         break;        /* not an unsafe word */
  601.         }
  602.     }
  603.     }
  604.     return;
  605. }
  606.