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