home *** CD-ROM | disk | FTP | other *** search
- /*
- * Process Product-Info files.
- * Written by Fred Fish.
- * All portions not covered by GNU GPL are public domain.
- *
- * usage: pitool [options] file1 file2 file3 ...
- * Options:
- *
- * -b Find Product-Info files and append a suitable entry to
- * the files.bbs file in the directory in which it is
- * found.
- *
- * -F <s> Use <s> as a format specifier for writing entries
- * with -b option. EX: "%-30.30B %8.8V %4.4KK %-40.40S\n"
- * Format specs are:
- *
- * %% Literal '%' character.
- * %B Basename of "stored-in" path.
- * %D Dirname of "stored-in" path.
- * %K Size of the "stored-in" file/directory, in Kb.
- * %P Full "stored-in" path.
- * %S Contents of the ".short" field, 40 chars max.
- * %V Version number of the product.
- *
- * Other characters are passed through literally, as with
- * printf. Note that %D & %B are defined such that
- * %P=%D%B without having to know what the separator was.
- *
- * -f <s> Use <s> as the name of the file to use to write entries
- * to as a result of using the -b option. If <s> is "-",
- * the entries are written to stdout rather than to a file
- * in each directory containing product info files.
- *
- * -h Print help message.
- *
- * -n Find Product-Info files and print names to stdout.
- *
- * -s Gather product info files and add ".stored-in" fields.
- *
- * -t Test Product Info files, report possible problems.
- *
- * -v Verbose flag, print name of each Product Info file to
- * stdout.
- *
- */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include "ftw.h"
-
- #define STREQ(a,b) ((*(a) == *(b)) && !strcmp ((a),(b)))
-
- extern int strlen (const char *);
- extern char *strrchr (const char *, char);
- extern char *strchr (const char *, char);
- extern char *strdup ();
-
- static char *format = "%-30.30B %8.8V %4.4KK %-40.40S\n";
- static char *bbs_file_name = "files.bbs";
- static int verbose;
-
- struct pif {
- const char *pi_fname; /* Name of file containing the product info */
- int pi_line; /* Current line number being read */
- FILE *pi_fp; /* "FILE *" pointer to the open file */
- const char *pi_basename;
- int pi_size;
- char *pi_address;
- char *pi_aminet_dir;
- char *pi_author;
- char *pi_construction;
- char *pi_comment;
- char *pi_contents;
- char *pi_date;
- char *pi_described_by;
- char *pi_description;
- char *pi_distribution;
- char *pi_docs;
- char *pi_email;
- char *pi_exectype;
- char *pi_fax;
- char *pi_fullname;
- char *pi_installsize;
- char *pi_keywords;
- char *pi_locale;
- char *pi_name; /* Contents of the ".name" field */
- char *pi_phone;
- char *pi_price;
- char *pi_reference;
- char *pi_requirements;
- char *pi_restrictions;
- char *pi_run;
- char *pi_short;
- char *pi_source;
- char *pi_stored_in;
- char *pi_submittal;
- char *pi_tested;
- char *pi_type;
- char *pi_version;
- };
-
- /* Note, we can't use isspace() here, because it counts
- international characters as whitespace. So just look
- for the characters we are actually concerned about */
-
- #define WHITESPACE(a) (((a) == ' ') || ((a) == '\t') || ((a) == '\n'))
-
- void
- stripwhite (struct pif *pip, char *fieldp)
- {
- char *endp;
-
- for (endp = fieldp; *endp != '\000'; endp++) {;}
- endp--;
-
- if (WHITESPACE (*endp))
- {
- #if 0 /* Currently there are too many Product Info files where this
- is true, to make this a useful message. Enable it once
- we have processed all of them to eliminate the whitespace. */
- fprintf (stderr, "%s:%d: extraneous whitespace at end of line\n",
- pip -> pi_fname, pip -> pi_line);
- #endif
- }
- while ((endp >= fieldp) && (WHITESPACE (*endp)))
- {
- *endp-- = '\000';
- }
- }
-
- int
- filesize (const char *fname)
- {
- struct stat statbuf;
-
- if (stat (fname, &statbuf) == 0)
- {
- return (statbuf.st_size);
- }
- else
- {
- return (0);
- }
- }
-
- const char *
- basename (const char *sp)
- {
- const char *bname;
-
- /* Look first for typical Unix or AmigaDOS separator */
-
- bname = strrchr (sp, '/');
-
- /* Look for special AmigaDOS separator */
-
- #ifdef __amigados__
- if (bname == NULL)
- {
- bname = strrchr (sp, ':');
- }
- #endif
-
- /* If any separator found, skip over it, otherwise the
- basename is just the entire string. */
-
- if (bname == NULL)
- {
- bname = sp;
- }
- else
- {
- bname++;
- }
-
- return (bname);
- }
-
- /*
- * Note that dirname is defined such that it *includes* the
- * separator. This way you don't need to know whether it
- * was a '/' or ':' to reconstruct the full path. I.E.
- * %P=%D%B not %D/%B if '/' or %D%B if ':'
- */
-
- const char *
- dirname (const char *sp)
- {
- char *endp;
- static char buf[256];
-
- strcpy (buf, sp);
-
- /* Look first for typical Unix or AmigaDOS separator */
-
- endp = strrchr (buf, '/');
-
- /* Look for special AmigaDOS separator */
-
- #ifdef __amigados__
- if (endp == NULL)
- {
- endp = strrchr (buf, ':');
- }
- #endif
-
- /* If there was a separator, set up to zap the next
- character, otherwise the dirname is just the empty
- string. */
-
- if (endp == NULL)
- {
- endp = buf;
- }
- else
- {
- endp++;
- }
- *endp = '\000';
- return ((const char *) buf);
- }
-
- /*
- * Given a name of a file that is potentially a product info file,
- * generate the name of the directory or file that the associated
- * product is stored in. By convention, the "stored in" name is
- * terminated with a '/' if it is a directory.
- *
- * Returns NULL if the name is not a product info file.
- */
-
- char *
- stored_in (const char *name)
- {
- int length;
- char *namep;
- char *rtnval = NULL;
- static char stored_in_buf[256];
-
- strcpy (stored_in_buf, name);
- length = strlen (stored_in_buf);
- if ((stored_in_buf[length - 1] == 'i') &&
- (stored_in_buf[length - 2] == 'p') &&
- (stored_in_buf[length - 3] == '.'))
- {
- stored_in_buf[length - 3] = '\000';
- rtnval = stored_in_buf;
- }
- else
- {
- namep = strrchr (stored_in_buf, '/');
- if ((namep != NULL) && (STREQ (namep + 1, "Product-Info")))
- {
- *++namep = '\000';
- rtnval = stored_in_buf;
- }
- }
- if (rtnval != NULL)
- {
- #ifdef __amigados__
- /* Convert "/path1/path2/file" to "path1:path2/file" */
- if ((*rtnval == '/') &&
- ((namep = strchr (rtnval + 1, '/')) != NULL))
- {
- rtnval++;
- *namep = ':';
- }
- #endif
- }
- return (rtnval);
- }
-
- int
- find_pifiles (const char *name, struct stat *statbuf, int flags)
- {
- if (stored_in (name) != NULL)
- {
- printf ("%s\n", name);
- }
- return (0);
- }
-
- void
- copy_out (const char *filename)
- {
- int ch;
- FILE *fin;
- char buf[256];
- char *endp;
-
- fin = fopen (filename, "r");
- if (fin != NULL)
- {
- while (fgets (buf, 256, fin) != NULL)
- {
- for (endp = buf; *endp != '\000'; endp++) {;}
- endp--;
- while ((endp >= buf) && (WHITESPACE (*endp)))
- {
- *endp-- = '\000';
- }
- puts (buf);
- }
- fclose (fin);
- }
- }
-
- int
- add_stored_in (const char *name, struct stat *statbuf, int flags)
- {
- char *sname;
-
- sname = stored_in (name);
- if (sname != NULL)
- {
- copy_out (name);
- printf (".stored-in\n%s\n", sname);
- }
- return (0);
- }
-
- void
- free_pif (struct pif *pip)
- {
- if (pip -> pi_fp != NULL) fclose (pip -> pi_fp);
- if (pip -> pi_stored_in != NULL) free (pip -> pi_stored_in);
- if (pip -> pi_address != NULL) free (pip -> pi_address);
- if (pip -> pi_aminet_dir != NULL) free (pip -> pi_aminet_dir);
- if (pip -> pi_author != NULL) free (pip -> pi_author);
- if (pip -> pi_construction != NULL) free (pip -> pi_construction);
- if (pip -> pi_comment != NULL) free (pip -> pi_comment);
- if (pip -> pi_contents != NULL) free (pip -> pi_contents);
- if (pip -> pi_date != NULL) free (pip -> pi_date);
- if (pip -> pi_described_by != NULL) free (pip -> pi_described_by);
- if (pip -> pi_description != NULL) free (pip -> pi_description);
- if (pip -> pi_distribution != NULL) free (pip -> pi_distribution);
- if (pip -> pi_docs != NULL) free (pip -> pi_docs);
- if (pip -> pi_email != NULL) free (pip -> pi_email);
- if (pip -> pi_exectype != NULL) free (pip -> pi_exectype);
- if (pip -> pi_fax != NULL) free (pip -> pi_fax);
- if (pip -> pi_fullname != NULL) free (pip -> pi_fullname);
- if (pip -> pi_installsize != NULL) free (pip -> pi_installsize);
- if (pip -> pi_keywords != NULL) free (pip -> pi_keywords);
- if (pip -> pi_locale != NULL) free (pip -> pi_locale);
- if (pip -> pi_name != NULL) free (pip -> pi_name);
- if (pip -> pi_phone != NULL) free (pip -> pi_phone);
- if (pip -> pi_price != NULL) free (pip -> pi_price);
- if (pip -> pi_reference != NULL) free (pip -> pi_reference);
- if (pip -> pi_requirements != NULL) free (pip -> pi_requirements);
- if (pip -> pi_restrictions != NULL) free (pip -> pi_restrictions);
- if (pip -> pi_run != NULL) free (pip -> pi_run);
- if (pip -> pi_short != NULL) free (pip -> pi_short);
- if (pip -> pi_source != NULL) free (pip -> pi_source);
- if (pip -> pi_submittal != NULL) free (pip -> pi_submittal);
- if (pip -> pi_tested != NULL) free (pip -> pi_tested);
- if (pip -> pi_type != NULL) free (pip -> pi_type);
- if (pip -> pi_version != NULL) free (pip -> pi_version);
-
- free (pip);
- }
-
- struct pif *
- open_pif (const char *name)
- {
- struct pif *pip;
- extern char *malloc ();
- char *stor;
-
- pip = (struct pif *) malloc (sizeof (struct pif));
- if (pip == NULL)
- {
- fprintf (stderr, "pitool: out of virtual memory\n");
- exit (1);
- }
- memset (pip, 0, sizeof (struct pif));
- pip -> pi_fname = name;
- pip -> pi_line = 0;
- pip -> pi_basename = basename (pip -> pi_fname);
- pip -> pi_fp = fopen (pip -> pi_fname, "r");
- stor = stored_in (pip -> pi_fname);
- if (stor != NULL)
- {
- pip -> pi_stored_in = strdup (stor);
- }
- if (pip -> pi_fp == NULL)
- {
- perror (pip -> pi_fname);
- free_pif (pip);
- pip = NULL;
- }
- else if (verbose)
- {
- printf ("%s\n", pip -> pi_fname);
- }
- return (pip);
- }
-
- /* Find next field marker and strip off any whitespace from end of the
- line. */
-
- char *
- next_field (struct pif *pip)
- {
- static char readbuf[256];
- char *bufp;
-
- while (fgets (readbuf, sizeof (readbuf), pip -> pi_fp) != NULL)
- {
- pip -> pi_line++;
- if (readbuf[0] == '.')
- {
- for (bufp = readbuf; *bufp != '\000'; bufp++) {;}
- bufp--;
- while ((bufp >= readbuf) && WHITESPACE (*bufp))
- {
- *bufp-- = '\000';
- }
- return (readbuf);
- }
- }
- return (NULL);
- }
-
- char *
- read_field (struct pif *pip)
- {
- static char readbuf[16 * 1024];
- char *sp = readbuf;
- int ch = '\n'; /* Detect '.' if first char read */
- int prevch = 0;
-
- for (;;)
- {
- prevch = ch;
- ch = getc (pip -> pi_fp);
- if (ch == '\n')
- {
- pip -> pi_line++;
- }
- if (ch == '.')
- {
- if (prevch == '\n')
- {
- ungetc (ch, pip -> pi_fp);
- break;
- }
- }
- else if (ch == EOF)
- {
- if (prevch != '\n')
- {
- fprintf (stderr, "%s:%d: does not end with newline\n",
- pip -> pi_fname, pip -> pi_line);
- *sp++ = '\n';
- }
- break;
- }
- *sp++ = ch;
- }
- if (sp > readbuf)
- {
- sp--;
- }
- *sp = '\000';
- stripwhite (pip, readbuf);
- return (readbuf);
- }
-
- struct pif *
- read_pif (const char *name)
- {
- struct pif *pip;
- char *fieldname;
- char *rawfield;
- char *temp;
-
- pip = open_pif (name);
- if (pip != NULL)
- {
- while ((fieldname = next_field (pip)) != NULL)
- {
- rawfield = read_field (pip);
- if (STREQ (fieldname + 1, "address"))
- { pip -> pi_address = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "aminet-dir"))
- { pip -> pi_aminet_dir = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "author"))
- { pip -> pi_author = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "construction"))
- { pip -> pi_construction = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "comment"))
- { pip -> pi_comment = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "contents"))
- { pip -> pi_contents = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "date"))
- { pip -> pi_date = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "described-by"))
- { pip -> pi_described_by = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "description"))
- { pip -> pi_description = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "distribution"))
- { pip -> pi_distribution = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "docs"))
- { pip -> pi_docs = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "email"))
- { pip -> pi_email = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "exectype"))
- { pip -> pi_exectype = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "fax"))
- { pip -> pi_fax = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "fullname"))
- { pip -> pi_fullname = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "installsize"))
- { pip -> pi_installsize = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "keywords"))
- { pip -> pi_keywords = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "locale"))
- { pip -> pi_locale = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "name"))
- { pip -> pi_name = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "phone"))
- { pip -> pi_phone = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "price"))
- { pip -> pi_price = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "reference"))
- { pip -> pi_reference = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "requirements"))
- { pip -> pi_requirements = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "restrictions"))
- { pip -> pi_restrictions = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "run"))
- { pip -> pi_run = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "short"))
- {
- pip -> pi_short = strdup (rawfield);
- if (strlen (rawfield) > 40)
- {
- fprintf (stderr,
- "%s:%d: '.short' field > 40 characters\n",
- pip -> pi_fname, pip -> pi_line);
- }
- if ((temp = strchr (rawfield, '\n')) != NULL)
- {
- *temp = '\000';
- fprintf (stderr,
- "%s:%d: '.short' field contains newline\n",
- pip -> pi_fname, pip -> pi_line);
- }
- continue;
- }
- if (STREQ (fieldname + 1, "source"))
- { pip -> pi_source = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "submittal"))
- { pip -> pi_submittal = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "tested"))
- { pip -> pi_tested = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "type"))
- { pip -> pi_type = strdup (rawfield); continue; }
- if (STREQ (fieldname + 1, "version"))
- { pip -> pi_version = strdup (rawfield); continue; }
- fprintf (stderr, "%s:%d: unrecognized field '%s'\n",
- pip -> pi_fname, pip -> pi_line, fieldname);
- }
- fclose (pip -> pi_fp);
- pip -> pi_fp = NULL;
- }
- if (pip -> pi_name == NULL)
- {
- fprintf (stderr, "%s:%d: missing '.name' field\n", pip -> pi_fname,
- pip -> pi_line);
- }
- if (pip -> pi_short == NULL)
- {
- fprintf (stderr, "%s:%d: missing '.short' field\n", pip -> pi_fname,
- pip -> pi_line);
- }
- return (pip);
- }
-
- void
- fprintf_pif (FILE *fp, struct pif *pip)
- {
- char pformat[256];
- char *pformatp = pformat;
- char *formatp = format;
- int argc = 0;
- int needspec = 0;
- const char *argv[10]; /* FIXME: check for overflow */
- static char sizebuf[16];
-
- while (*formatp != '\000')
- {
- if (needspec)
- {
- switch (*formatp)
- {
- case 'B': /* Basename of "stored-in" path */
- argv[argc++] = basename (pip -> pi_stored_in);
- *pformatp++ = 's';
- formatp++;
- needspec = 0;
- break;
- case 'D': /* Dirname of "stored-in" path */
- argv[argc++] = dirname (pip -> pi_stored_in);
- *pformatp++ = 's';
- formatp++;
- needspec = 0;
- break;
- case 'K': /* "Stored-in" filesize in Kb */
- sprintf (sizebuf, "%d", filesize (pip -> pi_stored_in) / 1024);
- argv[argc++] = sizebuf;
- *pformatp++ = 's';
- formatp++;
- needspec = 0;
- break;
- case 'P': /* Full "stored-in" path */
- argv[argc++] = pip -> pi_stored_in;
- *pformatp++ = 's';
- formatp++;
- needspec = 0;
- break;
- case 'S': /* The ".short" field */
- argv[argc++] = pip -> pi_short ? pip -> pi_short : "<no description avail>";
- *pformatp++ = 's';
- *formatp++;
- needspec = 0;
- break;
- case 'V': /* Version */
- argv[argc++] = pip -> pi_version ? pip -> pi_version : "?.?";
- *pformatp++ = 's';
- formatp++;
- needspec = 0;
- break;
- case '%':
- if (*(formatp - 1) != '%')
- {
- fprintf (stderr, "bad -F format string, missing spec\n");
- exit (1);
- }
- /* A '%' can quote the following '%' */
- *pformatp++ = *formatp++;
- needspec = 0;
- break;
- default:
- /* Stuff to print verbatum */
- *pformatp++ = *formatp++;
- break;
- }
- }
- else if (*formatp == '%')
- {
- /* We are not looking for a spec because of a previous '%' */
- needspec++;
- *pformatp++ = *formatp++;
- }
- else if (*formatp == '\\')
- {
- /* Simple '\' evaluation, only single chars now. FIXME */
- formatp++;
- switch (*formatp)
- {
- case '\\':
- *pformatp++ = '\\'; /* quoted it */
- break;
- case 't':
- *pformatp++ = '\t';
- break;
- case 'n':
- *pformatp++ = '\n';
- break;
- case 'b':
- *pformatp++ = '\b';
- break;
- case 'r':
- *pformatp++ = '\r';
- break;
- case 'f':
- *pformatp++ = '\f';
- break;
- default:
- *pformatp++ = *formatp;
- break;
- }
- formatp++;
- }
- else
- {
- /* stuff that needs no interpretation */
- *pformatp++ = *formatp++;
- }
- }
- *pformatp++ = '\000';
- fprintf (fp, pformat,
- argv[0], argv[1], argv[2], argv[3], argv[4],
- argv[5], argv[6], argv[7], argv[8], argv[9]);
- }
-
- int
- add_files_bbs (const char *name, struct stat *statbuf, int flags)
- {
- struct pif *pip;
- char bbsfile[256];
- char *scan;
- FILE *bbsfp;
-
- if (stored_in (name) != NULL)
- {
- pip = read_pif (name);
- if (pip != NULL)
- {
- if (STREQ (bbs_file_name, "-"))
- {
- fprintf_pif (stdout, pip);
- }
- else
- {
- strcpy (bbsfile, name);
- scan = strrchr (bbsfile, '/');
- if (scan != NULL)
- {
- scan++;
- strcpy (scan, bbs_file_name);
- bbsfp = fopen (bbsfile, "a");
- if (bbsfp != NULL)
- {
- fprintf_pif (bbsfp, pip);
- fclose (bbsfp);
- }
- }
- }
- free_pif (pip);
- }
- }
- return (0);
- }
-
- int
- test_pifiles (const char *name, struct stat *statbuf, int flags)
- {
- struct pif *pip;
- char bbsfile[256];
- char *scan;
- FILE *bbsfp;
-
- if (stored_in (name) != NULL)
- {
- pip = read_pif (name);
- if (pip != NULL)
- {
- free_pif (pip);
- }
- }
- return (0);
- }
-
- int
- main (argc, argv)
- int argc;
- char *argv[];
- {
- int ch;
- int action;
- int errflg = 0;
- extern int optind;
- extern char *optarg;
-
- while ((ch = getopt (argc, argv, "F:f:bfhnstv")) != EOF)
- {
- switch (ch)
- {
- case 'F':
- format = optarg;
- break;
- case 'f':
- bbs_file_name = optarg;
- break;
- case 'h':
- break;
- case 'v':
- verbose++;
- break;
- case '?':
- errflg++;
- break;
- default:
- action = ch;
- break;
- }
- if (errflg)
- {
- fprintf (stderr, "unknown arg, use -h for help\n");
- exit (1);
- }
- }
- for ( ; optind < argc; optind++)
- {
- switch (action)
- {
- case 'b':
- ftw ((const char *) argv[optind], add_files_bbs, 30);
- break;
- case 'n':
- ftw ((const char *) argv[optind], find_pifiles, 30);
- break;
- case 's':
- ftw ((const char *) argv[optind], add_stored_in, 30);
- break;
- case 't':
- ftw ((const char *) argv[optind], test_pifiles, 30);
- break;
- default:
- fprintf (stderr, "unknown action '%c'\n", action);
- exit (1);
- }
- }
- exit (0);
- return (0);
- }
-