lx-gdb-1.03.orig/ 40755 1750 62 0 6160076734 12161 5ustar torinstafflx-gdb-1.03.orig/gdbdump.1100644 1750 62 10556 6107454342 14005 0ustar torinstaff.TH gdbdump 1 "February 1996" "Steve Roth et.al." "HP100LX Tools" .SH NAME gdbdump \- dump HP 100LX database into ASCII format .SH SYNOPSIS .B gdbdump .RB [ -noqswm ] .I file .SH DESCRIPTION .B gdbdump exports the contents of an HP 100LX database into an ASCII form. .I file is the name of the 100LX database to read; the results are written to the terminal and can be redirected or piped as needed. The output format is suitable for input to many database packages as well as to .IR gdbload (1). .SS Options .B gdbdump recognizes the following options: .RS .TP .B -n Suppress the first line of the output, which normally contains the names of all of the database fields. Note that if this option is specified, the output is not compatible with .IR gdbload (1). However, this option may be needed for compatibility with other database programs trying to read the output. .TP .B -o Omit note fields from the output. Note fields are included by default. .TP .B -q Suppress warning messages. .TP .B -s Write special characters (character codes 128-254, inclusive) directly to the output. The default is to represent such characters in \\nnn notation. .TP .B -w Wrap long lines. For some databases, the output line length can be larger than some programs (notably .IR vi (1)) can handle, especially if records contain long notes. This option wraps each output line at about 75 characters, marking the end of lines to be continued with a backslash (\\). .IR gdbload (1) understands this format. .TP .B -m Write multi-line string (i.e. note) fields on multiple lines. Thus the quoted string will span newlines. Without this option, newlines in strings will be output as \\r\\n sequences, and the complete string will be subject to line wrapping if specified by the .B -w option. .RE .SS "Output Format Description" The output of this program is an ASCII text file which starts with a line containing field names (unless .B -n was specified) and is followed by one line for each record of the database. Note that any of these lines may be split into multiple lines if .B -w is specified, and that newlines in strings may cause further splitting if specified by the .B -m option. Each "logical" line contains all of the fields of the database, in the same order in which their field names appeared on the first line of the output. The fields are separated by commas. .PP Exactly how each field appears in the output depends on its type. Text fields, category fields, and note fields appear with the contents inside quote marks ("). Quote marks and backslashes within the text of the field are escaped by preceding them with a backslash (\\). Newlines are printed as \\n and carriage returns as \\r, unless the .B -m option is used. Non-printing or non-ASCII characters as \\nnn, where nnn is an octal character code. (See the description of the .B -s flag, above.) .PP Number fields appear as they do in the database. Date fields appear in the format YYYYMMDD; for example, August 15, 1993 would appear as 19930815. Time fields appear in the format HHMM, where HH is in the range 00-23. .PP Radio buttons and check boxes appear as 1 if selected, 0 otherwise. .PP All other field types, including application-defined types, are omitted from the output. .PP This output format can be used as input to .IR gdbload (1). .SH WARNINGS .B gdbdump cannot handle the application-defined records and fields in HP 100LX Appointment Book and World Time databases. Running this program on such databases will give useful, but incomplete, output. .PP Records are printed in the order stored in the file, i.e., randomly. .PP This program cannot handle password-protected databases. Attempts to dump password-protected databases will have unpredictable results. .SH AUTHOR .B gdbdump was written by Steven Roth, stever@cup.hp.com, and is being maintained by Arne Christensen, arc@pine.dk. Contact the latter for bug reports, enhancement requests, or to get a copy of the source code. .SH DISCLAIMER This program is released into the public domain and neither the author nor the maintainer place any restrictions on its use. We make no warranties or guarantees for this program and you use it at your own risk. This program is supplied by us personally and not by Hewlett-Packard Co. or Pine Tree Systems, which incur no obligations pertaining to it. .SH ACKNOWLEDGEMENTS Many thanks to Andy Gryc for publishing the details of the database file formats! .SH SEE ALSO gdbload(1). lx-gdb-1.03.orig/gdbload.c100644 1750 62 70202 6107451102 14022 0ustar torinstaff/* GDBLOAD.C -- Put data into an HP100LX database. * * usage: gdbload [-an] [] * * -a means add records to the database. The default is to replace the * records in the database. * -n means do not make a backup of the database before beginning. The * default is to back up the database. * is a 100LX database file (.GDB, .PDB, .NDB, or .WDB). * Appointment book files (.ADB) are not supported (yet). * is a file containing the records to add to the database. * If not specified, standard input is used. * The first line of the file must contain a list of field names, * separated by commas and quoted with "" marks if they contain * commas. Subsequent lines of the file represent records (one * per line except that line breaks may occur within note fields), * with the values of the fields in the record presented * in the same order as the field names were on the first line. * String fields must be quoted using "" if they contain commas * or line breaks. * Within strings, the usual C escape codes \r \n \\ \" \nnn and * \xnn are understood. Dates should be encoded as YYYYMMDD, for * example 19930730. Times should be encoded as HHMM, with HH in * the range 00-23. Radio buttons and check boxes are considered * off if the field is empty or contains 0, on otherwise. * * The database file written by gdbload will not be accepted * immediately by the 100LX database engine. Rebuild the index by * pressing F6 and selecting any "subset" (even the current one). * * Programming notes: * There are lots of magic numbers in this code, which come from * the document "Format of 100LX Databases" posted by Andrew J. * Gryc of Hewlett-Packard to the comp.sys.palmtops newsgroup on * 18 June 1993. * I do not use structures in this code to map into the database format, * because they are not portable across architectures with different * byte orders. * * Steve Roth's disclaimer: * These programs are released into the public domain and I place no * restrictions on their use. I make no warranties or guarantees for * these programs and you use them at your own risk. These programs are * supplied by me personally and not by Hewlett-Packard Co., which * incurs no obligations pertaining to them. (Please note that my work * with HP has no connection with palmtops or PC products, and only * information and resources available to the general public were used * in writing these programs. Thanks to Andy Gryc for publishing the * necessary information!) * * Arne Christensen's disclaimer: * Read the above again except for the HP sentences :-) * * Revision History: * 1.03 - Now accepts line breaks in string (note) fields. * 1.02 - (Maintenance taken over by arc@pine.dk) * Added handling of national characters. * Removed compiler dependency (credit Charles Stroom) * 1.01 - Fixed -n bug and added comments. * 1.00 - Initial version. * * Written by: * Steve Roth (stever@cup.hp.com) * * Currently maintained by: * Arne Christensen (arc@pine.dk) * * 24 July 1995 */ #include #include #include /* This define is needed for HP-UX systems, and probably not anywhere else * I would guess. Comment it in if needed. */ /*#define stricmp strcasecmp*/ #define MAXFIELDS 100 /* Max. fields in a database */ #if !defined(TRUE) #define TRUE 1 #define FALSE 0 #endif typedef unsigned char uchar; int fAdd; /* Add to existing database? */ int fBackup; /* Backup existing database? */ int cFields = 0; /* # fields in database */ int cInFields = 0; /* # fields in input stream */ int mapFields[MAXFIELDS]; /* mapping from input stream to db */ int hiNote; /* First available note record # */ int hiData; /* First available data record # */ int relStart; /* Offset of relative data in record */ int cRecords; /* Number of records in output */ int lineNum = 0; /* Line number in input stream */ char * pszDatabase = NULL; /* Database file name */ char * pszInput = NULL; /* Input file name */ char szNew[BUFSIZ]; /* Database.NEW */ char szOld[BUFSIZ]; /* Database.BAK */ char szCategories[257]; /* Categories in database */ uchar dbhdr[29]; /* Buffer for database header */ uchar record[32769]; /* Buffer for a record */ uchar fields[MAXFIELDS][28]; /* Field definitions */ uchar * lineFields[MAXFIELDS]; /* Fields in a line of the input */ FILE * hfNew; /* New database handle */ FILE * hfOld; /* Old database handle */ FILE * hfIn; /* Input stream handle */ /* This routine exits the program with an error message. */ die(char * message) { fprintf(stderr, "gdbload(%d): %s\n", lineNum, message); exit(1); } /* This routine exits the program with a usage message. */ usage() { fprintf(stderr, "usage: gdbload [-an] []\n"); exit(1); } /* This routine parses the command line arguments. */ void parseArgs(int argc, char * argv[]) { /* Switch defaults: */ fAdd = FALSE; /* Replace the contents of the db */ fBackup = TRUE; /* Back up the unchanged db */ for (argc--, argv++; argc; argc--, argv++) { /* For each argument */ if (**argv == '-') /* Is it an option? */ for ((*argv)++; **argv; (*argv)++) /* For each character in it */ switch (**argv) { case 'a': fAdd = TRUE; break; case 'n': fBackup = FALSE; break; default: usage(); break; } else if (!pszDatabase) /* Is it the database name? */ pszDatabase = *argv; else if (!pszInput) /* Is it the input file name? */ pszInput = *argv; else usage(); /* I don't know what it is! */ } if (!pszDatabase) /* Did we get a database name? */ usage(); } /* Generate the file names we need. */ void makeFileNames() { char * pch; /* Work backwards through the database file name */ pch = strchr(pszDatabase, '\0'); while (pch > pszDatabase) switch (*(--pch)) { case '.': /* Found a (.). Replace extension with .new and .bak * and return. */ *pch = '\0'; strcpy(szNew, pszDatabase); strcpy(szOld, pszDatabase); strcat(szNew, ".new"); strcat(szOld, ".bak"); *pch = '.'; pch = pszDatabase; /* breaks while loop */ break; case '/': case '\\': /* Found a path separator. Add .new or .bak * extension and return. */ strcpy(szNew, pszDatabase); strcpy(szOld, pszDatabase); strcat(szNew, ".new"); strcat(szOld, ".bak"); pch = pszDatabase; /* breaks while loop */ break; } if (!*szNew) /* Did we find either one? */ { strcpy(szNew, pszDatabase); strcpy(szOld, pszDatabase); strcat(szNew, ".new"); strcat(szOld, ".bak"); } } /* This array of structures is used to build the record index table at the * end of the database file. The 32 array elements correspond to the 32 * record types in a database. For each, the array contains the location * (file offset) and size of the first 32 records of that type, plus a * link to additional, identical structures in case there are more than * 32 records of that type. */ struct index_s { long location[32]; int size[32]; struct index_s * next; } Index[32]; /* This array contains the number of the highest record in use of each type, * plus 1. (E.g. if the highest data record is #11, highIndex[11] == 12; * if there are no note records, highIndex[9] == 0.) */ int highIndex[32]; /* This routine adds an entry to the index table described above. */ void addIndexEntry(uchar * rechdr, long location) { struct index_s * indexptr; /* Ptr to the index table entry */ int recnum; /* Record number */ int i; indexptr = &Index[rechdr[0]]; /* Find the head of the index list */ recnum = rechdr[4] + rechdr[5] * 256; if (recnum > highIndex[rechdr[0]] - 1) /* We have a new high record number for this type. */ highIndex[rechdr[0]] = recnum + 1; /* Scan through the linked list of index table entries, looking * for the one that should contain this record. We may have to * allocate and and new entries to the list. */ for (i = 0; i < recnum / 32; i++) if (indexptr->next) indexptr = indexptr->next; else { indexptr->next = (struct index_s *) malloc(sizeof (struct index_s)); memset((char *)indexptr->next, 0, sizeof(struct index_s)); indexptr = indexptr->next; } /* Store the location and size in the index table. */ indexptr->location[recnum % 32] = location; indexptr->size[recnum % 32] = rechdr[2] + rechdr[3] * 256; } /* This routine writes the index table into the new database. */ void writeIndexEntries(void) { struct index_s * indexptr; /* Index pointer */ uchar rechdr[6]; /* Record header buffer */ uchar indexent[8]; /* Index entry */ uchar intbuf[2]; /* Buffer for integer write */ int reclen; /* Length of the index table */ int first; /* First record # of each type */ int i; int j; /* Count the number of records. Note that, for the purposes of the * database manager, we don't care exactly how many records are in * the file; what we want is the sum of the highest record numbers * of each type. */ cRecords = 0; for (i = 0; i < 32; i++) cRecords += highIndex[i]; /* Generate and write the record header for the index table. */ rechdr[0] = 31; rechdr[1] = 2; reclen = cRecords * 8 + 6; rechdr[2] = reclen % 0x100; rechdr[3] = reclen / 0x100; rechdr[4] = 0; rechdr[5] = 0; if (1 != fwrite(rechdr, 6, 1, hfNew)) die("can't write index table header"); /* Write the index table itself. */ for (i = 0; i < 32; i++) /* For each record type */ { indexptr = &Index[i]; for (j = 0; j < highIndex[i]; j++) { /* For each record of that type */ if (j % 32 == 0 && j > 0) indexptr = indexptr->next; /* Generate and write the index table entry. */ indexent[0] = indexptr->size[j%32] % 0x100; indexent[1] = indexptr->size[j%32] / 0x100; indexent[2] = -1; indexent[3] = -1; if (indexptr->location[j%32]) indexent[4] = 0; else /* Record was deleted */ indexent[4] = 0xc0; indexent[5] = indexptr->location[j%32] % 0x100; indexent[6] = indexptr->location[j%32] / 0x100 % 0x100; indexent[7] = indexptr->location[j%32] / 0x10000; if (1 != fwrite(indexent, 8, 1, hfNew)) die("can't write index table entry"); } } /* Now generate and write the type first table. */ first = 0; for (i = 0; i < 32; i++) { intbuf[0] = first % 0x100; intbuf[1] = first / 0x100; if (1 != fwrite(intbuf, 2, 1, hfNew)) die("can't write type first table entry"); first += highIndex[i]; } } /* This routine copies a record from the old database to the new, unchanged. */ void CopyRecord(uchar * rechdr, uchar * record, int reclen) { int recnum; if (rechdr[1] & 0x01) /* Record was deleted */ return; /* Save the record's information in our index table. */ addIndexEntry(rechdr, ftell(hfNew)); /* Write it out. */ if (1 != fwrite(rechdr, 6, 1, hfNew) || (reclen != 0 && 1 != fwrite(record, reclen, 1, hfNew))) die("can't write record"); /* Update the available data and note record numbers */ recnum = rechdr[5] * 256 + rechdr[4]; if (rechdr[0] == 9 && recnum > hiNote-1) hiNote = recnum+1; if (rechdr[0] == 11 && recnum > hiData-1) hiData = recnum+1; } /* This routine copies a record from the old database to the new, but only * if -a was specified. */ void CopyIfAdd(uchar * rechdr, uchar * record, int reclen) { if (fAdd) CopyRecord(rechdr, record, reclen); } /* This routine saves the categories of the old database if -a was specified, * and ignores them otherwise. */ void Categories(uchar * rechdr, uchar * record, int reclen) { if (fAdd) { szCategories[0] = ';'; strcpy(szCategories+1, (char *)record); strcat(szCategories, ";"); } } /* This routine copies a field definition from the old database to the new, * and saves it in memory for later use also. */ void DoFieldDef(uchar * rechdr, uchar * record, int reclen) { int ptr; /* Data offset for field */ /* Save the field def. */ memcpy(fields[cFields], record, reclen); ptr = record[3] * 256 + record[2] + 2; if (!(record[4] & 0x80)) /* Field contains data? */ { /* Save the beginning of relative data in relStart. */ if (record[0] == 8 && !(record[4] & 0x20)) /* Date fields take 3 bytes */ ptr++; if ((record[0] == 0 || record[0] == 9) && !(record[4] & 0x20)) /* Byte booleans and radio buttons take 1 byte */ ptr--; if (ptr > relStart) /* The absolute data for this field ends later than * any other we have looked at. Save this new end * point. */ relStart = ptr; } cFields++; CopyRecord(rechdr, record, reclen); } /* This routine writes an empty viewpoint table to the new database for * every viewpoint table found in the old. */ void ViewTable(uchar * rechdr, uchar * record, int reclen) { rechdr[2] = 6+2; rechdr[3] = 0; record[0] = 0xff; record[1] = 0xff; addIndexEntry(rechdr, ftell(hfNew)); if (1 != fwrite(rechdr, 6, 1, hfNew) || 1 != fwrite(record, 2, 1, hfNew)) die("can't write viewpoint table"); } /* This array contains the routines to be used to handle each type of record * in the database during a copy operation. */ typedef void (*RecordHandler)(uchar * rechdr, uchar * record, int reclen); RecordHandler RecordHandlers[32] = { CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, Categories, DoFieldDef, CopyRecord, CopyRecord, CopyIfAdd, ViewTable, CopyIfAdd, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord, CopyRecord }; /* This routine copies the appropriate records of the old database to the * new. */ void copyDatabase() { uchar rechdr[6]; /* Record header buffer */ int reclen; /* Record length */ if (!(hfOld = fopen(pszDatabase, "rb")) && !(hfOld = fopen(pszDatabase, "r"))) die("can't read database"); if (!(hfNew = fopen(szNew, "wb")) && !(hfNew = fopen(szNew, "w"))) die("can't create .new file"); /* Copy the database header from old to new. Note that the database * header in the new database will be overwritten once we know the * number of records and such. For now, it's just a placeholder. * However, the magic ViewPtHash value is important to read, we would * not know how to compute it otherwise. */ if (1 != fread(dbhdr, 29, 1, hfOld)) die("can't read database header"); addIndexEntry(dbhdr+4, 4); if (1 != fwrite(dbhdr, 29, 1, hfNew)) die("can't write database header"); /* Various checks for valid data. */ if (memcmp(dbhdr, "hcD", 4)) die("not a database file"); if (dbhdr[11] != 1 || dbhdr[10] != 2) die ("wrong version of database format"); if (dbhdr[12] == '2') die ("can't handle appointment book databases"); if (dbhdr[12] == 'W') die ("can't handle world time databases"); /* Scan through the old database, handling each record we find * according to the handler table above. Stop when we reach the * index table or end of file. */ *szCategories = '\0'; do { if (1 != fread(rechdr, 6, 1, hfOld)) break; /* Can't read, we're done */ if (rechdr[0] == 31) break; /* Index table, we're done */ /* Read the record and process it. */ reclen = rechdr[3] * 256 + rechdr[2] - 6; if (reclen != 0 && 1 != fread(record, reclen, 1, hfOld)) die("read error in middle of record"); (*RecordHandlers[rechdr[0]])(rechdr, record, reclen); } while (1); /* Did we find any fields? any categories? */ if (cFields == 0) die("no FIELDDEF records found in database"); if (!*szCategories) strcpy(szCategories, ";;"); } /* This routine reads a line of input and breaks it into fields. */ void readLine() { int field = 0; /* Field number being read */ int matchQuote = FALSE; /* Inside quotation marks */ int fDone = FALSE; /* Finished reading */ uchar ch; uchar * pch = record; /* Wipe out any old data. */ memset(lineFields, 0, sizeof(lineFields)); /* Forgive the goto... * We want to skip blank and comment lines. */ nextline: while ((ch = fgetc(hfIn)) == '\r' || ch == '\n') if (ch == '\n') lineNum++; if (ch == ';') { while ((ch = fgetc(hfIn)) != '\n' && !feof(hfIn)) ; lineNum++; goto nextline; } do /* For all characters in input */ { if (feof(hfIn)) /* End of file? */ { lineFields[0] = NULL; return; } switch (ch) { default: /* Normally, copy chars into buffer */ *pch++ = ch; break; case '"': /* Quote marks toggle the flag */ matchQuote = ! matchQuote; break; case ',': /* Commas skip to next field unless */ if (matchQuote) /* they are inside quotes */ *pch++ = ch; else { *pch = '\0'; lineFields[field++] = (uchar *)strdup(record); pch = record; } break; case '\\': /* Backslashes handled specially */ ch = fgetc(hfIn); switch (ch) /* Look at next character */ { case '\r': /* Skip carriage returns */ fgetc(hfIn); case '\n': /* Line feeds are ignored */ lineNum++; break; case 'n': /* \n is a newline */ *pch++ = '\n'; break; case 'r': /* \r is a carriage return */ *pch++ = '\r'; break; case 'x': /* \xnn is a hexadecimal char */ { uchar ch2; ch = fgetc(hfIn) - '0'; ch2 = fgetc(hfIn) - '0'; if (ch > 9) ch -= 'A' - '0' + 10; if (ch > 9) ch -= 'a' - 'A'; if (ch2 > 9) ch2 -= 'A' - '0' + 10; if (ch2 > 9) ch2 -= 'a' - 'A'; *pch++ = ch * 16 + ch2; break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* \nnn is an octal char */ { uchar chstr_1, chstr_2; chstr_1 = fgetc(hfIn); chstr_2 = fgetc(hfIn); *pch++ = 64 * (ch - '0') + 8 * (chstr_1 - '0') + (chstr_2 - '0'); break; } default: /* Anything else is copied to buffer */ *pch++ = ch; } break; case '\r': /* Skip carriage returns */ fgetc(hfIn); case '\n': /* Newlines end the record */ lineNum++; if (matchQuote) { *pch++ = '\r'; *pch++ = '\n'; } else { *pch = '\0'; lineFields[field++] = (uchar *)strdup(record); pch = record; fDone = TRUE; } break; } ch = fgetc(hfIn); } while (!fDone); ungetc(ch, hfIn); /* Put back the last character */ lineFields[field] = NULL; /* Mark end of the record */ } /* This routine compares two field names and returns TRUE if they match. */ int matchField(uchar * name1, uchar * name2) { char namebuf1[21], namebuf2[21], * pch; /* Copy the field names into local buffers */ strncpy(namebuf1, (char *)name1, 20); strncpy(namebuf2, (char *)name2, 20); namebuf1[20] = namebuf2[20] = '\0'; /* Remove &s from the field names */ while (pch = strchr(namebuf1, '&')) memmove(pch, pch+1, strlen(pch)); while (pch = strchr(namebuf2, '&')) memmove(pch, pch+1, strlen(pch)); /* Return a case-insensitive comparison of the result. */ return (!stricmp(namebuf1, namebuf2)); } /* This routine reads the line of field names and compares it with the * field definitions in the database. */ void readFields() { int i; /* First open the input file. */ if (!pszInput) hfIn = stdin; else if (!(hfIn = fopen(pszInput, "rt")) && !(hfIn = fopen(pszInput, "r"))) die("can't open input file"); memset(mapFields, -1, sizeof(mapFields)); /* Read the line of field names */ readLine(); while (lineFields[cInFields]) /* For each field name listed */ { for (i = 0; i < cFields; i++) /* For each field in the database */ if (matchField(lineFields[cInFields], fields[i]+7)) { /* Do they match? */ mapFields[cInFields] = i; break; } if (mapFields[cInFields] == -1) { /* Didn't find a match for this field name */ fprintf(stderr, "gdbload(%d): can't match field '%s'\n", lineNum, lineFields[cInFields]); exit(1); } if (fields[mapFields[cInFields]][4] & 0x80) { /* Does this field contain data? */ fprintf(stderr, "gdbload(%d): field '%s' has no data\n", lineNum, lineFields[cInFields]); exit(1); } if (fields[mapFields[cInFields]][0] == 16) { /* Is this field app-specific? */ fprintf(stderr, "gdbload(%d): field '%s' is not " "handled by gdbload\n", lineNum, lineFields[cInFields]); exit(1); } free(lineFields[cInFields++]); } if (cInFields == 0) /* Did we find any fields? */ die("no field mappings found"); } /* This routine adds a note to the database. The record number of the * created note is returned. */ int addNote(uchar * line, uchar * fielddef) { uchar rechdr[6]; /* Record header buffer */ int reclen = strlen((char *)line); /* Size of note */ /* Fill in record header */ rechdr[0] = 9; rechdr[1] = 2; rechdr[2] = (reclen+6) % 0x100; rechdr[3] = (reclen+6) / 0x100; rechdr[4] = hiNote % 0x100; rechdr[5] = hiNote / 0x100; hiNote++; /* Write index entry and record. */ addIndexEntry(rechdr, ftell(hfNew)); fwrite(rechdr, 6, 1, hfNew); fwrite(line, reclen, 1, hfNew); /* Return note record number. */ return hiNote-1; } /* This routine adds the categories in a data record to the list of * categories used in the database. */ void addCategories(uchar * line) { static char buf[256]; /* Buffer for category name */ char * end; if (!*line) /* Any categories in this record? */ return; do { /* For each category in the record */ end = strchr((char *)line, ';'); if (end) *end = '\0'; sprintf(buf, ";%s;", line); /* Copy to buffer */ if (!strstr(szCategories, buf)) /* Is it already on the list? */ if (strlen(szCategories) + strlen(buf) - 2 > 256) /* Would it overflow the list? */ die("too many categories"); else if (!strcmp(szCategories, ";;")) /* Is there anything on the list? */ strcpy(szCategories, buf); else strcat(szCategories, buf+1); if (end) { *end = ';'; line = (uchar *)end+1; } } while (end && *line); } /* This routine adds a field of a record to the record buffer. */ void DoField(uchar * line, uchar * fielddef, int note, int * endrec) { int offset = fielddef[2] + fielddef[3] * 256; switch (*fielddef) { case 6: /* Category field */ addCategories(line); /* Add to categ list */ /* FALL-THROUGH */ case 2: /* String fields of various types */ case 3: case 4: case 5: case 13: case 15: if (!*line) /* Is there any text? */ /* No, point to the null byte at the beginning of * the relative data. */ record[offset] = relStart % 0x100, record[offset+1] = relStart / 0x100; else { /* Yes, so point to the end of the relative data * area, and copy the string there. */ record[offset] = *endrec % 0x100; record[offset+1] = *endrec / 0x100; strcpy((char *)record+*endrec, (char *)line); *endrec += strlen((char *)line) + 1; } break; case 10: /* Note field */ if (!*line) /* Is there any text? */ /* No, so put a -1 for the note record number */ record[offset] = -1, record[offset+1] = -1; else /* Yes, so put in the note record number */ record[offset] = note % 0x100, record[offset+1] = note / 0x100; break; case 9: /* Radio button */ if (*line && strcmp((char *)line, "0")) /* If anything other than 0 is in the field, set * the radio button value. */ record[offset] = fielddef[5]; break; case 8: /* Date field */ { long date = atol((char *)line); int year = date / 10000; int month = date / 100 % 100; int day = date % 100; if (!*line) /* Is there any text? */ { /* No, put in a null date. */ record[offset] = record[offset+1] = record[offset+2] = -1; break; } /* Check the date for validity. */ if (year < 1900 || year > 2099 || month < 1 || month > 12 || day < 1 || day > 31) { fprintf(stderr, "gdbdump(%d): %s is not a valid date\n", lineNum, line); exit(1); } /* Put in the date. */ record[offset] = year - 1900; record[offset+1] = month - 1; record[offset+2] = day - 1; break; } case 7: /* Time */ { int time = atoi((char *)line); int hour = time / 100; int minutes = time % 100; if (!*line) /* Is there any text? */ { /* No, so put in a null time */ record[offset] = 0; record[offset+1] = 0x80; break; } /* Check the time for validity */ if (hour < 0 || hour > 23 || minutes < 0 || minutes > 59) { fprintf(stderr, "gdbdump(%d): %s is not a valid time\n", lineNum, line); exit(1); } /* Put in the time */ time = hour * 60 + minutes; record[offset] = time % 0x100; record[offset+1] = time / 0x100; break; } case 0: /* Bit in a byte (checkbox) */ case 1: /* Bit in a word (checkbox) */ { int word = record[offset] + record[offset+1] * 256; int mask = fielddef[5] + fielddef[6] * 256; /* Is there anything other than 0 in the text? */ if (*line && strcmp((char *)line, "0")) /* Yes, set the bit. */ word |= mask; else /* No, clear it. */ word &= ~mask; record[offset] = word % 0x100; record[offset+1] = word / 0x100; break; } } } /* This routine adds the input records to the database. */ void addRecords() { uchar rechdr[6]; /* Record header buffer */ int endrec; /* Offset of first unused by in rec */ int note; /* Record # of associated note */ int i; do { /* For all records */ readLine(); if (!lineFields[0]) /* End of file? */ return; /* Check for correct number of fields on line. */ if (!lineFields[cInFields-1]) die("not enough fields specified"); if (cInFields < MAXFIELDS && lineFields[cFields]) die("too many fields specified"); /* Does the record have a note? */ for (i = 0; i < cInFields; i++) if (fields[mapFields[i]][0] == 10 && lineFields[i][0]) /* Yes. Add it to database. */ note = addNote(lineFields[i], fields[mapFields[i]]); /* Now do the record */ endrec = relStart + 1; memset(record, 0, relStart+1); for (i = 0; i < cInFields; i++) { DoField(lineFields[i], fields[mapFields[i]], note, &endrec); free(lineFields[i]); } /* Generate the data record header */ rechdr[0] = 11; rechdr[1] = 2; rechdr[2] = (endrec + 6) % 0x100; rechdr[3] = (endrec + 6) / 0x100; rechdr[4] = hiData % 0x100; rechdr[5] = hiData / 0x100; hiData++; /* Write the index entry and the record. */ addIndexEntry(rechdr, ftell(hfNew)); fwrite(rechdr, 6, 1, hfNew); fwrite(record, endrec, 1, hfNew); } while(1); } /* This routine writes out the database header, the categories record, * and the index table. */ void writeHeader() { uchar rechdr[6]; /* Record header buffer */ int reclen; /* Record length */ long index; /* Offset of index in file */ /* Write out categories record. */ rechdr[0] = 5; rechdr[1] = 2; reclen = strlen(szCategories) - 2 /* extra semicolons */ + 1 /* null terminator */ + 6 /* record header */ ; rechdr[2] = reclen % 0x100; rechdr[3] = reclen / 0x100; rechdr[4] = 0; rechdr[5] = 0; addIndexEntry(rechdr, ftell(hfNew)); fwrite(rechdr, 6, 1, hfNew); fwrite(szCategories + 1, strlen (szCategories) - 2, 1, hfNew); fwrite(rechdr+5, 1, 1, hfNew); /* Update the database header with the location of the index table. */ index = ftell(hfNew); dbhdr[18] = index % 0x100; dbhdr[19] = index / 0x100 % 0x100; dbhdr[20] = index / 0x10000 % 0x100; dbhdr[21] = index / 0x1000000; writeIndexEntries(); /* Update the database header and write it out. */ dbhdr[13] |= 0x02; /* Database has been modified */ dbhdr[16] = cRecords % 0x100; dbhdr[17] = cRecords / 0x100; fseek(hfNew, 0, SEEK_SET); fwrite(dbhdr, 29, 1, hfNew); } /* Main program. */ main(int argc, char * argv[]) { parseArgs(argc, argv); makeFileNames(); copyDatabase(); readFields(); addRecords(); writeHeader(); fclose(hfIn); fclose(hfOld); fclose(hfNew); unlink(szOld); rename(pszDatabase, szOld); rename(szNew, pszDatabase); if (!fBackup) unlink(szOld); exit(0); } lx-gdb-1.03.orig/gdbload.1100644 1750 62 11572 6107456260 13757 0ustar torinstaff.TH gdbload 1 "February 1996" "Steve Roth et.al." "HP100LX Tools" .SH NAME gdbload \- load ASCII formatted data into an HP 100LX database .SH SYNOPSIS .B gdbload .RB [ -an ] .I database [ .I input ] .SH DESCRIPTION .B gdbload loads ASCII formatted data into an HP 100LX database. .I database is the name of the 100LX database to modify. .I input is the name of a file of ASCII data to load into the database. If no input file is specified, the standard input is used. The ASCII file format is one exported by many database packages as well as by .IR gdbdump (1). .SS Options .B gdbload recognizes the following options: .RS .TP .B -a Add the records from the ASCII file to the database, keeping the records already present in the database. By default, the records in the ASCII file replace those already in the database. .TP .B -n Do not back up the database file. By default, the original, unmodified database file is left in a file with the same name and a .bak extension. .RE .SS "Input Format Description" The input to this program is an ASCII text file which starts with a line containing field names. This line indicates the order in which fields appear on subsequent lines. Not all fields of the database need be specified; unspecified fields will be left blank in all added records. Field names are not case distinct and ampersands (&) are ignored in comparing field names. .PP This first line is followed by one line for each record of the database. Note that any of these lines may be split into multiple lines if needed, by placing a backslash (\\) at the ends of lines which are continued. Also note that string fields may span multiple lines provided they are quoted. Apart from line splitting according to these rules, each "logical" line contains all of the fields whose names were listed on the first line, in the same order as on the first line. The fields (and the field names, on the first line) are separated by commas. .PP Exactly how each field should appear depends on its type. Text fields, category fields, number fields, and note fields should have their text quoted if it contains commas or newlines. The following escape sequences are understood: .RS . Can't produce a \ on Solaris nroff, need to manually substitute \ for % ! .TP .B "%r" Carriage return (ASCII 13). .TP .B "%n" Line feed (ASCII 10). .TP .B "%nnn" nnn are octal digits representing a character. .TP .B "%xnn" nn are hexadecimal digits representing a character. .RE .PP Any other character following a backslash is treated as a standard character with no special meaning, i.e., backslash and quote marks can be escaped by preceding them with a backslash. .PP Date fields should appear in the format YYYYMMDD; for example, August 15, 1993 should appear as 19930815. Time fields appear in the format HHMM, where HH is in the range 00-23. Date and time fields may also be left blank, i.e., nothing between the commas. .PP Radio buttons and check boxes are turned off if the field is empty or contains a 0; they are turned on otherwise. .PP No other field types, including application-defined types, are accepted by .B gdbload. .PP The output from .IR gdbdump (1) matches this input format, unless the .B -n flag is given to .IR gdbdump (1). .SH WARNINGS When the newly constructed database is first opened by the 100LX, it will (erroneously) be reported as being empty. This is because .B gdbload does not construct database indexes, and the 100LX expects the index for the current "subset" to be valid. The situation is remedied by pressing F6 and selecting any "subset" (even the current one!) This will rebuild the index for that "subset", causing records to display normally. The delay caused by the rebuild depends on the size of the database (among other things), and ranges from imperceptible for small databases up to several minutes. This delay will occur whenever a new "subset" is selected for the first time. .PP .B gdbload will not attempt to modify HP 100LX Appointment Book and World Time databases. .PP .B gdbload will not allow you to specify data for application-defined fields of a database. .PP This program cannot handle password-protected databases. Attempts to load data into password-protected databases will have unpredictable results. .SH AUTHOR .B gdbload was written by Steven Roth, stever@cup.hp.com, and is being maintained by Arne Christensen, arc@pine.dk. Contact the latter for bug reports, enhancement requests, or to get a copy of the source code. .SH DISCLAIMER This program is released into the public domain and neither the author nor the maintainer place any restrictions on its use. We make no warranties or guarantees for this program and you use it at your own risk. This program is supplied by us personally and not by Hewlett-Packard Co. or Pine Tree Systems, which incur no obligations pertaining to it. .SH ACKNOWLEDGEMENTS Many thanks to Andy Gryc for publishing the details of the database file formats! .SH SEE ALSO gdbdump(1). lx-gdb-1.03.orig/gdbdump.c100644 1750 62 33551 6107451150 14061 0ustar torinstaff/* GDBDUMP.C -- Generic HP100LX Database Dump Program * * Usage: gdbdump [-noqsw] file * -n causes the first line of the output, which normally contains field * names, to be omitted. * -o causes note fields to be omitted. * -q causes warning messages to be suppressed. * -s causes special characters (those with character codes between 128 * and 254, inclusive) to be written unmodified to the output. The * default is to write them in \nnn notation. * -w causes lines to be wrapped near 75 columns, with a trailing * backslash on lines to be continued. Lines will be broken at 70 * columns except that an escape sequence will not be broken (the * break is delayed). * -m causes multi-line string (note) fields to be written as multiple * lines, with line breaks in the output where line breaks are found * in the string field. Default (i.e. the good ol' behaviour) is to * output line breaks as \r\n sequences. * * Output: * One line of field names (unless -n is specified), followed by one line * for each record in the database. Each line contains the fields of a * record, separated by commas, in the order indicated by the first line. * Only fields of the database which have values are output. * * Fields are output as follows: * Text, Category, or Note * Text of the field is contained in double quote marks (""). * Double quote marks within the data are escaped by a backslash * (\), as are backslashes themselves. Characters with ASCII * codes outside the range 32-126 are represented as \nnn, * where nnn is in octal, except that 10 and 13 are represented * as \n and \r, respectively. * Categories are represented as the concatenation of the * category names, separated by semicolons (;). * Number, Date, or Time * Stored in standard ASCII representation. Dates are in the * form YYYYMMDD, for example 19930724. Times are in the form * HHMM, where HH ranges from 0-23. * Option Button or Check Box * Selected buttons are stored as 1; unselected buttons are * stored as 0. * * Programming notes: * There are lots of magic numbers in this code, which come from * the document "Format of 100LX Databases" posted by Andrew J. * Gryc of Hewlett-Packard to the comp.sys.palmtops newsgroup on * 18 June 1993. * I do not use structures in this code to map into the database format, * because they are not portable across architectures with different * byte orders. * This program reads the records in the database file sequentially, * without looking at the index table. This results in some speed * degradation but allows operation on databases with incomplete * indexes. * * Steve Roth's disclaimer: * These programs are released into the public domain and I place no * restrictions on their use. I make no warranties or guarantees for * these programs and you use them at your own risk. These programs are * supplied by me personally and not by Hewlett-Packard Co., which * incurs no obligations pertaining to them. (Please note that my work * with HP has no connection with palmtops or PC products, and only * information and resources available to the general public were used * in writing these programs. Thanks to Andy Gryc for publishing the * necessary information!) * * Arne Christensen's disclaimer: * Read the above again except for the HP sentences :-) * * Revision History: * 1.03 - Added -m option. * 1.02 - (Maintenance taken over by arc@pine.dk) * Fixed bitmask offsets of check boxes (credit Diomidis Spinellis) * 1.01 - Added -s flag and more comments. * 1.00 - Initial version. * * Written by: * Steve Roth (stever@cup.hp.com) * * Currently being maintained by: * Arne Christensen (arc@pine.dk) * * 11 February 1996 */ #include #include #if !defined(TRUE) #define TRUE 1 #define FALSE 0 #endif #define LINELEN 70 /* Wrap lines here if -w specified */ #define MAXFIELDS 100 /* Max fields in a database */ typedef unsigned char uchar; FILE * hf; /* Database file handle */ int fNames; /* Field names requested */ int fNotes; /* Notes requested */ int fWarnings; /* Warnings requested */ int fSpecials; /* Special characters requested */ int fWrap; /* Wrapped lines requested */ int fMultiLine; /* Notes on multiple lines requested */ long lFirst; /* Offset of first database record */ int cFields = 0; /* Number of fields in database */ uchar fields[MAXFIELDS][28]; /* Field definitions */ int cChars = 0; /* # chars written on curr. line */ /* Exit the program with an error message. */ void die(char * msg) { fprintf(stderr, "%s\n", msg); exit(1); } /* Exit the program with a usage message. */ void usage(void) { die("usage: gdbdump [-noqsw] file"); } /* Write out a string, wrapping lines appropriately. If fBreak is FALSE, * do not wrap lines within this string; postpone the wrap until the entire * string has been written. */ void out(char * s, int fBreak) { if (!fWrap) /* Are we wrapping lines at all? */ { fputs(s, stdout); return; } if (fBreak) /* Can we wrap within this string? */ { while (cChars + strlen(s) > LINELEN) { /* Print to end of line, repeatedly */ printf("%.*s\\\n", LINELEN - cChars, s); s += LINELEN - cChars; cChars = 0; } if (*s) /* Any left over? */ { fputs(s, stdout); cChars += strlen(s); } } else /* Can't wrap within this string */ { fputs(s, stdout); cChars += strlen(s); if (cChars > LINELEN) /* Do we need to wrap after it? */ { printf("\\\n"); cChars = 0; } } } /* Start the next line of output. */ void newline(void) { putchar('\n'); cChars = 0; } /* Print a string field, adding escape codes as needed. */ void PrintString(uchar * s) { static char charbuf[2] = { 0, 0 }; static char intbuf[5] = { '\\', 0, 0, 0, 0 }; out("\"", FALSE); /* Open quotes */ for (; *s; s++) switch (*s) /* What type of char are we printing? */ { /* Escape CRs, LFs, \'s, quotes */ case '\r': if (fMultiLine && *(s+1) == '\n') { newline(); s++; /* skip an extra char so this eats \r\n */ } else out("\\r", FALSE); break; case '\n': out("\\n", FALSE); break; case '\\': out("\\\\", FALSE); break; case '"': out("\\\"", FALSE); break; default: /* Otherwise: */ if (*s < 32 || (*s > 127 && !fSpecials) || *s == 255) { /* Is it non-ASCII? */ sprintf(intbuf, "\\%03o", *s); out(intbuf, FALSE); } else /* No, it's ASCII, print it. */ { charbuf[0] = *s; out(charbuf, FALSE); } } out("\"", FALSE); /* Close quotes */ } /* Read the field definitions from the database */ void ReadFields(void) { uchar rechdr[6]; /* Record header buffer */ short fFirst = TRUE; /* First field? */ for (;;) { if (6 != fread(rechdr, 1, 6, hf)) break; /* Read error */ if (rechdr[0] == 31) break; /* Hit index table at end of file */ if (rechdr[0] != 6) { /* Not a field def, skip it */ fseek(hf, rechdr[3]*256+rechdr[2]-6, SEEK_CUR); continue; } /* This is a field definition. Read it. */ fread(fields[cFields], 1, rechdr[3]*256+rechdr[2]-6, hf); if (fields[cFields][4] & 0x80) /* Skip fields without data */ continue; if (!fNotes && *fields[cFields] == 10) /* Skip notes if requested */ continue; if (*fields[cFields] >= 16) { if (fWarnings) fprintf(stderr, "warning: application-defined " "field '%s' skipped\n", &fields[cFields][7]); continue; } if (fNames) /* Are we printing field names? */ { if (fFirst) /* Is this the first one? */ fFirst = FALSE; else /* No, so put a comma first. */ out(",", FALSE); PrintString (&fields[cFields][7]); } cFields++; } if (fNames) /* Are we printing field names? */ newline(); } /* Print out a note. */ void PrintNote(short note) { long lCurrent = ftell(hf); /* Save current position in file */ uchar rechdr[6]; /* Record header buffer */ uchar * pch; fseek(hf, lFirst, SEEK_SET); /* Go back to first record */ for (;;) { /* Search for note record */ if (6 != fread(rechdr, 1, 6, hf)) break; /* Read error */ if (rechdr[0] == 31) break; /* Hit index table at end of file */ if (rechdr[0] != 9 || rechdr[5]*256+rechdr[4] != note) { /* Not a note or not our note, skip */ fseek(hf, rechdr[3]*256+rechdr[2]-6, SEEK_CUR); continue; } /* read and print note */ pch = (uchar *)malloc(rechdr[3]*256+rechdr[2]-5); fread(pch, 1, rechdr[3]*256+rechdr[2]-6, hf); pch[rechdr[3]*256+rechdr[2]-6] = '\0'; PrintString(pch); free(pch); /* go back to where we were and return */ fseek(hf, lCurrent, SEEK_SET); return; } /* Didn't find the note, so print a null string and a warning. */ out("\"\"", FALSE); if (fWarnings) /* Are we printing warnings? */ fprintf(stderr, "warning: note record %d not found!\n", note); /* go back to where we were and return */ fseek(hf, lCurrent, SEEK_SET); } /* Print out a record */ void PrintRecord(uchar * rec) { int field; /* Field counter */ short fFirst = TRUE; /* First field? */ short offset; /* offset of string data */ short word; /* word containing data */ char buf[20]; /* date/time formatting buffer */ for (field = 0; field < cFields; field++) { /* Print each field */ if (fFirst) /* First field? */ fFirst = FALSE; else /* No, start with comma */ out(",", FALSE); /* get the offset of field data */ offset = fields[field][3]*256+fields[field][2]; if (fields[field][4] & 0x20) offset = rec[offset+1]*256+rec[offset]; switch (fields[field][0]) { /* What type of field? */ case 0: case 1: /* Bitmasks */ word = rec[offset+1]*256+rec[offset]; word &= fields[field][6]*256+fields[field][5]; out(word ? "1" : "0", FALSE); break; case 2: case 3: case 4: case 5: case 6: case 13: case 15: /* Strings of various types */ PrintString(&rec[offset]); break; case 7: /* Time */ if (rec[offset] == 0 && rec[offset+1] == 0x80) break; /* Empty time */ word = rec[offset+1]*256+rec[offset]; sprintf(buf, "%02d%02d", word / 60, word % 60); out(buf, TRUE); break; case 8: /* Date */ if (rec[offset] == 255 && rec[offset+1] == 255 && rec[offset+2] == 255) break; /* Empty date */ sprintf(buf, "%04d%02d%02d", rec[offset]+1900, rec[offset+1]+1, rec[offset+2]+1); out(buf, TRUE); break; case 9: /* Radio button */ if (rec[offset] == fields[field][5]) out("1", FALSE); else out("0", FALSE); break; case 10: /* Note */ word = rec[offset+1]*256+rec[offset]; if (word == -1) out("\"\"", FALSE); else PrintNote(word); break; } } newline(); } /* Read the data records */ void ReadData(void) { static int fAppSpecFound = FALSE; uchar rechdr[6]; uchar * pch; for (;;) { /* Search the records for data */ if (6 != fread(rechdr, 1, 6, hf)) break; /* Read error */ if (rechdr[0] == 31) break; /* Hit index table at end */ if (rechdr[0] > 13) { /* User-defined record */ if (fWarnings && !fAppSpecFound) fprintf(stderr, "warning: database contains " "application-specific records " "not handled by gdbdump\n"); fAppSpecFound = TRUE; } if (rechdr[0] != 11 || /* Non-data record */ (rechdr[1] & 0x01)) /* Deleted record */ { /* Skip it */ fseek(hf, rechdr[3]*256+rechdr[2]-6, SEEK_CUR); continue; } /* Read and print data record */ pch = (uchar *)malloc(rechdr[3]*256+rechdr[2]-6); fread(pch, 1, rechdr[3]*256+rechdr[2]-6, hf); PrintRecord(pch); free(pch); } } /* Dump the database */ void DoDump(void) { uchar sig[5]; /* File signature */ uchar dbhdr[25]; /* Database header */ sig[4] = '\0'; if (4 != fread(sig, 1, 4, hf) || strcmp(sig, "hcD")) die("gdbdump: not a valid database"); if (25 != fread(dbhdr, 1, 25, hf)) die("gdbdump: read error"); if (dbhdr[6] != 2 || dbhdr[7] != 1) die("gdbdump: wrong version of database format"); if (dbhdr[8] == 'W' && fWarnings) fprintf(stderr, "warning: world time databases cannot " "be loaded with gdbload\n"); if (dbhdr[8] == '2' && fWarnings) fprintf(stderr, "warning: appointment book databases cannot " "be loaded with gdbload\n"); lFirst = ftell(hf); ReadFields(); fseek(hf, lFirst, SEEK_SET); ReadData(); } /* Main program. Parse arguments, open files, and get started. */ int main (int argc, char * argv[]) { /* Switch defaults: */ fNames = TRUE; /* Print field names */ fNotes = TRUE; /* Print notes */ fWarnings = TRUE; /* Print warnings */ fSpecials = FALSE; /* Print special characters */ fWrap = FALSE; /* Don't wrap lines */ fMultiLine = FALSE; /* Don't put notes on multiple lines*/ argc--; /* Skip argv[0] */ argv++; while (argc && **argv == '-') /* Read option arguments */ { (*argv)++; /* Skip - */ while (**argv) /* Read option flags */ { switch (**argv) { /* Which flag? */ case 'n': fNames = FALSE; break; case 'o': fNotes = FALSE; break; case 'q': fWarnings = FALSE; break; case 's': fSpecials = TRUE; break; case 'w': fWrap = TRUE; break; case 'm': fMultiLine= TRUE; break; default: usage(); break; } (*argv)++; /* Skip to next flag */ } argc--; /* Skip to next argument */ argv++; } if (argc != 1) /* Make sure one non-flag argument */ usage(); if (!(hf = fopen(*argv, "rb")) && !(hf = fopen(*argv, "r"))) /* Open the database file */ { perror("gdbdump: can't open database"); exit (1); } DoDump(); /* Dump the database */ fclose (hf); /* Close it */ exit(0); } lx-gdb-1.03.orig/prepdoc100644 1750 62 767 6004275310 13613 0ustar torinstaff#!/bin/sh # Produces documentation for gdbload/gdbdump. Works on Solaris 2.3. # Calls nroff with an almost suitable device parameter (the best I can find) # and then # postprocesses the result to remove the Bold/Italic escape # sequences and the \n characters (which are inserted only sometimes). # # Additionally, percent signs are changed into backslashes, as the # nroff I use can't be persuaded into accepting backslashes in macro # parameters. nroff -man -TX $1 | sed 's/.//g s/ //g s/%/\\/g '