home *** CD-ROM | disk | FTP | other *** search
/ back2roots/padua / padua.7z / padua / uucp / auucp+-1.02 / fuucp_plus_src.lzh / unsharmap / unsharmap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-11-21  |  20.3 KB  |  891 lines

  1. /* @(#)unsharmap.c    1.6 2/5/89 14:14:01 */
  2. /*
  3.  *    Copyright (C) 1988 Ronald S. Karr and Landon Curt Noll
  4.  * 
  5.  * See the file COPYING, distributed with smail, for restriction
  6.  * and warranty information.
  7.  */
  8.  
  9. /*
  10.  * unsharmap - unshar a USENET comp.news.maps article
  11.  *
  12.  * usage:  unsharmap [-d dir] < filelist > log
  13.  *
  14.  *       -d dir    - cd to 'dir' before unsharing
  15.  *       -n newsgroups - colon separated list of allowed newsgroups
  16.  *       -p        - use stdin (for piped input from 'sys')
  17.  *
  18.  * where "filelist" contains a list of files that are in the format below.
  19.  *
  20.  * The USENET map project distributes files in the following format:
  21.  *
  22.  * The header, which starts at the firts line and continues up until
  23.  * there is a blank line as the follow lins it:
  24.  *
  25.  *    Newsgroups: comp.mail.maps
  26.  *    Subject: UUCP ...
  27.  *    Message-ID: ...
  28.  *    Date: ...
  29.  *    Approved: ...
  30.  *
  31.  * These lines are not the complete list, nor do they show up in that order.
  32.  * After the header is a blank line.  Next comes the shar file preamble
  33.  * which consists of a bunch of lines that begin with a :, followed
  34.  * by the 2 lines:
  35.  *
  36.  *    echo shar: extracting THE-FILE-NAME
  37.  *    cat << 'SHAR_EOF' > THE-FILE-NAME
  38.  *
  39.  * what follows is the map data intended to override THE-FILE-NAME in the
  40.  * UNSHAR_MAP_DIR directory.  The final 3 lines:
  41.  *
  42.  *    SHAR_EOF
  43.  *    :   End of shell archive
  44.  *    exit 0
  45.  *
  46.  * terminate the shar file, and are not considered a part of the map data.
  47.  *
  48.  * For each shar file processed, the following information is written
  49.  * to stdout:
  50.  *
  51.  *    filename read
  52.  *    filename written
  53.  *    subject line
  54.  *    message-ID
  55.  *    date
  56.  *    approved info
  57.  *    any error message
  58.  *
  59.  * All error message lines begin with 'error:'.
  60.  *
  61.  * exits by 0 if there was no errors, non-0 otherwise.
  62.  */
  63.  
  64. #include <stdio.h>
  65. #include <stdlib.h>
  66. #include <string.h>
  67.  
  68. #define HEADER_OK 1            /* the article header is correct */
  69. #define HEADER_ERROR 2            /* bad header, skip and log */
  70. #define HEADER_SKIP 3            /* not a map shar, ignore file */
  71.  
  72. #define USAGE "AmigaUUCP Plus: unsharmap 1.00 (5 Aug 90)\nWritten by Ingo Feulner\n\nerror: usage: %s [-d dir] < filelist > log 2> error\n"
  73.  
  74. static char *version20 = "$VER: unsharmap 1.00 (5 Aug 90)\n\r";
  75.  
  76. char *program;                /* our name */
  77. char err[BUFSIZ+1];            /* error and log message */
  78. char buf[BUFSIZ+1];            /* input buffer */
  79. char mapname[BUFSIZ+1];            /* name of the output map file */
  80. int lineno = 0;                /* current line number */
  81. char *newsgroups = "sub.config.maps";    /* : list of allowed newsgroups */
  82.  
  83. char *get_date();            /* system dependent date string */
  84. void log();                /* write to log and error log */
  85. int check_header();            /* check the article header */
  86. void skip_article();            /* skip a bad article */
  87. void ignore_article();            /* ignore the non-map article */
  88. char *check_preamble();            /* check shar preamble, get map name */
  89. int write_map();            /* write a map file */
  90. char *check_newline();            /* check to be sure we read a '\n' */
  91.  
  92. char *xmalloc();            /* for string.c and strcolon()*/
  93. char *xrealloc();            /* for string.c and strcolon() */
  94.  
  95. char *strcolon();            /* grap strcolon() from src/string.c */
  96.  
  97. main( argc, argv )
  98.     int argc;                /* arg count */
  99.     char *argv[];            /* args */
  100. {
  101.     char filename[BUFSIZ+1];        /* name of the input article */
  102.     char *p;                /* pointer */
  103.     int c;                /* the option flag */
  104.     int header;                /* HEADER_OK,ERROR,SKIP */
  105.     int errors=0;            /* number of problems found */
  106.     int stdinfile;            /* get just one file from std input */
  107.     FILE *article;            /* input article stream */
  108.     extern char *optarg;        /* the option argument */
  109.     extern int optind;            /* operand index */
  110.  
  111.     char path[256];
  112.  
  113.     (void)getcwd(path, 256);
  114.  
  115.     /*
  116.      * parse args
  117.      */
  118.     program = argv[0];
  119.     while ((c = getopt(argc, argv, "pd:n:")) != EOF) {
  120.     switch(c) {
  121.     case 'p':
  122.         stdinfile = 1;
  123.         break;
  124.     case 'd':            /* directory to cd to */
  125.         if (chdir(optarg) < 0) {
  126.         sprintf(err, "error: can not cd to %s\n", optarg);
  127.         log(err);
  128.         sprintf(err, "exiting: status: 5 -  %s", get_date());
  129.         log(err);
  130.         exit(5);
  131.         }
  132.         break;
  133.     case 'n':
  134.         newsgroups = optarg;
  135.         break;
  136.  
  137.     case '?':
  138.         sprintf(err, USAGE, program);
  139.         log(err);
  140.         sprintf(err, "exiting: status: 1 -  %s", get_date());
  141.         log(err);
  142.         exit(1);
  143.         break;
  144.     }
  145.     }
  146.     if (optind != argc) {
  147.     sprintf(err, USAGE, program);
  148.     log(err);
  149.     sprintf(err, "exiting: status: 1 -  %s", get_date());
  150.     log(err);
  151.     exit(1);
  152.     }
  153.     
  154.  
  155.     /*
  156.      * process all file names on input
  157.      */
  158.     sprintf(err, "starting: %s", get_date());
  159.     log(err);
  160.     while (stdinfile || fgets(filename, BUFSIZ, stdin) != NULL)
  161.     {
  162.       if(!stdinfile)
  163.       {
  164.     /*
  165.      * open the map file
  166.      */
  167.     lineno = 0;            /* clear the line count */
  168.     if ((p = check_newline(filename)) == NULL)
  169.     {
  170.         sprintf(err, "error: filename longer than %d chars\n",
  171.             program, BUFSIZ-2);
  172.         log(err);
  173.         sprintf(err, "exiting: status: 2 -  %s", get_date());
  174.         log(err);
  175.         exit(2);
  176.     }
  177.     *p = '\0';    /* remove the newline from the filename */
  178.     if ((article = fopen(filename, "r")) == NULL)
  179.     {
  180.         sprintf(err, "error: can not open: %s\n", filename);
  181.         log(err);
  182.         ++errors;
  183.         continue;
  184.     }
  185.     sprintf(err, "filename: %s\n", filename);
  186.     log(err);
  187.       }
  188.       else
  189.       {
  190.     strcpy(filename,"stdin");
  191.     article = stdin;
  192.       }
  193.     /*
  194.      * verify the article header
  195.      *
  196.      * close file if error or skip
  197.      */
  198.     switch (header = check_header(article))
  199.     {
  200.       case HEADER_OK:        /* we have a good header */
  201.         break;
  202.       case HEADER_ERROR:        /* the article is bad, skip & log */
  203.         skip_article(article, filename);
  204.         ++errors;
  205.         break;
  206.       case HEADER_SKIP:        /* not a map article, ignore it */
  207.         ignore_article(article, filename);
  208.         break;
  209.       default:            /* how did we get here? */
  210.         sprintf(err, "error: check_header returned %d, why?\n", header);
  211.         log(err);
  212.         sprintf(err, "exiting: status: 3 -  %s", get_date());
  213.         log(err);
  214.         exit(3);
  215.     }
  216.     if (header != HEADER_OK) {
  217.         continue;            /* try another file */
  218.     }
  219.  
  220.     /*
  221.      * check the shar preamble, get the shar file name
  222.      */
  223.     if (check_preamble(article) == NULL) {
  224.         skip_article(article, filename);
  225.         ++errors;
  226.         continue;
  227.     }
  228.  
  229.     if(stdinfile) strcpy(filename,mapname);
  230.     /*
  231.      * write the map, verify final lines
  232.      */
  233.     if (write_map(article) != 0) {
  234.         skip_article(article, filename);
  235.         ++errors;
  236.         continue;
  237.     }
  238.  
  239.     /*
  240.      * all done with this article
  241.      */
  242.     fclose(article);
  243.     fflush(stdout);
  244.     if(stdinfile)break;
  245.     }
  246.  
  247.     /*
  248.      * note if everything went ok
  249.      */
  250.     sprintf(err, "exiting: status: %d -  %s", (errors>0) ? 4 : 0, get_date());
  251.     log(err);
  252.  
  253.     chdir(path);
  254.     exit( (errors>0) ? 4 : 0 );
  255. }
  256.  
  257.  
  258. /*
  259.  * get_date - get the date followed by newline in the standard way
  260.  *
  261.  * returns the date in ctime(3) format
  262.  */
  263. char *
  264. get_date()
  265. {
  266.     extern long time();
  267.     long clock = time((long *)0);   /* seconds since 1-jan-1970 0:00:00 GMT */
  268.     char *ctime();            /* convert clock into string */
  269.  
  270.     return(ctime(&clock));
  271. }
  272.  
  273.  
  274. /*
  275.  * log - write a message to the log
  276.  */
  277. void
  278. log( msg )
  279.     char *msg;                /* the message to write */
  280. {
  281.     /* write to the log */
  282.     fputs(msg, stdout);
  283. }
  284.  
  285.  
  286. /*
  287.  * check_header - check the article header
  288.  *
  289.  * This routine verifies that a article header has the following lines:
  290.  *
  291.  *    Newsgroups: comp.mail.maps
  292.  *    Subject: UUCP ...
  293.  *    Message-ID: ...
  294.  *    Date: ...
  295.  *    Approved: ...
  296.  *
  297.  * The text of all but the Newsgroups lines are logged.  If the Newsgroups
  298.  * line is bad, that too is logged.  The header ends before a blank line.
  299.  * The final blank line is read and discarded.
  300.  *
  301.  * returns HEADER_OK is all 5 header lines were read and were valid,
  302.  *         HEADER_ERROR is the header was bad and needs to be skipped & logged,
  303.  *       HEADER_SKIP if the file is not a UUCP shar file and should be skipped
  304.  */
  305. int
  306. check_header( article )
  307.     FILE *article;            /* the article stream */
  308. {
  309.     int saw_groups=0;            /* 1 ==> saw Newsgroups: */
  310.     int saw_subject=0;            /* 1 ==> saw Subject: */
  311.     int saw_message=0;            /* 1 ==> saw Message-ID: */
  312.     int saw_date=0;            /* 1 ==> saw Date: */
  313.     int saw_approved=0;            /* 1 ==> saw Approved: */
  314.     char *p;                /* temp */
  315.  
  316.     /*
  317.      * read the header
  318.      */
  319.     do {
  320.     /*
  321.      * read the line
  322.      */
  323.     clearerr(article);
  324.     if (fgets(buf, BUFSIZ, article) == NULL) {
  325.         if (ferror(article)) {
  326.         sprintf(buf, "error: bad header read after line %d\n", lineno);
  327.         log(err);
  328.         } else {
  329.         log("error: EOF while reading the header\n");
  330.         }
  331.         return(HEADER_ERROR);
  332.     }
  333.     ++lineno;            /* count this line */
  334.     if (! check_newline(buf)) {
  335.         sprintf(err, "error: line %d, header line too long\n", lineno);
  336.         log(err);
  337.         return(HEADER_ERROR);
  338.     }
  339.  
  340.     /*
  341.      * look for special types
  342.      */
  343.     switch (buf[0]) {
  344.     case 'n':
  345.     case 'N':            /* could be Newsgroups: */
  346.         if (strncmpic("Newsgroups: ", buf, 12) == 0) {
  347.         for (p = strcolon(newsgroups); p; p = strcolon((char *)NULL)) {
  348.             if (strncmpic(buf + 12, p, strlen(p)) == 0 &&
  349.             buf[12 + strlen(p)] == '\n')
  350.             {
  351.             break;
  352.             }
  353.         }
  354.         if (p != NULL) {
  355.             saw_groups = 1;
  356.         } else {
  357.             sprintf(err, "error: line %d, bad %s", lineno, buf);
  358.             log(err);
  359.             return(HEADER_ERROR);
  360.         }
  361.         }
  362.         break;
  363.     case 's':
  364.     case 'S':            /* Subject: buf */
  365.         if (strncmpic("Subject: ", buf, 9) == 0) {
  366.         if (strncmpic("Subject: UUCP ", buf, 14) == 0) {
  367.             saw_subject = 1;
  368.             log("    ");
  369.             log(buf);
  370.         } else {
  371.             log("    ");
  372.             log(err);
  373.             return(HEADER_SKIP); /* not a shar map file */
  374.         }
  375.         }
  376.         break;
  377.     case 'm':
  378.     case 'M':            /* Message-ID: buf */
  379.         if (strncmpic("Message-ID: ", buf, 12) == 0) {
  380.             saw_message = 1;
  381.             log("    ");
  382.             log(buf);
  383.         }
  384.         break;
  385.     case 'd':
  386.     case 'D':            /* Message-ID: buf */
  387.         if (strncmpic("Date: ", buf, 6) == 0) {
  388.         saw_date = 1;
  389.         log("    ");
  390.         log(buf);
  391.         }
  392.         break;
  393.     case 'a':
  394.     case 'A':            /* Message-ID: buf */
  395.         if (strncmpic("Approved: ", buf, 10) == 0) {
  396.         saw_approved = 1;
  397.         log("    ");
  398.         log(buf);
  399.         }
  400.         break;
  401.     }
  402.  
  403.     } while(strcmp("\n", buf) != 0);    /* while heading the header */
  404.  
  405.     /*
  406.      * report if er got everything
  407.      */
  408.     if (saw_groups == 0) {
  409.     log("error: no Newsgroups: line\n");
  410.     return(HEADER_ERROR);
  411.     }
  412.     if (saw_subject == 0) {
  413.     log("error: no Subject: line\n");
  414.     return(HEADER_ERROR);
  415.     }
  416.     if (saw_message == 0) {
  417.     log("error: no Message-ID: line\n");
  418.     return(HEADER_ERROR);
  419.     }
  420.     if (saw_date == 0) {
  421.     log("error: no Date: line\n");
  422.     return(HEADER_ERROR);
  423.     }
  424.     if (saw_approved == 0) {
  425.     log("error: no Approved: line\n");
  426.     return(HEADER_ERROR);
  427.     }
  428.     return(HEADER_OK);            /* passed all the tests */
  429. }
  430.  
  431. /*
  432.  * skip_article - skip article, log and close it
  433.  */
  434. void
  435. skip_article( article, filename )
  436.     FILE *article;            /* the article stream */
  437.     char *filename;            /* the filename we are skipping */
  438. {
  439.     /*
  440.      * log that we are skipping the remainder of the article
  441.      */
  442.     sprintf(err, "    skipping: %s after reading line %d\n", filename, lineno);
  443.     log(err);
  444.  
  445.     /*
  446.      * close the stream
  447.      */
  448.     fclose(article);
  449.  
  450.     /*
  451.      * report the close
  452.      */
  453.     sprintf(err, "finished: %s\n", filename);
  454.     log(err);
  455.     return;
  456. }
  457.  
  458.  
  459. /*
  460.  * ingore_article - ignore an article, close and report it
  461.  */
  462. void
  463. ignore_article( article, filename )
  464.     FILE *article;            /* the article stream */
  465.     char *filename;            /* the filename we are skipping */
  466. {
  467.     /*
  468.      * log that we are ignoring the remainder of the article
  469.      */
  470.     sprintf(err, "    ignore non map file: %s after reading line %d\n",
  471.         filename, lineno);
  472.     log(err);
  473.  
  474.     /*
  475.      * close the stream
  476.      */
  477.     fclose(article);
  478.  
  479.     /*
  480.      * report the close
  481.      */
  482.     sprintf(err, "finished: %s\n", filename);
  483.     log(err);
  484.     return;
  485. }
  486.  
  487.  
  488. /*
  489.  * check_preamble - check for a valid shar preamble
  490.  *
  491.  * The shar preamble consists of a bunch of lines that begin with :,
  492.  * followed by the 2 lines:
  493.  *
  494.  *    echo shar: extracting THE-FILE-NAME
  495.  *    cat << 'SHAR_EOF' > THE-FILE-NAME
  496.  *
  497.  * Any other line is printed to both logs as an error.  The "THE-FILE-NAME"
  498.  * string is returned if a valid preamble is found, NULL otherwise.
  499.  */
  500. char *
  501. check_preamble( article )
  502.     FILE *article;            /* the article stream */
  503. {
  504.     char *p;                /* pointer */
  505.     char *q;                /* pointer, NULL byte of mapname */
  506.     int bad_echo=0;            /* 1 ==> a bad echo was found */
  507.  
  508.     /*
  509.      * read the preamble
  510.      */
  511.     mapname[0] = '\0';    /* no mapname yet */
  512.     while (1) {
  513.     /*
  514.      * read the line
  515.      */
  516.     clearerr(article);
  517.     if (fgets(buf, BUFSIZ, article) == NULL) {
  518.         if (ferror(article)) {
  519.         sprintf(err, "error: bad preamble read after line %d\n",
  520.             lineno);
  521.         log(err);
  522.         } else {
  523.         log("error: EOF while reading the preamble\n");
  524.         }
  525.         return(NULL);
  526.     }
  527.     ++lineno;            /* count line */
  528.     if (! check_newline(buf)) {
  529.         sprintf(err, "error: line %d, preamble line too long\n", lineno);
  530.         log(err);
  531.         return(NULL);
  532.     }
  533.  
  534.     /*
  535.      * skip the : lines
  536.      */
  537.     if (buf[0] == ':') {
  538.         continue;
  539.     }
  540.  
  541.     /*
  542.      * look for the echo line
  543.      */
  544.     if (strncmp("echo shar: extracting ", buf, 22) == 0) {
  545.         /* grab the mapname */
  546.         for (p=buf+22, q=mapname, bad_echo=0; *p; ++p) {
  547.         /* be sure it is well formed */
  548.         switch (*p) {
  549.         case ' ':
  550.         case '\t':
  551.             bad_echo = 1;    /* unlikely char in a filename */
  552.             *q++ = *p;
  553.             break;
  554.         case '\n':        /* should be the end */
  555.             if (*(p+1) != '\0') {
  556.             bad_echo = 1;    /* why is '\n' not the end */
  557.             }
  558.             break;        /* don't copy the newline */
  559.         case '\\':        /* avoid \ and / in filenames */
  560.         case '/':
  561.             bad_echo = 1;
  562.             *q++ = *p;
  563.             break;
  564.         default:
  565.             *q++ = *p;
  566.             break;
  567.         }
  568.         }
  569.         *q = '\0';            /* NULL terminate filename */
  570.  
  571.         /* verify mapname */
  572.         if (bad_echo == 1) {
  573.         sprintf(err, "error: line %d, bad echo mapname: %s\n",
  574.             lineno, mapname);
  575.         log(err);
  576.         return(NULL);        /* bad preamble */
  577.         }
  578.  
  579.     /*
  580.      * watch for the cat line
  581.      */
  582.     } else if (strncmp("cat << 'SHAR_EOF' > ", buf, 20) == 0) {
  583.  
  584.         /*
  585.          * compare cat filename against echo filename
  586.          */
  587.         if (mapname[0] == '\0') {
  588.         sprintf(err, "error: line %d, cat with no preceding echo: %s",
  589.             lineno, buf);
  590.         log(err);
  591.         return(NULL);        /* bad preamble */
  592.         } else if (strncmp(buf+20, mapname, q-mapname) != 0) {
  593.         sprintf(err,
  594.             "error: line %d, echo mapname: %s != cat mapname: %s",
  595.             lineno, mapname, buf+20);
  596.         log(err);
  597.         return(NULL);        /* bad preamble */
  598.         } else {
  599.         return(mapname);    /* found end of preamble */
  600.         }
  601.         
  602.     /*
  603.      * watch for unkown lines
  604.      */
  605.     } else {
  606.         sprintf(err, "error: line %d, unknown preamble: %s", lineno, buf);
  607.         log(err);
  608.         return(NULL);
  609.     }
  610.     }
  611.     /* NOT REACHED */
  612. }
  613.  
  614.  
  615. /*
  616.  * write_map - write a map file
  617.  *
  618.  * Given an verified article header and shar preamble, copy the contents
  619.  * of the shar HERE_IS document up until the ending shar lines:
  620.  *
  621.  *    SHAR_EOF
  622.  *    :   End of shell archive
  623.  *    exit 0
  624.  *
  625.  * A previous call to check_preamble placed the name of the mapfile in
  626.  * the mapname array.
  627.  *
  628.  * returns 1 if all was ok, 0 otherwise.
  629.  */
  630. int
  631. write_map( article )
  632.     FILE *article;            /* the article stream */
  633. {
  634.     int fd;                /* map file descriptor */
  635.     FILE *map;                /* the map file stream */
  636.  
  637.     /*
  638.      * open and truncate the map file
  639.      */
  640.     fd = creat(mapname, 0644);
  641.     if (fd < 0) {
  642.     sprintf(err, "error: unable to creat/truncate %s\n", mapname);
  643.     log(err);
  644.     return(1);
  645.     }
  646.     map = fdopen(fd, "w");
  647.     sprintf(err, "extracting: %s\n", mapname);
  648.     log(err);
  649.  
  650.     /*
  651.      * copy article shar data to the map file
  652.      */
  653.     while (1) {
  654.     /*
  655.      * read the line
  656.      */
  657.     clearerr(article);
  658.     if (fgets(buf, BUFSIZ, article) == NULL) {
  659.         if (ferror(article)) {
  660.         sprintf(err, "error: bad map data read after line %d\n",
  661.             lineno);
  662.         log(err);
  663.         } else {
  664.         log("error: EOF while reading the map data\n");
  665.         }
  666.         return(1);
  667.     }
  668.     ++lineno;
  669.     if (! check_newline(buf)) {
  670.         sprintf(err, "error: line %d, map data line too long\n", lineno);
  671.         log(err);
  672.         return(1);
  673.     }
  674.  
  675.     /*
  676.      * watch for the end of the shar
  677.      */
  678.     if (strcmp("SHAR_EOF\n", buf) == 0) {
  679.         /* end of the shar file */
  680.         if (fclose(map) == EOF) {
  681.         log("error: bad fclose\n");
  682.         return(1);
  683.         }
  684.         mapname[0] = '\0';
  685.         break;            /* look for final two lines */
  686.     }
  687.  
  688.     /*
  689.      * write the line
  690.      */
  691.     if (fputs(buf, map) == EOF) {
  692.         log("error: bad write\n");
  693.         return(1);
  694.     }
  695.     }
  696.  
  697.     /*
  698.      * verify the : line after the end of the map
  699.      */
  700.     clearerr(article);
  701.     if (fgets(buf, BUFSIZ, article) == NULL) {
  702.     if (ferror(article)) {
  703.         sprintf(err, "error: bad ending : read after line %d\n",
  704.             lineno);
  705.         log(err);
  706.     } else {
  707.         log("error: EOF while reading the ending : line\n");
  708.     }
  709.     return(1);
  710.     }
  711.     ++lineno;
  712.     if (! check_newline(buf)) {
  713.     sprintf(err, "error: line %d, ending : line too long\n", lineno);
  714.     log(err);
  715.     return(1);
  716.     }
  717.     if (buf[0] != ':') {
  718.     sprintf(err, "error: line %d, not an ending : line: %s", lineno, buf);
  719.     log(err);
  720.     return(1);
  721.     }
  722.  
  723.     /*
  724.      * verify the exit 0
  725.      */
  726.     clearerr(article);
  727.     if (fgets(buf, BUFSIZ, article) == NULL) {
  728.     if (ferror(article)) {
  729.         sprintf(err, "error: bad ending exit read after line %d\n",
  730.             lineno);
  731.         log(err);
  732.     } else {
  733.         log("error: EOF while reading the ending exit line\n");
  734.     }
  735.     return(1);
  736.     }
  737.     ++lineno;
  738.     if (! check_newline(buf)) {
  739.     sprintf(err, "error: line %d, ending exit line too long\n", lineno);
  740.     log(err);
  741.     return(1);
  742.     }
  743.     if (strcmp("exit 0\n", buf) != 0) {
  744.     sprintf(err, "error: line %d, not an ending exit line: %s",
  745.         lineno, buf);
  746.     log(err);
  747.     return(1);
  748.     }
  749.  
  750.     /*
  751.      * if we are here, all must be ok
  752.      */
  753.     return(0);
  754. }
  755.  
  756.  
  757. /*
  758.  * check_newline - check to be sure that the string endds in a newline
  759.  *
  760.  * returns a pointer to the newline, or NULL if none found
  761.  */
  762. char *
  763. check_newline( str )
  764.     char *str;                /* check for newline in str */
  765. {
  766.     char *p;                /* pointer */
  767.  
  768.     /* guess the newline location */
  769.     p = str + strlen(str);
  770.     p -= ((p > str) ? 1 : 0);
  771.  
  772.     /* return result of guess */
  773.     return( (*p == '\n') ? p : NULL );
  774. }
  775.  
  776. char *
  777. xmalloc(size)
  778.     unsigned size;
  779. {
  780.     char *ret = malloc(size);
  781.  
  782.     if (ret == NULL) {
  783.     sprintf(err, "error: out of memory in xmalloc\n");
  784.     log(err);
  785.     sprintf(err, "exiting: status: 1 -  %s", get_date());
  786.     log(err);
  787.     exit(1);
  788.     }
  789.  
  790.     return ret;
  791. }
  792.  
  793. char *
  794. xrealloc(region, size)
  795.     char *region;
  796.     unsigned size;
  797. {
  798.     register char *ret = realloc(region, size);
  799.  
  800.     if (ret == NULL) {
  801.     sprintf(err, "error: out of memory in xrealloc\n");
  802.     log(err);
  803.     sprintf(err, "exiting: status: 1 - %s", get_date());
  804.     log(err);
  805.     exit(1);
  806.     }
  807.  
  808.     return ret;
  809. }
  810.  
  811.  
  812. /*
  813.  * strcmpic - case independent strcmp function
  814.  */
  815. int
  816. strncmpic(s1, s2, n)
  817.     register char *s1, *s2;        /* strings to compare */
  818.     int n;                /* compare up to this many chars */
  819. {
  820.     register int c1, c2;        /* temp */
  821.     register int cnt = n;        /* count of chars so far compared */
  822.  
  823.     while (*s1 && *s2 && cnt > 0) {
  824.     if ((c1 = tolower(*s1)) != (c2 = tolower(*s2))) {
  825.         return c1-c2;
  826.     }
  827.     s1++;
  828.     s2++;
  829.     cnt--;                /* count this character */
  830.     }
  831.  
  832.     /*
  833.      * If we ran out of chars, then the string segments are equal, otherwise
  834.      * one or both strings must have ended. In this case the subtraction
  835.      * will show which one is shorter, if any.
  836.      */
  837.     return cnt ? (int)((*s1)-(*s2)) : 0;
  838. }
  839.  
  840. /*
  841.  * strcolon - step through string parts separated by colons
  842.  *
  843.  * when called with a string, return a copy of the first part of
  844.  * the string up to, but excluding the first `:'.  When called with
  845.  * NULL return a copy of the next part of the previously passed string,
  846.  * with each part separated by a colon `:'.
  847.  *
  848.  * return NULL if no more parts are left.
  849.  *
  850.  * strcolon is typically used in a loop on ':' separated names such as:
  851.  *
  852.  *    for (p = strcolon(names); p; p = strcolon((char *)NULL)) {
  853.  *        ... do something with the name p ...
  854.  *    }
  855.  *
  856.  * the malloc region returned is reused, so if you wish to keep a string
  857.  * around, you will need to copy it.
  858.  */
  859. char *
  860. strcolon(s)
  861.     register char *s;            /* string or NULL */
  862. {
  863.     static char *next = NULL;        /* pointer to next ':' */
  864.     static char *region = NULL;        /* region used to store result */
  865.     static int alloc = 0;        /* alloc size of region */
  866.  
  867.     if (!s) {
  868.     s = next;
  869.     if (s == NULL) {
  870.         return NULL;
  871.     }
  872.     }
  873.     next = strchr(s, ':');
  874.     if (next) {
  875.     register int len = next - s;
  876.  
  877.     if (len >= alloc) {
  878.         if (region == NULL) {
  879.         region = xmalloc(alloc = len + 1);
  880.         } else {
  881.         region = xrealloc(region, alloc = len + 1);
  882.         }
  883.     }
  884.     (void) memcpy(region, s, next - s);
  885.     region[next - s] = '\0';
  886.     next++;
  887.     return region;
  888.     }
  889.     return s;
  890. }
  891.