1 /***************************************************************************
2 * Pinfo is a ncurses based lynx style info documentation browser
3 *
4 * Copyright (C) 1999 Przemek Borys <pborys@dione.ids.pl>
5 * Copyright (C) 2005 Bas Zoetekouw <bas@debian.org>
6 * Copyright (C) 2005 Nathanael Nerode <neroden@gcc.gnu.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 ***************************************************************************/
22
23 #include "common_includes.h"
24
25 typedef struct
26 {
27 char *suffix;
28 char *command;
29 }
30 Suffixes;
31
32
33 /******************************************************************************
34 * This piece of declarations says what to do with info files stored with *
35 * different formats/compression methods, before putting them into a temporary *
36 * file. I.e. you don't do anything to plain `.info' suffix; for a `.info.gz' *
37 * you dump the file through `gunzip -d -c', etc. *
38 ******************************************************************************/
39
40 #define SuffixesNumber 4
41
42 Suffixes suffixes[SuffixesNumber] =
43 {
44 {"", "cat"},
45 {".gz", "gzip -d -q -c"},
46 {".Z", "gzip -d -q -c"},
47 {".bz2", "bzip2 -d -c"}
48 };
49
50 /*****************************************************************************/
51
52 char **infopaths = 0;
53 int infopathcount = 0;
54
55 int
qsort_cmp(const void * base,const void * compared)56 qsort_cmp(const void *base, const void *compared)
57 {
58 char *cbase =((TagTable *) base)->nodename;
59 char *ccompared =((TagTable *) compared)->nodename;
60 return compare_tag_table_string(cbase, ccompared);
61 }
62
63 int
matchfile(char ** buf,char * name)64 matchfile(char **buf, char *name)
65 {
66 #define Buf (*buf)
67 DIR *dir;
68 char *bname = basename(name);
69 struct dirent *dp;
70 int matched = 0;
71
72 /* remove a possible ".info" from the end of the file name
73 * we're looking for */
74 strip_info_suffix(bname);
75
76 /* fix the name of the dir */
77 if (Buf[strlen(Buf)-1]!='/')
78 {
79 strcat(Buf,"/");
80 }
81 strncat(Buf,name,bname-name);
82
83 /* open the directory */
84 dir = opendir(Buf);
85 if (dir == NULL)
86 {
87 return 0;
88 }
89
90 /* iterate over all files in the directory */
91 while ((dp = readdir(dir)) != NULL)
92 {
93 /* use strcat rather than strdup, because xmalloc handles all
94 * malloc errors */
95 char *filename = xmalloc(strlen(dp->d_name)+1);
96 char *pagename = xmalloc(strlen(dp->d_name)+1);
97 strcat(filename, dp->d_name);
98 strcat(pagename, dp->d_name);
99
100 /* strip suffixes (so "gcc.info.gz" -> "gcc") */
101 strip_compression_suffix(pagename);
102 strip_info_suffix(pagename);
103
104 /* strip compression suffix from returned filename
105 * decompresison and type matching will happen later
106 * (sigh)
107 */
108 strip_compression_suffix(filename);
109
110 //fprintf(stdout,"Found filename `%s' (%s)\n", filename, pagename);
111
112 /* compare this file with the file we're looking for */
113 if (strcmp(pagename,bname) == 0)
114 {
115 /* we found a match! */
116 matched++;
117 /* put it in the buffer */
118 strncat(Buf, filename, 1023-strlen(Buf));
119
120 /* clean up, and exit the loop */
121 xfree(filename);
122 xfree(pagename);
123 break;
124 }
125 xfree(filename);
126 xfree(pagename);
127 }
128 closedir(dir);
129
130 if (matched) return 1;
131
132 return 0;
133 #undef Buf
134 }
135
136 FILE *
dirpage_lookup(char ** type,char *** message,unsigned long * lines,char * filename,char ** first_node)137 dirpage_lookup(char **type, char ***message, unsigned long *lines,
138 char *filename, char **first_node)
139 {
140 #define Type (*type)
141 #define Message (*message)
142 #define Lines (*lines)
143 FILE *id = 0;
144 int filenamelen = strlen(filename);
145 int goodHit = 0;
146 char name[256];
147 char file[256];
148 char *nameend, *filestart, *fileend, *dot;
149
150 id = opendirfile(0);
151 if (!id)
152 return NULL;
153
154 read_item(id, type, message, lines);
155
156 /* search for node-links in every line */
157 for (unsigned long i = 1; i < Lines; i++)
158 {
159 if ( (Message[i][0] == '*') && (Message[i][1] == ' ')
160 && ( nameend = strchr(Message[i], ':') )
161 && (*(nameend + 1) != ':') /* form: `* name:(file)node.' */
162 && (filestart = strchr(nameend, '(') )
163 && (fileend = strchr(filestart, ')') )
164 && (dot = strchr(fileend, '.') )
165 && (strncasecmp(filename, Message[i] + 2, filenamelen) == 0)
166 )
167 {
168 char *tmp;
169
170 /* skip this hit if it is not a perfect match and
171 * we have already found a previous partial match */
172 if ( ! ( (nameend - Message[i]) - 2 == filenamelen )
173 && goodHit )
174 {
175 continue;
176 }
177
178 /* find the name of the node link */
179 tmp = name;
180 strncpy(file, filestart + 1, fileend - filestart - 1);
181 file[fileend - filestart - 1] = 0;
182 strncpy(name, fileend + 1, dot - fileend - 1);
183 name[dot - fileend - 1] = 0;
184 while (isspace(*tmp)) tmp++;
185
186 if (strlen(name))
187 {
188 *first_node = xmalloc(strlen(tmp) + 1);
189 strcpy((*first_node), tmp);
190 }
191
192 /* close the previously opened file */
193 if (id)
194 {
195 fclose(id);
196 id = 0;
197 }
198
199 /* see if this info file exists */
200 id = openinfo(file, 0);
201 if (id)
202 {
203 goodHit = 1;
204 }
205 }
206 }
207
208 /* if we haven't found anything, clean up and exit */
209 if (id && !goodHit)
210 {
211 fclose(id);
212 id = 0;
213 }
214
215 /* return file we found */
216 return id;
217 #undef Lines
218 #undef Message
219 #undef Type
220 }
221
222 void
freeitem(char ** type,char *** buf,unsigned long * lines)223 freeitem(char **type, char ***buf, unsigned long *lines)
224 {
225 #define Type (*type)
226 #define Buf (*buf)
227 #define Lines (*lines)
228
229 if (Type != 0)
230 {
231 xfree(Type);
232 Type = 0;
233 }
234 if (Buf != 0)
235 {
236 for (unsigned long i = 1; i <= Lines; i++)
237 if (Buf[i] != 0)
238 {
239 xfree(Buf[i]);
240 Buf[i] = 0;
241 }
242 xfree(Buf);
243 Buf = 0;
244 }
245 #undef Type
246 #undef Buf
247 #undef Lines
248 }
249
250 void
read_item(FILE * id,char ** type,char *** buf,unsigned long * lines)251 read_item(FILE * id, char **type, char ***buf, unsigned long *lines)
252 {
253
254 #define Type (*type)
255 #define Buf (*buf)
256 #define Lines (*lines)
257 int i;
258
259 freeitem(type, buf, lines); /* free previously allocated memory */
260
261 /* set number of lines to 0 */
262 Lines = 0;
263
264 /* initial buffer allocation */
265 Buf = xmalloc(sizeof(char **));
266
267 /* seek precisely on the INFO_TAG (the seeknode function may be imprecise
268 * in combination with some weird tag_tables). */
269 while (!feof(id) && fgetc(id) != INFO_TAG);
270 /* then skip the trailing `\n' */
271 while (!feof(id) && fgetc(id) != '\n');
272
273 /* allocate and read the header line */
274 Type = xmalloc(1024);
275 if (fgets(Type, 1024, id)==NULL)
276 {
277 /* nothing to do */
278 return;
279 }
280 Type = xrealloc(Type, strlen(Type) + 1);
281 /* now iterate over the lines */
282 do
283 {
284 /* don't read after eof in info file */
285 if (feof(id))
286 break;
287
288 /* realloc the previous line for it to fit exactly */
289 if (Lines)
290 {
291 Buf[Lines] = xrealloc(Buf[Lines], strlen(Buf[Lines]) + 1);
292 }
293
294 /* TODO: Weirdness going on here; looks like off-by-one error as Buf[0] is always "\0" */
295 /* increase the read lines number */
296 Lines++;
297
298 /* allocate space for the new line */
299 Buf = xrealloc(Buf, sizeof(char **) *(Lines + 1));
300 Buf[Lines] = xmalloc(1024);
301 Buf[Lines][0] = 0;
302
303 /* if the line was not found in input file, fill the allocated space
304 * with empty line. */
305 if (fgets(Buf[Lines], 1024, id) == NULL)
306 strcpy(Buf[Lines], "\n");
307 else /* we can be sure that at least 1 char was read! */
308 {
309 /* *sigh* indices contains \0's
310 * which totally fucks up all strlen()s.
311 * so replace it by a space */
312 i = 1023;
313 /* find the end of the string */
314 while (Buf[Lines][i]=='\0' && i>=0) i--;
315 /* and replace all \0's in the rest of the string by spaces */
316 while (i>=0)
317 {
318 if (Buf[Lines][i]=='\0' || Buf[Lines][i]=='\b')
319 Buf[Lines][i]=' ';
320 i--;
321 }
322 }
323 }
324 while (Buf[Lines][0] != INFO_TAG); /* repeat until new node mark is found */
325
326
327 /* added for simplifing two-line ismenu and isnote functs */
328 if (Lines)
329 {
330 strcpy(Buf[Lines], "\n");
331 Buf[Lines] = xrealloc(Buf[Lines], strlen(Buf[Lines]) + 1);
332 }
333
334 fseek(id, -2, SEEK_CUR);
335
336 #undef Type
337 #undef Buf
338 #undef Lines
339
340 }
341 void
load_indirect(char ** message,unsigned long lines)342 load_indirect(char **message, unsigned long lines)
343 {
344 char *wsk;
345 int cut = 0; /* number of invalid entries */
346 indirect = xmalloc((lines + 1) * sizeof(Indirect));
347 for (unsigned long i = 1; i < lines; i++)
348 {
349 char *check;
350 wsk = message[i];
351 check = wsk + strlen(wsk);
352 while (*(++wsk) != ':') /* check if this line keeps a real entry */
353 {
354 if (wsk == check) /*
355 * make sure wsk won't go out of range
356 * in case the wsk would be corrupted.
357 */
358 break;
359 }
360 if (*wsk) /* if the entry holds some data... */
361 {
362 (*wsk) = 0;
363 strncpy(indirect[i - cut].filename, message[i], 200);
364 (*wsk) = ':';
365 indirect[i - cut].offset = atoi(wsk + 2);
366 }
367 else
368 cut++; /* if the entry was invalid, make inirect count shorter */
369 }
370 IndirectEntries = lines - 1 - cut;
371 }
372
373 void
load_tag_table(char ** message,unsigned long lines)374 load_tag_table(char **message, unsigned long lines)
375 {
376 char *wsk, *wsk1;
377 int is_indirect = 0;
378 register unsigned int j;
379 register char *res;
380 int cut = 0; /* holds the number of corrupt lines */
381
382 /*
383 * if in the first line there is a(indirect) string, skip that line
384 * by adding the value of is_indirect=1 to all message[line] references.
385 */
386 if (strcasecmp("(Indirect)", message[1]) == 0)
387 is_indirect = 1;
388 tag_table = xmalloc((lines + 1) * sizeof(TagTable));
389 for (unsigned long i = 1; i < lines - is_indirect; i++)
390 {
391 char *check;
392 wsk = message[i + is_indirect];
393 check = wsk + strlen(wsk);
394 while (!isspace(*(++wsk)))
395 {
396 if (wsk >= check)
397 {
398 wsk--;
399 break;
400 }
401 }
402 wsk++;
403 wsk1 = wsk;
404 check = wsk1 + strlen(wsk1);
405 while (*(++wsk1) != INDIRECT_TAG)
406 {
407 if (wsk1 >= check)
408 break;
409 }
410 if (wsk1 < check)
411 {
412 (*wsk1) = 0;
413 /*
414 * original: sprintf(tag_table[i-cut].nodename,"%s",wsk);
415 * below is a faster version.
416 */
417 res = memcpy(tag_table[i - cut].nodename, wsk, j =(size_t)(wsk1 - wsk));
418 (*(res += j + 1)) = 0;
419 (*wsk1) = INDIRECT_TAG;
420 wsk1++;
421 tag_table[i - cut].offset = atoi(wsk1);
422 }
423 else
424 cut++; /* increment the number of corrupt entries */
425 }
426 TagTableEntries = lines - 1 - is_indirect - cut;
427
428 /* FIXME: info should ALWAYS start at the 'Top' node, not at the first
429 mentioned node(vide ocaml.info) */
430
431 for (unsigned int i = 1; i <= TagTableEntries; i++)
432 {
433 if (strcasecmp(tag_table[i].nodename, "Top") == 0)
434 {
435 FirstNodeOffset = tag_table[i].offset;
436 strcpy(FirstNodeName, tag_table[i].nodename);
437 }
438 }
439 qsort(&tag_table[1], TagTableEntries, sizeof(TagTable), qsort_cmp);
440 }
441
442 /* TODO: seek_indirect() and seek_tag_table() are almost identical: remove duplicate code */
443 int
seek_indirect(FILE * id)444 seek_indirect(FILE * id)
445 {
446 int finito = 0;
447 long seek_pos;
448 int input;
449 char *type = xmalloc(1024);
450 fseek(id, 0, SEEK_SET);
451 while (!finito) /*
452 * scan through the file, searching for "indirect:"
453 * string in the type(header) line of node.
454 */
455 {
456 while ((input = fgetc(id)) != INFO_TAG)
457 if (input == EOF)
458 {
459 if (type)
460 {
461 xfree(type);
462 type = 0;
463 }
464 return 0;
465 }
466 seek_pos = ftell(id) - 2;
467 fgetc(id);
468 if (fgets(type, 1024, id)==0)
469 {
470 /* we're at the end of the file and haven't found any indirect refs. so bail out */
471 if (type)
472 {
473 xfree(type);
474 type = 0;
475 }
476 return 0;
477 }
478 if (strncasecmp("Indirect:", type, strlen("Indirect:")) == 0)
479 {
480 finito = 1;
481 }
482 }
483 xfree(type);
484 type = 0;
485 if (!curses_open)
486 {
487 printf(_("Searching for indirect done"));
488 printf("\n");
489 }
490 else
491 {
492 attrset(bottomline);
493 mvhline(maxy - 1, 0, ' ', maxx);
494 mvaddstr(maxy - 1, 0, _("Searching for indirect done"));
495 attrset(normal);
496 }
497 fseek(id, seek_pos, SEEK_SET);
498 return 1;
499 }
500
501 /*
502 * second arg for dumping out verbose debug info or not :)
503 */
504 int
seek_tag_table(FILE * id,int quiet)505 seek_tag_table(FILE * id,int quiet)
506 {
507 int finito = 0;
508 long seek_pos;
509 int input;
510 char *type = xmalloc(1024);
511 fseek(id, 0, SEEK_SET);
512 /*
513 * Scan through the file, searching for a string
514 * "Tag Table:" in the type(header) line of node.
515 */
516 while (!finito)
517 {
518 while ((input = fgetc(id)) != INFO_TAG)
519 {
520 if (input == EOF)
521 {
522 if (!quiet)
523 {
524 if (!curses_open)
525 {
526 printf(_("Warning: could not find tag table"));
527 printf("\n");
528 }
529 else
530 {
531 attrset(bottomline);
532 mvhline(maxy - 1, 0, ' ', maxx);
533 mvaddstr(maxy - 1, 0, _("Warning: could not find tag table"));
534 attrset(normal);
535 }
536 }
537 if (type)
538 {
539 xfree(type);
540 type = 0;
541 }
542 return 2;
543 }
544 }
545 seek_pos = ftell(id) - 2;
546 while (fgetc(id) != '\n');
547 if (fgets(type, 1024, id)==NULL)
548 {
549 /* we're at the end of the file and haven't found a tag table. so bail out */
550 if (type)
551 {
552 xfree(type);
553 type = 0;
554 }
555 return 2;
556 }
557 if (strncasecmp("Tag Table:", type, strlen("Tag Table:")) == 0)
558 {
559 finito = 1;
560 }
561 }
562 xfree(type);
563 type = 0;
564 if (!curses_open)
565 printf(_("Searching for tag table done\n"));
566 else
567 {
568 attrset(bottomline);
569 mvhline(maxy - 1, 0, ' ', maxx);
570 mvaddstr(maxy - 1, 0, "Searching for tag table done");
571 attrset(normal);
572 }
573 fseek(id, seek_pos, SEEK_SET);
574 return 1;
575 }
576
577 void
buildcommand(char * dest,char * command,char * filename,const char * tmpfilename)578 buildcommand(char *dest, char *command, char *filename, const char *tmpfilename)
579 {
580 strcpy(dest, command);
581 strcat(dest, " ");
582 strcat(dest, filename);
583 strcat(dest, "> ");
584 strcat(dest, tmpfilename);
585 }
586
587 void
builddircommand(char * dest,char * command,char * filename,const char * tmpfilename)588 builddircommand(char *dest, char *command, char *filename, const char *tmpfilename)
589 {
590 strcpy(dest, command);
591 strcat(dest, " ");
592 strcat(dest, filename);
593 strcat(dest, ">> ");
594 strcat(dest, tmpfilename);
595 }
596
597 FILE *
opendirfile(int number)598 opendirfile(int number)
599 {
600 FILE *id = NULL;
601 char buf[1024]; /* holds local copy of filename */
602 char *bufend; /* points at the trailing 0 of initial name */
603 char command[1128]; /* holds command to evaluate for decompression of file */
604 int i, j;
605 char *tmpfilename = NULL;
606 size_t *fileendentries = xmalloc(infopathcount * sizeof(*fileendentries)); /* should really be off_t, but a signed type really doesn't make sense here */
607 int dir_found = 0;
608 int dircount = 0;
609 int lang_found;
610 struct stat status;
611
612 if (number == 0) /* initialize tmp filename for file 1 */
613 {
614 /* close and delete old tmp file */
615 if (tmpfilename1)
616 {
617 unlink(tmpfilename1); /* erase old tmpfile */
618 free(tmpfilename1);
619 }
620 tmpfilename1 = make_tempfile();
621 tmpfilename = tmpfilename1; /* later we will refere only to tmp1 */
622 }
623 for (i = 0; i < infopathcount; i++) /* go through all paths */
624 {
625 lang_found = 0;
626 strcpy(buf, infopaths[i]); /* build a filename */
627 strcat(buf, "/");
628 if (getenv("LANG") != NULL)
629 strcat(buf, getenv("LANG"));
630 strcat(buf, "/dir");
631 /*
632 * remember the bufend to make it
633 * possible later to glue compression suffixes.
634 */
635 bufend = buf;
636 bufend += strlen(buf);
637 for (j = 0; j < SuffixesNumber; j++) /* go through all suffixes */
638 {
639 strcat(buf, suffixes[j].suffix);
640 if ((id = fopen(buf, "r")) != NULL)
641 {
642 fclose(id);
643 builddircommand(command, suffixes[j].command, buf, tmpfilename);
644 xsystem(command);
645 lstat(tmpfilename, &status);
646 fileendentries[dircount] = status.st_size;
647 dircount++;
648 dir_found = 1;
649 lang_found = 1;
650 }
651 (*bufend) = 0;
652 }
653
654 /* same as above, but without $LANG support */
655 if (!lang_found)
656 {
657 strcpy(buf, infopaths[i]); /* build a filename */
658 strcat(buf, "/");
659 strcat(buf, "dir");
660 /*
661 * remember the bufend to make it possible later to glue
662 * compression suffixes.
663 */
664 bufend = buf;
665 bufend += strlen(buf);
666 for (j = 0; j < SuffixesNumber; j++) /* go through all suffixes */
667 {
668 strcat(buf, suffixes[j].suffix);
669 if ((id = fopen(buf, "r")) != NULL)
670 {
671 fclose(id);
672 builddircommand(command, suffixes[j].command, buf, tmpfilename);
673 xsystem(command);
674 lstat(tmpfilename, &status);
675 fileendentries[dircount] = status.st_size;
676 dircount++;
677 dir_found = 1;
678 }
679 (*bufend) = 0;
680 }
681 }
682 }
683 if (dir_found)
684 id = fopen(tmpfilename, "r");
685 /*
686 * Filter the concatenated dir pages to exclude hidden parts of info
687 * entries
688 */
689 if (id)
690 {
691 char *tmp;
692 size_t filelen, l;
693 int aswitch = 0;
694 int firstswitch = 0;
695 dircount = 0;
696
697 fseek(id, 0, SEEK_END);
698 filelen = ftell(id);
699
700 tmp = xmalloc(filelen);
701 fseek(id, 0, SEEK_SET);
702 if (fread(tmp, 1, filelen, id)!=filelen)
703 {
704 printf(_("Error while reading file '%s'"), tmp);
705 closeprogram();
706 exit(1);
707 }
708 fclose(id);
709 id = fopen(tmpfilename, "w");
710 for (l = 0; l < filelen; l++)
711 {
712 if (tmp[l] == INFO_TAG)
713 {
714 aswitch ^= 1;
715 if (!firstswitch)
716 fputc(tmp[l], id);
717 firstswitch = 1;
718 }
719 else if ((aswitch) ||(!firstswitch))
720 fputc(tmp[l], id);
721 if (l + 1 == fileendentries[dircount])
722 {
723 if (aswitch != 0)
724 aswitch = 0;
725 dircount++; /* the last dircount should fit to the end of filelen */
726 }
727 }
728 fputc(INFO_TAG, id);
729 fputc('\n', id);
730 xfree(fileendentries);
731 fclose(id);
732 id = fopen(tmpfilename, "r");
733 xfree(tmp);
734
735 return id;
736 }
737 return NULL;
738 }
739
740 /*
741 * Note: openinfo is a function for reading info files, and putting
742 * uncompressed content into a temporary filename. For a flexibility, there
743 * are two temporary files supported, i.e. one for keeping opened info file,
744 * and second for i.e. regexp search across info nodes, which are in other
745 * info-subfiles. The temporary file 1 is refrenced by number=0, and file 2 by
746 * number=1 Openinfo by default first tries the path stored in char
747 * *filenameprefix and then in the rest of userdefined paths.
748 */
749 FILE *
openinfo(char * filename,int number)750 openinfo(char *filename, int number)
751 {
752 FILE *id = NULL;
753 #define BUF_LEN 1024
754 char *buf = xmalloc(BUF_LEN); /* holds local copy of filename */
755 char *bufend; /* points at the trailing 0 of initial name */
756 char command[1128]; /* holds command to evaluate for decompression of file */
757 int i, j;
758 char *tmpfilename;
759
760 if ((strncmp(filename, "dir", 3)==0) && !isalnum(filename[3]))
761 {
762 xfree(buf);
763 return opendirfile(number);
764 }
765
766 if (number == 0) /* initialize tmp filename for file 1 */
767 {
768 if (tmpfilename1)
769 {
770 unlink(tmpfilename1); /* erase old tmpfile */
771 free(tmpfilename1);
772 }
773 tmpfilename1 = make_tempfile();
774 tmpfilename = tmpfilename1; /* later we will refere only to tmp1 */
775 }
776 else /* initialize tmp filename for file 2 */
777 {
778 if (tmpfilename2)
779 {
780 unlink(tmpfilename2); /* erase old tmpfile */
781 free(tmpfilename2);
782 }
783 tmpfilename2 = make_tempfile();
784 tmpfilename = tmpfilename2; /* later we will refere only to tmp2 */
785 }
786
787 for (i = -2; i < infopathcount; i++) /* go through all paths */
788 {
789 if (i < 0)
790 {
791 /*
792 * no filenameprefix, we don't navigate around any specific
793 * infopage set, so simply scan all directories for a hit
794 */
795 if (!filenameprefix)
796 continue;
797 /* build a filename: First (i == -2) try filenameprefix/filename,
798 * then try with a .info appended */
799 if (i == -2)
800 snprintf(buf, BUF_LEN, "%s/%s", filenameprefix, basename(filename));
801 else
802 snprintf(buf, BUF_LEN, "%s/%s.info", filenameprefix, basename(filename));
803 }
804 else
805 {
806 /* build a filename */
807 strcpy(buf, infopaths[i]);
808 /* no match found in this directory */
809 if (! matchfile(&buf, filename))
810 continue;
811 }
812 bufend = buf;
813 /* remember the bufend to make it possible later to glue compression
814 * suffixes. */
815 bufend += strlen(buf);
816 for (j = 0; j < SuffixesNumber; j++) /* go through all suffixes */
817 {
818 strcat(buf, suffixes[j].suffix);
819 if ((id = fopen(buf, "r")) != NULL)
820 {
821 fclose(id);
822 clearfilenameprefix();
823 filenameprefix = strdup(buf);
824 { /* small scope for removal of filename */
825 int prefixi, prefixlen = strlen(filenameprefix);
826 for (prefixi = prefixlen; prefixi > 0; prefixi--)
827 if (filenameprefix[prefixi] == '/')
828 {
829 filenameprefix[prefixi] = 0;
830 break;
831 }
832 }
833 buildcommand(command, suffixes[j].command, buf, tmpfilename);
834 xsystem(command);
835 id = fopen(tmpfilename, "r");
836 if (id)
837 {
838 xfree(buf);
839 return id;
840 }
841 }
842 (*bufend) = 0;
843 }
844
845 /* if we have a nonzero filename prefix, that is we view a set of
846 * infopages, we don't want to search for a page in all
847 * directories, but only in the prefix directory. Therefore break
848 * here. */
849 if ((i == -1) &&(filenameprefix))
850 break;
851 }
852 xfree(buf);
853
854
855 return 0;
856 #undef BUF_LEN
857 }
858
859 void
addrawpath(char * filename)860 addrawpath(char *filename)
861 {
862 int len = strlen(filename);
863 int i, pos;
864 char tmp = '\0';
865 for (i = len; i >= 0; i--)
866 {
867 if (filename[i] == '/')
868 {
869 tmp = filename[i+1];
870 filename[i+1] = 0;
871 pos = i+1;
872 break;
873 }
874 }
875 if (i < 0)
876 pos = -1;
877
878 infopaths = xrealloc(infopaths,(infopathcount + 3) *(sizeof(char *)));
879 for (i = infopathcount; i > 0; i--) /* move entries to the right */
880 infopaths[i] = infopaths[i - 1];
881
882 if (pos > 0)
883 infopaths[0]=strdup(filename); /* add new(raw) entry */
884 else
885 infopaths[0]=strdup("./");
886 infopathcount++;
887
888 if (pos > 0) /* recreate original filename */
889 filename[pos] = tmp;
890 }
891
892 int
isininfopath(char * name)893 isininfopath(char *name)
894 {
895 int i;
896 for (i = 0; i < infopathcount; i++)
897 {
898 if (strcmp(name, infopaths[i]) == 0)
899 return 1; /* path already exists */
900 }
901 return 0; /* path not found in previous links */
902 }
903
904 /* returns the number of chars ch in string str */
905 unsigned int
charcount(const char * str,const char ch)906 charcount(const char *str, const char ch)
907 {
908 int num = 0;
909 const char *c;
910
911 c = str;
912
913 while (*c != '\0')
914 {
915 if (*c++ == ch)
916 num++;
917 }
918 return num;
919 }
920
921 /*
922 * find the paths where info files are to be found,
923 * and put them in the global var infopaths[]
924 */
925 void
initpaths()926 initpaths()
927 {
928 char emptystr[1] = "";
929 char **paths = NULL;
930 char *infopath = NULL, *langpath = NULL;
931 char *c, *dir, *env, *next;
932 char *rawlang = NULL, *lang = NULL, *langshort = NULL;
933 int ret;
934 unsigned int i, j, maxpaths, numpaths = 0, infolen, langlen;
935 size_t len;
936 struct stat sbuf;
937 ino_t *inodes;
938
939 /* first concat the paths */
940 env = getenv("INFOPATH");
941 if (env == NULL)
942 {
943 env = emptystr;
944 }
945 infolen = strlen(env) + strlen(configuredinfopath) + 3;
946 infopath = (char *) xmalloc( infolen );
947 strcat(infopath, env);
948 strcat(infopath, ":");
949 strcat(infopath, configuredinfopath);
950 /* end with a :, otherwise the strchr below will fail for the last entry */
951 strcat(infopath, ":");
952
953 /* alloc the paths[] array */
954 maxpaths = 3 * (charcount( infopath, ':' ) + 1); // *3 for $LANG
955 paths = (char **) xmalloc( maxpaths * sizeof(char *) );
956
957 /* split at ':' and put the path components into paths[] */
958 dir = infopath;
959 /* if this actually is a non-empty string, add it to paths[] */
960 while ( (next = strchr(dir, ':')) != NULL )
961 {
962 *next = '\0'; /* terminate the string */
963
964 /* if the dir actually is a non-empty string, add it to paths[] */
965 if ( dir && strlen(dir)>0 )
966 {
967 paths[numpaths++] = dir;
968 }
969
970 /* and advance the pointer to the next entry */
971 dir = next+1;
972 }
973
974 /* get the current $LANG, if any (to use for localized info pages) */
975 rawlang = getenv("LANG");
976 if (rawlang) {
977 lang = strdup(rawlang);
978 /* fix the lang string */
979 for (i=0; lang[i]!='\0'; i++)
980 {
981 /* cut off the charset */
982 if (lang[i]=='.')
983 {
984 lang[i]='\0';
985 }
986 /* if lang is sublocalized (nl_BE or so), also use short version */
987 if (lang[i]=='_' && langshort==NULL)
988 {
989 langshort = strdup(lang);
990 langshort[i] = '\0';
991 }
992 }
993 }
994 /* if we have a LANG defined, add paths with this lang to the paths[] */
995 if (lang && strlen(lang)>0 )
996 {
997 /* crude upper limit */
998 langlen = infolen + (strlen(lang)+2) * numpaths + 1;
999 if (langshort!=NULL) langlen *= 2;
1000 langpath = (char *) xmalloc( langlen * sizeof(char) );
1001
1002 c = langpath;
1003 for (i=0; i<numpaths; i++)
1004 {
1005 /* TODO: check for negative return values of sprintf */
1006 len = sprintf(c, "%s/%s", paths[i], lang);
1007 /* add the lang specific dir at the beginning */
1008 paths[numpaths+i] = paths[i];
1009 paths[i] = c;
1010
1011 c += len+1;
1012
1013 if (langshort)
1014 {
1015 /* TODO: check for negative return values of sprintf */
1016 len = sprintf(c, "%s/%s", paths[numpaths+i], langshort);
1017 /* add the lang specific dir at the beginning */
1018 paths[2*numpaths+i] = paths[numpaths+i];
1019 paths[numpaths+i] = c;
1020
1021 c += len+1;
1022 }
1023
1024 }
1025 numpaths *= (langshort?3:2);
1026 }
1027
1028 #ifdef ___DEBUG___
1029 /* for debugging */
1030 for (i=0; i<numpaths; i++)
1031 fprintf(stderr,"--> %s\n", paths[i]);
1032 #endif
1033
1034 /* ok, now we have all the (possibly) revelevant paths in paths[] */
1035 /* now loop over them, see if they are valid and if they are duplicates*/
1036 /* TODO: cleanup all malloc calls (get rid of cast, use sizeof(varname) instead of sizeof(type) */
1037 inodes = (ino_t *) xmalloc( maxpaths * sizeof(ino_t) );
1038 numpaths = 0;
1039 len = 0;
1040 for (i=0; i< maxpaths; i++)
1041 {
1042 /* TODO: check where these NULL paths come from */
1043 if (paths[i]==NULL)
1044 continue;
1045
1046 /* stat() the dir */
1047 ret = stat( paths[i], &sbuf);
1048 /* and see if it could be opened */
1049 if (ret < 0)
1050 {
1051 #ifdef ___DEBUG___
1052 fprintf(stderr, "error while opening `%s': %s\n",
1053 paths[i], strerror(errno) );
1054 #endif
1055 paths[i] = NULL;
1056 inodes[i] = 0;
1057 }
1058 else
1059 {
1060 inodes[i] = sbuf.st_ino;
1061 }
1062
1063 /* now check if this path is a duplicate */
1064 for (j=0; j<i; j++)
1065 {
1066 if (inodes[j]==inodes[i]) paths[i] = NULL;
1067 }
1068
1069 /* calculate the total number of vali paths and the size of teh strings */
1070 if (paths[i]!=NULL)
1071 {
1072 numpaths++;
1073 len += strlen(paths[i]) + 1;
1074 }
1075 }
1076
1077
1078 /* and alloc and copy to global var */
1079 infopathcount = numpaths;
1080 infopaths = (char **) xmalloc( numpaths * sizeof(char *) );
1081 c = (char *) xmalloc( len * sizeof(char) );
1082 j=0;
1083 for (i=0; i<maxpaths; i++)
1084 {
1085 if (paths[i]!=NULL)
1086 {
1087 /* copy path to c buffer */
1088 strcpy(c, paths[i]);
1089 infopaths[j++] = c;
1090 c += strlen(paths[i]) + 1;
1091 }
1092 }
1093
1094
1095 xfree(infopath);
1096 xfree(langpath);
1097 xfree(paths);
1098 xfree(lang);
1099 xfree(langshort);
1100 xfree(inodes);
1101
1102 #ifdef ___DEBUG___
1103 /* for debugging */
1104 fprintf(stderr, "%i valid info paths found:\n", infopathcount);
1105 for (i=0; i<infopathcount; i++)
1106 if (infopaths[i]) fprintf(stderr,"--> %s\n", infopaths[i]);
1107 #endif
1108
1109
1110 }
1111
1112
1113
1114 void
create_indirect_tag_table()1115 create_indirect_tag_table()
1116 {
1117 FILE *id = 0;
1118 int initial;
1119 for (unsigned i = 1; i <= IndirectEntries; i++)
1120 {
1121 id = openinfo(indirect[i].filename, 1);
1122 initial = TagTableEntries + 1;
1123 if (!id)
1124 {
1125 /* display error message to make the user aware of
1126 * the broken info page
1127 */
1128 char msg[1024];
1129 snprintf(msg, 1024, "%s '%s' (%s)",
1130 _("Can't open file"), indirect[i].filename,
1131 _("press a key to continue") );
1132 attrset(bottomline);
1133 mvhline(maxy - 1, 0, ' ', maxx);
1134 mvaddstr(maxy - 1, 0, msg);
1135 move(0, 0);
1136 attrset(normal);
1137 getch();
1138
1139 continue;
1140 }
1141 create_tag_table(id);
1142 FirstNodeOffset = tag_table[1].offset;
1143 strcpy(FirstNodeName, tag_table[1].nodename);
1144 fclose(id);
1145 for (unsigned j = initial; j <= TagTableEntries; j++)
1146 {
1147 tag_table[j].offset +=(indirect[i].offset - FirstNodeOffset);
1148 }
1149 }
1150 FirstNodeOffset = tag_table[1].offset;
1151 strcpy(FirstNodeName, tag_table[1].nodename);
1152 qsort(&tag_table[1], TagTableEntries, sizeof(TagTable), qsort_cmp);
1153 }
1154 void
create_tag_table(FILE * id)1155 create_tag_table(FILE * id)
1156 {
1157 char *buf = xmalloc(1024);
1158 long oldpos;
1159 fseek(id, 0, SEEK_SET);
1160 if (!tag_table)
1161 tag_table = xmalloc((TagTableEntries + 2) * sizeof(TagTable));
1162 else
1163 tag_table = xrealloc(tag_table,(TagTableEntries + 2) * sizeof(TagTable));
1164 while (!feof(id))
1165 {
1166 if (fgetc(id) == INFO_TAG) /* We've found a node entry! */
1167 {
1168 while (fgetc(id) != '\n'); /* skip '\n' */
1169 TagTableEntries++; /* increase the nuber of tag table entries */
1170 oldpos = ftell(id); /* remember this file position! */
1171 /*
1172 * it is a an eof-fake-node (in some info files it happens, that
1173 * the eof'ish end of node is additionaly signalised by an INFO_TAG
1174 * We give to such node an unlike to meet nodename.
1175 */
1176 if (fgets(buf, 1024, id) == NULL)
1177 {
1178 tag_table = xrealloc(tag_table, sizeof(TagTable) *(TagTableEntries + 1));
1179 strcpy(tag_table[TagTableEntries].nodename, "12#!@#4");
1180 tag_table[TagTableEntries].offset = 0;
1181 }
1182 else
1183 {
1184 int colons = 0, i, j;
1185 int buflen = strlen(buf);
1186 for (i = 0; i < buflen; i++)
1187 {
1188 if (buf[i] == ':')
1189 colons++;
1190 if (colons == 2) /*
1191 * the string after the second colon
1192 * holds the name of current node.
1193 * The name may then end with `.',
1194 * or with a newline, which is scanned
1195 * bellow.
1196 */
1197 {
1198 for (j = i + 2; j < buflen; j++)
1199 {
1200 if ((buf[j] == ',') ||(buf[j] == '\n'))
1201 {
1202 tag_table = xrealloc(tag_table, sizeof(TagTable) *(TagTableEntries + 1));
1203 buf[j] = 0;
1204 buflen = j;
1205 strcpy(tag_table[TagTableEntries].nodename, buf + i + 2);
1206 tag_table[TagTableEntries].offset = oldpos - 2;
1207 break;
1208 }
1209 }
1210 break;
1211 }
1212 } /* end: for loop, looking for second colon */
1213 } /* end: not a fake node */
1214 } /* end: we've found a node entry(INFO_TAG) */
1215 } /* end: global while loop, looping until eof */
1216 xfree(buf);
1217 buf = 0;
1218 if (!indirect)
1219 {
1220 FirstNodeOffset = tag_table[1].offset;
1221 strcpy(FirstNodeName, tag_table[1].nodename);
1222 qsort(&tag_table[1], TagTableEntries, sizeof(TagTable), qsort_cmp);
1223 }
1224 }
1225
1226 int
seeknode(int tag_table_pos,FILE ** Id)1227 seeknode(int tag_table_pos, FILE ** Id)
1228 {
1229 int i;
1230 FILE * newid;
1231 #define id (*Id)
1232 /*
1233 * Indirect nodes are seeked using a formula:
1234 * file-offset = tagtable_offset - indirect_offset +
1235 * + tagtable[1]_offset
1236 */
1237 if (indirect)
1238 {
1239 for (i = IndirectEntries; i >= 1; i--)
1240 {
1241 if (indirect[i].offset <= tag_table[tag_table_pos].offset)
1242 {
1243 long off = tag_table[tag_table_pos].offset - indirect[i].offset + FirstNodeOffset - 4;
1244 newid = openinfo(indirect[i].filename, 0);
1245 if (newid == NULL)
1246 {
1247 return -1;
1248 closeprogram();
1249 printf(_("Error: could not open info file part"));
1250 printf("\n");
1251 exit(1);
1252 }
1253 fclose(id);
1254 id = newid;
1255 if (off > 0)
1256 fseek(id, off, SEEK_SET);
1257 else
1258 fseek(id, off, SEEK_SET);
1259 break;
1260 }
1261 }
1262 }
1263 else
1264 {
1265 long off = tag_table[tag_table_pos].offset - 4;
1266 if (off > 0)
1267 fseek(id, off, SEEK_SET);
1268 else
1269 fseek(id, off, SEEK_SET);
1270 }
1271 #undef id
1272 return 0;
1273 }
1274
1275 /* removes trailing .gz, .bz2, etc. */
1276 void
strip_compression_suffix(char * file)1277 strip_compression_suffix(char *file)
1278 {
1279 const size_t len = strlen(file);
1280 assert(len<1024); /* just some random limit */
1281 char *found = 0;
1282
1283 for (unsigned j = 0; j < SuffixesNumber; j++)
1284 {
1285 if ( (found = strstr(file, suffixes[j].suffix)) != NULL )
1286 {
1287 if ( file + len == found + strlen(suffixes[j].suffix) )
1288 {
1289 *found = '\0';
1290 break;
1291 }
1292 }
1293 }
1294 }
1295
1296 /* strip .info from and of string */
1297 void
strip_info_suffix(char * file)1298 strip_info_suffix(char *file)
1299 {
1300 const size_t len = strlen(file);
1301 assert(len<1024); /* just some random limit */
1302
1303 char *found = 0;
1304 const char suffix[6] = ".info";
1305
1306 if ( (found = strstr(file, suffix)) != NULL )
1307 {
1308 if ( file + len == found + strlen(suffix) )
1309 {
1310 *found = '\0';
1311 }
1312 }
1313 }
1314
1315