home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / games / volume13 / dominion / part25 / cinfo.c next >
C/C++ Source or Header  |  1992-02-11  |  12KB  |  495 lines

  1.   /* cinfo.c  --  curses-based info browsing utility/library */
  2.  
  3. /*
  4.  * Copyright (C) 1990 Free Software Foundation, Inc.
  5.  * Written by Mark Galassi.
  6.  *
  7.  * This file is part of cinfo.
  8.  *
  9.  * cinfo is free software; you can redistribute it and/or
  10.  * modify it under the terms of the GNU General Public License as published
  11.  * by the Free Software Foundation; either version 1, or (at your option)
  12.  * any later version.
  13.  *
  14.  * This software is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this software; see the file COPYING.  If not, write to
  21.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  */
  23.  
  24. #include <stdio.h>
  25. #include <curses.h>
  26. #ifdef SYSV
  27. # include <string.h>
  28. #else
  29. # include <strings.h>
  30. #endif /* SYSV */
  31.  
  32. /* some systems already have strstr() */
  33. /* #define HAVE_STRSTR */
  34.  
  35. #define LINE_LEN 80        /* longer than this is truncated */
  36.   /* this is defined in the Makefile */
  37. /* #define STANDALONE        /* if we run standalone... */
  38. #define INFO_PATH "/usr/local/lib/emacs/info"
  39. #ifndef INFO_INTRO
  40. # define INFO_INTRO "/usr/local/lib/info_intro"
  41. #endif /* INFO_INTRO */
  42. #define INFO_CHAR 0x1F        /* ^_, used to specify an info line */
  43. #define DEL 0x7F        /* DELETE character */
  44.  
  45.   /* global variables */
  46.  
  47.   /* current node fields */
  48. char node[LINE_LEN], node_prev[LINE_LEN],
  49.   node_next[LINE_LEN], node_up[LINE_LEN];
  50.  
  51.   /* seek pointer in info file where the tag table is */
  52. long tag_table_pos = 0;
  53.  
  54.   /* this tells us that we must show a new page */
  55. int update_info_page = 1;
  56.  
  57.   /* main routine if this program is run standalone.  Otherwise,
  58.      the cinfo() routine can be called from anywhere.
  59.    */
  60. #ifdef STANDALONE
  61. main(argc, argv)
  62.      int argc;
  63.      char *argv[];
  64. {
  65.   switch (argc) {
  66.   case 2:
  67.     cinfo(argv[1], "Top");
  68.     break;
  69.   case 3:
  70.     cinfo(argv[1], argv[2]);
  71.     break;
  72.   default:
  73.     printf("Useage: %s filename [tag]\n", argv[0]);
  74.     clean_exit();
  75.     exit(1);
  76.   }
  77.   clean_exit();
  78.   exit(0);
  79. }
  80. clean_exit(){};
  81.  
  82. #endif /* STANDALONE */
  83.  
  84. static WINDOW *infow;        /* screen-size window in which it happens */
  85.  
  86.   /* run curses-based info browsing.
  87.      fname: name of info file
  88.      tag:   topic we should try to search for
  89.    */
  90. cinfo(fname, tag)
  91.      char fname[], tag[];
  92. {
  93.   char item[LINE_LEN];
  94.   int done = 0;            /* for the input loop */
  95.   long old_pos;
  96.   char c;
  97.   FILE *fp, *intro_fp, *find_info_file();
  98.  
  99.     /* open up the info file */
  100.   fp = find_info_file(fname);
  101.   find_tag_table(fp);
  102.   if ((intro_fp = fopen(INFO_INTRO, "r")) == NULL) {
  103.     printf("Cannot open info file %s, exiting.\n", INFO_INTRO);
  104.     clean_exit();
  105.     exit(1);
  106.   }
  107.   cinfo_init_screen();
  108.     /* if they start at top level, give them the help screen */
  109.   if (strcmp(tag, "Top") == 0) {
  110.     cinfo_intro_screen(intro_fp, infow);
  111.   }
  112.  
  113.   find_menu_item(fp, tag);
  114.   parse_info_line(fp);        /* make sure we have the current line parsed */
  115. /*  wclear(infow); */
  116.  
  117.   while (!done) {
  118.     old_pos = ftell(fp);    /* before this page is shown */
  119.     if (show_page(fp, infow) == -1) {
  120.       /* means that we ran into a new info line */
  121.       parse_info_line(fp);
  122.     }
  123.     cinfo_statline("(SPACE, DEL/BS, m, n, p, u, q, ?)");
  124.     switch (wgetch(infow)) {
  125.     case ' ':
  126.       update_info_page = 1;
  127.       break;
  128.     case DEL:            /* should go back a screen */
  129.     case '\b':
  130.       break;
  131.     case 'm':            /* let user choose a tag */
  132.       if (prompt(item) != -1) {    /* did they type something? */
  133.     fseek(fp, old_pos, 0);    /* in case find_menu_item does not work */
  134.     find_menu_item(fp, item);
  135.     parse_info_line(fp);    /* make sure we have the current line parsed */
  136.     update_info_page = 1;
  137.       }
  138.       break;
  139.     case 'n':
  140.       if (strlen(node_next) > 0) {
  141.     find_menu_item(fp, node_next);
  142.     parse_info_line(fp);
  143.       }
  144.       update_info_page = 1;
  145.       break;
  146.     case 'p':
  147.       if (strlen(node_prev) > 0) {
  148.     find_menu_item(fp, node_prev);
  149.     parse_info_line(fp);
  150.       }
  151.       update_info_page = 1;
  152.       break;
  153.     case 'u':
  154.       if (strlen(node_up) > 0) {
  155.     find_menu_item(fp, node_up);
  156.     parse_info_line(fp);
  157.       }
  158.       break;
  159.     case 'q':
  160.       done = 1;
  161.       break;
  162.     case '?':
  163.       cinfo_intro_screen(intro_fp, infow);
  164.       wclear(infow);
  165.       update_info_page = 1;
  166.       break;
  167.     default:
  168.       fseek(fp, old_pos, 0);
  169.       break;
  170.     }
  171.   }
  172.   fclose(fp);
  173.   fclose(intro_fp);
  174.   cinfo_cleanup();
  175. }
  176.  
  177.   /* prepare the screen */
  178. cinfo_init_screen()
  179. {
  180. #ifdef STANDALONE
  181.   printf("initializing screen...\r\n");
  182.   initscr();
  183.   savetty();
  184.   nonl();
  185.   cbreak();
  186.   noecho();
  187. #endif /* STANDALONE */
  188.     /* prepare a full-size window to work in */
  189.   infow = newwin(LINES, COLS, 0, 0);
  190.   wclear(infow);
  191.   wmove(infow, 0, 0);
  192.   wrefresh(infow);
  193. }
  194.  
  195.   /* clean up curses, if necessary, and free memory */
  196. cinfo_cleanup()
  197. {
  198.   wclear(infow);
  199.   wrefresh(infow);
  200.   delwin(infow);
  201. #ifdef STANDALONE
  202.   resetty();
  203.   endwin();
  204. #endif /* STANDALONE */
  205. }
  206.  
  207.   /* shows the introductory page and waits for a key hit */
  208. cinfo_intro_screen(intro_fp, infow)
  209.      FILE *intro_fp;
  210.      WINDOW *infow;
  211. {
  212.   char line[2*LINE_LEN];
  213.   int n = 0;
  214.  
  215.   wclear(infow);
  216.   rewind(intro_fp);
  217.   while (fgets(line, 2*LINE_LEN, intro_fp) != NULL) {
  218.     line[LINE_LEN-1] = '\0';    /* truncate it */
  219.     mvwaddstr(infow, n, 0, line);
  220.     wclrtoeol(infow);
  221.     wrefresh(infow);
  222.     ++n;
  223.   }
  224.   wgetch(infow);
  225. }
  226.  
  227.   /* shows the given string at the bottom of the page */
  228. cinfo_statline(s)
  229.      char s[];
  230. {
  231.   wmove(infow, LINES-1, 0);
  232.   waddstr(infow, s);
  233.   wclrtoeol(infow);
  234.   wrefresh(infow);
  235. }
  236.  
  237. cinfo_statline2()
  238. {
  239.   wmove(infow, LINES-2, 0);
  240.   wstandout(infow);
  241.   wprintw(infow ,"%s", node);
  242.   wstandend(infow);
  243.   wprintw(infow, ", p: %s, u: %s, n: %s", node_prev, node_up, node_next);
  244.   wclrtoeol(infow);
  245.   wrefresh(infow);
  246. }
  247.  
  248.   /* this is kind of the guts of it:
  249.      setting the file pointer to a menu item.
  250.      this function also returns the seek pointer
  251.    */
  252. find_menu_item(fp, tag)
  253.      FILE *fp;
  254.      char tag[];
  255. {
  256.   long old_pos = ftell(fp), pos;
  257.   char line[2*LINE_LEN], s[LINE_LEN], item[LINE_LEN];
  258. #ifndef HAVE_STRSTR
  259. #ifdef __STDC__
  260.   extern char *strstr(char *, char *);
  261. #else /* STDC */
  262.   extern char *strstr();
  263. #endif /* STDC */
  264. #endif /* HAVE_STRSTR */
  265.  
  266.   fseek(fp, tag_table_pos, 0);
  267.     /* prepare a scanning format string */
  268.   sprintf(s, "Node: %%[-A-Z a-z0-9_`\']");
  269.   while (1) {
  270.     if ((fgets(line, 2*LINE_LEN, fp) == NULL) || (sscanf(line, s, item) < 0)) {
  271.       fseek(fp, old_pos, 0);
  272.       return old_pos;
  273.     }
  274.       /* now convert all to lower case */
  275.     str_lower(item);
  276.     str_lower(tag);
  277.     if (strncmp(item, tag, strlen(tag)) == 0) {
  278.         /* ugly kludge, but scanf seems to not do what I want */
  279.       sscanf(line+strlen("Node: ")+strlen(item)+1, "%ld", &pos);
  280.       fseek(fp, pos-1, 0);
  281.       return pos;
  282.     }
  283.   }
  284. }
  285.  
  286.   /* this function is essential:
  287.      it sets the node[], node_prev[], node_next[] and node_up[] strings
  288.    */
  289. parse_info_line(fp)
  290.      FILE *fp;
  291. {
  292.   char line[2*LINE_LEN];
  293.   long old_pos = ftell(fp);
  294.   char *ptr;
  295.   int n;
  296.  
  297.   fgets(line, 2*LINE_LEN, fp);
  298.     /* we can only parse it if it is really a node line */
  299.   if (strncmp(line, "File:", strlen("File:")) != 0) {
  300.     fseek(fp, old_pos, 0);
  301.     return -1;
  302.   }
  303. #define BAD_HACK
  304. #ifdef BAD_HACK
  305.     /* now I play funny pointer games to parse
  306.        this line.  I assume that strstr() returns NULL
  307.        if the second string is not a substring of the
  308.        first.
  309.      */
  310.   if (ptr = strstr(line, "Node: ")) {
  311.     n = (int) ((long) strchr(ptr, ',') - (long) ptr - strlen("Node: "));
  312.     strncpy(node, ptr+strlen("Node: "), n);
  313.     node[n] = '\0';
  314.   } else {            /* field is not there */
  315.     strcpy(node, "");
  316.   }
  317.  
  318.   if (ptr = strstr(line, "Prev: ")) {
  319.     n = (int) ((long) strchr(ptr, ',') - (long) ptr - strlen("Prev: "));
  320.     strncpy(node_prev, ptr+strlen("Prev: "), n);
  321.     node_prev[n] = '\0';
  322.   } else {            /* field is not there */
  323.     strcpy(node_prev, "");
  324.   }
  325.  
  326.   if (ptr = strstr(line, "Up: ")) {
  327.     n = (int) ((long) strchr(ptr, ',') - (long) ptr - strlen("Up: "));
  328.     strncpy(node_up, ptr+strlen("Up: "), n);
  329.     node_up[n] = '\0';
  330.   } else {            /* field is not there */
  331.     strcpy(node_up, "");
  332.   }
  333.  
  334.     /* this last one is easier, because it is
  335.        the last field in an info line.  but
  336.        remember to remove the newline.
  337.      */
  338.   if (ptr = strstr(line, "Next: ")) {
  339.     strcpy(node_next, ptr+strlen("Next: "));
  340.     node_next[strlen(node_next)-1] = '\0';
  341.   } else {            /* field is not there */
  342.     strcpy(node_next, "");
  343.   }
  344.  
  345. #endif /* BAD HACK */
  346.   fseek(fp, old_pos, 0);    /* go back to where we were */
  347.   return 1;
  348. }
  349.  
  350.   /* this is also an essential function:
  351.      it shows the current information page
  352.    */
  353. show_page(fp, infow)
  354.      FILE *fp;
  355.      WINDOW *infow;
  356. {
  357.   int n = 0;            /* line counter */
  358.   char line[2*LINE_LEN];
  359.  
  360.   if (!update_info_page) {    /* do we need to redraw? */
  361.     return 0;
  362.   }
  363.   update_info_page = 0;
  364.   wclear(infow);
  365.   for (n = 0; n < LINES-2; ++n) {
  366.     fgets(line, 2*LINE_LEN, fp);
  367.     line[LINE_LEN-1] = '\0';    /* truncate it */
  368.       /* stop if you get to a new info format entry */
  369.     if (line[0] == INFO_CHAR) {
  370.       cinfo_statline2();
  371.       return -1;
  372.     } else {
  373.       mvwaddstr(infow, n, 0, line);
  374.       wclrtoeol(infow);
  375.       wrefresh(infow);
  376.     }
  377.   }
  378.   cinfo_statline2();
  379.   return 1;
  380. }
  381.  
  382.   /* prompt the user for a tag; return -1 if there is no input */
  383. prompt(item)
  384.      char item[];
  385. {
  386.   int ret;
  387.  
  388.   echo();
  389.   nocbreak();
  390.   cinfo_statline("Enter your menu choice: ");
  391.   ret = wscanw(infow, "%[^\n]", item);
  392.   cbreak();
  393.   noecho();
  394.   if (ret <= 0) {
  395.     return -1;
  396.   }
  397.   return 1;
  398. }
  399.  
  400.   /* called at the start, this finds the location of the tag table */
  401. find_tag_table(fp)
  402.      FILE *fp;
  403. {
  404.   char line[2*LINE_LEN];
  405.   long pos;
  406.  
  407.   while (1) {
  408.     pos = ftell(fp);
  409.     if (fgets(line, 2*LINE_LEN, fp) == NULL) {
  410.       break;
  411.     }
  412.     if (line[0] == INFO_CHAR) {
  413.       fgets(line, 2*LINE_LEN, fp);
  414.       if (strncmp(line, "Tag table:", strlen("Tag table:")) == 0) {
  415.     tag_table_pos = ftell(fp); /* got it!!! */
  416.     break;
  417.       }
  418.     }
  419.   }
  420. }
  421.  
  422.   /* converts a string to lower case */
  423. str_lower(s)
  424.      char s[];
  425. {
  426.   while (*s) {
  427.     if (*s >= 'A' && *s <= 'Z') { /* is it upper case? */
  428.       *s = *s - ('A' - 'a');    /* if so, convert */
  429.     }
  430.     ++s;
  431.   }
  432. }
  433.  
  434.   /* tries to find and fopen() the .info file in a variety
  435.      of paths.  returns the file pointer or exits.
  436.    */
  437. FILE *find_info_file(fname)
  438.      char fname[];
  439. {
  440.   char full_fname[LINE_LEN];
  441.   FILE *fp, *fopen();
  442.  
  443.     /* try several file names, see if one of them works */
  444.   if ((fp = fopen(fname, "r")) == NULL) {
  445.     strcpy(full_fname, fname);
  446.     strcat(full_fname, ".info");    /* try it with a ".info" */
  447.     if ((fp = fopen(full_fname, "r")) == NULL) {
  448.       strcpy(full_fname, INFO_PATH);
  449.       strcat(full_fname, "/");
  450.       strcat(full_fname, fname);
  451.       if ((fp = fopen(full_fname, "r")) == NULL) {
  452.     strcat(full_fname, ".info");
  453.     if ((fp = fopen(full_fname, "r")) == NULL) {
  454.       printf("Cannot open file %s or %s, exiting.\n", fname, full_fname);
  455.           clean_exit();
  456.       exit(1);
  457.     }
  458.       }
  459.     }
  460.   }
  461.   return fp;
  462. }
  463.  
  464.  
  465. #ifndef HAVE_STRSTR
  466.  
  467.   /* System V has strstr(), which is useful, but bsd
  468.      does not.  This is a replacement for strstr that
  469.      should make up for that.  The algorithm is a
  470.      total hack, and probably not too fast.
  471.    */
  472.   /* strstr() returns a pointer to the first occurrence of
  473.      s2 inside s1.  A generalization of index() or strchr().
  474.      strstr() returns NULL if s2 is not a substring of s1.
  475.    */
  476. char *
  477. strstr(s1, s2)
  478.      char *s1, *s2;
  479. {
  480.   char *p = NULL;        /* what we are looking for */
  481.   char *s;
  482.   int n = strlen(s2);
  483.  
  484.     /* run through s1, and see if at any point s2 appears */
  485.   for (s = s1; *s != '\0'; ++s) {
  486.     if (strncmp(s, s2, n) == 0) {
  487.       p = s;            /* we found it!!! */
  488.       break;
  489.     }
  490.   }
  491.   return p;
  492. }
  493.  
  494. #endif /* HAVE_STRSTR */
  495.