home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD1.bin / tools / pitools / pitool.c < prev    next >
C/C++ Source or Header  |  1995-01-30  |  26KB  |  1,034 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 emit information extracted
  10.  *        from each file using the format specified by the -F option.
  11.  *        By default, the information is appended to the file
  12.  *        "files.bbs" in the directory in which the product info
  13.  *        file is found.  See the -f option to use another destination.
  14.  *
  15.  *    -D    Enable some special MS-DOS support, such as contructing the
  16.  *        "stored in" name from the basename of the product info file,
  17.  *        with a few explicit possible extensions added, using a
  18.  *        different default format for the files.bbs file, and
  19.  *        using a different default name (FILES.BBS).
  20.  *
  21.  *    -F <s>    Use <s> as a format specifier for writing entries
  22.  *        with -b option.  EX: "%-30.30B %8.8V %4.4KK %-40.40S\n"
  23.  *        Format specs are:
  24.  *
  25.  *        %%    Literal '%' character.
  26.  *        %a    Contents of ".author" field.
  27.  *        %B    Basename of "stored-in" path.
  28.  *        %D    Dirname of "stored-in" path.
  29.  *        %d    Contents of ".description" field.
  30.  *        %F    Contents of ".fullname" (or ".name") field.
  31.  *        %K    Size of the "stored-in" file/directory, in Kb.
  32.  *        %N    Contents of ".name" field.
  33.  *        %P    Full "stored-in" path.
  34.  *        %s    Size of the "stored-in" file in bytes.
  35.  *        %S    Contents of the ".short" field, 40 chars max.
  36.  *        %T    Timestamp of the file or dir, as DD-MMM-YY.
  37.  *        %V    Version number of the product.
  38.  *        
  39.  *        Other characters are passed through literally, as with
  40.  *        printf.  Note that %D & %B are defined such that
  41.  *        %P=%D%B without having to know what the separator was.
  42.  *
  43.  *    -f <s>    Use <s> as the name of the file to use to write entries
  44.  *        to as a result of using the -b option.  If <s> is "-",
  45.  *        the entries are written to stdout rather than to a file
  46.  *        in each directory containing product info files.
  47.  *
  48.  *    -h    Print help message.
  49.  *
  50.  *    -n    Find Product-Info files and print names to stdout.
  51.  *
  52.  *    -s    Gather product info files and add ".stored-in" fields.
  53.  *
  54.  *    -t    Test Product Info files, report possible problems.
  55.  *
  56.  *    -v    Verbose flag, print name of each Product Info file to
  57.  *        stdout.
  58.  *
  59.  *    Generally, "file1", "file2", etc can be replaced with "-" to
  60.  *    indicate that pitool should operate on a list of files provided
  61.  *    on stdin rather than walking a file tree rooted in the path given
  62.  *    by "file1", "file2", etc.
  63.  *
  64.  */
  65.  
  66. #include <stdio.h>
  67. #include <sys/types.h>
  68. #include <sys/stat.h>
  69. #include <time.h>
  70. #include <ctype.h>
  71. #include "ftw.h"
  72.  
  73. #define STREQ(a,b) ((*(a) == *(b)) && !strcmp ((a),(b)))
  74.  
  75. extern int strlen (const char *);
  76. extern char *strrchr (const char *, char);
  77. extern char *strchr (const char *, char);
  78. extern char *strdup ();
  79.  
  80. #define AMIGA_PRINT_FORMAT "%-30.30B %8.8V %4.4KK %-40.40S\n"
  81. #define DOS_PRINT_FORMAT "%-18.18B%8s %8.8T %-40.40S\r\n"
  82. static char *print_format;
  83.  
  84. #define AMIGA_BBS_FILE_NAME "files.bbs"
  85. #define DOS_BBS_FILE_NAME "FILES.BBS"
  86. static char *bbs_file_name;
  87.  
  88. static int verbose = 0;
  89. static int dosflag = 0;
  90.  
  91. struct pif {
  92.   const char *pi_fname;    /* Name of file containing the product info */
  93.   int pi_line;        /* Current line number being read */
  94.   FILE *pi_fp;        /* "FILE *" pointer to the open file */
  95.   int pi_size;
  96.   char *pi_address;
  97.   char *pi_aminet_dir;
  98.   char *pi_author;
  99.   char *pi_construction;
  100.   char *pi_comment;
  101.   char *pi_contents;
  102.   char *pi_date;
  103.   char *pi_described_by;
  104.   char *pi_description;
  105.   char *pi_distribution;
  106.   char *pi_docs;
  107.   char *pi_email;
  108.   char *pi_exectype;
  109.   char *pi_fax;
  110.   char *pi_fullname;
  111.   char *pi_installsize;
  112.   char *pi_keywords;
  113.   char *pi_locale;
  114.   char *pi_name;    /* Contents of the ".name" field */
  115.   char *pi_phone;
  116.   char *pi_price;
  117.   char *pi_reference;
  118.   char *pi_requirements;
  119.   char *pi_restrictions;
  120.   char *pi_run;
  121.   char *pi_short;
  122.   char *pi_source;
  123.   char *pi_stored_in;
  124.   char *pi_submittal;
  125.   char *pi_tested;
  126.   char *pi_type;
  127.   char *pi_version;
  128. };
  129.  
  130. /* This list of common DOS extensions is used to find the "stored in" name
  131.    for the DOS convention for product info files (where files can only have
  132.    one extension and are length limited). */
  133.  
  134. static char *dosextens[] =
  135. {
  136.   ".LHA",    /* Lha archive */
  137.   ".ZIP",    /* Zip archive */
  138.   ".GZ",    /* Gzip compressed file */
  139.   ".TAR",    /* Tar archive */
  140.   ".TAZ",    /* Gzip compressed tar archive */
  141.   ".JPG",    /* Jpeg image */
  142.   ".GIF",    /* Gif image */
  143.   ".IFF",    /* Amiga IFF format image */
  144.   ".ANM",    /* Amiga animation */
  145.   NULL
  146. };
  147.  
  148. /* Note, we can't use isspace() here, because it counts
  149.    international characters as whitespace.  So just look
  150.    for the characters we are actually concerned about */
  151.  
  152. #define WHITESPACE(a) (((a) == ' ') || ((a) == '\t') || ((a) == '\n'))
  153.  
  154. void
  155. stripwhite (struct pif *pip, char *fieldp)
  156. {
  157.   char *endp;
  158.  
  159.   for (endp = fieldp; *endp != '\000'; endp++) {;}
  160.   endp--;
  161.  
  162.   if (WHITESPACE (*endp))
  163.     {
  164. #if 0    /* Currently there are too many Product Info files where this
  165.        is true, to make this a useful message.  Enable it once
  166.        we have processed all of them to eliminate the whitespace. */
  167.       fprintf (stderr, "%s:%d: extraneous whitespace at end of line\n",
  168.            pip -> pi_fname, pip -> pi_line);
  169. #endif
  170.     }
  171.   while ((endp >= fieldp) && (WHITESPACE (*endp)))
  172.     {
  173.       *endp-- = '\000';
  174.     }
  175. }
  176.  
  177. int
  178. filesize (const char *fname)
  179. {
  180.   struct stat statbuf;
  181.  
  182.   if (stat (fname, &statbuf) == 0)
  183.     {
  184.       return (statbuf.st_size);
  185.     }
  186.   else
  187.     {
  188.       return (0);
  189.     }
  190. }
  191.  
  192. char *
  193. timestamp (const char *fname)
  194. {
  195.   struct stat statbuf;
  196.   struct tm *fdate;
  197.   static char datebuf[64];
  198.   static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  199.                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  200.  
  201.   if (stat (fname, &statbuf) != 0)
  202.     {
  203.       return ("??-???-??");
  204.     }
  205.   else
  206.     {
  207.       fdate = localtime (&statbuf.st_mtime);
  208.       if (dosflag)
  209.     {
  210.       sprintf (datebuf, "%02d-%02d-%02d", fdate -> tm_mon + 1,
  211.            fdate -> tm_mday, fdate -> tm_year);
  212.     }
  213.       else
  214.     {
  215.       sprintf (datebuf, "%02d-%3s-%2d", fdate -> tm_mday, 
  216.            months[fdate -> tm_mon], fdate -> tm_year);
  217.     }
  218.       return (datebuf);
  219.     }
  220. }
  221.  
  222. /* Get the basename of a path */
  223.  
  224. const char *
  225. basename (const char *sp)
  226. {
  227.   const char *bname;
  228.  
  229.   /* Look first for typical Unix or AmigaDOS separator, and
  230.      leave bname pointing at the separator. */
  231.  
  232.   bname = strrchr (sp, '/');
  233.  
  234.   /* Look for special AmigaDOS separator */
  235.  
  236. #ifdef __amigados__
  237.   if (bname == NULL)
  238.     {
  239.       bname = strrchr (sp, ':');
  240.     }
  241. #endif
  242.  
  243.   /* If any separator found, skip over it, otherwise the
  244.      basename is just the entire string. */
  245.  
  246.   if (bname == NULL)
  247.     {
  248.       bname = sp;
  249.     }
  250.   else
  251.     {
  252.       bname++;
  253.     }
  254.  
  255.   return (bname);
  256. }
  257.  
  258. /*
  259.  *  Note that dirname is defined such that it *includes* the
  260.  *  separator.  This way you don't need to know whether it 
  261.  *  was a '/' or ':' to reconstruct the full path.  I.E.
  262.  *  %P=%D%B  not %D/%B if '/' or %D%B if ':'
  263.  */
  264.  
  265. const char *
  266. dirname (const char *sp)
  267. {
  268.   char *endp;
  269.   static char buf[256];
  270.  
  271.   strcpy (buf, sp);
  272.  
  273.   /* Look first for typical Unix or AmigaDOS separator */
  274.  
  275.   endp = strrchr (buf, '/');
  276.  
  277.   /* Look for special AmigaDOS separator */
  278.  
  279. #ifdef __amigados__
  280.   if (endp == NULL)
  281.     {
  282.       endp = strrchr (buf, ':');
  283.     }
  284. #endif
  285.  
  286.   /* If there was a separator, set up to zap the next
  287.      character, otherwise the dirname is just the empty
  288.      string. */
  289.  
  290.   if (endp == NULL)
  291.     {
  292.       endp = buf;
  293.     }
  294.   else
  295.     {
  296.       endp++;
  297.     }
  298.   *endp = '\000';
  299.   return ((const char *) buf);
  300. }
  301.  
  302. /*
  303.  *    Given a name of a file that is potentially a product info file,
  304.  *    generate the name of the directory or file that the associated
  305.  *    product is stored in.  By convention, the "stored in" name is
  306.  *    terminated with a '/' if it is a directory.
  307.  *
  308.  *    Normally, the "stored in" name can be generated simply from 
  309.  *    examination of the name of the product info file, such as deriving
  310.  *    "foo/bar/bell.lha" from "foo/bar/bell.lha.pi", or deriving
  311.  *    "foo/bar/bell/" from "foo/bar/bell/Product-Info".
  312.  *
  313.  *    However, because of the braindead naming conventions of MS-DOS,
  314.  *    as a special case for DOS support, the convention of naming the
  315.  *    product info file that goes with another f