home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / pitools / pitool.c < prev    next >
C/C++ Source or Header  |  1994-04-06  |  20KB  |  820 lines

  1. /*
  2.  *    Process Product-Info files.
  3.  *    Written by Fred Fish.
  4.  *    All portions not covered by GNU GPL are public domain.
  5.  *
  6.  *    usage:  pitool [options] file1 file2 file3 ...
  7.  *    Options:
  8.  *
  9.  *    -b    Find Product-Info files and append a suitable entry to
  10.  *        the files.bbs file in the directory in which it is
  11.  *        found.
  12.  *
  13.  *    -F <s>    Use <s> as a format specifier for writing entries
  14.  *        with -b option.  EX: "%-30.30B %8.8V %4.4KK %-40.40S\n"
  15.  *        Format specs are:
  16.  *
  17.  *        %%    Literal '%' character.
  18.  *        %B    Basename of "stored-in" path.
  19.  *        %D    Dirname of "stored-in" path.
  20.  *        %K    Size of the "stored-in" file/directory, in Kb.
  21.  *        %P    Full "stored-in" path.
  22.  *        %S    Contents of the ".short" field, 40 chars max.
  23.  *        %V    Version number of the product.
  24.  *        
  25.  *        Other characters are passed through literally, as with
  26.  *        printf.  Note that %D & %B are defined such that
  27.  *        %P=%D%B without having to know what the separator was.
  28.  *
  29.  *    -f <s>    Use <s> as the name of the file to use to write entries
  30.  *        to as a result of using the -b option.  If <s> is "-",
  31.  *        the entries are written to stdout rather than to a file
  32.  *        in each directory containing product info files.
  33.  *
  34.  *    -h    Print help message.
  35.  *
  36.  *    -n    Find Product-Info files and print names to stdout.
  37.  *
  38.  *    -s    Gather product info files and add ".stored-in" fields.
  39.  *
  40.  *    -t    Test Product Info files, report possible problems.
  41.  *
  42.  *    -v    Verbose flag, print name of each Product Info file to
  43.  *        stdout.
  44.  *
  45.  */
  46.  
  47. #include <stdio.h>
  48. #include <sys/types.h>
  49. #include <sys/stat.h>
  50. #include "ftw.h"
  51.  
  52. #define STREQ(a,b) ((*(a) == *(b)) && !strcmp ((a),(b)))
  53.  
  54. extern int strlen (const char *);
  55. extern char *strrchr (const char *, char);
  56. extern char *strchr (const char *, char);
  57. extern char *strdup ();
  58.  
  59. static char *format = "%-30.30B %8.8V %4.4KK %-40.40S\n";
  60. static char *bbs_file_name = "files.bbs";
  61. static int verbose;
  62.  
  63. struct pif {
  64.   const char *pi_fname;    /* Name of file containing the product info */
  65.   int pi_line;        /* Current line number being read */
  66.   FILE *pi_fp;        /* "FILE *" pointer to the open file */
  67.   const char *pi_basename;
  68.   int pi_size;
  69.   char *pi_address;
  70.   char *pi_aminet_dir;
  71.   char *pi_author;
  72.   char *pi_construction;
  73.   char *pi_comment;
  74.   char *pi_contents;
  75.   char *pi_date;
  76.   char *pi_described_by;
  77.   char *pi_description;
  78.   char *pi_distribution;
  79.   char *pi_docs;
  80.   char *pi_email;
  81.   char *pi_exectype;
  82.   char *pi_fax;
  83.   char *pi_fullname;
  84.   char *pi_installsize;
  85.   char *pi_keywords;
  86.   char *pi_locale;
  87.   char *pi_name;    /* Contents of the ".name" field */
  88.   char *pi_phone;
  89.   char *pi_price;
  90.   char *pi_reference;
  91.   char *pi_requirements;
  92.   char *pi_restrictions;
  93.   char *pi_run;
  94.   char *pi_short;
  95.   char *pi_source;
  96.   char *pi_stored_in;
  97.   char *pi_submittal;
  98.   char *pi_tested;
  99.   char *pi_type;
  100.   char *pi_version;
  101. };
  102.  
  103. /* Note, we can't use isspace() here, because it counts
  104.    international characters as whitespace.  So just look
  105.    for the characters we are actually concerned about */
  106.  
  107. #define WHITESPACE(a) (((a) == ' ') || ((a) == '\t') || ((a) == '\n'))
  108.  
  109. void
  110. stripwhite (struct pif *pip, char *fieldp)
  111. {
  112.   char *endp;
  113.  
  114.   for (endp = fieldp; *endp != '\000'; endp++) {;}
  115.   endp--;
  116.  
  117.   if (WHITESPACE (*endp))
  118.     {
  119. #if 0    /* Currently there are too many Product Info files where this
  120.        is true, to make this a useful message.  Enable it once
  121.        we have processed all of them to eliminate the whitespace. */
  122.       fprintf (stderr, "%s:%d: extraneous whitespace at end of line\n",
  123.            pip -> pi_fname, pip -> pi_line);
  124. #endif
  125.     }
  126.   while ((endp >= fieldp) && (WHITESPACE (*endp)))
  127.     {
  128.       *endp-- = '\000';
  129.     }
  130. }
  131.  
  132. int
  133. filesize (const char *fname)
  134. {
  135.   struct stat statbuf;
  136.  
  137.   if (stat (fname, &statbuf) == 0)
  138.     {
  139.       return (statbuf.st_size);
  140.     }
  141.   else
  142.     {
  143.       return (0);
  144.     }
  145. }
  146.  
  147. const char *
  148. basename (const char *sp)
  149. {
  150.   const char *bname;
  151.  
  152.   /* Look first for typical Unix or AmigaDOS separator */
  153.  
  154.   bname = strrchr (sp, '/');
  155.  
  156.   /* Look for special AmigaDOS separator */
  157.  
  158. #ifdef __amigados__
  159.   if (bname == NULL)
  160.     {
  161.       bname = strrchr (sp, ':');
  162.     }
  163. #endif
  164.  
  165.   /* If any separator found, skip over it, otherwise the
  166.      basename is just the entire string. */
  167.  
  168.   if (bname == NULL)
  169.     {
  170.       bname = sp;
  171.     }
  172.   else
  173.     {
  174.       bname++;
  175.     }
  176.  
  177.   return (bname);
  178. }
  179.  
  180. /*
  181.  *  Note that dirname is defined such that it *includes* the
  182.  *  separator.  This way you don't need to know whether it 
  183.  *  was a '/' or ':' to reconstruct the full path.  I.E.
  184.  *  %P=%D%B  not %D/%B if '/' or %D%B if ':'
  185.  */
  186.  
  187. const char *
  188. dirname (const char *sp)
  189. {
  190.   char *endp;
  191.   static char buf[256];
  192.  
  193.   strcpy (buf, sp);
  194.  
  195.   /* Look first for typical Unix or AmigaDOS separator */
  196.  
  197.   endp = strrchr (buf, '/');
  198.  
  199.   /* Look for special AmigaDOS separator */
  200.  
  201. #ifdef __amigados__
  202.   if (endp == NULL)
  203.     {
  204.       endp = strrchr (buf, ':');
  205.     }
  206. #endif
  207.  
  208.   /* If there was a separator, set up to zap the next
  209.      character, otherwise the dirname is just the empty
  210.      string. */
  211.  
  212.   if (endp == NULL)
  213.     {
  214.       endp = buf;
  215.     }
  216.   else
  217.     {
  218.       endp++;
  219.     }
  220.   *endp = '\000';
  221.   return ((const char *) buf);
  222. }
  223.  
  224. /*
  225.  *    Given a name of a file that is potentially a product info file,
  226.  *    generate the name of the directory or file that the associated
  227.  *    product is stored in.  By convention, the "stored in" name is
  228.  *    terminated with a '/' if it is a directory.
  229.  *
  230.  *    Returns NULL if the name is not a product info file.
  231.  */
  232.  
  233. char *
  234. stored_in (const char *name)
  235. {
  236.   int length;
  237.   char *namep;
  238.   char *rtnval = NULL;
  239.   static char stored_in_buf[256];
  240.  
  241.   strcpy (stored_in_buf, name);
  242.   length = strlen (stored_in_buf);
  243.   if ((stored_in_buf[length - 1] == 'i') &&
  244.       (stored_in_buf[length - 2] == 'p') &&
  245.       (stored_in_buf[length - 3] == '.'))
  246.     {
  247.       stored_in_buf[length - 3] = '\000';
  248.       rtnval = stored_in_buf;
  249.     }
  250.   else
  251.     {
  252.       namep = strrchr (stored_in_buf, '/');
  253.       if ((namep != NULL) && (STREQ (namep + 1, "Product-Info")))
  254.     {
  255.       *++namep = '\000';
  256.       rtnval = stored_in_buf;
  257.     }
  258.     }
  259.   if (rtnval != NULL)
  260.     {
  261. #ifdef __amigados__
  262.       /* Convert "/path1/path2/file" to "path1:path2/file" */
  263.       if ((*rtnval == '/') &&
  264.       ((namep = strchr (rtnval + 1, '/')) != NULL))
  265.     {
  266.       rtnval++;
  267.       *namep = ':';
  268.     }
  269. #endif
  270.     }
  271.   return (rtnval);
  272. }
  273.  
  274. int
  275. find_pifiles (const char *name, struct stat *statbuf, int flags)
  276. {
  277.   if (stored_in (name) != NULL)
  278.     {
  279.       printf ("%s\n", name);
  280.     }
  281.   return (0);
  282. }
  283.  
  284. void
  285. copy_out (const char *filename)
  286. {
  287.   int ch;
  288.   FILE *fin;
  289.   char buf[256];
  290.   char *endp;
  291.  
  292.   fin = fopen (filename, "r");
  293.   if (fin != NULL)
  294.     {
  295.       while (fgets (buf, 256, fin) != NULL)
  296.     {
  297.       for (endp = buf; *endp != '\000'; endp++) {;}
  298.       endp--;
  299.       while ((endp >= buf) && (WHITESPACE (*endp)))
  300.         {
  301.           *endp-- = '\000';
  302.         }
  303.       puts (buf);
  304.     }
  305.       fclose (fin);
  306.     }
  307. }
  308.  
  309. int
  310. add_stored_in (const char *name, struct stat *statbuf, int flags)
  311. {
  312.   char *sname;
  313.  
  314.   sname = stored_in (name);
  315.   if (sname != NULL)
  316.     {
  317.       copy_out (name);
  318.       printf (".stored-in\n%s\n", sname);
  319.     }
  320.   return (0);
  321. }
  322.  
  323. void
  324. free_pif (struct pif *pip)
  325. {
  326.   if (pip -> pi_fp != NULL)        fclose (pip -> pi_fp);
  327.   if (pip -> pi_stored_in != NULL)    free (pip -> pi_stored_in);
  328.   if (pip -> pi_address != NULL)    free (pip -> pi_address);
  329.   if (pip -> pi_aminet_dir != NULL)    free (pip -> pi_aminet_dir);
  330.   if (pip -> pi_author != NULL)        free (pip -> pi_author);
  331.   if (pip -> pi_construction != NULL)    free (pip -> pi_construction);
  332.   if (pip -> pi_comment != NULL)    free (pip -> pi_comment);
  333.   if (pip -> pi_contents != NULL)    free (pip -> pi_contents);
  334.   if (pip -> pi_date != NULL)        free (pip -> pi_date);
  335.   if (pip -> pi_described_by != NULL)    free (pip -> pi_described_by);
  336.   if (pip -> pi_description != NULL)    free (pip -> pi_description);
  337.   if (pip -> pi_distribution != NULL)    free (pip -> pi_distribution);
  338.   if (pip -> pi_docs != NULL)        free (pip -> pi_docs);
  339.   if (pip -> pi_email != NULL)        free (pip -> pi_email);
  340.   if (pip -> pi_exectype != NULL)    free (pip -> pi_exectype);
  341.   if (pip -> pi_fax != NULL)        free (pip -> pi_fax);
  342.   if (pip -> pi_fullname != NULL)    free (pip -> pi_fullname);
  343.   if (pip -> pi_installsize != NULL)    free (pip -> pi_installsize);
  344.   if (pip -> pi_keywords != NULL)    free (pip -> pi_keywords);
  345.   if (pip -> pi_locale != NULL)        free (pip -> pi_locale);
  346.   if (pip -> pi_name != NULL)        free (pip -> pi_name);
  347.   if (pip -> pi_phone != NULL)        free (pip -> pi_phone);
  348.   if (pip -> pi_price != NULL)        free (pip -> pi_price);
  349.   if (pip -> pi_reference != NULL)    free (pip -> pi_reference);
  350.   if (pip -> pi_requirements != NULL)    free (pip -> pi_requirements);
  351.   if (pip -> pi_restrictions != NULL)    free (pip -> pi_restrictions);
  352.   if (pip -> pi_run != NULL)        free (pip -> pi_run);
  353.   if (pip -> pi_short != NULL)        free (pip -> pi_short);
  354.   if (pip -> pi_source != NULL)        free (pip -> pi_source);
  355.   if (pip -> pi_submittal != NULL)    free (pip -> pi_submittal);
  356.   if (pip -> pi_tested != NULL)        free (pip -> pi_tested);
  357.   if (pip -> pi_type != NULL)        free (pip -> pi_type);
  358.   if (pip -> pi_version != NULL)    free (pip -> pi_version);
  359.  
  360.   free (pip);
  361. }
  362.  
  363. struct pif *
  364. open_pif (const char *name)
  365. {
  366.   struct pif *pip;
  367.   extern char *malloc ();
  368.   char *stor;
  369.  
  370.   pip = (struct pif *) malloc (sizeof (struct pif));
  371.   if (pip == NULL)
  372.     {
  373.       fprintf (stderr, "pitool: out of virtual memory\n");
  374.       exit (1);
  375.     }
  376.   memset (pip, 0, sizeof (struct pif));
  377.   pip -> pi_fname = name;
  378.   pip -> pi_line = 0;
  379.   pip -> pi_basename = basename (pip -> pi_fname);
  380.   pip -> pi_fp = fopen (pip -> pi_fname, "r");
  381.   stor = stored_in (pip -> pi_fname);
  382.   if (stor != NULL)
  383.     {
  384.       pip -> pi_stored_in = strdup (stor);
  385.     }
  386.   if (pip -> pi_fp == NULL)
  387.     {
  388.       perror (pip -> pi_fname);
  389.       free_pif (pip);
  390.       pip = NULL;
  391.     }
  392.   else if (verbose)
  393.     {
  394.       printf ("%s\n", pip -> pi_fname);
  395.     }
  396.   return (pip);
  397. }
  398.  
  399. /* Find next field marker and strip off any whitespace from end of the
  400.    line. */
  401.  
  402. char *
  403. next_field (struct pif *pip)
  404. {
  405.   static char readbuf[256];
  406.   char *bufp;
  407.   
  408.   while (fgets (readbuf, sizeof (readbuf), pip -> pi_fp) != NULL)
  409.     {
  410.       pip -> pi_line++;
  411.       if (readbuf[0] == '.')
  412.     {
  413.       for (bufp = readbuf; *bufp != '\000'; bufp++) {;}
  414.       bufp--;
  415.       while ((bufp >= readbuf) && WHITESPACE (*bufp))
  416.         {
  417.           *bufp-- = '\000';
  418.         }
  419.       return (readbuf);
  420.     }
  421.     }
  422.   return (NULL);
  423. }
  424.  
  425. char *
  426. read_field (struct pif *pip)
  427. {
  428.   static char readbuf[16 * 1024];
  429.   char *sp = readbuf;
  430.   int ch = '\n';    /* Detect '.' if first char read */
  431.   int prevch = 0;
  432.   
  433.   for (;;)
  434.     {
  435.       prevch = ch;
  436.       ch = getc (pip -> pi_fp);
  437.       if (ch == '\n')
  438.     {
  439.       pip -> pi_line++;
  440.     }
  441.       if (ch == '.')
  442.     {
  443.       if (prevch == '\n')
  444.         {
  445.           ungetc (ch, pip -> pi_fp);
  446.           break;
  447.         }
  448.     }
  449.       else if (ch == EOF)
  450.     {
  451.       if (prevch != '\n')
  452.         {
  453.           fprintf (stderr, "%s:%d: does not end with newline\n",
  454.                pip -> pi_fname, pip -> pi_line);
  455.           *sp++ = '\n';
  456.         }
  457.       break;
  458.     }
  459.       *sp++ = ch;
  460.     }
  461.   if (sp > readbuf)
  462.     {
  463.       sp--;
  464.     }
  465.   *sp = '\000';
  466.   stripwhite (pip, readbuf);
  467.   return (readbuf);
  468. }
  469.  
  470. struct pif *
  471. read_pif (const char *name)
  472. {
  473.   struct pif *pip;
  474.   char *fieldname;
  475.   char *rawfield;
  476.   char *temp;
  477.  
  478.   pip = open_pif (name);
  479.   if (pip != NULL)
  480.     {
  481.       while ((fieldname = next_field (pip)) != NULL)
  482.     {
  483.       rawfield = read_field (pip);
  484.       if (STREQ (fieldname + 1, "address"))
  485.         { pip -> pi_address = strdup (rawfield); continue; }
  486.       if (STREQ (fieldname + 1, "aminet-dir"))
  487.         { pip -> pi_aminet_dir = strdup (rawfield); continue; }
  488.       if (STREQ (fieldname + 1, "author"))
  489.         { pip -> pi_author = strdup (rawfield); continue; }
  490.       if (STREQ (fieldname + 1, "construction"))
  491.         { pip -> pi_construction = strdup (rawfield); continue; }
  492.       if (STREQ (fieldname + 1, "comment"))
  493.         { pip -> pi_comment = strdup (rawfield); continue; }
  494.       if (STREQ (fieldname + 1, "contents"))
  495.         { pip -> pi_contents = strdup (rawfield); continue; }
  496.       if (STREQ (fieldname + 1, "date"))
  497.         { pip -> pi_date = strdup (rawfield); continue; }
  498.       if (STREQ (fieldname + 1, "described-by"))
  499.         { pip -> pi_described_by = strdup (rawfield); continue; }
  500.       if (STREQ (fieldname + 1, "description"))
  501.         { pip -> pi_description = strdup (rawfield); continue; }
  502.       if (STREQ (fieldname + 1, "distribution"))
  503.         { pip -> pi_distribution = strdup (rawfield); continue; }
  504.       if (STREQ (fieldname + 1, "docs"))
  505.         { pip -> pi_docs = strdup (rawfield); continue; }
  506.       if (STREQ (fieldname + 1, "email"))
  507.         { pip -> pi_email = strdup (rawfield); continue; }
  508.       if (STREQ (fieldname + 1, "exectype"))
  509.         { pip -> pi_exectype = strdup (rawfield); continue; }
  510.       if (STREQ (fieldname + 1, "fax"))
  511.         { pip -> pi_fax = strdup (rawfield); continue; }
  512.       if (STREQ (fieldname + 1, "fullname"))
  513.         { pip -> pi_fullname = strdup (rawfield); continue; }
  514.       if (STREQ (fieldname + 1, "installsize"))
  515.         { pip -> pi_installsize = strdup (rawfield); continue; }
  516.       if (STREQ (fieldname + 1, "keywords"))
  517.         { pip -> pi_keywords = strdup (rawfield); continue; }
  518.       if (STREQ (fieldname + 1, "locale"))
  519.         { pip -> pi_locale = strdup (rawfield); continue; }
  520.       if (STREQ (fieldname + 1, "name"))
  521.         { pip -> pi_name = strdup (rawfield); continue; }
  522.       if (STREQ (fieldname + 1, "phone"))
  523.         { pip -> pi_phone = strdup (rawfield); continue; }
  524.       if (STREQ (fieldname + 1, "price"))
  525.         { pip -> pi_price = strdup (rawfield); continue; }
  526.       if (STREQ (fieldname + 1, "reference"))
  527.         { pip -> pi_reference = strdup (rawfield); continue; }
  528.       if (STREQ (fieldname + 1, "requirements"))
  529.         { pip -> pi_requirements = strdup (rawfield); continue; }
  530.       if (STREQ (fieldname + 1, "restrictions"))
  531.         { pip -> pi_restrictions = strdup (rawfield); continue; }
  532.       if (STREQ (fieldname + 1, "run"))
  533.         { pip -> pi_run = strdup (rawfield); continue; }
  534.       if (STREQ (fieldname + 1, "short"))
  535.         {
  536.           pip -> pi_short = strdup (rawfield);
  537.           if (strlen (rawfield) > 40)
  538.         {
  539.           fprintf (stderr,
  540.                "%s:%d: '.short' field > 40 characters\n",
  541.                pip -> pi_fname, pip -> pi_line);
  542.         }
  543.           if ((temp = strchr (rawfield, '\n')) != NULL)
  544.         {
  545.           *temp = '\000';
  546.           fprintf (stderr,
  547.                "%s:%d: '.short' field contains newline\n",
  548.                pip -> pi_fname, pip -> pi_line);
  549.         }
  550.           continue;
  551.         }
  552.       if (STREQ (fieldname + 1, "source"))
  553.         { pip -> pi_source = strdup (rawfield); continue; }
  554.       if (STREQ (fieldname + 1, "submittal"))
  555.         { pip -> pi_submittal = strdup (rawfield); continue; }
  556.       if (STREQ (fieldname + 1, "tested"))
  557.         { pip -> pi_tested = strdup (rawfield); continue; }
  558.       if (STREQ (fieldname + 1, "type"))
  559.         { pip -> pi_type = strdup (rawfield); continue; }
  560.       if (STREQ (fieldname + 1, "version"))
  561.         { pip -> pi_version = strdup (rawfield); continue; }
  562.       fprintf (stderr, "%s:%d: unrecognized field '%s'\n", 
  563.            pip -> pi_fname, pip -> pi_line, fieldname);
  564.     }
  565.       fclose (pip -> pi_fp);
  566.       pip -> pi_fp = NULL;
  567.     }
  568.   if (pip -> pi_name == NULL)
  569.     {
  570.       fprintf (stderr, "%s:%d: missing '.name' field\n", pip -> pi_fname,
  571.            pip -> pi_line);
  572.     }
  573.   if (pip -> pi_short == NULL)
  574.     {
  575.       fprintf (stderr, "%s:%d: missing '.short' field\n", pip -> pi_fname,
  576.            pip -> pi_line);
  577.     }
  578.   return (pip);
  579. }
  580.  
  581. void
  582. fprintf_pif (FILE *fp, struct pif *pip)
  583. {
  584.   char pformat[256];
  585.   char *pformatp = pformat;
  586.   char *formatp = format;
  587.   int argc = 0;
  588.   int needspec = 0;
  589.   const char *argv[10];        /* FIXME:  check for overflow */
  590.   static char sizebuf[16];
  591.  
  592.   while (*formatp != '\000')
  593.     {
  594.       if (needspec)
  595.     {
  596.       switch (*formatp)
  597.         {
  598.         case 'B':        /* Basename of "stored-in" path */
  599.           argv[argc++] = basename (pip -> pi_stored_in);
  600.           *pformatp++ = 's';
  601.           formatp++;
  602.           needspec = 0;
  603.           break;
  604.         case 'D':        /* Dirname of "stored-in" path */
  605.           argv[argc++] = dirname (pip -> pi_stored_in);
  606.           *pformatp++ = 's';
  607.           formatp++;
  608.           needspec = 0;
  609.           break;
  610.         case 'K':        /* "Stored-in" filesize in Kb */
  611.           sprintf (sizebuf, "%d", filesize (pip -> pi_stored_in) / 1024);
  612.           argv[argc++] = sizebuf;
  613.           *pformatp++ = 's';
  614.           formatp++;
  615.           needspec = 0;
  616.           break;
  617.         case 'P':        /* Full "stored-in" path */
  618.           argv[argc++] = pip -> pi_stored_in;
  619.           *pformatp++ = 's';
  620.           formatp++;
  621.           needspec = 0;
  622.           break;
  623.         case 'S':        /* The ".short" field */
  624.           argv[argc++] = pip -> pi_short ? pip -> pi_short : "<no description avail>";
  625.           *pformatp++ = 's';
  626.           *formatp++;
  627.           needspec = 0;
  628.           break;
  629.         case 'V':        /* Version */
  630.           argv[argc++] = pip -> pi_version ? pip -> pi_version : "?.?";
  631.           *pformatp++ = 's';
  632.           formatp++;
  633.           needspec = 0;
  634.           break;
  635.         case '%':
  636.           if (*(formatp - 1) != '%')
  637.         {
  638.           fprintf (stderr, "bad -F format string, missing spec\n");
  639.           exit (1);
  640.         }
  641.           /* A '%' can quote the following '%' */
  642.           *pformatp++ = *formatp++;
  643.           needspec = 0;
  644.           break;
  645.         default:
  646.           /* Stuff to print verbatum */
  647.           *pformatp++ = *formatp++;
  648.           break;
  649.         }
  650.     }
  651.       else if (*formatp == '%')
  652.     {
  653.       /* We are not looking for a spec because of a previous '%' */
  654.       needspec++;
  655.       *pformatp++ = *formatp++;
  656.     }
  657.       else if (*formatp == '\\')
  658.     {
  659.       /* Simple '\' evaluation, only single chars now.  FIXME */
  660.       formatp++;
  661.       switch (*formatp)
  662.         {
  663.         case '\\':
  664.           *pformatp++ = '\\'; /* quoted it */
  665.           break;
  666.         case 't':
  667.           *pformatp++ = '\t';
  668.           break;
  669.         case 'n':
  670.           *pformatp++ = '\n';
  671.           break;
  672.         case 'b':
  673.           *pformatp++ = '\b';
  674.           break;
  675.         case 'r':
  676.           *pformatp++ = '\r';
  677.           break;
  678.         case 'f':
  679.           *pformatp++ = '\f';
  680.           break;
  681.         default:
  682.           *pformatp++ = *formatp;
  683.           break;
  684.         }
  685.       formatp++;
  686.     }
  687.       else
  688.     {
  689.       /* stuff that needs no interpretation */
  690.       *pformatp++ = *formatp++;
  691.     }
  692.     }
  693.   *pformatp++ = '\000';
  694.   fprintf (fp, pformat,
  695.        argv[0], argv[1], argv[2], argv[3], argv[4],
  696.        argv[5], argv[6], argv[7], argv[8], argv[9]);
  697. }
  698.  
  699. int
  700. add_files_bbs (const char *name, struct stat *statbuf, int flags)
  701. {
  702.   struct pif *pip;
  703.   char bbsfile[256];
  704.   char *scan;
  705.   FILE *bbsfp;
  706.  
  707.   if (stored_in (name) != NULL)
  708.     {
  709.       pip = read_pif (name);
  710.       if (pip != NULL)
  711.     {
  712.       if (STREQ (bbs_file_name, "-"))
  713.         {
  714.           fprintf_pif (stdout, pip);
  715.         }
  716.       else
  717.         {
  718.           strcpy (bbsfile, name);
  719.           scan = strrchr (bbsfile, '/');
  720.           if (scan != NULL)
  721.         {
  722.           scan++;
  723.           strcpy (scan, bbs_file_name);
  724.           bbsfp = fopen (bbsfile, "a");
  725.           if (bbsfp != NULL)
  726.             {
  727.               fprintf_pif (bbsfp, pip);
  728.               fclose (bbsfp);
  729.             }
  730.         }
  731.         }
  732.       free_pif (pip);
  733.     }
  734.     }
  735.   return (0);
  736. }
  737.  
  738. int
  739. test_pifiles (const char *name, struct stat *statbuf, int flags)
  740. {
  741.   struct pif *pip;
  742.   char bbsfile[256];
  743.   char *scan;
  744.   FILE *bbsfp;
  745.  
  746.   if (stored_in (name) != NULL)
  747.     {
  748.       pip = read_pif (name);
  749.       if (pip != NULL)
  750.     {
  751.       free_pif (pip);
  752.     }
  753.     }
  754.   return (0);
  755. }
  756.  
  757. int
  758. main (argc, argv)
  759. int argc;
  760. char *argv[];
  761. {
  762.   int ch;
  763.   int action;
  764.   int errflg = 0;
  765.   extern int optind;
  766.   extern char *optarg;
  767.  
  768.   while ((ch = getopt (argc, argv, "F:f:bfhnstv")) != EOF)
  769.     {
  770.       switch (ch)
  771.     {
  772.     case 'F':
  773.       format = optarg;
  774.       break;
  775.     case 'f':
  776.       bbs_file_name = optarg;
  777.       break;
  778.     case 'h':
  779.       break;
  780.     case 'v':
  781.       verbose++;
  782.       break;
  783.     case '?':
  784.       errflg++;
  785.       break;
  786.     default:
  787.       action = ch;
  788.       break;
  789.     }
  790.       if (errflg)
  791.     {
  792.       fprintf (stderr, "unknown arg, use -h for help\n");
  793.       exit (1);
  794.     }
  795.     }
  796.   for ( ; optind < argc; optind++)
  797.     {
  798.       switch (action)
  799.     {
  800.     case 'b':
  801.       ftw ((const char *) argv[optind], add_files_bbs, 30);
  802.       break;
  803.     case 'n':
  804.       ftw ((const char *) argv[optind], find_pifiles, 30);
  805.       break;
  806.     case 's':
  807.       ftw ((const char *) argv[optind], add_stored_in, 30);
  808.       break;
  809.     case 't':
  810.       ftw ((const char *) argv[optind], test_pifiles, 30);
  811.       break;
  812.     default:
  813.       fprintf (stderr, "unknown action '%c'\n", action);
  814.       exit (1);
  815.     }
  816.     }
  817.   exit (0);
  818.   return (0);
  819. }
  820.