00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <strings.h>
00026 #include <errno.h>
00027
00028 #include "pi-socket.h"
00029 #include "pi-dlp.h"
00030 #include "pi-address.h"
00031 #include "pi-header.h"
00032 #include "pi-userland.h"
00033
00034
00035 enum terminators { term_newline=0,
00036 term_comma=1,
00037 term_semi=2,
00038 term_tab=3 } ;
00039 char tabledelims[4] = { '\n', ',', ';', '\t' };
00040
00041
00042
00043
00044 int inchar(FILE * in);
00045 int read_field(char *dest, FILE * in, size_t length);
00046 void outchar(char c, FILE * out);
00047 int write_field(FILE * out, const char *source, enum terminators more);
00048 int match_phone(char *buf, struct AddressAppInfo *aai);
00049 int read_file(FILE * in, int sd, int db, struct AddressAppInfo *aai);
00050 int write_file(FILE * out, int sd, int db, struct AddressAppInfo *aai, int human );
00051
00052 int realentry[21] =
00053 { 0, 1, 13, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20 };
00054
00055 char *tableheads[21] = {
00056 "Last name",
00057 "First name",
00058 "Title",
00059 "Company",
00060 "Phone1",
00061 "Phone2",
00062 "Phone3",
00063 "Phone4",
00064 "Phone5",
00065 "Address",
00066 "City",
00067 "State",
00068 "Zip Code",
00069 "Country",
00070 "Custom 1",
00071 "Custom 2",
00072 "Custom 3",
00073 "Custom 4",
00074 "Note",
00075 "Private",
00076 "Category"
00077 };
00078
00079 int
00080 tabledelim = term_comma,
00081 augment = 0,
00082 defaultcategory = 0;
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098 int inchar(FILE * in)
00099 {
00100 int c;
00101
00102 c = getc(in);
00103 if (c == '\\') {
00104 c = getc(in);
00105 switch (c) {
00106 case 'b':
00107 c = '\b';
00108 break;
00109 case 'f':
00110 c = '\f';
00111 break;
00112 case 'n':
00113 c = '\n';
00114 break;
00115 case 't':
00116 c = '\t';
00117 break;
00118 case 'r':
00119 c = '\r';
00120 break;
00121 case 'v':
00122 c = '\v';
00123 break;
00124 case '\\':
00125 c = '\\';
00126 break;
00127 default:
00128 ungetc(c, in);
00129 c = '\\';
00130 break;
00131 }
00132 }
00133 return c;
00134 }
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 int read_field(char *dest, FILE *in, size_t length)
00158 {
00159 int c;
00160
00161 if (length<=1) return -1;
00162
00163 length--;
00164
00165 do {
00166 c = getc(in);
00167 if(c == '\n') {
00168 *dest = 0;
00169 return term_newline;
00170 }
00171
00172 } while ((c != EOF) && ((c == ' ') || (c == '\t') || (c == '\r')));
00173
00174 if (c == '"') {
00175 c = inchar(in);
00176
00177 while (c != EOF) {
00178 if (c == '"') {
00179 c = inchar(in);
00180 if (c != '"')
00181 break;
00182 }
00183 *dest++ = c;
00184 if (!(--length))
00185 break;
00186 c = inchar(in);
00187 }
00188 } else {
00189 while (c != EOF) {
00190 if ((c == '\n') || (c == tabledelims[tabledelim])) {
00191 break;
00192 }
00193 *dest++ = c;
00194 if (!(--length))
00195 break;
00196 c = inchar(in);
00197 }
00198 }
00199 *dest++ = '\0';
00200
00201
00202 while ((c != EOF) && ((c == ' ') || (c == '\t')))
00203 c = getc(in);
00204
00205 if (c == ',')
00206 return term_comma;
00207
00208 else if (c == ';')
00209 return term_semi;
00210
00211 else if (c == '\t')
00212 return term_tab;
00213
00214 else if (c == EOF)
00215 return -1;
00216 else
00217 return term_newline;
00218 }
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232 void outchar(char c, FILE * out)
00233 {
00234 switch (c) {
00235 case '"':
00236 putc('"', out);
00237 putc('"', out);
00238 break;
00239 case '\b':
00240 putc('\\', out);
00241 putc('b', out);
00242 break;
00243 case '\f':
00244 putc('\\', out);
00245 putc('f', out);
00246 break;
00247 case '\n':
00248 putc('\\', out);
00249 putc('n', out);
00250 break;
00251 case '\t':
00252 putc('\\', out);
00253 putc('t', out);
00254 break;
00255 case '\r':
00256 putc('\\', out);
00257 putc('r', out);
00258 break;
00259 case '\v':
00260 putc('\\', out);
00261 putc('v', out);
00262 break;
00263 case '\\':
00264 putc('\\', out);
00265 putc('\\', out);
00266 break;
00267 default:
00268 putc(c, out);
00269 break;
00270 }
00271 }
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 int write_field(FILE * out, const char *source, enum terminators more)
00288 {
00289 putc('"', out);
00290
00291 while (*source) {
00292 outchar(*source, out);
00293 source++;
00294 }
00295 putc('"', out);
00296
00297 putc(tabledelims[more], out);
00298 return 0;
00299 }
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315 int match_phone(char *buf, struct AddressAppInfo *aai)
00316 {
00317 int i;
00318
00319 for (i = 0; i < 8; i++)
00320 if (strncasecmp(buf, aai->phoneLabels[i], sizeof(aai->phoneLabels[0])) == 0)
00321 return i;
00322 return atoi(buf);
00323 }
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337 int read_file(FILE *f, int sd, int db, struct AddressAppInfo *aai)
00338 {
00339 int i = -1,
00340 l,
00341 attribute,
00342 category;
00343 char buf[0xffff];
00344 int showPhone = -1;
00345
00346 pi_buffer_t *record;
00347
00348 struct Address addr;
00349
00350 int fields = 0;
00351 int count = 0;
00352 const char *progress = " Reading CSV entries, writing to Palm Address Book... ";
00353
00354 if (!plu_quiet) {
00355 printf("%s",progress);
00356 fflush(stdout);
00357 }
00358
00359 while (!feof(f)) {
00360 fields = 0;
00361 l = getc(f);
00362 if (feof(f) || (l<0)) {
00363 break;
00364 }
00365 if ('#' == l) {
00366
00367 while (!feof(f) && (l!='\n') && (l>=0)) {
00368 l = getc(f);
00369 }
00370 continue;
00371 } else {
00372 ungetc(l,f);
00373 }
00374 i = read_field(buf, f, sizeof(buf));
00375
00376
00377 memset(&addr, 0, sizeof(addr));
00378 addr.showPhone = 0;
00379 showPhone = -1;
00380
00381 if ((i == term_semi) && (tabledelim != term_semi)) {
00382
00383 category = plu_findcategory(&aai->category,buf,
00384 PLU_CAT_CASE_INSENSITIVE | PLU_CAT_DEFAULT_UNFILED);
00385 i = read_field(buf, f, sizeof(buf));
00386 if (i == term_semi) {
00387 showPhone = match_phone(buf, aai);
00388 i = read_field(buf, f, sizeof(buf));
00389 }
00390 } else {
00391 category = defaultcategory;
00392 }
00393
00394 if (i < 0)
00395 break;
00396
00397 attribute = 0;
00398
00399 for (l = 0; (i >= 0) && (l < 21); l++) {
00400 int l2 = realentry[l];
00401
00402 if ((l2 >= 3) && (l2 <= 7)) {
00403 if ((i != term_semi) || (tabledelim == term_semi)) {
00404 addr.phoneLabel[l2 - 3] = l2 - 3;
00405 }
00406 else {
00407 addr.phoneLabel[l2 - 3] = match_phone(buf, aai);
00408 i = read_field(buf, f, sizeof(buf));
00409 }
00410 if (buf[0]) {
00411 addr.entry[l2] = strdup(buf);
00412 ++fields;
00413 } else {
00414 addr.entry[l2] = NULL;
00415 }
00416 } else if (19 <= l2) {
00417 if (19 == l2) {
00418 attribute = (atoi(buf) ? dlpRecAttrSecret : 0);
00419 }
00420 if (20 == l2) {
00421 category = plu_findcategory(&aai->category,buf,
00422 PLU_CAT_CASE_INSENSITIVE | PLU_CAT_DEFAULT_UNFILED);
00423 }
00424 } else {
00425 if (buf[0]) {
00426 addr.entry[l2] = strdup(buf);
00427 ++fields;
00428 } else {
00429 addr.entry[l2] = NULL;
00430 }
00431
00432 }
00433
00434 if (i == 0)
00435 break;
00436
00437 i = read_field(buf, f, sizeof(buf));
00438 }
00439
00440
00441 while (i > 0) {
00442 i = read_field(buf, f, sizeof(buf));
00443 }
00444
00445 if (showPhone >= 0) {
00446
00447 addr.showPhone = 0;
00448 for (i=0; i<5; ++i) {
00449 if (showPhone == addr.phoneLabel[i]) {
00450 addr.showPhone = i;
00451 break;
00452 }
00453 }
00454 }
00455
00456 if (fields>0) {
00457 record = pi_buffer_new(0);
00458 pack_Address(&addr, record, address_v1);
00459 dlp_WriteRecord(sd, db, attribute, 0, category,
00460 (unsigned char *) record->data, record->used, 0);
00461 pi_buffer_free(record);
00462 ++count;
00463 }
00464 free_Address(&addr);
00465
00466 if (!plu_quiet) {
00467 printf("\r%s%d",progress,count);
00468 fflush(stdout);
00469 }
00470
00471 }
00472
00473 if (!plu_quiet) {
00474 printf("\r%s%d\n Done.\n",progress,count);
00475 fflush(stdout);
00476 }
00477 return 0;
00478 }
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493 void write_record_CSV(FILE *out, const struct AddressAppInfo *aai, const struct Address *addr, const int attribute, const int category)
00494 {
00495 int j;
00496 char buffer[16];
00497
00498 if (augment && (category || addr->showPhone)) {
00499 write_field(out,
00500 aai->category.name[category],
00501 term_semi);
00502 write_field(out,
00503 aai->phoneLabels[addr->phoneLabel[addr->showPhone]],
00504 term_semi);
00505 }
00506
00507 for (j = 0; j < 19; j++) {
00508 if (addr->entry[realentry[j]]) {
00509 if (augment && (j >= 4) && (j <= 8)) {
00510 write_field(out,
00511 aai->phoneLabels[addr->phoneLabel
00512 [j - 4]], term_semi);
00513 }
00514 write_field(out, addr->entry[realentry[j]],
00515 tabledelim);
00516 } else {
00517 write_field(out, "", tabledelim);
00518 }
00519 }
00520
00521 snprintf(buffer, sizeof(buffer), "%d", (attribute & dlpRecAttrSecret) ? 1 : 0);
00522 write_field(out, buffer, tabledelim);
00523
00524 write_field(out,
00525 aai->category.name[category],
00526 term_newline);
00527 }
00528
00529 void write_record_human(struct AddressAppInfo *aai, struct Address *addr, const int category)
00530 {
00531 int i;
00532
00533 printf("Category: %s\n", aai->category.name[category]);
00534
00535 for (i = 0; i < 19; i++) {
00536 if (addr->entry[i]) {
00537 int l = i;
00538
00539 if ((l >= entryPhone1) && (l <= entryPhone5)) {
00540 printf("%s: %s\n",
00541 aai->phoneLabels[addr->phoneLabel[l - entryPhone1]],
00542 addr->entry[i]);
00543 } else {
00544 printf("%s: %s\n", aai->labels[l],
00545 addr->entry[i]);
00546 }
00547 }
00548 }
00549 printf("\n");
00550 }
00551
00552 int write_file(FILE *out, int sd, int db, struct AddressAppInfo *aai, int human)
00553 {
00554 int i,
00555 j,
00556 attribute,
00557 category;
00558 struct Address addr;
00559 pi_buffer_t *buf;
00560
00561 int count = 0;
00562 const char *progress = " Writing Palm Address Book entries to file... ";
00563
00564 if (!human) {
00565
00566
00567
00568 fprintf(out, "# ");
00569 for (j = 0; j < 21; j++) {
00570 write_field(out, tableheads[j],
00571 j<20 ? tabledelim : term_newline);
00572 }
00573 if (augment) {
00574 fprintf(out,"### This in an augmented (non-standard) CSV file.\n");
00575 }
00576 }
00577
00578 if (!plu_quiet) {
00579 printf("%s",progress);
00580 fflush(stdout);
00581 }
00582
00583 buf = pi_buffer_new (0xffff);
00584 for (i = 0;
00585 (j =
00586 dlp_ReadRecordByIndex(sd, db, i, buf, 0,
00587 &attribute, &category)) >= 0;
00588 i++) {
00589
00590
00591 if (attribute & dlpRecAttrDeleted)
00592 continue;
00593 unpack_Address(&addr, buf, address_v1);
00594
00595 if (!human) {
00596 write_record_CSV(out,aai,&addr,attribute,category);
00597 } else {
00598 write_record_human(aai,&addr,category);
00599 }
00600
00601
00602 ++count;
00603 if (!plu_quiet) {
00604 printf("\r%s%d",progress,count);
00605 fflush(stdout);
00606 }
00607 }
00608 pi_buffer_free (buf);
00609
00610 if (!plu_quiet) {
00611 printf("\r%s%d\n Done.\n",progress,count);
00612 fflush(stdout);
00613 }
00614 return 0;
00615 }
00616
00617
00618 int main(int argc, const char *argv[])
00619 {
00620
00621 int c,
00622 db,
00623 l,
00624 sd = -1;
00625
00626 enum { mode_none, mode_read, mode_write, mode_delete_all, mode_delete }
00627 run_mode = mode_none;
00628
00629 const char
00630 *progname = argv[0];
00631
00632 char *defaultcategoryname = 0,
00633 *deletecategory = 0,
00634 *wrFilename = NULL,
00635 *rdFilename = NULL,
00636 buf[0xffff];
00637
00638 int writehuman = 0;
00639
00640 pi_buffer_t *appblock;
00641
00642 struct AddressAppInfo aai;
00643 struct PilotUser User;
00644 struct SysInfo info;
00645
00646 poptContext po;
00647
00648 struct poptOption options[] = {
00649 USERLAND_RESERVED_OPTIONS
00650 {"delete-all", 0 , POPT_ARG_NONE, NULL, mode_delete_all, "Delete all Palm records in all categories", NULL},
00651 {"delimiter", 't', POPT_ARG_INT, &tabledelim, 0, "Include category, use delimiter (3=tab, 2=;, 1=,)", "<delimeter>"},
00652 {"delete-category", 'd', POPT_ARG_STRING, &deletecategory,'d', "Delete old Palm records in <category>", "category"},
00653 {"category", 'c', POPT_ARG_STRING, &defaultcategoryname, 0, "Category to install to", "category"},
00654 {"augment", 'a', POPT_ARG_NONE, &augment, 0, "Augment records with additional information", NULL},
00655 {"read", 'r', POPT_ARG_STRING, &rdFilename, 'r', "Read records from <file> and install them to Palm", "file"},
00656 {"write", 'w', POPT_ARG_STRING, &wrFilename, 'w', "Get records from Palm and write them to <file>", "file"},
00657 {"human-readable",'C', POPT_ARG_NONE, &writehuman, 0, "Write generic human-readable output instead of CSV", NULL},
00658 POPT_TABLEEND
00659 };
00660
00661 const char *mode_error = " ERROR: Specify exactly one of read, write, delete or delete all.\n";
00662
00663 po = poptGetContext("pilot-addresses", argc, argv, options, 0);
00664 poptSetOtherOptionHelp(po,"\n\n"
00665 " Reads addresses from a file and installs on the Palm, or\n"
00666 " writes addresses from the Palm to a file.\n\n"
00667 " Provide exactly one of --read or --write.\n\n");
00668 plu_popt_alias(po,"delall",0,"--bad-option --delete-all");
00669 plu_popt_alias(po,"delcat",0,"--bad-option --delete-category");
00670 plu_popt_alias(po,"install",0,"--bad-option --category");
00671
00672 plu_popt_alias(po,"no-csv",0,"--human-readable");
00673
00674 if (argc < 2) {
00675 poptPrintUsage(po,stderr,0);
00676 return 1;
00677 }
00678
00679 while ((c = poptGetNextOpt(po)) >= 0) {
00680 switch (c) {
00681
00682
00683
00684
00685
00686
00687 case mode_delete_all :
00688 if (run_mode != mode_none) {
00689 fprintf(stderr,"%s",mode_error);
00690 return 1;
00691 }
00692 run_mode = mode_delete_all;
00693 break;
00694 case 'r':
00695 if (run_mode != mode_none) {
00696 fprintf(stderr,"%s",mode_error);
00697 return 1;
00698 }
00699 run_mode = mode_read;
00700 break;
00701 case 'w':
00702 if ((run_mode != mode_none) && (run_mode != mode_delete)) {
00703 fprintf(stderr,"%s",mode_error);
00704 return 1;
00705 }
00706 run_mode = mode_write;
00707 break;
00708 case 'd':
00709 if ((run_mode != mode_none) && (run_mode != mode_write)) {
00710 fprintf(stderr,"%s",mode_error);
00711 return 1;
00712 }
00713 run_mode = mode_delete;
00714 break;
00715 default:
00716 fprintf(stderr," ERROR: Unhandled option %d.\n",c);
00717 return 1;
00718 }
00719 }
00720
00721 if (c < -1)
00722 plu_badoption(po,c);
00723
00724 if (mode_none == run_mode) {
00725 fprintf(stderr,"%s",mode_error);
00726 return 1;
00727 }
00728
00729
00730
00731 if ((tabledelim < 0) || (tabledelim > sizeof(tabledelim))) {
00732 fprintf(stderr," ERROR: Invalid delimiter number %d (use 0-%d).\n",
00733 tabledelim,(int)(sizeof(tabledelim)));
00734 return 1;
00735 }
00736
00737 sd = plu_connect();
00738
00739 if (sd < 0)
00740 goto error;
00741
00742 if (dlp_ReadUserInfo(sd, &User) < 0)
00743 goto error_close;
00744
00745 if (dlp_ReadSysInfo(sd,&info) < 0) {
00746 fprintf(stderr," ERROR: Could not read Palm System Information.\n");
00747 return -1;
00748 }
00749
00750 if (info.romVersion > 0x05003000) {
00751 printf("PalmOS 5.x (Garnet) and later devices are not currently supported by this\n"
00752 "tool. The data format of the AddressBook has changed. The legacy format is\n"
00753 "called \"Classic\" and PalmOS 5.x and later uses \"Extended\" databases with a\n"
00754 "different structure. Your Palm has \"Contacts\", and this tool reads the\n"
00755 "\"AddressBook\" database. (Found: %x)\n\n"
00756
00757 "Due to this change, pilot-addresses and other tools must be rewritten to\n"
00758 "compensate. Sorry about the inconvenience.\n\n", info.romVersion);
00759 return -1;
00760 }
00761
00762
00763 if (dlp_OpenDB(sd, 0, 0x80 | 0x40, "AddressDB", &db) < 0) {
00764 puts("Unable to open AddressDB");
00765 dlp_AddSyncLogEntry(sd, "Unable to open AddressDB.\n");
00766 goto error_close;
00767 }
00768
00769 appblock = pi_buffer_new(0xffff);
00770 l = dlp_ReadAppBlock(sd, db, 0, 0xffff, appblock);
00771 unpack_AddressAppInfo(&aai, appblock->data, l);
00772 pi_buffer_free(appblock);
00773
00774 if (defaultcategoryname) {
00775 defaultcategory =
00776 plu_findcategory(&aai.category,defaultcategoryname,
00777 PLU_CAT_CASE_INSENSITIVE | PLU_CAT_DEFAULT_UNFILED);
00778 } else {
00779 defaultcategory = 0;
00780 }
00781
00782 switch(run_mode) {
00783 FILE *f;
00784 int i;
00785 int old_quiet;
00786 case mode_none:
00787
00788 fprintf(stderr,"%s",mode_error);
00789 break;
00790 case mode_write:
00791
00792 if (strcmp(wrFilename,"-") == 0) {
00793 f = stdout;
00794 old_quiet = plu_quiet;
00795 plu_quiet = 1;
00796 } else {
00797 f = fopen(wrFilename, "w");
00798 }
00799 if (f == NULL) {
00800 sprintf(buf, "%s: %s", progname, wrFilename);
00801 perror(buf);
00802 goto error_close;
00803 }
00804 write_file(f, sd, db, &aai, writehuman);
00805 if (f == stdout) {
00806 plu_quiet = old_quiet;
00807 }
00808 if (deletecategory) {
00809 dlp_DeleteCategory(sd, db,
00810 plu_findcategory(&aai.category,deletecategory,PLU_CAT_CASE_INSENSITIVE | PLU_CAT_WARN_UNKNOWN));
00811 }
00812 if (f != stdout) {
00813 fclose(f);
00814 }
00815 break;
00816 case mode_read:
00817 f = fopen(rdFilename, "r");
00818
00819 if (f == NULL) {
00820 fprintf(stderr, "Unable to open input file");
00821 fprintf(stderr, " '%s' (%s)\n\n",
00822 rdFilename, strerror(errno));
00823 fprintf(stderr, "Please make sure the file");
00824 fprintf(stderr, "'%s' exists, and that\n",
00825 rdFilename);
00826 fprintf(stderr, "it is readable by this user");
00827 fprintf(stderr, " before launching.\n\n");
00828
00829 goto error_close;
00830 }
00831 read_file(f, sd, db, &aai);
00832 fclose(f);
00833 break;
00834 case mode_delete:
00835 i = plu_findcategory (&aai.category,deletecategory,PLU_CAT_CASE_INSENSITIVE | PLU_CAT_WARN_UNKNOWN);
00836 if (i>=0) {
00837 dlp_DeleteCategory(sd, db, i);
00838 }
00839 break;
00840 case mode_delete_all:
00841 for (i = 0; i < 16; i++)
00842 if (aai.category.name[i][0])
00843 dlp_DeleteCategory(sd, db, i);
00844 break;
00845 }
00846
00847
00848 dlp_CloseDB(sd, db);
00849
00850
00851 User.lastSyncPC = 0x00010000;
00852 User.successfulSyncDate = time(NULL);
00853 User.lastSyncDate = User.successfulSyncDate;
00854 dlp_WriteUserInfo(sd, &User);
00855
00856 if (run_mode == mode_read) {
00857 dlp_AddSyncLogEntry(sd, "Wrote entries to Palm Address Book.\n");
00858 } else if (run_mode == mode_write) {
00859 dlp_AddSyncLogEntry(sd, "Successfully read Address Book from Palm.\n");
00860 }
00861
00862 dlp_EndOfSync(sd, 0);
00863 pi_close(sd);
00864
00865 return 0;
00866
00867 error_close:
00868 pi_close(sd);
00869
00870 error:
00871 return -1;
00872 }
00873
00874
00875
00876
00877
00878
00879