/* * fake docviewer for X11R5 * Charles Hedrick, hedrick@cs.rutgers.edu */ #include #include #include #include /* * parse strings that have attr=value * start is the string to search * attr is "attr=" * breaks is a string with all characters that terminate a value * mallocs the result, so make sure to free it. * return NULL if not found */ char *getvalue(start, attr, breaks) char *start, *attr, *breaks; { char *result, *end, *retval; result = strstr(start, attr); if (! result) return NULL; result += strlen(attr); end = strpbrk (result, breaks); if (! end) return NULL; retval = (char *)malloc( end - result + 1); if (! retval) { fprintf (stderr, "malloc failed\n"); exit(1); } strncpy(retval, result, end - result); retval[end-result] = 0; return retval; } main (argc, argv) int argc; char **argv; { char *docspec = NULL; char *catspec = NULL, *thiscat; char *bs, *bk, *dc, *id, *tocpath, *pspath, *psfile, *pspage; char buffer[512], buffer2[512], *cp, *cp2; int ch; FILE *catfile, *recfile; int i, maxfd; char **a; DBM *db; struct keyst { int count1; int count2; char stkey[128]; } keyst; datum key, fetch; #ifdef DEBUG i = argc; a = argv; while (i > 0) { printf("|%s| ", a[0]); i--; a++; } printf("\n"); #endif /* * call the real thing is we are runninw NeWS */ if (cp = (char *)getenv("hasnews")) { if (strcmp(cp, "yes") == 0) { (void) fprintf(stderr, "execvp docviewer.REAL\n"); execvp("docviewer.REAL", argv); (void) fprintf(stderr, "Nope, execvp docviewer\n"); execvp("docviewer", argv); (void) fprintf(stderr, "Nope, bailing out\n"); exit(99); } } (void) fprintf(stderr, "Using ghostview\n"); /* * process only -d and -c * arguments look like * |docviewer| |-Wp| |441| |0| |-p| |C.i5guO X 128.6.26.11 0| |-d| || |-l| |C| |-c| |/rutgers/AnswerBook/ab_cardcatalog:/rutgers/AnswerBook/Solaris_2.2_AB/ab_cardcatalog| * docspec to the -d argument. This specifies the document and place * within the document * catspec to the -c argument. This is a list of card catalogs to * search */ while (argc > 1) { if (strcmp(argv[1], "-Wp") == 0) {argv += 3; argc -= 3;} else if (strcmp(argv[1], "-p") == 0) {argv += 2; argc -= 2;} else if (strcmp(argv[1], "-d") == 0) {docspec = argv[2]; argv += 2; argc -= 2;} else if (strcmp(argv[1], "-l") == 0) {argv += 2; argc -= 2;} else if (strcmp(argv[1], "-c") == 0) {catspec = argv[2]; argv += 2; argc -= 2;} else {argv += 1; argc -= 1;} } if (! catspec) {fprintf(stderr, "Missing -c\n"); exit(1);} if (! docspec) {fprintf(stderr, "Missing -d\n"); exit(1);} /* * Now look at the document specifier: * * bs is the id of the documentset in the card catalog file * bk is the individual document * dc is a location within the document */ bs = getvalue(docspec, "bs=", ";>"); if (!bs) {fprintf(stderr, "-d arg doesn't have bs="); exit(1);} bk = getvalue(docspec, "bk=", ";>"); if (!bk) {fprintf(stderr, "-d arg doesn't have bk="); exit(1);} dc = getvalue(docspec, "dc=", ";>"); if (!bs) {fprintf(stderr, "-d arg doesn't have dc="); exit(1);} /* * Now find an entry in some card catalog whose id matches bs */ while (catspec) { /* loop over catalogs in colon separated list */ thiscat = catspec; catspec = strchr(catspec, ':'); if (catspec) *catspec++ = 0; else catspec = NULL; catfile = fopen(thiscat, "r"); if (! catfile) {fprintf(stderr, "Can't open card catalog file %s", thiscat); exit(1);} tocpath = NULL; while (fgets(buffer, sizeof(buffer) -1, catfile)) { buffer[sizeof(buffer)-1] = 0; /* ignore comment lines */ if (buffer[0] == '#') continue; /* get all the continuation lines */ while (1) { i = strlen(buffer); if (buffer[i-1] == '\n') { buffer[i-1] = 0; i--; } if (buffer[i-1] != '\\') break; if (! fgets(buffer+i, sizeof(buffer) - 1 - i, catfile)) {fprintf(stderr, "Continuation at EOF in %s\n", thiscat); exit(1);} } /* is this the right entry? */ id = getvalue(buffer, "id=", ":"); if (!id) {fprintf(stderr, "Card catalog %s has line without id= %s\n", thiscat, buffer); exit(1);} if (strcmp(id, bs) != 0) { free(id); continue; /* no, try next */ } /* yes. Parse the rest of the entry to get tocpath and pspath */ tocpath = getvalue(buffer, "tocpath=", ":"); if (!tocpath) {fprintf(stderr, "Card catalog %s has line without tocpath= %s\n", thiscat, buffer); exit(1);} pspath = getvalue(buffer, "pspath=", ":"); if (!pspath) {fprintf(stderr, "Card catalog %s has line without pspath= %s\n", thiscat, buffer); exit(1);} } fclose(catfile); if (tocpath) /* found a matching entry ? */ break; /* yes */ /* no, continue with next card catalog in colon-separated list */ } if (!tocpath) /* nothing in any card catalog */ {fprintf(stderr, "Card catalog %s has no entry for %s\n", thiscat, id); exit(1);} #ifdef DEBUG printf("bs %s bk %s dc %s tocpath %s pspath %s\n", bs, bk, dc, tocpath, pspath); #endif /* * Now we've got all the file names and other information. * Unfortunately it can be either Solaris 2.2, which uses * a netISAM .rec file, or older code, which uses dbm. * First see if we've got an ISAM file */ sprintf(buffer, "%s/%s.rec", tocpath, bk); recfile = fopen(buffer, "r"); if (recfile) { /* * Yup. I don't have code to process ISAM. I simply read the * file sequentially looking for ^A followed by the key. This * seems OK in all the examples I've looked at. But I have * no idea what the format of an ISAM file is. */ ch = getc(recfile); while (ch != EOF) { if (ch != 1) { /* search for ^A */ ch = getc(recfile); continue; } cp = buffer; /* copy next characters so we can compare with key */ while ((ch = getc(recfile)) != EOF) { if (ch == 1) break; *cp++ = ch; if ((cp - buffer) > strlen(dc)) break; } if (ch == EOF) break; if (ch == 1) /* another ^A before we got enough characters */ continue; if (strncmp(buffer, dc, strlen(dc)) == 0 && buffer[strlen(dc)] == ' ') break; /* key matches */ } if (ch == EOF) {fprintf(stderr, "Rec file %s/%s.rec no entry for %s ", tocpath, bk, dc); exit(1); } fgets(buffer, sizeof(buffer), recfile); fclose(recfile); } else { /* * here if no .rec file. Hope it's a DBM file */ sprintf(buffer, "%s/%s", tocpath, bk); db = dbm_open(buffer, 0, 0); if (! db) {fprintf(stderr, "Rec file %s/%s.rec not found\n", tocpath, bk); exit(1); } memset(&keyst, 0, sizeof(keyst)); /* * bk is always the file name, dc is the key. Normally dc is * an integer. But if you choose the cover, they give the * name of the whole document as dc. Turns out that is indexed * slightly differently */ if (strcmp(dc, bk) == 0) sprintf(keyst.stkey, "<%s>", dc); else sprintf(keyst.stkey, "<%s>%s", bk, dc); /* * No idea why they don't just use the string as the key, but * they make up this odd binary structure with two counts */ keyst.count2 = strlen(keyst.stkey); keyst.count1 = keyst.count2 + 1; key.dsize = (keyst.count2 + 8 + 3) & 0xfffffffc; key.dptr = (char *)&keyst; fetch = dbm_fetch(db, key); if (fetch.dptr == NULL) {fprintf(stderr, "DBM file %s/%s no entry for %s ", tocpath, bk, dc); exit(1); } i = fetch.dsize; if (i > sizeof(buffer)) i = sizeof(buffer); /* * There's lots of junk in the data. The only thing I understand * is the view: property. I'm going to use strstr to look for it. * For that to work, we can't have any nulls, as that will stop * the search. Turn nulls into spaces. */ for (cp = buffer, cp2 = fetch.dptr; i > 0; i--) { if (*cp2 == 0) { *cp++ = ' '; cp2++; } else *cp++ = *cp2++; } *cp = ' '; dbm_close(db); } /* * Here with buffer having the description of the entry we're * looking for. The format of this is close enough in 2.2 * and older to use the same code. * view:CHAPTER:PAGE * CHAPTER is the name of the file containing the chapter * PAGE is the page number within the file */ #ifdef DEBUG printf("%s|\n",buffer); #endif cp = strstr(buffer, "view:"); if (! cp) {fprintf(stderr, "Rec file %s/%s.rec entry for %s has no view: ", tocpath, bk, dc); exit(1);} cp += 5; psfile = cp; cp = strchr(psfile, ':'); if (!cp) {fprintf(stderr, "Rec file %s/%s.rec entry for %s has no page number 1:%s", tocpath, bk, dc, psfile); exit(1);} *cp++ = 0; pspage = cp; cp2 = strchr(cp, ' '); if (!cp2) {fprintf(stderr, "Rec file %s/%s.rec entry for %s has no page number 2:%s", tocpath, bk, dc, cp); exit(1);} *cp2 = 0; /* * Now we've got it all. So call ghostview */ sprintf(buffer, "%s/%s/%s", pspath, bk, psfile); /* * This seems to be necessary. I'm not sure why. */ maxfd = sysconf(_SC_OPEN_MAX); for (i = 3; i < maxfd; i++) close(i); execlp("ghostview", "ghostview", "-page", pspage, buffer, 0); fprintf(stderr, "Can't exec ghostview\n"); exit(1); }