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