home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 Mobile / Chip_Mobile_2001.iso / palm / hobby / setiatpa / setiatpa.exe / seti@palm / pdbmake / pdbmake.c < prev    next >
C/C++ Source or Header  |  1999-07-22  |  10KB  |  397 lines

  1. /*
  2.  
  3.                 P D B M A K E
  4.                 =============
  5.  
  6.     Make a ready-to-install Palm .pdb file from the contents
  7.     of one or more files on the desktop.  Binary files can be
  8.     embedded into a single database record, while text files
  9.     may be encoded as a database with one record per line,
  10.     each null terminated with the original end of line sentinels
  11.     trimmed.
  12.  
  13.                 by John Walker
  14.                http://www.fourmilab.ch/
  15.          This program is in the public domain
  16.  
  17. */
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <time.h>
  23. #include <assert.h>
  24.  
  25. #include "pdb.h"
  26.  
  27. #ifdef _WIN32
  28. #include "getopt.h"
  29. #else
  30. #include <unistd.h>
  31. #endif
  32.  
  33. static FILE *fi, *fo;
  34.  
  35. /*  Byte-order independent output of various length objects.  */
  36.  
  37. static void outbyte(int b)          /* Single byte */
  38. {
  39.     putc(b, fo);
  40. }
  41.  
  42. static void outshort(int s)          /* Two byte short */
  43. {
  44.     putc(s >> 8, fo);
  45.     putc(s, fo);
  46. }
  47.  
  48. static void outlong(long l)          /* Four byte long */
  49. {
  50.     putc(l >> 24, fo);
  51.     putc(l >> 16, fo);
  52.     putc(l >> 8, fo);
  53.     putc(l, fo);
  54. }
  55.  
  56. static void outbytes(char *b, int len) /* Sequence of bytes */
  57. {
  58.     fwrite(b, len, 1, fo);
  59. }
  60.  
  61. static void outtext(char *b, int len)  /* Text string, zero padded to len */
  62. {
  63.     int sl = strlen(b);
  64.  
  65.     assert((sl + 1) <= len);
  66.     outbytes(b, sl + 1);
  67.     len -= sl + 1;
  68.     while (len-- > 0) {
  69.     outbyte(0);
  70.     }
  71. }
  72.  
  73. /*  TEXTRECORDINIT  --    Initialise extraction of records from
  74.             in-memory text file.  */
  75.  
  76. static char *recptr;
  77. static long reclen, ri;
  78.  
  79. static void textRecordInit(char *r, long l)
  80. {
  81.     recptr = r;
  82.     reclen = l;
  83.     ri = 0;
  84. }
  85.  
  86. /*  TEXTRECORDNEXT  --    Return next record from in-memory text file.  Returns
  87.             0 when end of file reached.  Truncates records as
  88.             required.  Understands PC, Mac, Unix, and VAX end of
  89.             line conventions.  If called with a dest argument
  90.                         of NULL, doesn't copy the text; this can be used
  91.             to count lines in a text buffer.  When dest is
  92.             NULL, destl is irrelevant.  */
  93.                
  94. static int textRecordNext(char *dest, int destl)
  95. {
  96. #define isEOL(c)    (((c) == '\r') || ((c) == '\n'))
  97.     long rend, rl;
  98.     
  99.     if (ri >= reclen) {
  100.     return 0;
  101.     }
  102.     
  103.     for (rend = ri; (rend < reclen) && (!isEOL(recptr[rend])); rend++)
  104.     ;
  105.     
  106.     rl = rend - ri;
  107.     if (dest != NULL) {
  108.     if (rl == 0) {
  109.         *dest = 0;
  110.     } else {
  111.         if (rl < (destl - 1)) {
  112.         memcpy(dest, recptr + ri, rl);
  113.         dest[rl] = 0;
  114.         } else {
  115.         memcpy(dest, recptr + ri, destl - 1);
  116.         dest[destl - 1] = 0;
  117.         }
  118.     }
  119.     }
  120.     ri = rend + 1;
  121.     if ((ri < reclen) && (isEOL(recptr[ri]) && (recptr[ri - 1] != recptr[ri]))) {
  122.     ri++;
  123.     }
  124.     return 1;
  125. }              
  126.  
  127. /*  TEXTRECORDCOUNT  --  Count number of text lines in in-memory buffer.  */
  128.  
  129. static int textRecordCount(char *r, long l)
  130. {
  131.     int n = 0;
  132.  
  133.     textRecordInit(r, l);
  134.     while (textRecordNext(NULL, 0L)) {
  135.     n++;
  136.     }
  137.     return n;
  138. }
  139.  
  140. /*  STRPAD  --    Pack an option string into a zero-padded buffer of
  141.         a given length.  */
  142.  
  143. static char *strpad(int option, char *arg, int len)
  144. {
  145.     char *parg;
  146.  
  147.     if (((int) strlen(arg)) > len) {
  148.         fprintf(stderr, "Argument to -%c option exceeds %d character maximum length.\n", option, len);
  149.     exit(2);
  150.     }
  151.  
  152.     /*    If the string fills the buffer or is one character shorter
  153.         we can use as-is, taking advantage of the "natural pad"
  154.     in the one character less case.  */
  155.  
  156.     if ((((int) strlen(arg)) == len) || (((int) strlen(arg)) == (len - 1))) {
  157.     return arg;
  158.     }
  159.  
  160.     /*    Otherwise, we need to allocate a buffer of the required
  161.     length and copy the argument string to it, adding zero
  162.     fill for the balance of the buffer.  */
  163.  
  164.     parg = malloc(len);
  165.     if (parg == NULL) {
  166.         fprintf(stderr, "Cannot allocate %d byte buffer for %c option argument.\n", len, option);
  167.     exit(1);
  168.     }
  169.     memset(parg, 0, len);
  170.     strcpy(parg, arg);
  171.     return parg;
  172. }
  173.  
  174. /*  USAGE  --  Print how-to-call information.  */
  175.  
  176. static void usage(void)
  177. {
  178.     fprintf(stderr, "pdbmake [options] ifile [ofile] \n");
  179.     fprintf(stderr, "Embed file in Palm Computing(R) Platform PDB file.\n");
  180.     fprintf(stderr, "   Options:\n");
  181.     fprintf(stderr, "       -a             Text file: one record per line\n");
  182.     fprintf(stderr, "       -b             Set backup flag\n");
  183.     fprintf(stderr, "       -c crid        Creator ID (application unique)\n");
  184.     fprintf(stderr, "       -n name        Database name (max 32 characters)\n");
  185.     fprintf(stderr, "       -r             Mark read only\n");
  186.     fprintf(stderr, "       -t type        Database type for application\n");
  187.     fprintf(stderr, "       -u             Print this message\n");
  188.     fprintf(stderr, "       -w             Raw binary (no length before binary data)\n");
  189.     fprintf(stderr, "by John Walker (http://www.fourmilab.ch/)\n");
  190.     fprintf(stderr, "This program is in the public domain.\n");
  191. }
  192.  
  193. /*  Main program.  */
  194.  
  195. int main(int argc, char *argv[])
  196. {
  197.     extern char *optarg;
  198.     extern int optind;
  199.  
  200.     char *inname,              /* Input file name */
  201.      *outname,              /* Output file name */
  202.          *dbname = "Database-PdbM",   /* Database name on Palm */
  203.          *creator = "PdbM",           /* Creator ID */
  204.          *dtype = "PdbM";             /* Application database type */
  205.     short pdbflags = 0;           /* Database flags */
  206.     static char pdbext[] = ".pdb";    /* Default output file extension */
  207.     int i, opt, rawBinary = 0, text = 0, textlines;
  208.     long inflen, today;
  209.     char *dbuf, *record;
  210.  
  211.     while ((opt = getopt(argc, argv, "abc:n:rt:uw")) != -1) {
  212.     switch (opt) {
  213.             case 'a':                 /* -a       ASCII text file mode */
  214.         text = 1;
  215.         break;
  216.  
  217.             case 'b':                 /* -b       Set backup flag */
  218.         pdbflags |= pdbBackupFlag;
  219.         break;
  220.  
  221.             case 'c':                 /* -c crid  Creator ID */
  222.         creator = strpad(opt, optarg, 4);
  223.         break;
  224.  
  225.             case 'n':                 /* -n name  Database name */
  226.         dbname = strpad(opt, optarg, 32);
  227.         break;
  228.  
  229.             case 'r':                 /* -r       Set read-only flag */
  230.         pdbflags |= pdbReadOnlyFlag;
  231.         break;
  232.  
  233.             case 't':                 /* -t type  Database type */
  234.         dtype = strpad(opt, optarg, 4);
  235.         break;
  236.  
  237.             case 'u':
  238.         usage();
  239.         return 0;
  240.  
  241.             case 'w':                 /* -w       Raw binary (no length before data) */
  242.         rawBinary = 1;
  243.         break;
  244.  
  245.             case '?':
  246.         usage();
  247.         return 2;
  248.     }
  249.     }
  250.  
  251.     i = 0;
  252.  
  253.     for (; optind < argc; optind++) {
  254.     switch (i) {
  255.         case 0:
  256.         inname = argv[optind];
  257.         break;
  258.  
  259.         case 1:
  260.         outname = argv[optind];
  261.         break;
  262.     }
  263.     i++;
  264.     }
  265.  
  266.     /* Error if no input file name specified. */
  267.  
  268.     if (i == 0) {
  269.         fprintf(stderr, "No input file name specified.\n");
  270.     usage();
  271.     return 2;
  272.     }
  273.  
  274.     /* If no output file name specified, synthesise by replacing
  275.        input file extension with ".pdb" or appending ".pdb" if
  276.        the input file has no extension. */
  277.  
  278.     if (i == 1) {
  279.     char *ext;
  280.  
  281.     outname = malloc(strlen(inname) + 5);
  282.     if (outname == NULL) {
  283.             fprintf(stderr, "Unable to allocate output file name buffer.\n");
  284.         return 1;
  285.     }
  286.     strcpy(outname, inname);
  287.         ext = strrchr(outname, '.');
  288.     if (ext == NULL) {
  289.         strcat(outname, pdbext);
  290.     } else {
  291.         strcpy(ext, pdbext);
  292.     }
  293.     }
  294.  
  295.     fi = fopen(inname, "rb");
  296.     if (fi == NULL) {
  297.         fprintf(stderr, "Cannot open input file %s.\n", inname);
  298.     return 2;
  299.     }
  300.  
  301.     /* Read input file into memory.  Since the target is
  302.        a handheld with far less memory than this desktop,
  303.        there're no reason to worry about whether the file
  304.        will fit in memory here. */
  305.  
  306.     fseek(fi, 0L, 2);
  307.     inflen = ftell(fi);           /* Input file length */
  308.     rewind(fi);
  309.     dbuf = malloc(inflen);
  310.     if (dbuf == NULL) {
  311.         fprintf(stderr, "Unable to allocate %ld byte I/O buffer.\n", inflen);
  312.     return 1;
  313.     }
  314.     fread(dbuf, inflen, 1, fi);
  315.     fclose(fi);
  316.  
  317.     if (text) {
  318.     textlines = textRecordCount(dbuf, inflen);
  319.     }
  320.  
  321.     fo = fopen(outname, "wb");
  322.     if (fo == NULL) {
  323.         fprintf(stderr, "Cannot create output file %s.\n", outname);
  324.     return 2;
  325.     }
  326.  
  327.     /* Create PDB file header. */
  328.  
  329.     time(&today);
  330.  
  331.     outtext(dbname, kMaxPDBNameSize); /* name */
  332.     outshort(pdbflags);           /* flags */
  333.     outshort(0);              /* version */
  334.     outlong(today + timeOffset);      /* creationTime */
  335.     outlong(today + timeOffset);      /* modificationTime */
  336.     outlong(0);               /* backupTime */
  337.     outlong(0);               /* modificationNumber */
  338.     outlong(0);               /* appInfoOffset */
  339.     outlong(0);               /* sortInfoOffset */
  340.     outbytes(dtype, 4);           /* type */
  341.     outbytes(creator, 4);          /* Creator */
  342.     outlong(0);               /* uniqueID */
  343.     outlong(0);               /* nextRecordID */
  344.     outshort(text ? textlines : 1);   /* numRecords */
  345.  
  346.     /* Create PDB record entry. */
  347.  
  348.     if (text) {
  349.     long roffset = kPDBHeaderSize + (textlines * kPDBRecordEntrySize) + 2;
  350.  
  351.     record = malloc(inflen); /* File may be just one line, after all. */
  352.     if (record == NULL) {
  353.             fprintf(stderr, "Unable to allocate %ld bytes for record buffer.\n", inflen);
  354.     }
  355.     textRecordInit(dbuf, inflen);
  356.     for (i = 0; i < textlines; i++) {
  357.         outlong(roffset);         /* offset */
  358.         outlong(i);          /* attr, uniqueID:24 */
  359.         textRecordNext(record, inflen);
  360.         roffset += strlen(record) + 1;
  361.     }
  362.     } else {
  363.     outlong(kPDBFirstRecordOffset);   /* offset */
  364.     outlong(0);              /* attr, uniqueID:24 */
  365.     }
  366.  
  367.     /* Emit two pad bytes before first record. */
  368.  
  369.     outbyte(0);
  370.     outbyte(0);
  371.  
  372.     /* From now on we're creating the content of the database. */
  373.  
  374.     if (!text) {
  375.     if (!rawBinary) {
  376.  
  377.         /* As a courtesy to the Palm application, precede a binary
  378.            record with its length, as a long. */
  379.  
  380.         outlong(inflen);
  381.     }
  382.     outbytes(dbuf, inflen);
  383.     } else {
  384.     textRecordInit(dbuf, inflen);
  385.     for (i = 0; i < textlines; i++) {
  386.         textRecordNext(record, inflen);
  387.         outbytes(record, strlen(record) + 1);
  388.     }
  389.     free(record);
  390.     }
  391.  
  392.     free(dbuf);
  393.     fclose(fo);
  394.  
  395.     return 0;
  396. }
  397.