home *** CD-ROM | disk | FTP | other *** search
- /* info -- a stand-alone Info program.
-
- Version 1.40
- 24 October 1991
-
- Copyright (C) 1987, 1991 Free Software Foundation, Inc.
-
- This file is part of GNU Info.
-
- GNU Info is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY. No author or distributor accepts
- responsibility to anyone for the consequences of using it or for
- whether it serves any particular purpose or works at all, unless he
- says so in writing. Refer to the GNU Emacs General Public License
- for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- GNU Info, but only under the conditions described in the GNU Emacs
- General Public License. A copy of this license is supposed to
- have been given to you along with GNU Emacs so you can know your
- rights and responsibilities. It should be in a file named COPYING.
- Among other things, the copyright notice and this notice must be
- preserved on all copies. */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <signal.h>
- #include <pwd.h>
- #include <errno.h>
- #if !defined (errno)
- extern int errno;
- #endif /* !errno */
- #include <ctype.h>
-
- #include "getopt.h"
-
- #if defined (USG)
- struct passwd *getpwnam ();
- #include <fcntl.h>
- #include <termio.h>
- #include <string.h>
-
- # if defined (USGr3)
- # include <sys/stream.h>
- # include <sys/ptem.h>
- # undef TIOCGETC
- # endif /* USGr3 */
- #define bcopy(source, dest, count) memcpy(dest, source, count)
- char *index(s,c) char *s; { char *strchr(); return strchr(s,c); }
- char *rindex(s,c) char *s; { char *strrchr(); return strrchr(s,c); }
- #else /* !USG */
- #include <sys/file.h>
- #include <sgtty.h>
- #include <strings.h>
- #endif /* USG */
-
- #if !defined (DEFAULT_INFOPATH)
- #define DEFAULT_INFOPATH ".:/usr/gnu/info:/usr/local/emacs/info:/usr/local/lib/emacs/info"
- #endif /* DEFAULT_INFOPATH */
-
- typedef int boolean;
- #define true 1
- #define false 0
- #define UNIX
-
- typedef struct nodeinfo {
- char *filename;
- char *nodename;
- int pagetop;
- int nodetop;
- struct nodeinfo *next;
- } NODEINFO;
-
- typedef struct indirectinfo {
- char *filename;
- int first_byte;
- } INDIRECT_INFO;
-
- typedef int Function ();
-
- #define PROJECT_NAME "GNU Info"
-
- #define barf(msg) fprintf(stderr, "%s\n", msg)
-
- /* Some character stuff. */
- #define control_character_threshold 0x020 /* smaller than this is control */
- #define meta_character_threshold 0x07f /* larger than this is Meta. */
- #define control_character_bit 0x40 /* 0x000000, must be off. */
- #define meta_character_bit 0x080/* x0000000, must be on. */
-
- #define info_separator_char '\037'
- #define start_of_node_string "\037"
-
- #ifdef CTRL
- #undef CTRL
- #endif
-
- #define CTRL(c) ((c) & (~control_character_bit))
- #define META(c) ((c) | meta_character_bit)
-
- #define UNMETA(c) ((c) & (~meta_character_bit))
- #define UNCTRL(c) to_upper(((c)|control_character_bit))
-
- #ifndef to_upper
- #define to_upper(c) (((c) < 'a' || (c) > 'z') ? c : c-32)
- #define to_lower(c) (((c) < 'A' || (c) > 'Z') ? c : c+32)
- #endif
-
- #define CTRL_P(c) ((unsigned char) (c) < control_character_threshold)
- #define META_P(c) ((unsigned char) (c) > meta_character_threshold)
-
- #define NEWLINE '\n'
- #define RETURN CTRL('M')
- #define DELETE 0x07f
- #define TAB '\t'
- #define ABORT_CHAR CTRL('G')
- #define PAGE CTRL('L')
- #define SPACE 0x020
- #define ESC CTRL('[')
- #define control_display_prefix '^'
-
- #define TAG_TABLE_END_STRING "\037\nEND TAG TABLE"
- #define TAG_TABLE_BEG_STRING "\nTAG TABLE:\n"
- #define NODE_ID "Node:"
- #define NNODENAME 4 /* Default amount to grow nodename list by. */
- #define FILENAME_LEN 256
- #define NODENAME_LEN 256
- #define STRING_SIZE 256
- #define nodeend_sequence "\n\037"
-
- /* All right, some windows stuff. */
-
- typedef struct {
- /* Absolute x and y coordinates for usable portion of this window. */
- int left, top, right, bottom;
- /* Absolute cursor position in this window. */
- int ch, cv;
- } WINDOW;
-
- typedef struct _wind_list {
- int left, top, right, bottom;
- int ch, cv;
- struct _wind_list *next_window;
- } WINDOW_LIST;
-
- WINDOW the_window = {0, 0, 80, 24, 0, 0};
- WINDOW_LIST *window_stack = (WINDOW_LIST *)NULL;
- WINDOW terminal_window = {0, 0, 80, 24, 0, 0};
-
- /* Not really extern, but defined later in this file. */
- extern WINDOW echo_area;
- char *xmalloc (), *xrealloc (), *getenv ();
- char *next_info_file (), *opsys_filename ();
- boolean build_menu (), find_menu_node ();
- void swap_filestack (), pop_filestack ();
-
- /* A crock, this should be done in a different way. */
- #define MAX_INDIRECT_FILES 100
-
- /* The info history list. */
- NODEINFO *Info_History = NULL;
-
- /* ?Can't have more than xx files in the indirect list? */
- INDIRECT_INFO indirect_list[MAX_INDIRECT_FILES];
-
- /* The filename of the currently loaded info file. */
- char current_info_file[FILENAME_LEN];
-
- /* The nodename of the node the user is looking at. */
- char current_info_node[NODENAME_LEN];
-
- /* The last file actually loaded. Not the same as current info file. */
- char last_loaded_info_file[FILENAME_LEN];
-
- /* Offsets in info_file of top and bottom of current_info_node. */
- int nodetop, nodebot;
-
- /* Number of lines in this node. */
- int nodelines;
-
- /* Buffer for the info file. */
- char *info_file = NULL;
-
- /* Length of the above buffer. */
- int info_buffer_len;
-
- /* Pointer to the start of a tag table, or NULL to show none. */
- char *tag_table = NULL;
-
- /* Length of the above buffer. */
- int tag_buffer_len;
-
- /* If true, the tag table is indirect. */
- boolean indirect = false;
- int indirect_top;
-
- /* Offset in the buffer of the current pagetop. */
- int pagetop;
-
- /* Offset in the buffer of the last displayed character. */
- int pagebot = 0;
-
- /* If non-NULL, this is a colon separated list of directories to search
- for a specific info file. The user places this variable into his or
- her environment. */
- char *infopath = NULL;
-
- /* If filled, the name of a file to write to. */
- char dumpfile[FILENAME_LEN];
-
- /* This is the command to print a node. A default value is compiled in,
- or it can be found from the environment as $INFO_PRINT_COMMAND. */
- char *print_command;
-
- /* **************************************************************** */
- /* */
- /* Getting Started. */
- /* */
- /* **************************************************************** */
-
- /* Begin the Info session. */
-
- /* Global is on until we are out of trouble. */
- int totally_inhibit_errors = 1;
-
- struct option long_options[] = {
- {"directory", 1, 0, 'd'},
- {"node", 1, 0, 'n'},
- {"file", 1, 0, 'f'},
- {"output", 1, 0, 'o'},
- {NULL, 0, NULL, 0}
- };
-
- #define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
-
- main (argc, argv)
- int argc;
- char **argv;
- {
- int c, ind;
- char filename[FILENAME_LEN];
- char *nodename;
- char **nodenames;
- int nodenames_size, nodenames_index;
- char *ptr, *env_infopath, *env_print_command;
-
- nodenames_index = 0;
- nodenames = (char **)xmalloc ((nodenames_size = 1) * sizeof (char *));
- nodenames[0] = (char *)NULL;
-
- env_infopath = getenv ("INFOPATH");
- env_print_command = getenv ("INFO_PRINT_COMMAND");
-
- filename[0] = '\0';
-
- if (env_infopath && *env_infopath)
- infopath = savestring (env_infopath);
- else
- infopath = savestring (DEFAULT_INFOPATH);
-
- if (env_print_command && *env_print_command)
- print_command = savestring (env_print_command);
- else
- print_command = savestring (INFO_PRINT_COMMAND);
-
- while ((c = getopt_long (argc, argv, "d:n:f:o:", long_options, &ind)) != EOF)
- {
- if (c == 0 && long_options[ind].flag == 0)
- c = long_options[ind].val;
- switch (c)
- {
- case 0:
- break;
-
- case 'd':
- free (infopath);
- infopath = savestring (optarg);
- break;
-
- case 'n':
-
- if (nodenames_index + 2 > nodenames_size)
- nodenames = (char **)
- xrealloc (nodenames, (nodenames_size += 10) * sizeof (char *));
-
- nodenames[nodenames_index++] = optarg;
- nodenames[nodenames_index] = (char *)NULL;
- break;
-
- case 'f':
- strncpy (filename, optarg, FILENAME_LEN);
- break;
-
- case 'o':
- strncpy (dumpfile, optarg, FILENAME_LEN);
- break;
-
- default:
- usage ();
- }
- }
-
- /* Okay, flags are parsed. Get possible Info menuname. */
-
- if (*filename && (ptr = rindex (filename,'/')) != NULL )
- {
- /* Add filename's directory to path. */
- char *temp;
-
- temp = (char *)xmalloc (2 + strlen (filename) + strlen (infopath));
- strncpy (temp, filename, ptr - filename);
- sprintf (temp + (ptr - filename), ":%s", infopath);
- free (infopath);
- infopath = temp;
- }
-
- /* Start with DIR or whatever was specified. */
- if (!get_node (filename, (nodenames[0] == NULL) ? "" : nodenames[0], false)
- && !get_node ((char *)NULL, (char *)NULL, true))
- {
- if (!*filename)
- strcpy (filename, "dir");
-
- fprintf (stderr, "%s: Cannot find \"%s\", anywhere along the\n",
- argv[0], filename);
- fprintf (stderr, "search path of \"%s\".\n", infopath);
-
- exit (1);
- }
-
- totally_inhibit_errors = 0;
-
- for (ind = 1 ; ind < nodenames_index ; ind++)
- get_node (filename, nodenames[ind], false);
-
- nodename = nodenames[nodenames_index > 0 ? nodenames_index - 1 : 0];
- if (!nodename)
- {
- nodename = (char *)xmalloc (NODENAME_LEN);
- *nodename = '\0';
- }
-
- if (optind != argc)
- {
- putchar ('\n');
-
- while (optind != argc)
- {
- if (!build_menu ())
- {
- display_error ("There is no menu in node \"%s\"",
- current_info_node );
- break;
- }
- else if (!find_menu_node (argv[optind], nodename))
- {
- display_error
- ("There is no menu entry for \"%s\" in node \"%s\"",
- argv[optind], nodename );
- break;
- }
- else if (!get_node ((char *)NULL, nodename, false))
- {
- break;
- }
- else
- {
- #if defined (NOTDEF)
- /* RMS says not to type this stuff out because he expects
- programs to call Info instead of interactive users. */
- printf ("%s.. ",argv[optind]);
- fflush (stdout);
- #endif
- optind++;
- }
- }
- }
- begin_info_session ();
- exit (0);
- }
-
- usage ()
- {
- fprintf (stderr,"%s\n%s\n%s\n%s\n",
- "Usage: info [-d dir-path] [-f info-file] [-n node-name -n node-name ...]",
- " [-o output-file] [+directory dir-path] [+file info-file]",
- " [+node node-name +node node-name ...]",
- " [+output output-file] [menu-selection...]");
- exit (1);
- }
-
- #if defined (SIGTSTP)
- Function *old_tstp;
- Function *old_ttou, *old_ttin;
- #endif /* SIGTSTP */
-
- #if defined (SIGWINCH)
- Function *old_winch;
- #endif /* SIGWINCH */
-
- /* Start using Info. */
- begin_info_session ()
- {
- int info_signal_handler ();
-
- /* If the user just wants to dump the node, then do that. */
- if (dumpfile[0])
- {
- dump_current_node (dumpfile);
- exit (0);
- }
-
- init_terminal_io ();
-
- /* Install handlers for restoring/breaking the screen. */
-
- install_signals ();
- new_echo_area ();
-
- print_string ("Welcome to Info! Type \"?\" for help. ");
- close_echo_area ();
- toploop ();
- goto_xy (the_window.left, the_window.bottom + 1);
- restore_io ();
- }
-
- /* What to do before processing a stop signal. */
- before_stop_signal ()
- {
- restore_io ();
- }
-
- /* What to do after processing a stop signal. */
- after_stop_signal ()
- {
- clear_screen ();
- display_page ();
- goto_xy (the_window.ch, the_window.cv);
- opsys_init_terminal ();
- }
-
- /* Do the right thing with this signal. */
- info_signal_handler (sig)
- int sig;
- {
- switch (sig)
- {
- #if defined (SIGTSTP)
- case SIGTSTP:
- case SIGTTOU:
- case SIGTTIN:
- before_stop_signal ();
- signal (sig, SIG_DFL);
- kill (getpid (), sig);
- after_stop_signal ();
- signal (sig, info_signal_handler);
- break;
- #endif /* SIGTSTP */
-
- #if defined (SIGWINCH)
- case SIGWINCH:
- /* Window has changed. Get the size of the new window, and rebuild our
- window chain. */
- {
- int display_page ();
- extern char *widest_line;
- extern WINDOW terminal_window;
- extern WINDOW_LIST *window_stack;
- extern int terminal_rows, terminal_columns;
- int delta_width, delta_height, right, bottom;
-
- right = get_terminal_columns ();
- bottom = get_terminal_rows ();
-
- delta_width = right - terminal_columns;
- delta_height = bottom - terminal_rows;
-
- terminal_columns = right;
- terminal_rows = bottom;
-
- /* Save current window, whatever it is. */
- push_window ();
-
- /* Change the value of the widest_line. */
- free (widest_line);
- widest_line = (char *) xmalloc (right);
-
- /* Make the new window. Map over all windows in window list. */
- {
- WINDOW_LIST *wind = window_stack;
- extern WINDOW modeline_window;
-
- while (wind != (WINDOW_LIST *)NULL)
- {
- adjust_wind ((WINDOW *)wind, delta_width, delta_height);
- wind = wind->next_window;
- }
-
- /* Adjust the other windows that we know about. */
- adjust_wind (&terminal_window, delta_width, delta_height);
- adjust_wind (&echo_area, delta_width, delta_height);
- adjust_wind (&modeline_window, delta_width, delta_height);
- }
-
- /* Clear and redisplay the entire terminal window. */
- set_window (&terminal_window);
- clear_screen ();
-
- /* Redisplay the contents of the screen. */
- with_output_to_window (&terminal_window, display_page);
-
- /* Get back the current window. */
- pop_window ();
- }
- break;
- #endif /* SIGWINCH */
-
- case SIGINT:
- restore_io ();
- exit (1);
- break;
- }
- }
-
- install_signals ()
- {
- #if defined (SIGTSTP)
- old_tstp = (Function *) signal (SIGTSTP, info_signal_handler);
- old_ttou = (Function *) signal (SIGTTOU, info_signal_handler);
- old_ttin = (Function *) signal (SIGTTIN, info_signal_handler);
- #endif /* SIGTSTP */
-
- #if defined (SIGWINCH)
- old_winch = (Function *) signal (SIGWINCH, info_signal_handler);
- #endif /* SIGWINCH */
-
- signal (SIGINT, info_signal_handler);
- }
-
- adjust_wind (wind, delta_width, delta_height)
- WINDOW *wind;
- int delta_width, delta_height;
- {
- wind->right += delta_width;
- wind->bottom += delta_height;
- wind->ch += delta_width;
- wind->cv += delta_height;
-
- /* Ugly hack to fix busted windows code. If the window we are adjusting
- already has a TOP offset, then adjust that also. */
- if (wind->top)
- wind->top += delta_height;
- }
-
- /* **************************************************************** */
- /* */
- /* Completing Things */
- /* */
- /* **************************************************************** */
-
- typedef struct completion_entry
- {
- char *identifier;
- char *data;
- struct completion_entry *next;
- } COMP_ENTRY;
-
- /* The linked list of COMP_ENTRY structures that you create. */
- COMP_ENTRY *completion_list = (COMP_ENTRY *) NULL;
-
- /* The vector of COMP_ENTRY pointers that COMPLETE returns. */
- COMP_ENTRY **completions = NULL;
-
- /* The number of elements in the above vector. */
- int completion_count;
-
- /* Initial size of COMPLETIONS array. */
- #define INITIAL_COMPLETIONS_CORE_SIZE 200
-
- /* Current size of the completion array in core. */
- int completions_core_size = 0;
-
- /* Ease the typing task. Another name for the I'th
- IDENTIFIER of COMPLETIONS. */
- #define completion_id(i) ((completions[(i)])->identifier)
-
- /* The number of completions that can be present before the help
- function starts asking you about whether it should print them
- all or not. */
- int completion_query_threshold = 100;
-
- free_completion_list ()
- {
- COMP_ENTRY *temp;
- while (completion_list)
- {
- temp = completion_list;
-
- if (completion_list->identifier)
- free (completion_list->identifier);
-
- if (completion_list->data)
- free (completion_list->data);
-
- completion_list = completion_list->next;
- free (temp);
- }
- }
-
- /* Add a single completion to COMPLETION_LIST.
- IDENTIFIER is the string that the user should type.
- DATA should just be a pointer to some random data that you wish to
- have associated with the identifier, but I'm too stupid for that, so
- it must be a string as well. This allocates the space for the strings
- so you don't necessarily have to. */
- add_completion (identifier, data)
- char *identifier, *data;
- {
- COMP_ENTRY *temp = (COMP_ENTRY *) xmalloc (sizeof (COMP_ENTRY));
-
- temp->identifier = (char *) xmalloc (strlen (identifier) + 1);
- strcpy (temp->identifier, identifier);
-
- temp->data = (char *) xmalloc (strlen (data) + 1);
- strcpy (temp->data, data);
-
- temp->next = completion_list;
- completion_list = temp;
- }
-
- /* Function for reading a line. Supports completion on COMPLETION_LIST
- if you pass COMPLETING as true. Prompt is either a prompt or NULL,
- LINE is the place to store the characters that are read. LINE may start
- out already containing some characters; if so, they are printed. MAXCHARS
- tells how many characters can fit in the buffer at LINE. READLINE returns
- FALSE if the user types the abort character. LINE is returned with a '\0'
- at the end, not a '\n'. */
- boolean
- readline (prompt, line, maxchars, completing)
- char *prompt, *line;
- int maxchars;
- boolean completing;
- {
- int character;
- int readline_ch, readline_cv;
- int current_len = strlen (line);
- int just_completed = 0; /* Have we just done a completion? */
- int meta_flag = 0;
-
- new_echo_area ();
-
- if (prompt)
- print_string (prompt);
-
- readline_ch = the_window.ch;
- readline_cv = the_window.cv;
-
- print_string (line);
-
- while (true)
- {
- line[current_len] = '\0';
- goto_xy (readline_ch, readline_cv);
- print_string (line);
- clear_eol ();
-
- if (just_completed)
- just_completed--;
-
- character = blink_cursor ();
- if (meta_flag)
- {
- character = META (character);
- meta_flag = 0;
- }
-
- if (META_P (character))
- character = META (to_upper (UNMETA (character)));
-
- switch (character)
- {
- case EOF:
- character = '\n';
-
- case ESC:
- meta_flag++;
- break;
-
- case META (DELETE):
- case CTRL ('W'):
- while (current_len && line[current_len] == SPACE)
- current_len--;
-
- if (!current_len)
- break;
-
- while (current_len && line[current_len] != SPACE)
- current_len--;
-
- break;
-
- case CTRL ('U'):
- current_len = 0;
- break;
-
- case '\b':
- case 0x07f:
- if (current_len)
- current_len--;
- else
- ding ();
- break;
-
- case '\n':
- case '\r':
- if (completing)
- {
- extern int completion_count;
-
- try_complete (line);
-
- if (completion_count >= 1)
- {
- close_echo_area ();
- return (true);
- }
- else
- {
- current_len = strlen (line);
- break;
- }
- }
- else
- {
- close_echo_area ();
- return (true);
- }
-
- case ABORT_CHAR:
- ding ();
-
- if (current_len)
- {
- current_len = 0;
- }
- else
- {
- close_echo_area ();
- clear_echo_area ();
- return (false);
- }
- break;
-
- case ' ':
- case '\t':
- case '?':
- if (completing)
- {
- extern int completion_count;
-
- if (character == '?' || just_completed)
- {
- help_possible_completions (line);
- break;
- }
- else
- {
- char temp_line[NODENAME_LEN];
- strcpy (temp_line, line);
- try_complete (line); just_completed = 2;
- if (completion_count != 1 && character == SPACE)
- {
- if (strcmp (temp_line, line) == 0)
- {
- line[current_len] = SPACE;
- line[current_len + 1] = '\0';
- strcpy (temp_line, line);
- try_complete (line);
- if (completion_count == 0)
- {
- line[current_len] = '\0';
- ding ();
- }
- }
- }
- current_len = strlen (line);
- if (completion_count == 0)
- ding ();
- break;
- }
- }
- /* Do *NOT* put anything in-between the completing cases and
- the default: case. No. Because the SPC, TAB and `?' get
- treated as normal characters by falling through the
- "if (completing)" test above. */
- default:
- if (!CTRL_P (character) &&
- !META_P (character) &&
- current_len < maxchars)
- line[current_len++] = character;
- else
- ding ();
- }
- }
- }
-
- /* Initialize whatever the completer is using. */
- init_completer ()
- {
- if (completions_core_size != INITIAL_COMPLETIONS_CORE_SIZE)
- {
- if (completions)
- free (completions);
-
- completions = (COMP_ENTRY **)
- xmalloc ((sizeof (COMP_ENTRY *))
- * (completions_core_size = INITIAL_COMPLETIONS_CORE_SIZE));
- }
- completion_count = 0;
- }
-
- /* Reverse the completion list passed in LIST, and
- return a pointer to the new head. */
- COMP_ENTRY *
- reverse_list (list)
- COMP_ENTRY *list;
- {
- COMP_ENTRY *next;
- COMP_ENTRY *prev = (COMP_ENTRY *) NULL;
-
- while (list)
- {
- next = list->next;
- list->next = prev;
- prev = list;
- list = next;
- }
- return (prev);
- }
-
- /* Remember the possible completion passed in POINTER on the
- completions list. */
- remember_completion (pointer)
- COMP_ENTRY *pointer;
- {
- if (completion_count == completions_core_size)
- {
- COMP_ENTRY **temp = (COMP_ENTRY **)
- realloc (completions, ((sizeof (COMP_ENTRY *))
- * (completions_core_size +=
- INITIAL_COMPLETIONS_CORE_SIZE)));
- if (!temp)
- {
- display_error ("Too many completions (~d)! Out of core!",
- completion_count);
- return;
- }
- else
- completions = temp;
- }
- completions[completion_count++] = pointer;
- }
-
- /* Complete TEXT from identifiers in LIST. Place the resultant
- completions in COMPLETIONS, and the number of completions in
- COMPLETION_COUNT. Modify TEXT to contain the least common
- denominator of all the completions found. */
- boolean
- complete (text, list)
- char *text;
- COMP_ENTRY *list;
- {
- int low_match, i, idx;
- int string_length = strlen (text);
-
- init_completer ();
- low_match = 100000; /* Some large number. */
-
- while (list)
- {
- if (strnicmp (text, list->identifier, string_length) == 0)
- remember_completion (list);
- list = list->next;
- }
-
- if (completion_count == 0)
- return (false);
-
- if (completion_count == 1)
- { /* One completion */
- strcpy (text, completion_id (0));
- return (true);
- }
-
- /* Else find the least common denominator */
-
- idx = 1;
-
- while (idx < completion_count)
- {
- int c1, c2;
- for (i = 0;
- (c1 = to_lower (completion_id (idx - 1)[i])) &&
- (c2 = to_lower (completion_id (idx)[i]));
- i++)
- if (c1 != c2)
- break;
-
- if (low_match > i)
- low_match = i;
- idx++;
- }
-
- strncpy (text, completion_id (0), low_match);
- text[low_match] = '\0';
- return (true);
- }
-
- /* Complete TEXT from the completion structures in COMPLETION_LIST. */
- boolean
- try_complete (text)
- char *text;
- {
- return (complete (text, completion_list));
- }
-
- /* The function that prints out the possible completions. */
- help_possible_completions (text)
- char *text;
- {
- char temp_string[2000];
-
- goto_xy (the_window.left, the_window.top);
- strcpy (temp_string, text);
- try_complete (temp_string);
-
- open_typeout ();
-
- if (completion_count == 0)
- {
- print_string ("There are no possible completions.\n");
- goto print_done;
- }
-
- if (completion_count == 1)
- {
- print_string
- ("The only possible completion of what you have typed is:\n\n");
- print_string (completion_id(0));
- goto print_done;
- }
-
- if (completion_count >= completion_query_threshold)
- {
- print_string
- ("\nThere are %d completions. Do you really want to see them all",
- completion_count);
-
- if (!get_y_or_n_p ())
- return;
- }
-
- print_string ("\nThe %d completions of what you have typed are:\n\n",
- completion_count);
-
- {
- int idx = 0;
- int counter = 0;
- int columns = (the_window.right - the_window.left) / 30;
-
- while (idx < completion_count)
- {
- if (counter == columns)
- {
- charout ('\n');
- counter = 0;
- }
- indent_to (counter * 30);
- print_string (completion_id (idx));
- counter++;
- idx++;
- }
- }
-
- print_done:
- print_string ("\n\n-----------------\n");
- close_typeout ();
- }
-
- /* Return the next file that should be searched, or NULL if we are at the end
- of the info file. If the FILE argument is provided, begin the search there,
- if REWIND is true start the search at the beginning of the list.
-
- The list is the one built by an indirect tag table, on the supposition
- that those files form a logical set to search if we are in one of them.
- If no such list is current (either it doesn't exist, or FILE isn't on
- it) the search list is set to be last_loaded_info_file */
- char *
- next_info_file (file, rewind)
- char *file; /* file to set `next' to. May be NULL. */
- boolean rewind; /* should I rewind the file list? */
- {
- static int index = -1;
-
- if (file != NULL)
- {
- char *ptr = rindex (file,'/');
-
- if (ptr != NULL)
- file = ptr + 1;
-
- for (index = 0;
- index < MAX_INDIRECT_FILES &&
- indirect_list[index].filename != (char *)NULL;
- index++)
- {
- if (strcmp (file, indirect_list[index].filename) == 0)
- return (file);
- }
-
- /* OK, we are not on the current indirect_list. This means that
- we have switched to another node that has no indirect list,
- so forget the old one. */
- for (index = 0;
- index < MAX_INDIRECT_FILES &&
- indirect_list[index].filename != (char *)NULL;
- index++)
- {
- free (indirect_list[index].filename);
- indirect_list[index].filename = (char *)NULL;
- }
- return (indirect_list[0].filename = savestring (file));
- }
- else if (rewind)
- {
- index = 0;
-
- if (indirect_list[0].filename == (char *)NULL)
- indirect_list[0].filename = savestring (last_loaded_info_file);
- }
- else
- index++;
-
- if (index < MAX_INDIRECT_FILES &&
- indirect_list[index].filename != (char *)NULL)
- return (indirect_list[index].filename);
-
- index = -1;
- return (NULL);
- }
-
- /* **************************************************************** */
- /* */
- /* Getting Nodes */
- /* */
- /* **************************************************************** */
-
- /* A node name looks like:
- Node: nodename with spaces but not a comma,
- or Node: (filename-containing-node)node-within-file
- or Node: (filename)
-
- The latter case implies a nodename of "Top". All files are
- supposed to have one.
-
- Lastly, the nodename specified could be "*", which specifies the
- entire file. */
-
- /* Return the directory portion of FILENAME, i.e., everything before the
- last slash. */
- static char *
- file_directory (filename)
- char *filename;
- {
- register char *scan;
- register int length;
- char *result;
-
- scan = filename;
-
- while (*scan++ != '\0');
-
- while (1)
- {
- if (scan == filename)
- break;
-
- if ((*--scan) == '/')
- {
- scan++;
- break;
- }
- }
-
- length = (scan - filename);
- result = (xmalloc (length + 1));
- strncpy (result, filename, length);
- result[length] = '\0';
-
- return (result);
- }
-
- /* Given FILENAME and DIRECTORY return a newly allocated string which
- is either the two concatenated, or simply FILENAME if it is absolute
- already. */
- static char *
- file_absolutize (filename, directory)
- char *filename, *directory;
- {
- register int filename_len, directory_len;
- char *result;
-
- if (filename[0] == '/')
- return (savestring (filename));
-
- filename_len = strlen (filename);
- directory_len = strlen (directory);
- result = xmalloc (directory_len + filename_len + 1);
-
- strcpy (result, directory);
- strcat (result, filename);
-
- return (result);
- }
-
- /* Load FILENAME. If REMEMBER_NAME is true, then remember the
- loaded filename in CURRENT_INFO_FILE. In either case, remember
- the name of this file in LAST_LOADED_INFO_FILE. */
- boolean
- get_info_file (filename, remember_name)
- char *filename;
- boolean remember_name;
- {
- FILE *input_stream;
- struct stat file_info;
- int pointer, result;
- char tempname[FILENAME_LEN];
-
- /* Get real filename. */
- strcpy (tempname, opsys_filename (filename));
-
- /* See if the file exists. */
- if ((result = stat (tempname, &file_info)) != 0)
- {
- /* Try again, this time with the name in lower-case. */
- char temp_tempname[FILENAME_LEN];
- int i;
-
- for (i = 0; temp_tempname[i] = to_lower (tempname[i]); i++);
- strcpy (temp_tempname, opsys_filename (temp_tempname));
-
- result = stat (temp_tempname, &file_info);
- if (!result)
- strcpy (tempname, temp_tempname);
- }
-
- /* See if this file is the last loaded one. */
- if (!result && (strcmp (last_loaded_info_file, tempname) == 0))
- return (true);
-
- /* Now try to open the file. */
- if (result || (input_stream = fopen (tempname, "r")) == NULL)
- {
- file_error (tempname);
- return (false);
- }
-
- /* If we already have a file loaded, then free it first. */
- if (info_file)
- {
- free (info_file);
-
- if (!indirect)
- {
- /* Then the tag table is also no longer valid. */
- tag_table = (char *) NULL;
- }
- }
-
- /* Read the contents of the file into a new buffer. */
-
- info_file = (char *) xmalloc (info_buffer_len = file_info.st_size);
- fread (info_file, 1, info_buffer_len, input_stream);
- fclose (input_stream);
- strcpy (last_loaded_info_file, tempname);
- if (remember_name)
- {
- strcpy (current_info_file, tempname);
- if (indirect)
- {
- int idx;
- indirect = false;
- free (tag_table);
- }
- }
- else
- return (true);
-
- /* The file has been read, and we don't know anything about it.
- Find out if it contains a tag table. */
-
- tag_table = NULL; /* assume none. */
- indirect = false;
- tag_buffer_len = 0;
-
- set_search_constraints (info_file, info_buffer_len);
-
- /* Go to the last few lines in the file. */
- pointer = back_lines (8, info_buffer_len);
- pointer = search_forward (TAG_TABLE_END_STRING, pointer);
-
- if (pointer > -1)
- {
- /* Then there is a tag table. Find the start of it,
- and remember that. */
- pointer = search_backward (TAG_TABLE_BEG_STRING, pointer);
-
- /* Handle error for malformed info file. */
- if (pointer < 0)
- display_error ("Start of tag table not found!");
- else
- {
- /* No problem. If this file is an indirect file, then the contents
- of the tag table must remain in RAM the entire time. Otherwise,
- we can flush the tag table with the file when the file is flushed.
- So, if indirect, remember that, and copy the table to another
- place.*/
-
- int indirect_check = forward_lines (2, pointer);
-
- tag_table = info_file + pointer;
- tag_buffer_len = info_buffer_len - pointer;
-
- /* Shorten the search constraints. */
- info_buffer_len = pointer;
-
- if (looking_at ("(Indirect)\n", indirect_check))
- {
- /* We have to find the start of the indirect file's
- information. */
- tag_table = (char *) xmalloc (tag_buffer_len);
-
- bcopy (&info_file[indirect_check], tag_table, tag_buffer_len);
-
- /* Find the list of filenames. */
- indirect_top = search_backward ("Indirect:\n", indirect_check);
- if (indirect_top < 0)
- {
- free (tag_table);
- tag_table = (char *) NULL;
- display_error ("Start of INDIRECT tag table not found!");
- return (false);
- }
-
- /* Remember the filenames, and their byte offsets. */
- {
- /* Index into the filename/offsets array. */
- int idx, temp_first_byte;
- char temp_filename[FILENAME_LEN];
- char *directory = file_directory (tempname);
-
- info_buffer_len = indirect_top;
-
- /* For each line, scan the info into globals. Then save
- the information in the INDIRECT_INFO structure. */
-
- for (idx = 0; idx < MAX_INDIRECT_FILES &&
- indirect_list[idx].filename != (char *) NULL;
- idx++)
- {
- free (indirect_list[idx].filename);
- indirect_list[idx].filename = (char *) NULL;
- }
-
- for (idx = 0;info_file[indirect_top] != info_separator_char &&
- idx < MAX_INDIRECT_FILES;)
- {
- indirect_top = forward_lines (1, indirect_top);
- if (info_file[indirect_top] == info_separator_char)
- break;
-
- /* Ignore blank lines. */
- if (info_file[indirect_top] == '\n')
- continue;
-
- sscanf (&info_file[indirect_top], "%s%d",
- temp_filename, &temp_first_byte);
-
- if (strlen (temp_filename))
- {
- temp_filename[strlen (temp_filename) - 1] = '\0';
- indirect_list[idx].filename =
- file_absolutize (temp_filename, directory);
- indirect_list[idx].first_byte = temp_first_byte;
- idx++;
- }
- }
-
- free (directory);
-
- /* Terminate the table. */
- if (idx == MAX_INDIRECT_FILES)
- {
- display_error
- ("Sorry, the INDIRECT file array isn't large enough.");
- idx--;
- }
- indirect_list[idx].filename = (char *) NULL;
- }
- indirect = true;
- } else {
- ;
- }
- }
- }
- return (true);
- }
-
- /* Make current_info_node be NODENAME. This could involve loading
- a file, etc. POPPING is true if we got here because we are popping
- one level. */
- boolean
- get_node (filename, nodename, popping)
- char *nodename, *filename;
- boolean popping;
- {
- int pointer;
- char internal_filename[FILENAME_LEN];
- char internal_nodename[NODENAME_LEN];
-
- if (nodename && *nodename)
- {
- /* Maybe nodename looks like: (filename)nodename, or worse: (filename).
- If so, extract the stuff out. */
- if (*nodename == '(')
- {
- int temp = 1, temp1 = 0;
- char character;
-
- filename = internal_filename;
-
- while ((character = nodename[temp]) && character != ')')
- {
- filename[temp - 1] = character;
- temp++;
- }
- filename[temp - 1] = '\0';
- temp++; /* skip the closing ')' */
-
- /* We have the filename now. The nodename follows. */
- internal_nodename[0] = '\0';
-
- while (nodename[temp] == ' ' ||
- nodename[temp] == '\t' ||
- nodename[temp] == '\n')
- temp++;
-
- if (nodename[temp])
- while (internal_nodename[temp1++] = nodename[temp++])
- ;
- else if (*filename != '\0')
- strcpy (internal_nodename,"Top");
-
- nodename = internal_nodename;
- }
- }
-
- if (!popping)
- push_node (current_info_file, current_info_node, pagetop, nodetop);
-
- if (!nodename || !*nodename)
- {
- nodename = internal_nodename;
- strcpy (nodename, "Top");
- }
-
- if (!filename || !*filename)
- {
- filename = internal_filename;
- strcpy (filename, current_info_file);
- }
-
- if (!*filename)
- strcpy (filename, "dir");
-
- if (!get_info_file (filename, true))
- goto node_not_found;
-
- if (strcmp (nodename, "*") == 0)
- {
- /* The "node" that we want is the entire file. */
- pointer = 0;
- goto found_node;
- }
-
- /* If we are using a tag table, see if we can find the nodename in it. */
- if (tag_table)
- {
- pointer = find_node_in_tag_table (nodename, 0);
- if (pointer < 1)
- {
- boolean pop_node ();
-
- /* The search through the tag table failed. Maybe we
- should try searching the buffer? Nahh, just barf. */
- node_not_found:
- if (popping)
- return (false); /* Second time through. */
-
- {
- int save_inhibit = totally_inhibit_errors;
-
- totally_inhibit_errors = 0;
- display_error
- ("Sorry, unable to find the node \"%s\" in the file \"%s\".",
- nodename, filename);
- totally_inhibit_errors = save_inhibit;
- }
-
- current_info_file[0] = '\0';
- current_info_node[0] = '\0';
- last_loaded_info_file[0] = '\0';
- pop_node (internal_filename, internal_nodename, &nodetop, &pagetop);
- get_node (internal_filename, internal_nodename, true);
- return (false);
- }
-
- /* Otherwise, the desired location is right here.
- Scarf the position byte. */
- while (tag_table[pointer] != '\177')
- pointer++;
-
- sscanf (&tag_table[pointer + 1], "%d", &pointer);
-
- /* Okay, we have a position pointer. If this is an indirect file,
- then we should look through the indirect_list for the first
- element.first_byte which is larger than this. Then we can load
- the specified file, and win. */
- if (indirect)
- {
- /* Find the filename for this node. */
- int idx;
- for (idx = 0; idx < MAX_INDIRECT_FILES &&
- indirect_list[idx].filename != (char *) NULL; idx++)
- {
- if (indirect_list[idx].first_byte > pointer)
- {
- /* We found it. */
- break;
- }
- }
- if (!get_info_file (indirect_list[idx - 1].filename, true))
- goto node_not_found;
- pointer -= indirect_list[idx - 1].first_byte;
-
- /* Here is code to compensate for the header of an indirect file. */
- {
- int tt = find_node_start (0);
- if (tt > -1)
- pointer += tt;
- }
- }
- else
- {
- /* This tag table is *not* indirect. The filename of the file
- containing this node is the same as the current file. The
- line probably looks like:
- File: info, Node: Checking25796 */
- }
- }
- else
- {
- /* We don't have a tag table. The node can only be found by
- searching this file in its entirety. */
- get_info_file (filename, true);
- pointer = 0;
- }
-
- /* Search this file, using pointer as a good guess where to start. */
- /* This is the same number that RMS used. It might be right or wrong. */
- pointer -= 1000;
- if (pointer < 0)
- pointer = 0;
-
- pointer = find_node_in_file (nodename, pointer);
- if (pointer < 0)
- goto node_not_found;
-
- /* We found the node in it's file. Remember exciting information. */
-
- found_node:
- back_lines (0, pointer);
- nodetop = pagetop = pointer;
- strcpy (current_info_node, nodename);
- strcpy (current_info_file, filename);
- get_node_extent ();
- return (true);
- }
-
- /* Get the bounds for this node. NODETOP points to the start of the
- node. Scan forward looking for info_separator_char, and remember
- that in NODEBOT. */
- get_node_extent ()
- {
- int idx = nodetop;
- int character;
- boolean do_it_till_end = (strcmp (current_info_node, "*") == 0);
-
- nodelines = 0;
-
- again:
- while ((idx < info_buffer_len) &&
- ((character = info_file[idx]) != info_separator_char))
- {
- if (character == '\n')
- nodelines++;
- idx++;
- }
- if (do_it_till_end && idx != info_buffer_len)
- {
- idx++;
- goto again;
- }
- nodebot = idx;
- }
-
- /* Locate the start of a node in the current search_buffer. Return
- the offset to the node start, or minus one. START is the place in
- the file at where to begin the search. */
- find_node_start (start)
- int start;
- {
- return (search_forward (start_of_node_string, start));
- }
-
- /* Find NODENAME in TAG_TABLE. */
- find_node_in_tag_table (nodename, offset)
- char *nodename;
- int offset;
- {
- int temp;
-
- set_search_constraints (tag_table, tag_buffer_len);
-
- temp = offset;
- while (true)
- {
- offset = search_forward (NODE_ID, temp);
-
- if (offset < 0)
- return (offset);
-
- temp = skip_whitespace (offset + strlen (NODE_ID));
-
- if (strnicmp (tag_table + temp, nodename, strlen (nodename)) == 0)
- if (*(tag_table + temp + strlen (nodename)) == '\177')
- return (temp);
- }
- }
-
- /* Find NODENAME in INFO_FILE. */
- find_node_in_file (nodename, offset)
- char *nodename;
- int offset;
- {
- int temp, last_offset = -1;
-
- set_search_constraints (info_file, info_buffer_len);
-
- while (true)
- {
- offset = find_node_start (offset);
-
- if (offset == last_offset)
- offset = -1;
- else
- last_offset = offset;
-
- if (offset < 0)
- return (offset);
- else
- temp = forward_lines (1, offset);
-
- if (temp == offset)
- return (-1); /* At last line now, just a node start. */
- else
- offset = temp;
-
- temp = string_in_line (NODE_ID, offset);
-
- if (temp > -1)
- {
- temp = skip_whitespace (temp + strlen (NODE_ID));
- if (strnicmp (info_file + temp, nodename, strlen (nodename)) == 0)
- {
- int check_exact = *(info_file + temp + strlen (nodename));
-
- if (check_exact == '\t' ||
- check_exact == ',' ||
- check_exact == '.' ||
- check_exact == '\n')
- return (offset);
- }
- }
- }
- }
-
-
- /* **************************************************************** */
- /* */
- /* Dumping and Printing Nodes */
- /* */
- /* **************************************************************** */
-
- /* Make a temporary filename based on STARTER and the PID of this Info. */
- char *
- make_temp_filename (starter)
- char *starter;
- {
- register int i;
- char *temp;
-
- temp = (char *)xmalloc (strlen (starter) + 10);
- sprintf (temp, "%s-%d", starter, getpid ());
-
- for (i = 0; temp[i]; i++)
- if (!isalnum (temp[i]))
- temp[i] == '-';
-
- return (temp);
- }
-
- /* Delete a file. Print errors if necessary. */
- deletefile (filename)
- char *filename;
- {
- if (unlink (filename) != 0)
- {
- file_error (filename);
- return (1);
- }
- return (0);
- }
-
- printfile (filename)
- char *filename;
- {
- int length = strlen (print_command) + strlen (filename) + strlen ("\n") + 1;
- char *command = (char *) xmalloc (length);
- int error;
-
- display_error ("Printing file `%s'...\n", filename);
- sprintf (command, "%s %s", print_command, filename);
- error = system (command);
- if (error)
- display_error ("Can't invoke `%s'", command);
- free (command);
- return (error);
- }
-
- /* Dump the current node into a file named FILENAME.
- Return 0 if the dump was successful, otherwise,
- print error and exit. */
- dump_current_node (filename)
- char *filename;
- {
- int i = nodetop;
- int c;
- FILE *output_stream = fopen (filename, "w");
- if (output_stream == (FILE *) NULL)
- {
- file_error (filename);
- return (1);
- }
-
- while (i < nodebot && i < info_buffer_len)
- {
- c = info_file[i];
- if (c < 0x020 && !(index ("\n\t\f", c)))
- {
- putc ('^', output_stream);
- }
- if (putc (info_file[i++], output_stream) == EOF)
- {
- fclose (output_stream);
- file_error (filename);
- return (1);
- }
- }
- fclose (output_stream);
- return (0);
- }
-
- /* **************************************************************** */
- /* */
- /* Toplevel eval loop. */
- /* */
- /* **************************************************************** */
-
- #define MENU_HEADER "\n* Menu:"
- #define MENU_ID "\n* "
- #define FOOTNOTE_HEADER "*Note"
-
- /* Number of items that the current menu has. */
- int the_menu_size = 0;
-
- /* The node that last made a menus completion list. */
- char menus_nodename[NODENAME_LEN];
- char menus_filename[NODENAME_LEN];
-
- boolean window_bashed = false;
- static int search_start = 0;
-
- /* The default prompt string for the Follow Reference command. */
- char *visible_footnote = (char *)NULL;
- toploop ()
- {
- boolean done = false;
- boolean inhibit_display = false;
- int command = '\0', last_command = '\0';
- int last_pointer, count, new_ypos, last_pagetop;
- char nodename[NODENAME_LEN];
-
- new_ypos = last_pagetop = -1;
-
- while (!done)
- {
- if (!inhibit_display &&
- (window_bashed || (pagetop != last_pagetop)))
- display_page ();
-
- inhibit_display = window_bashed = false;
- last_pagetop = pagetop;
-
- nodename[0] = '\0'; /* Don't display old text in input line. */
-
- last_command = command;
-
- if (last_command == 'S')
- cursor_to (search_start);
- else
- goto_xy (echo_area.left, echo_area.top);
-
- command = blink_cursor ();
- clear_echo_area ();
-
- if (command == EOF)
- {
- done = true;
- continue;
- }
- command = to_upper (command);
-
- switch (command)
- {
- case 'D':
- get_node ((char *) NULL, "(dir)Top", false);
- break;
-
- case 'H':
- if ((the_window.bottom - the_window.top) < 24)
- get_node ((char *) NULL, "(info)Help-Small-Screen", false);
- else
- get_node ((char *) NULL, "(info)Help", false);
- break;
-
- case 'N':
- if (!next_node ())
- {
- display_error ("No NEXT for this node!");
- inhibit_display = true;
- }
- break;
-
- case 'P':
- if (!prev_node ())
- {
- display_error ("No PREV for this node!");
- inhibit_display = true;
- }
- break;
-
- case 'U':
- {
- int savetop = pagetop;
-
- if (!up_node ())
- {
- display_error ("No UP for this node!");
- inhibit_display = true;
- pagetop = savetop;
- }
- break;
- }
-
- case 'M':
- if (!build_menu ())
- {
- display_error ("No menu in this node!");
- inhibit_display = true;
- break;
- }
-
- if (!readline ("Menu item: ", nodename, NODENAME_LEN, true))
- {
- clear_echo_area ();
- inhibit_display = true;
- break;
- }
-
- I_goto_xy (echo_area.left, echo_area.top);
- if (!find_menu_node (nodename, nodename))
- {
- display_error ("\"%s\" is not a menu item!", nodename);
- inhibit_display = true;
- break;
- }
-
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
- break;
-
- case 'F':
- {
- char footnote[NODENAME_LEN];
- if (!build_notes ())
- {
- display_error ("No cross-references in this node!");
- inhibit_display = true;
- break;
- }
- strcpy (footnote, visible_footnote);
- if (!readline ("Follow reference: ", footnote, NODENAME_LEN, true))
- {
- inhibit_display = true;
- break;
- }
-
- I_goto_xy (echo_area.left, echo_area.top);
- if (!find_note_node (footnote, nodename))
- {
- display_error ("\"%s\" is not a cross-reference in this node!",
- footnote);
- inhibit_display = true;
- break;
- }
-
- if (get_node ((char *)NULL, nodename, false))
- clear_echo_area ();
- break;
- }
-
- case 'L':
- {
- char filename[FILENAME_LEN], nodename[NODENAME_LEN];
- int ptop, ntop;
- if (pop_node (filename, nodename, &ntop, &ptop) &&
- get_node (filename, nodename, true))
- {
- pagetop = ptop;
- }
- else
- inhibit_display = true;
- break;
- }
-
- case SPACE:
- case CTRL ('V'):
- if (!next_page ())
- {
- display_error ("At last page of this node now!");
- inhibit_display = true;
- }
- break;
-
- case DELETE:
- if (!prev_page ())
- {
- display_error ("At first page of this node now!");
- inhibit_display = true;
- }
- break;
-
- case 'B':
- if (pagetop == nodetop)
- {
- display_error ("Already at beginning of this node!");
- inhibit_display = true;
- }
- else
- pagetop = nodetop;
- break;
-
- /* I don't want to do this this way, but the documentation
- clearly states that '6' doesn't work. It states this for a
- reason, and ours is not to wonder why... */
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- {
- int item = command - '0';
-
- if (!build_menu ())
- {
- display_error ("No menu in this node!");
- inhibit_display = true;
- break;
- }
-
- if (item > the_menu_size)
- {
- display_error ("There are only %d items in the menu!",
- the_menu_size);
- inhibit_display = true;
- break;
- }
-
- if (!get_menu (item))
- inhibit_display = true;
- }
- break;
-
- case 'G':
- if (!readline ("Goto node: ", nodename, NODENAME_LEN, false))
- {
- inhibit_display = true;
- break;
- }
-
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
- break;
-
- /* Search from the starting position forward for a string.
- Select the node containing the desired string. Put the
- top of the page screen_lines / 2 lines behind it, but not
- before nodetop. */
- case 'S':
- {
- extern int info_buffer_len;
- int pointer, temp;
- char prompt[21 + NODENAME_LEN + 1];
- static char search_string[NODENAME_LEN] = "";
- static char *starting_filename = NULL,
- *starting_nodename = NULL;
- static int starting_pagetop = 0;
- static boolean wrap_search = false;
-
- sprintf (prompt, "Search for string [%s]: ", search_string);
-
- if (!readline (prompt , nodename, NODENAME_LEN, false))
- {
- inhibit_display = true;
- break;
- }
-
- /* If the user defaulted the search string, and the previous
- command was search, then this is a continuation of the
- previous search. */
- if (((strcmp (nodename, search_string) == 0) ||
- (!*nodename && *search_string)) &&
- (last_command == 'S'))
- {
- search_start++;
- }
- else
- {
- /* Initialize the start of a new search. */
- if (starting_filename)
- free (starting_filename);
-
- starting_filename = savestring (last_loaded_info_file);
-
- if (starting_nodename)
- free (starting_nodename);
-
- starting_nodename = savestring (nodename ? nodename : "");
-
- starting_pagetop = pagetop;
- search_start = pagetop;
- wrap_search = false;
-
- if (*nodename != '\0')
- strcpy (search_string, nodename);
- }
-
- I_goto_xy (echo_area.left, echo_area.top);
-
- {
- static int pushed = 0; /* how many files are pushed? */
- boolean found_string = false; /* did we find our string? */
-
- if (wrap_search)
- {
- push_filestack (next_info_file ((char *)NULL, true), false);
- pushed++;
- search_start = 0;
- }
-
- for (;;)
- {
- set_search_constraints (info_file, info_buffer_len);
- pointer = search_forward (search_string, search_start);
-
- if (pointer != -1)
- {
- found_string = true;
- break;
- }
- else
- {
- char *next_file;
-
- next_file = next_info_file ((char *)NULL, false);
-
- if (next_file != NULL)
- {
- if (pushed)
- {
- pop_filestack ();
- pushed--;
- }
-
- push_filestack (next_file, false);
- pushed++;
- search_start = 0;
- #if 1
- I_goto_xy (echo_area.left, echo_area.top);
- print_string ("Searching file %s...\n", next_file);
- #endif
- continue;
- }
-
- if (wrap_search)
- {
- display_error
- ("\"%s\" not found!",
- search_string);
-
- inhibit_display = true;
- wrap_search = false;
-
- if (pushed)
- {
- pop_filestack ();
- pushed--;
- }
- break;
- }
- else
- {
- display_error ("Search: End of file");
- inhibit_display = true;
- wrap_search = true;
-
- if (pushed)
- {
- pop_filestack ();
- pushed--;
- }
- break;
- }
- }
- }
-
- if (pushed)
- {
- swap_filestack ();
- pop_filestack ();
- pushed--;
- }
-
- if (!found_string)
- break;
-
- wrap_search = false;
- temp = search_backward (start_of_node_string, pointer);
-
- if (temp == -1)
- temp = search_forward (start_of_node_string, pointer);
-
- if (temp == -1)
- {
- brians_error ();
- break;
- }
-
- search_start = pointer;
- pointer = forward_lines (1, temp);
-
- if (!extract_field ("Node:", nodename, pointer))
- {
- display_error
- ("There doesn't appear to be a nodename for this node.");
- get_node ((char *)NULL, "*", false);
- pagetop = pointer;
- break;
- }
-
- /* Get the node if it is different than the one already
- loaded. */
- if (strcmp (nodename, starting_nodename) != 0)
- {
- free (starting_nodename);
- starting_nodename = savestring (nodename);
-
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
- }
-
- /* Reset the top of page if necessary. */
- {
- if ((strcmp (last_loaded_info_file, starting_filename) != 0) ||
- (starting_pagetop != pagetop) ||
- (search_start > pagebot))
- {
- pointer =
- back_lines ((the_window.bottom - the_window.top) / 2,
- forward_lines (1, search_start));
-
- if (pointer < nodetop)
- pointer = nodetop;
-
- pagetop = pointer;
- window_bashed = true;
- }
- else
- inhibit_display = true;
- }
- break;
- }
- }
-
- case CTRL ('H'):
- case '?':
- help_use_info ();
- last_pagetop = -1;
- break;
-
- case 'Q':
- done = true;
- break;
-
- case CTRL ('L'): /* Control-l is redisplay. */
- window_bashed = true;
- if (last_command == 'S')
- command = 'S';
- break;
-
- case '(': /* You *must* be trying to type a complete nodename. */
- strcpy (nodename, "(");
- if (!readline ("Goto node: ", nodename, NODENAME_LEN, false))
- {
- inhibit_display = true;
- clear_echo_area ();
- break;
- }
- I_goto_xy (echo_area.left, echo_area.top);
- if (get_node ((char *) NULL, nodename, false))
- clear_echo_area ();
- break;
-
- case CTRL ('P'):
- /* Print the contents of this node on the default printer. We
- would like to let the user specify the printer, but we don't
- want to ask her each time which printer to use. Besides, he
- might not know, which is why it (the user) is in the need of
- Info. */
- {
- char *tempname = make_temp_filename (current_info_node);
- if (dump_current_node (tempname) == 0 &&
- printfile (tempname) == 0 &&
- deletefile (tempname) == 0)
- {
- display_error ("Printed node. Go pick up your output.\n");
- }
- inhibit_display = true;
- free (tempname);
- }
- break;
-
- default:
- inhibit_display = true;
- display_error ("Unknown command! Press '?' for help.");
-
- }
- }
- }
-
- /* Return the screen column width that the line from START to END
- requires to display. */
- line_length (start, end)
- int start, end;
- {
- int count = 0;
-
- while (start < end)
- {
- if (info_file[start] == '\t')
- count += 7 - (count % 8);
- else if (CTRL_P (info_file[start]))
- count += 2;
- else
- count++;
-
- start++;
- }
-
- return (count);
- }
-
- /* Tell this person how to use Info. */
- help_use_info ()
- {
- open_typeout ();
- clear_screen ();
- print_string ("\n\
- Commands in Info\n\
- \n\
- h Invoke the Info tutorial.\n\
- \n\
- Selecting other nodes:\n\
- n Move to the \"next\" node of this node.\n\
- p Move to the \"previous\" node of this node.\n\
- u Move \"up\" from this node.\n\
- m Pick menu item specified by name.\n\
- Picking a menu item causes another node to be selected.\n\
- f Follow a cross reference. Reads name of reference.\n\
- l Move to the last node you were at.\n\
- \n\
- Moving within a node:\n\
- Space Scroll forward a page.\n\
- DEL Scroll backward a page.\n\
- b Go to the beginning of this node.\n\
- \n\
- Advanced commands:\n\
- q Quit Info.\n\
- 1 Pick first item in node's menu.\n\
- 2 - 5 Pick second ... fifth item in node's menu.\n\
- g Move to node specified by name.\n\
- You may include a filename as well, as (FILENAME)NODENAME.\n\
- s Search through this Info file for a specified string,\n\
- and select the node in which the next occurrence is found.\n\
- Ctl-p Print the contents of this node using `%s'.\n\
- \n\
- Done.\n\n",print_command);
- close_typeout ();
- }
-
- /* Move to the node specified in the NEXT field. */
- boolean
- next_node ()
- {
- char nodename[NODENAME_LEN];
-
- if (!extract_field ("Next:", nodename, nodetop))
- return (false);
- return (get_node ((char *) NULL, nodename, false));
- }
-
- /* Move to the node specified in the PREVIOUS field. */
- boolean
- prev_node ()
- {
- char nodename[NODENAME_LEN];
-
- if (!extract_field ("Previous:", nodename, nodetop)
- && !extract_field ("Prev:", nodename, nodetop))
- return (false);
- return (get_node ((char *) NULL, nodename, false));
- }
-
- /* Move to the node specified in the UP field. */
- boolean
- up_node ()
- {
- char nodename[NODENAME_LEN];
-
- if (!extract_field ("Up:", nodename, nodetop))
- return (false);
- return (get_node ((char *) NULL, nodename, false));
- }
-
- /* Build a completion list of menuname/nodename for each
- line in this node that is a menu item. */
- boolean
- build_menu ()
- {
- int pointer = nodetop;
- char menuname[NODENAME_LEN];
- char nodename[NODENAME_LEN];
-
- if (strcmp (menus_nodename, current_info_node) == 0 &&
- strcmp (menus_filename, current_info_file) == 0)
- return (the_menu_size != 0);
-
- strcpy (menus_nodename, current_info_node);
- strcpy (menus_filename, current_info_file);
- free_completion_list ();
- the_menu_size = 0;
-
- set_search_constraints (info_file, nodebot);
- if ((pointer = search_forward (MENU_HEADER, nodetop)) < 0)
- return (false);
-
- /* There is a menu here. Look for members of it. */
- pointer += strlen (MENU_HEADER);
-
- while (true)
- {
- int idx;
-
- pointer = search_forward (MENU_ID, pointer);
- if (pointer < 0)
- break; /* No more menus in this node. */
-
- pointer = (skip_whitespace (pointer + strlen (MENU_ID)));
-
- idx = 0;
- while ((menuname[idx] = info_file[pointer]) && menuname[idx] != ':')
- idx++, pointer++;
-
- menuname[idx] = '\0';
- pointer++;
-
- if (info_file[pointer] == ':')
- {
- strcpy (nodename, menuname);
- }
- else
- {
- pointer = skip_whitespace (pointer);
- idx = 0;
- while ((nodename[idx] = info_file[pointer]) &&
- nodename[idx] != '\t' &&
- nodename[idx] != '.' &&
- nodename[idx] != ',')
- {
- idx++, pointer++;
- }
- nodename[idx] = '\0';
- }
- add_completion (menuname, nodename);
- the_menu_size++;
- }
- if (the_menu_size)
- completion_list = reverse_list (completion_list);
- return (the_menu_size != 0);
- }
-
- /* Select ITEMth item from a list built by build_menu (). */
- boolean
- get_menu (item)
- int item;
- {
- if (!build_menu ())
- return (false);
-
- if (item > the_menu_size)
- return (false);
- else
- {
- COMP_ENTRY *temp = completion_list;
-
- while (--item && temp)
- temp = temp->next;
-
- return (get_node ((char *) NULL, temp->data, false));
- }
- }
-
- /* Scan through the ?already? built menu list looking
- for STRING. If you find it, put the corresponding nodes
- name in NODENAME. */
- boolean
- find_menu_node (string, nodename)
- char *string, *nodename;
- {
- return (scan_list (string, nodename));
- }
-
- /* The work part of find_menu_node and find_note_node. */
- boolean
- scan_list (string, nodename)
- char *string, *nodename;
- {
- COMP_ENTRY *temp = completion_list;
-
- while (temp)
- {
- if (stricmp (string, temp->identifier, strlen (string)) == 0)
- {
- strcpy (nodename, temp->data);
- return (true);
- }
- temp = temp->next;
- }
- return (false);
- }
-
- /* Remove <CR> and whitespace from string, replacing them with
- only one space. Exception: <CR> at end of string disappears. */
- clean_up (string)
- char *string;
- {
- char *to = string;
-
- while (*to = *string++)
- {
- if (*to == '\n' || *to == ' ')
- {
- *to = ' ';
-
- while (*string == ' ' || *string == '\t')
- string++;
- }
- to++;
- }
- }
-
- /* Find a reference to "*Note". Return the offset of the start
- of that reference, or -1. */
- find_footnote_ref (from)
- int from;
- {
- while (true)
- {
- from = search_forward (FOOTNOTE_HEADER, from);
- if (from < 0)
- return (from);
- else
- from += strlen (FOOTNOTE_HEADER);
- if (info_file[from] == ' ' ||
- info_file[from] == '\n' ||
- info_file[from] == '\t')
- return (from);
- }
- }
-
- /* Build an array of (footnote.nodename) for each footnote in this node. */
- boolean
- build_notes ()
- {
- int pointer;
- char notename[NODENAME_LEN];
- char nodename[NODENAME_LEN];
-
- set_search_constraints (info_file, nodebot);
-
- if ((find_footnote_ref (nodetop)) < 0)
- return (false);
- pointer = nodetop;
-
- menus_filename[0] = menus_nodename[0] = '\0';
- visible_footnote = "";
- free_completion_list ();
-
- while (true)
- {
- int idx;
-
- pointer = find_footnote_ref (pointer);
- if (pointer < 0)
- break; /* no more footnotes in this node. */
-
- pointer = skip_whitespace_and_cr (pointer);
- idx = 0;
-
- while ((notename[idx] = info_file[pointer]) && notename[idx] != ':')
- {
- idx++, pointer++;
- }
-
- notename[idx] = '\0';
- clean_up (notename);
- pointer++;
- if (info_file[pointer] == ':')
- {
- strcpy (nodename, notename);
- }
- else
- {
- int in_parens = 0;
-
- pointer = skip_whitespace (pointer);
- idx = 0;
-
- while ((nodename[idx] = info_file[pointer]) &&
- (in_parens ||
- (nodename[idx] != '\t' &&
- nodename[idx] != '.' &&
- nodename[idx] != ',')))
- {
- if (nodename[idx] == '(')
- in_parens++;
- else if (nodename[idx] == ')')
- in_parens--;
-
- idx++, pointer++;
- }
- nodename[idx] = '\0';
- clean_up (nodename);
- }
- /* Add the notename/nodename to the list. */
- add_completion (notename, nodename);
- the_menu_size++;
-
- /* Remember this identifier as the default if it is the first one in the
- page. */
- if (!(*visible_footnote) &&
- pointer > pagetop &&
- pointer < forward_lines (the_window.bottom - the_window.top, pointer))
- visible_footnote = completion_list->identifier;
- }
- if (the_menu_size)
- completion_list = reverse_list (completion_list);
- return (the_menu_size != 0);
- }
-
- /* Scan through the ?already? built footnote list looking
- for STRING. If found, place the corresponding node name
- in NODENAME. */
- boolean
- find_note_node (string, nodename)
- char *string, *nodename;
- {
- return (scan_list (string, nodename));
- }
-
- /* **************************************************************** */
- /* */
- /* Page Display */
- /* */
- /* **************************************************************** */
-
-
- /* The display functions for GNU Info. */
- int display_ch, display_cv;
- int display_point;
-
- /* Display the current page from pagetop down to the bottom of the
- page or the bottom of the node, whichever comes first. */
- display_page ()
- {
- display_point = pagetop;
- display_ch = the_window.left;
- display_cv = the_window.top;
- generic_page_display ();
- }
-
- /* Print the page from display_point to bottom of node, or window,
- whichever comes first. Start printing at display_ch, display_cv. */
- generic_page_display ()
- {
- int done_with_display = 0;
- int character;
-
- goto_xy (display_ch, display_cv);
-
- while (!done_with_display)
- {
- if (display_point == nodebot)
- {
- clear_eop ();
- goto display_finish;
- }
-
- character = info_file[display_point];
-
- if ((display_width (character, the_window.ch) + the_window.ch)
- >= the_window.right)
- display_carefully (character);
- else
- charout (character);
-
- if ((the_window.cv >= the_window.bottom)
- || (the_window.cv == the_window.top
- && the_window.ch == the_window.left))
- {
- display_finish:
- pagebot = display_point;
- make_modeline ();
- done_with_display++;
- continue;
- }
- else
- display_point++;
- }
- fflush (stdout);
- }
-
- /* Display character carefully, ensuring that no scrolling takes
- place, even in the case of funky control characters. */
- display_carefully (character)
- int character;
- {
- if (CTRL_P (character))
- {
- switch (character)
- {
- case RETURN:
- case NEWLINE:
- case TAB:
- clear_eol ();
- advance (the_window.right - the_window.ch);
- break;
- default:
- charout ('^');
- if (the_window.cv == the_window.bottom)
- break;
- else
- charout (UNCTRL (character));
- }
- }
- else
- charout (character);
- }
-
- /* Move the cursor to POSITION in page. Return non-zero if successful. */
- cursor_to (position)
- int position;
- {
- int ch, cv, character;
- int point;
-
- if (position > pagebot || position < pagetop)
- return (0);
-
- point = pagetop;
- ch = the_window.left;
- cv = the_window.top;
-
- while (point < position)
- {
- character = info_file[point++];
-
- ch += display_width (character, ch);
-
- if (ch >= the_window.right)
- {
- ch = ch - the_window.right;
- cv++;
-
- if (cv >= the_window.bottom)
- return (0);
- }
- }
- goto_xy (ch, cv);
- return (1);
- }
-
- /* Move to the next page in this node. Return FALSE if
- we can't get to the next page. */
- boolean
- next_page ()
- {
- int pointer;
-
- pointer =
- forward_lines ((the_window.bottom - the_window.top) - 2, pagetop);
-
- if (pointer >= nodebot)
- return (false);
-
- /* Hack for screens smaller than displayed line width. */
- if (pointer > display_point)
- {
- pointer = display_point;
- back_lines (1);
- }
- pagetop = pointer;
- return (true);
- }
-
- /* Move to the previous page in this node. Return FALSE if
- there is no previous page. */
- boolean
- prev_page ()
- {
- int pointer =
- back_lines ((the_window.bottom - the_window.top) - 2, pagetop);
-
- if (pagetop == nodetop)
- return (false);
-
- if (pointer < nodetop)
- pointer = nodetop;
-
- pagetop = pointer;
- return (true);
- }
-
-
- /* **************************************************************** */
- /* */
- /* Utility Functions */
- /* */
- /* **************************************************************** */
-
- char *search_buffer; /* area in ram to scan through. */
- int buffer_bottom; /* Length of this area. */
-
- /* Set the global variables that all of these routines use. */
- set_search_constraints (buffer, extent)
- char *buffer;
- int extent;
- {
- search_buffer = buffer;
- buffer_bottom = extent;
- }
-
- /* Move back to the start of this line. */
- to_beg_line (from)
- int from;
- {
- while (from && search_buffer[from - 1] != '\n')
- from--;
- return (from);
- }
-
- /* Move forward to the end of this line. */
- to_end_line (from)
- {
- while (from < buffer_bottom && search_buffer[from] != '\n')
- from++;
- return (from);
- }
-
- /* Move back count lines in search_buffer starting at starting_pos.
- Returns the start of that line. */
- back_lines (count, starting_pos)
- int count, starting_pos;
- {
- starting_pos = to_beg_line (starting_pos);
- while (starting_pos && count)
- {
- starting_pos = to_beg_line (starting_pos - 1);
- count--;
- }
- return (starting_pos);
- }
-
- /* Move forward count lines starting at starting_pos.
- Returns the start of that line. */
- forward_lines (count, starting_pos)
- int count, starting_pos;
- {
- starting_pos = to_end_line (starting_pos);
- while (starting_pos < buffer_bottom && count)
- {
- starting_pos = to_end_line (starting_pos + 1);
- count--;
- }
- return (to_beg_line (starting_pos));
- }
-
- /* Search for STRING in SEARCH_BUFFER starting at STARTING_POS.
- Return the location of the string, or -1 if not found. */
- search_forward (string, starting_pos)
- char *string;
- int starting_pos;
- {
- register int c, i, len;
- register char *buff, *end;
- char *alternate;
-
-
- /* We match characters in SEARCH_BUFFER against STRING and ALTERNATE.
- ALTERNATE is a case reversed version of STRING; this is cheaper than
- case folding each character before comparison. */
-
- /* Build the alternate string. */
- alternate = savestring (string);
- len = strlen (string);
-
- for (i = 0; i < len; i++)
- {
- c = alternate[i];
-
- if (c >= 'a' && c <= 'z')
- alternate[i] = c - 32;
- else if (c >= 'A' && c <= 'Z')
- alternate[i] = c + 32;
- }
-
- buff = search_buffer + starting_pos;
- end = search_buffer + buffer_bottom + 1;
-
- while (buff < end)
- {
- for (i = 0; i < len; i++)
- {
- c = buff[i];
-
- if (c != string[i] && c != alternate[i])
- break;
- }
-
- if (!string[i])
- {
- free (alternate);
- return (buff - search_buffer);
- }
-
- buff++;
- }
-
- free (alternate);
- return (-1);
- }
-
- /* Search for STRING in SEARCH_BUFFER starting at STARTING_POS.
- Return the location of the string, or -1 if not found. */
- search_backward (string, starting_pos)
- char *string;
- int starting_pos;
- {
- int len = strlen (string);
- while (starting_pos - len > -1)
- {
- if (strnicmp (search_buffer + (starting_pos - len), string, len) == 0)
- return (starting_pos - len);
- else
- starting_pos--;
- }
- return (-1);
- }
-
- /* Only search for STRING from POINTER to end of line. Return offset
- of string, or -1 if not found. */
- string_in_line (string, pointer)
- char *string;
- int pointer;
- {
- int old_buffer_bottom = buffer_bottom;
-
- set_search_constraints (search_buffer, to_end_line (pointer));
- pointer = search_forward (string, pointer);
- buffer_bottom = old_buffer_bottom;
- return (pointer);
- }
-
- /* Skip whitespace characters at OFFSET in SEARCH_BUFFER.
- Return the next non-whitespace character or -1 if BUFFER_BOTTOM
- is reached. */
- skip_whitespace (offset)
- int offset;
- {
- int character;
-
- while (offset < buffer_bottom)
- {
- character = search_buffer[offset];
- if (character == ' ' || character == '\t')
- offset++;
- else
- return (offset);
- }
- return (-1);
- }
-
- /* Skip whitespace characters including <CR> at OFFSET in
- SEARCH_BUFFER. Return the position of the next non-whitespace
- character, or -1 if BUFFER_BOTTOM is reached. */
- skip_whitespace_and_cr (offset)
- int offset;
- {
- while (true)
- {
- offset = skip_whitespace (offset);
- if (offset > 0 && search_buffer[offset] != '\n')
- return (offset);
- else
- offset++;
- }
- }
-
- /* Extract the node name part of the of the text after the FIELD.
- Place the node name into NODENAME. Assume the line starts at
- OFFSET in SEARCH_BUFFER. */
- boolean
- extract_field (field_name, nodename, offset)
- char *field_name, *nodename;
- int offset;
- {
- int temp, character;
-
- temp = string_in_line (field_name, offset);
- if (temp < 0)
- return (false);
-
- temp += strlen (field_name);
- temp = skip_whitespace (temp);
-
- /* Okay, place the following text into NODENAME. */
-
- while ((character = search_buffer[temp]) != ','
- && character != '\n'
- && character != '\t')
- {
- *nodename = character;
- nodename++;
- temp++;
- }
- *nodename = '\0';
- return (true);
- }
-
- /* Return true if pointer is exactly at string, else false. */
- boolean
- looking_at (string, pointer)
- char *string;
- int pointer;
- {
- if (strnicmp (search_buffer + pointer, string, strlen (string)) == 0)
- return (true);
- else
- return (false);
- }
-
- /* File stack stuff. This is currently only used to push one file while
- searching indirect files, but we may as well write it in full
- generality. */
- typedef struct filestack
- {
- struct filestack *next;
- char filename[FILENAME_LEN];
- char current_filename[FILENAME_LEN];
- char *tag_table;
- char *info_file;
- int info_buffer_len;
- } FILESTACK;
-
- FILESTACK *filestack = NULL;
-
- boolean
- push_filestack (filename, remember_name)
- char *filename;
- int remember_name;
- {
- FILESTACK *element = (FILESTACK *)xmalloc (sizeof (FILESTACK));
-
- element->next = filestack;
- filestack = element;
-
- strcpy (filestack->filename, last_loaded_info_file);
- strcpy (filestack->current_filename, current_info_file);
- filestack->tag_table = tag_table;
- filestack->info_file = info_file;
- filestack->info_buffer_len = info_buffer_len;
-
- *last_loaded_info_file = '\0'; /* force the file to be read */
- if (get_info_file (filename, remember_name))
- {
- return (true);
- }
- else
- {
- pop_filestack ();
- return (false);
- }
- }
-
- void
- pop_filestack ()
- {
- FILESTACK *temp;
-
- if (filestack == NULL)
- {
- fprintf (stderr , "File stack is empty and can't be popped\n");
- brians_error ();
- return;
- }
-
- free (info_file);
-
- strcpy (last_loaded_info_file, filestack->filename);
- strcpy (current_info_file, filestack->current_filename);
- tag_table = filestack->tag_table;
- info_file = filestack->info_file;
- info_buffer_len = filestack->info_buffer_len;
-
- temp = filestack;
- filestack = filestack->next;
- free ((char *)temp);
- }
-
- /* Swap the current info file with the bottom of the filestack */
- void
- swap_filestack ()
- {
- char t_last_loaded_info_file[FILENAME_LEN];
- char t_current_info_file[FILENAME_LEN];
- char *t_tag_table;
- char *t_info_file;
- int t_info_buffer_len;
-
- if (filestack == NULL)
- {
- fprintf (stderr , "File stack is empty and can't be swapped\n");
- brians_error ();
- return;
- }
-
- strcpy (t_last_loaded_info_file, filestack->filename);
- strcpy (t_current_info_file, filestack->current_filename);
- t_tag_table = filestack->tag_table;
- t_info_file = info_file;
- t_info_buffer_len = info_buffer_len;
-
- strcpy (filestack->filename, last_loaded_info_file);
- strcpy (filestack->current_filename, current_info_file);
- filestack->tag_table = tag_table;
- filestack->info_file = info_file;
- filestack->info_buffer_len = info_buffer_len;
-
- strcpy (last_loaded_info_file, t_last_loaded_info_file);
- strcpy (current_info_file, t_current_info_file);
- tag_table = t_tag_table;
- info_file = t_info_file;
- info_buffer_len = t_info_buffer_len;
- }
-
- /* Now the node history stack */
-
- extern NODEINFO *Info_History;
-
- /* Save the current filename, nodename, and position on the history list.
- We prepend. */
- boolean
- push_node (filename, nodename, page_position, node_position)
- char *filename, *nodename;
- int page_position, node_position;
- {
- NODEINFO *newnode = (NODEINFO *) xmalloc (sizeof (NODEINFO));
-
- newnode->next = Info_History;
-
- newnode->filename = (char *) xmalloc (strlen (filename) + 1);
- strcpy (newnode->filename, filename);
-
- newnode->nodename = (char *) xmalloc (strlen (nodename) + 1);
- strcpy (newnode->nodename, nodename);
-
- newnode->pagetop = page_position;
- newnode->nodetop = node_position;
-
- Info_History = newnode;
- return (true);
- }
-
- /* Pop one node from the node list, leaving the values in
- passed variables. */
- boolean
- pop_node (filename, nodename, nodetop, pagetop)
- char *filename, *nodename;
- int *nodetop, *pagetop;
- {
- if (Info_History->next == (NODEINFO *) NULL)
- {
- display_error ("At beginning of history now!");
- return (false);
- }
- else
- {
- NODEINFO *releaser = Info_History;
-
- /* If the popped file is not the current file, then force
- the popped file to be loaded. */
- if (strcmp (Info_History->filename, last_loaded_info_file) != 0)
- last_loaded_info_file[0] = '\0';
-
- strcpy (filename, Info_History->filename);
- strcpy (nodename, Info_History->nodename);
- *pagetop = Info_History->pagetop;
- *nodetop = Info_History->nodetop;
- free (Info_History->nodename);
- free (Info_History->filename);
- Info_History = Info_History->next;
- free (releaser);
- return (true);
- }
- }
-
- /* Whoops, Unix doesn't have strnicmp. */
-
- /* Compare at most COUNT characters from string1 to string2. Case
- doesn't matter. */
- int
- strnicmp (string1, string2, count)
- char *string1, *string2;
- {
- char ch1, ch2;
-
- while (count)
- {
- ch1 = *string1++;
- ch2 = *string2++;
- if (to_upper (ch1) == to_upper (ch2))
- count--;
- else
- break;
- }
- return (count);
- }
-
- /* Compare string1 to string2. Case doesn't matter. */
- int
- stricmp (string1, string2)
- char *string1, *string2;
- {
- char ch1, ch2;
-
- while (1)
- {
- ch1 = *string1++;
- ch2 = *string2++;
-
- if (ch1 == '\0')
- return (ch2 != '\0');
-
- if ((ch2 == '\0') ||
- (to_upper (ch1) != to_upper (ch2)))
- return (1);
- }
- }
-
- /* Make the user type "Y" or "N". */
- boolean
- get_y_or_n_p ()
- {
- int character;
- print_string (" (Y or N)?");
- clear_eol ();
-
- until_we_like_it:
-
- character = blink_cursor ();
- if (character == EOF)
- return (false);
- if (to_upper (character) == 'Y')
- {
- charout (character);
- return (true);
- }
-
- if (to_upper (character) == 'N')
- {
- charout (character);
- return (false);
- }
-
- if (character == ABORT_CHAR)
- {
- ding ();
- return (false);
- }
-
- goto until_we_like_it;
- }
-
- /* Move the cursor to the desired column in the window. */
- indent_to (screen_column)
- int screen_column;
- {
- int counter = screen_column - the_window.ch;
- if (counter > 0)
- {
- while (counter--)
- charout (' ');
- }
- else if (screen_column != 0)
- charout (' ');
- }
-
-
- /* **************************************************************** */
- /* */
- /* Error output/handling. */
- /* */
- /* **************************************************************** */
-
- /* Display specific error from known file error table. */
- file_error (file)
- char *file;
- {
- extern int errno;
- extern int sys_nerr;
- extern char *sys_errlist[];
-
- if (errno < sys_nerr)
- display_error ("%s: %s", file, sys_errlist[errno]);
- else
- display_error ("%s: Unknown error %d", file, errno);
- }
-
- /* Display the error in the echo-area using format_string and args.
- This is a specialized interface to printf. */
- display_error (format_string, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
- char *format_string;
- {
- extern boolean terminal_inited_p;
- char output_buffer[1024];
-
- if (totally_inhibit_errors)
- return;
-
- sprintf (output_buffer, format_string, arg1, arg2, arg3, arg4,
- arg5, arg6, arg7, arg8);
- if (terminal_inited_p)
- {
- new_echo_area ();
- ding ();
- print_string (output_buffer);
- close_echo_area ();
- }
- else
- {
- fprintf (stderr, "%s\n", output_buffer);
- sleep(1);
- }
- }
-
-
- /* Tell everybody what a loser I am. If you see this error,
- send me a bug report. */
- brians_error ()
- {
- display_error ("You are never supposed to see this error.\n");
- display_error ("Tell bfox@ai.mit.edu to fix this someday.\n");
- return (-1);
- }
-
- /* **************************************************************** */
- /* */
- /* Terminal IO, and Driver */
- /* */
- /* **************************************************************** */
-
- /* The Unix termcap interface code. */
-
- #define NO_ERROR 0
- #define GENERIC_ERROR 1
- #define NO_TERMINAL_DESCRIPTOR 2
- #define OUT_OF_MEMORY 3
- #define BAD_TERMINAL 4
-
- #define FERROR(msg) fprintf (stderr, msg); exit (GENERIC_ERROR)
-
- extern int tgetnum (), tgetflag ();
- extern char *tgetstr ();
- extern char *tgoto ();
-
- #define Certainly_enough_space 2048 /* page 3, Section 1.1, para 4 */
-
- #ifdef UNIX
- char termcap_buffer[Certainly_enough_space];
- #else
- #define termcap_buffer NULL
- #endif
-
- /* You CANNOT remove these next four vars. TERMCAP needs them to operate. */
- char PC;
- char *BC;
- char *UP;
-
- /* A huge array of stuff to get from termcap initialization. */
-
- #define tc_int 0
- #define tc_char tc_int+1
- #define tc_flag tc_char+1
- #define tc_last tc_flag+1
-
- typedef int flag;
-
- /* First, the variables which this array refers to */
-
- /* Capabilities */
-
- int terminal_columns; /* {tc_int, "co" */
- int terminal_rows; /* {tc_int, "li" */
- flag terminal_is_generic; /* {tc_flag,"gn" */
-
- /* Cursor Motion */
-
- char *terminal_goto; /* {tc_char,"cm" */
- char *terminal_home; /* {tc_char,"ho" */
-
- char *terminal_cursor_left; /* {tc_char,"le" */
- char *terminal_cursor_right; /* {tc_char,"nd" */
- char *terminal_cursor_up; /* {tc_char,"up" */
- char *terminal_cursor_down; /* {tc_char,"do" */
-
- /* Screen Clearing */
-
- char *terminal_clearpage; /* {tc_char,"cl" */
- char *terminal_clearEOP; /* {tc_char,"cd" */
- char *terminal_clearEOL; /* {tc_char,"ce" */
-
- /* "Standout" */
- char *terminal_standout_begin; /* {tc_char,"so" */
- char *terminal_standout_end; /* {tc_char,"se" */
-
- /* Reverse Video */
- char *terminal_inverse_begin; /* {tc_char,"mr" */
- char *terminal_end_attributes; /* {tc_char,"me" */
-
- /* Ding! */
-
- char *terminal_ear_bell; /* {tc_char,"bl" */
-
- /* Terminal Initialization */
-
- char *terminal_use_begin; /* {tc_char,"ti" */
- char *terminal_use_end; /* {tc_char,"te" */
-
- /* Padding Stuff */
-
- char *terminal_padding; /* {tc_char,"pc" */
-
- /* Now the whopping big array */
-
- typedef struct {
- char type;
- char *name;
- char *value;
- } termcap_capability_struct;
-
- termcap_capability_struct capabilities[] = {
-
- /* Capabilities */
-
- {tc_int, "co", (char *) &terminal_columns},
- {tc_int, "li", (char *) &terminal_rows},
- {tc_flag, "gn", (char *) &terminal_is_generic},
-
- /* Cursor Motion */
-
- {tc_char, "cm", (char *) &terminal_goto},
- {tc_char, "ho", (char *) &terminal_home},
-
- {tc_char, "le", (char *) &terminal_cursor_left},
- {tc_char, "nd", (char *) &terminal_cursor_right},
- {tc_char, "up", (char *) &terminal_cursor_up},
- {tc_char, "do", (char *) &terminal_cursor_down},
-
- /* Screen Clearing */
-
- {tc_char, "cl", (char *) &terminal_clearpage},
- {tc_char, "cd", (char *) &terminal_clearEOP},
- {tc_char, "ce", (char *) &terminal_clearEOL},
-
- /* "Standout" */
- {tc_char, "so", (char *) &terminal_standout_begin},
- {tc_char, "se", (char *) &terminal_standout_end},
-
- /* Reverse Video */
- {tc_char, "mr", (char *) &terminal_inverse_begin},
- {tc_char, "me", (char *) &terminal_end_attributes},
-
- /* Ding! */
-
- {tc_char, "bl", (char *) &terminal_ear_bell},
-
- /* Terminal Initialization */
-
- {tc_char, "ti", (char *) &terminal_use_begin},
- {tc_char, "te", (char *) &terminal_use_end},
-
- /* Padding Stuff */
-
- {tc_char, "pc", (char *) &terminal_padding},
-
- /* Terminate this array with a var of type tc_last */
- {tc_last, NULL, NULL}
-
- };
-
- int terminal_opened_p = 0;
-
- open_terminal_io ()
- {
- int error;
-
- if (terminal_opened_p)
- return (NO_ERROR);
-
- if ((error = get_terminal_info ()) != NO_ERROR)
- return (error);
-
- if ((error = get_terminal_vars (capabilities)) != NO_ERROR)
- return (error);
-
- /* Now, make sure we have the capabilites that we need. */
- if (terminal_is_generic)
- return (BAD_TERMINAL);
-
- terminal_opened_p++;
- return (NO_ERROR);
- }
-
- get_terminal_info ()
- {
- char temp_string_buffer[256];
- int result;
-
- char *terminal_name = getenv ("TERM");
-
- if (terminal_name == NULL || *terminal_name == 0
- || (strcmp (terminal_name, "dialup") == 0))
- {
- terminal_name = temp_string_buffer;
- printf ("\nTerminal Type:");
- fflush (stdout);
- fgets (terminal_name, 256, stdin);
- if (!(*terminal_name))
- return (NO_TERMINAL_DESCRIPTOR);
- }
-
- /* #define VERBOSE_GET_TERMINAL 1 */
- #ifdef VERBOSE_GET_TERMINAL
-
- #define buffer_limit 256
- #define opsys_termcap_filename "/etc/termcap"
-
- /* We hack question mark if that is what the user typed. All this means
- is we read /etc/termcap, and prettily print out the names of terminals
- that we find. */
-
- if (terminal_name[0] == '?' && !terminal_name[1])
- {
- FILE *termcap_file;
- if ((termcap_file = fopen (opsys_termcap_filename, "r")) != NULL)
- {
- int result;
- char line_buffer[buffer_limit];
- int terminal_count = 0;
-
- while ((readline_termcap (termcap_file, line_buffer)) != EOF)
- {
- char first_char = *line_buffer;
- if (first_char == '#' || first_char == ' '
- || first_char == '\t' || first_char == '\n')
- ;
- else
- {
- /* Print the names the pretty way. */
- printf ("\n%s", line_buffer); /* liar */
- terminal_count++;
- }
- }
- fclose (termcap_file);
-
- if (terminal_count)
- printf ("\n%d terminals listed.\n", terminal_count);
- else
- printf ("\nNo terminals were listed. Brian's mistake.\n");
- }
- else
- {
- fprintf (stderr,
- "\nNo such system file as %s!\nWe lose badly.\n",
- opsys_termcap_filename);
- return (NO_TERMINAL_DESCRIPTOR);
- }
- return (get_terminal_info ());
- }
- #endif /* VERBOSE_GET_TERMINAL */
-
- result = tgetent (termcap_buffer, terminal_name);
-
- if (!result)
- return (NO_TERMINAL_DESCRIPTOR);
- else
- return (NO_ERROR);
- }
-
- #ifdef VERBOSE_GET_TERMINAL
- readline_termcap (stream, buffer)
- FILE *stream;
- char *buffer;
- {
- int c;
- int buffer_index = 0;
-
- while ((c = getc (stream)) != EOF && c != '\n')
- {
- if (buffer_index != buffer_limit - 1)
- buffer[buffer_index++] = c;
- }
-
- buffer[buffer_index] = 0;
-
- if (c == EOF)
- return ((buffer_index) ? 0 : EOF);
- else
- return (0);
- }
- #endif /* VERBOSE_GET_TERMINAL */
-
- /* For each element of "from_array", read the corresponding variable's
- value into the right place. */
- get_terminal_vars (from_array)
- termcap_capability_struct from_array[];
- {
- int i;
- register termcap_capability_struct *item;
- char *buffer;
-
- #if !defined (GNU_TERMCAP)
- buffer = (char *) xmalloc (sizeof (termcap_buffer) + 1);
- # define buffer_space &buffer
- #else
- # define buffer_space 0
- #endif
-
- for (i = 0; (item = &from_array[i]) && (item->type != tc_last); i++)
- {
- switch (item->type)
- {
- case tc_int:
- *((int *) (item->value)) = tgetnum (item->name);
- break;
-
- case tc_flag:
- *((int *) item->value) = tgetflag (item->name);
- break;
-
- case tc_char:
- *((char **) item->value) = tgetstr (item->name, buffer_space);
- break;
-
- default:
- FERROR ("Bad entry scanned in tc_struct[].\n \
- Ask bfox@ai.mit.edu to fix this someday.\n");
- }
- }
-
- PC = terminal_padding ? terminal_padding[0] : 0;
- BC = terminal_cursor_left;
- UP = terminal_cursor_up;
- return (NO_ERROR);
- }
-
- /* Return the number of rows this terminal has. */
- int
- get_terminal_rows ()
- {
- int rows = 0;
-
- #if defined (TIOCGWINSZ)
- {
- int tty;
- struct winsize size;
-
- tty = fileno (stdin);
-
- if (ioctl (tty, TIOCGWINSZ, &size) != -1)
- rows = size.ws_row;
- }
- #endif /* TIOCGWINSZ */
-
- if (!rows)
- rows = tgetnum ("li");
-
- if (rows <= 0)
- rows = 24;
-
- return (rows);
- }
-
- /* Return the number of columns this terminal has. */
- get_terminal_columns ()
- {
- int columns = 0;
-
- #if defined (TIOCGWINSZ)
- {
- int tty;
- struct winsize size;
-
- tty = fileno (stdin);
-
- if (ioctl (tty, TIOCGWINSZ, &size) != -1)
- columns = size.ws_col;
- }
- #endif /* TIOCGWINSZ */
-
- if (!columns)
- columns = tgetnum ("co");
-
- if (columns <= 0)
- columns = 80;
-
- return (columns);
- }
-
- /* #define TERMINAL_INFO_PRINTING */
- #ifdef TERMINAL_INFO_PRINTING
-
- /* Scan this (already "get_terminal_vars"ed) array, printing out the
- capability name, and value for each entry. Pretty print the value
- so that the terminal doesn't actually do anything, shitbrain. */
- show_terminal_info (from_array)
- termcap_capability_struct from_array[];
- {
- register int i;
- register termcap_capability_struct *item;
-
- for (i = 0; ((item = &from_array[i]) && ((item->type) != tc_last)); i++)
- {
-
- char *type_name;
- switch (item->type)
- {
- case tc_int:
- type_name = "int ";
- break;
- case tc_flag:
- type_name = "flag";
- break;
- case tc_char:
- type_name = "char";
- break;
- default:
- type_name = "Broken";
- }
-
- printf ("\t%s\t%s = ", type_name, item->name);
-
- switch (item->type)
- {
- case tc_int:
- case tc_flag:
- printf ("%d", *((int *) item->value));
- break;
- case tc_char:
- tc_pretty_print (*((char **) item->value));
- break;
- }
- printf ("\n");
- }
- }
-
- /* Print the contents of string without sending anything that isn't
- a normal printing ASCII character. */
- tc_pretty_print (string)
- register char *string;
- {
- register char c;
-
- while (c = *string++)
- {
- if (CTRLP (c))
- {
- putchar ('^');
- c += 64;
- }
- putchar (c);
- }
- }
- #endif TERMINAL_INFO_PRINTING
-
-
- /* **************************************************************** */
- /* */
- /* Character IO, and driver */
- /* */
- /* **************************************************************** */
-
- char *widest_line;
- boolean terminal_inited_p = false;
-
- /* Start up the character io stuff. */
- init_terminal_io ()
- {
- if (!terminal_inited_p)
- {
- opsys_init_terminal ();
- terminal_rows = get_terminal_rows ();
- terminal_columns = get_terminal_columns ();
-
- widest_line = (char *) xmalloc (terminal_columns);
-
- terminal_inited_p = true;
- }
-
- terminal_window.left = 0;
- terminal_window.top = 0;
- terminal_window.right = terminal_columns;
- terminal_window.bottom = terminal_rows;
-
- set_window (&terminal_window);
-
- terminal_window.bottom -= 2;
-
- set_window (&terminal_window);
-
- init_echo_area (the_window.left, the_window.bottom + 1,
- the_window.right, terminal_rows);
-
- /* Here is a list of things that the terminal has to be able to do. Do
- you think that this is too harsh? */
- if (!terminal_goto || /* We can't move the cursor. */
- !terminal_rows) /* We don't how many lines it has. */
- {
- fprintf (stderr,
- "Your terminal is not clever enough to run info. Sorry.\n");
- exit (1);
- }
- }
-
- /* Ring the terminal bell. */
- ding ()
- {
- extern char *terminal_ear_bell;
-
- if (terminal_ear_bell)
- do_term (terminal_ear_bell);
- else
- putchar (CTRL ('G'));
-
- fflush (stdout);
- }
-
- int untyi_char = 0;
- boolean inhibit_output = false;
-
- /* Return a character from stdin, or the last unread character
- if there is one available. */
- blink_cursor ()
- {
- int character;
-
- fflush (stdout);
- if (untyi_char)
- {
- character = untyi_char;
- untyi_char = 0;
- }
- else
- do { character = getc (stdin); } while (character == -1 && errno == EINTR);
-
- return (character);
- }
-
- /* Display single character on the terminal screen. If the
- character would run off the right hand edge of the screen,
- advance the cursor to the next line. */
- charout (character)
- int character;
- {
- if (inhibit_output)
- return;
-
- /* This character may need special treatment if it is
- a control character. */
- if (CTRL_P (character))
- {
- switch (character)
- {
- case NEWLINE:
- case RETURN:
- print_cr ();
- break;
-
- case TAB:
- print_tab ();
- break;
-
- default:
- charout ('^');
- charout (UNCTRL (character));
- }
- }
- else
- {
- putchar (character);
- advance (1);
- }
- }
-
- /* Move the cursor AMOUNT character positions. */
- advance (amount)
- int amount;
- {
- int old_window_cv = the_window.cv;
-
- while (amount-- > 0)
- {
- the_window.ch++;
- if (the_window.ch >= the_window.right)
- {
- the_window.ch = (the_window.ch - the_window.right) + the_window.left;
- the_window.cv++;
-
- if (the_window.cv >= the_window.bottom)
- the_window.cv = the_window.top;
- }
- }
-
- if (the_window.cv != old_window_cv)
- goto_xy (the_window.ch, the_window.cv);
- }
-
- /* Print STRING and args using charout */
- print_string (string, a1, a2, a3, a4, a5)
- char *string;
- {
- int character;
- char buffer[2048];
- int idx = 0;
-
- sprintf (buffer, string, a1, a2, a3, a4, a5);
-
- while (character = buffer[idx++])
- charout (character);
-
- fflush (stdout);
- }
-
- /* Display a carriage return.
- Clears to the end of the line first. */
- print_cr ()
- {
- extern boolean typing_out;
- clear_eol ();
-
- if (typing_out)
- { /* Do the "MORE" stuff. */
- int response;
-
- if (the_window.cv + 2 == the_window.bottom)
- {
- goto_xy (the_window.left, the_window.cv + 1);
- clear_eol ();
- print_string ("[More]");
- response = blink_cursor ();
- if (response != SPACE)
- {
- untyi_char = response;
- inhibit_output = true;
- return;
- }
- else
- {
- goto_xy (the_window.left, the_window.cv);
- clear_eol ();
- goto_xy (the_window.left, the_window.top);
- return;
- }
- }
- }
- advance (the_window.right - the_window.ch);
- }
-
- /* Move the cursor to the next tab stop, blanking the intervening
- spaces along the way. */
- print_tab ()
- {
- int hpos, width, destination;
-
- hpos = the_window.ch - the_window.left;
- width = ((hpos + 8) & 0xf8) - hpos;
-
- destination = hpos + width + the_window.left;
-
- if (destination >= the_window.right)
- destination -= the_window.right;
-
- while (the_window.ch != destination)
- charout (SPACE);
- }
-
- display_width (character, hpos)
- int character, hpos;
- {
- int width = 1;
-
- if (CTRL_P (character))
- {
- switch (character)
- {
- case RETURN:
- case NEWLINE:
- width = the_window.right - hpos;
- break;
- case TAB:
- width = ((hpos + 8) & 0xf8) - hpos;
- break;
- default:
- width = 2;
- }
- }
- return (width);
- }
-
- /* Like GOTO_XY, but do it right away. */
- I_goto_xy (xpos, ypos)
- int xpos, ypos;
- {
- goto_xy (xpos, ypos);
- fflush (stdout);
- }
-
- /* Move the cursor, (and cursor variables) to xpos, ypos. */
- goto_xy (xpos, ypos)
- int xpos, ypos;
- {
- the_window.ch = xpos;
- the_window.cv = ypos;
- opsys_goto_pos (xpos, ypos);
- }
-
- /* Clear the screen, leaving ch and cv at the top of the window. */
- clear_screen ()
- {
- goto_xy (the_window.left, the_window.top);
- clear_eop_slowly ();
- }
-
- clear_eop_slowly ()
- {
- int temp_ch = the_window.ch;
- int temp_cv = the_window.cv;
-
- clear_eol ();
-
- while (++the_window.cv < the_window.bottom)
- {
- goto_xy (the_window.left, the_window.cv);
- clear_eol ();
- }
- goto_xy (temp_ch, temp_cv);
- }
-
- /* Clear from current cursor position to end of page. */
- clear_eop ()
- {
- if (terminal_clearEOP)
- do_term (terminal_clearEOP);
- else
- clear_eop_slowly ();
- }
-
- /* Clear from current cursor position to end of screen line */
- clear_eol ()
- {
- int temp_ch = the_window.ch;
-
- if (terminal_clearEOL)
- do_term (terminal_clearEOL);
- else
- {
- char *line = widest_line;
- int i;
-
- for (i = 0; i < the_window.right - the_window.ch; i++)
- line[i] = ' ';
- line[i] = '\0';
-
- printf ("%s", line);
- }
- goto_xy (temp_ch, the_window.cv);
- }
-
- /* Call FUNCTION with WINDOW active. You can pass upto 5 args to the
- function. This returns whatever FUNCTION returns. */
- int
- with_output_to_window (window, function, arg1, arg2, arg3, arg4, arg5)
- WINDOW *window;
- Function *function;
- {
- int result;
-
- push_window ();
- set_window (window);
- result = (*function) (arg1, arg2, arg3, arg4, arg5);
- pop_window ();
- return (result);
- }
-
- /* Given a pointer to a window data structure, make that
- the current window. */
- set_window (window)
- WINDOW *window;
- {
- bcopy (window, &the_window, sizeof (WINDOW));
- }
-
- /* Save the current window on the window stack. */
- push_window ()
- {
- WINDOW_LIST *new_window = (WINDOW_LIST *) xmalloc (sizeof (WINDOW_LIST));
-
- new_window->next_window = window_stack;
- window_stack = new_window;
- new_window->ch = the_window.ch;
- new_window->cv = the_window.cv;
- new_window->top = the_window.top;
- new_window->bottom = the_window.bottom;
- new_window->left = the_window.left;
- new_window->right = the_window.right;
- }
-
- /* Pop the top of the window_stack into the_window. */
- pop_window ()
- {
- set_window ((WINDOW *)window_stack);
-
- if (window_stack->next_window)
- {
- WINDOW_LIST *thing_to_free = window_stack;
- window_stack = window_stack->next_window;
- free (thing_to_free);
- }
-
- goto_xy (the_window.ch, the_window.cv);
- }
-
- /* **************************************************************** */
- /* */
- /* "Opsys" functions. */
- /* */
- /* **************************************************************** */
-
- /* The lowlevel terminal/file interface. Nothing ever really gets
- low level when you're writing in C, though.
-
- This file contains all of the "opsys" labels. You have to make
- a different one if you want GNU Info to run on machines that don't
- have Unix. */
-
- extern char *terminal_use_begin, *terminal_use_end, *terminal_goto;
-
- #if defined (TIOCGETC)
- struct tchars original_tchars;
- #endif
-
- #if defined (TIOCGLTC)
- struct ltchars original_ltchars;
- #endif
-
- #if defined (USG)
- struct termio original_termio, ttybuff;
- #else
- int original_tty_flags = 0;
- int original_lmode;
- struct sgttyb ttybuff;
- #endif /* !USG */
-
- /* Yes, that's right, do things that the machine needs to get
- the terminal into a usable mode. */
- opsys_init_terminal ()
- {
- int tty = fileno (stdin);
-
- #if defined (USG)
- ioctl (tty, TCGETA, &original_termio);
- ioctl (tty, TCGETA, &ttybuff);
- ttybuff.c_iflag &= (~ISTRIP & ~INLCR & ~IGNCR & ~ICRNL &~IXON);
- ttybuff.c_oflag &= (~ONLCR & ~OCRNL);
- ttybuff.c_lflag &= (~ICANON & ~ECHO);
-
- ttybuff.c_cc[VMIN] = 1;
- ttybuff.c_cc[VTIME] = 0;
-
- if (ttybuff.c_cc[VINTR] = DELETE)
- ttybuff.c_cc[VINTR] = -1;
-
- if (ttybuff.c_cc[VQUIT] = DELETE)
- ttybuff.c_cc[VQUIT] = -1;
-
- ioctl (tty, TCSETA, &ttybuff);
- #else /* !USG */
-
- ioctl (tty, TIOCGETP, &ttybuff);
-
- if (!original_tty_flags)
- original_tty_flags = ttybuff.sg_flags;
-
- /* Make this terminal pass 8 bits around while we are using it. */
- #ifdef PASS8
- ttybuff.sg_flags |= PASS8;
- #endif
-
- #if defined (TIOCLGET) && defined (LPASS8)
- {
- int flags;
- ioctl (tty, TIOCLGET, &flags);
- original_lmode = flags;
- flags |= LPASS8;
- ioctl (tty, TIOCLSET, &flags);
- }
- #endif
-
- #ifdef TIOCGETC
- {
- struct tchars temp;
-
- ioctl (tty, TIOCGETC, &original_tchars);
- bcopy (&original_tchars, &temp, sizeof (struct tchars));
-
- temp.t_startc = temp.t_stopc = -1;
-
- /* If the quit character conflicts with one of our commands, then
- make it go away. */
- if (temp.t_intrc == DELETE)
- temp.t_intrc == -1;
-
- if (temp.t_quitc == DELETE)
- temp.t_quitc == -1;
-
- ioctl (tty, TIOCSETC, &temp);
- }
- #endif /* TIOCGETC */
-
- #ifdef TIOCGLTC
- {
- struct ltchars temp;
-
- ioctl (tty, TIOCGLTC, &original_ltchars);
- bcopy (&original_ltchars, &temp, sizeof (struct ltchars));
-
- /* Make the interrupt keys go away. Just enough to make people happy. */
- temp.t_lnextc = -1; /* C-v */
-
- ioctl (tty, TIOCSLTC, &temp);
- }
- #endif /* TIOCGLTC */
-
- ttybuff.sg_flags &= ~ECHO;
- ttybuff.sg_flags |= CBREAK;
- ioctl (tty, TIOCSETN, &ttybuff);
- #endif /* !USG */
-
- open_terminal_io ();
- do_term (terminal_use_begin);
- }
-
- /* Fix the terminal that I broke. */
- restore_io ()
- {
- int tty = fileno (stdin);
-
- #if defined (USG)
- ioctl (tty, TCSETA, &original_termio);
- #else
- ioctl (tty, TIOCGETP, &ttybuff);
- ttybuff.sg_flags = original_tty_flags;
- ioctl (tty, TIOCSETN, &ttybuff);
-
- #ifdef TIOCGETC
- ioctl (tty, TIOCSETC, &original_tchars);
- #endif /* TIOCGETC */
-
- #ifdef TIOCGLTC
- ioctl (tty, TIOCSLTC, &original_ltchars);
- #endif /* TIOCGLTC */
-
- #if defined (TIOCLGET) && defined (LPASS8)
- ioctl (tty, TIOCLSET, &original_lmode);
- #endif
-
- #endif /* !USG */
- do_term (terminal_use_end);
- }
-
- opsys_goto_pos (xpos, ypos)
- int xpos, ypos;
- {
- do_term (tgoto (terminal_goto, xpos, ypos));
- }
-
- character_output_function (character)
- char character;
- {
- putchar (character);
- }
-
- /* Generic interface to tputs. */
- do_term (command)
- char *command;
- {
- /* Send command to the terminal, with appropriate padding. */
- tputs (command, 1, character_output_function);
- }
-
- /* Filename manipulators, and the like. */
- char local_temp_filename[FILENAME_LEN];
-
- char *info_suffixes[] = {
- "",
- ".info",
- "-info",
- (char *)NULL
- };
-
- /* Expand the filename in partial to make a real name for
- this operating system. This looks in INFO_PATHS in order to
- find the correct file. If it can't find the file, it just
- returns the path as you gave it. */
- char *
- opsys_filename (partial)
- char *partial;
- {
- int initial_character;
-
- if (partial && (initial_character = *partial))
- {
-
- if (initial_character == '/')
- return (partial);
-
- if (initial_character == '~')
- {
- if (partial[1] == '/')
- {
- /* Return the concatenation of HOME and the rest
- of the string. */
- strcpy (local_temp_filename, getenv ("HOME"));
- strcat (local_temp_filename, &partial[2]);
- return (local_temp_filename);
- }
- else
- {
- struct passwd *user_entry;
- int i, c;
- char username[257];
-
- for (i = 1; c = partial[i]; i++)
- {
- if (c == '/')
- break;
- else
- username[i - 1] = c;
- }
- username[i - 1] = '\0';
-
- if (!(user_entry = getpwnam (username)))
- {
- display_error ("Not a registered user!");
- return (partial);
- }
- strcpy (local_temp_filename, user_entry->pw_dir);
- strcat (local_temp_filename, &partial[i]);
- return (local_temp_filename);
- }
- }
-
- if (initial_character == '.')
- {
- #if defined (USG)
- if (!getcwd (local_temp_filename, FILENAME_LEN))
- #else
- if (!getwd (local_temp_filename))
- #endif
- {
- display_error (local_temp_filename);
- return (partial);
- }
-
- strcat (local_temp_filename, "/");
- strcat (local_temp_filename, partial);
- return (local_temp_filename);
- }
-
- /* Scan the list of directories in INFOPATH. */
- {
- struct stat finfo;
- char *temp_dirname, *extract_colon_unit ();
- int statable, dirname_index = 0;
-
- while (temp_dirname = extract_colon_unit (infopath, &dirname_index))
- {
- register int i;
-
- for (i = 0; info_suffixes[i]; i++)
- {
- strcpy (local_temp_filename, temp_dirname);
-
- if (temp_dirname[(strlen (temp_dirname)) - 1] != '/')
- strcat (local_temp_filename, "/");
-
- strcat (local_temp_filename, partial);
- strcat (local_temp_filename, info_suffixes[i]);
-
- statable = (stat (local_temp_filename, &finfo) == 0);
-
- if (statable && (finfo.st_mode & S_IFMT) == S_IFREG)
- {
- free (temp_dirname);
- return (local_temp_filename);
- }
- }
- free (temp_dirname);
- }
- }
- }
- return (partial);
- }
-
- /* Given a string containing units of information separated by colons,
- return the next one pointed to by IDX, or NULL if there are no more.
- Advance IDX to the character after the colon. */
- char *
- extract_colon_unit (string, idx)
- char *string;
- int *idx;
- {
- register int i, start;
-
- i = start = *idx;
- if ((i >= strlen (string)) || !string)
- return ((char *) NULL);
-
- while (string[i] && string[i] != ':')
- i++;
- if (i == start)
- {
- return ((char *) NULL);
- }
- else
- {
- char *value = xmalloc (1 + (i - start));
- strncpy (value, &string[start], (i - start));
- value[i - start] = '\0';
- if (string[i])
- ++i;
- *idx = i;
- return (value);
- }
- }
-
- /* **************************************************************** */
- /* */
- /* The echo area. */
- /* */
- /* **************************************************************** */
-
- /* echoarea.c -- some functions to aid in user interaction. */
-
- WINDOW echo_area = {0, 0, 0, 0, 0, 0};
- boolean echo_area_open_p = false;
- char modeline[256];
- WINDOW modeline_window = {0, 0, 0, 0, 0, 0};
-
- /* Define the location of the echo area. Also inits the
- modeline as well. */
- init_echo_area (left, top, right, bottom)
- int left, top, right, bottom;
- {
- echo_area.left = modeline_window.left = left;
- echo_area.top = top;
- modeline_window.top = top - 1;
- echo_area.right = modeline_window.right = right;
- echo_area.bottom = bottom;
- modeline_window.bottom = modeline_window.top;
- }
-
- /* Make the echo_area_window be the current window, and only allow
- output in there. Clear the window to start. */
- new_echo_area ()
- {
- if (!echo_area_open_p)
- {
- push_window ();
- set_window (&echo_area);
- echo_area_open_p = true;
- }
- goto_xy (the_window.left, the_window.top);
- clear_eop ();
- }
-
- /* Return output to the previous window. */
- close_echo_area ()
- {
- if (!echo_area_open_p)
- return;
-
- pop_window ();
- echo_area_open_p = false;
- }
-
- /* Clear the contents of the echo area. */
- clear_echo_area ()
- {
- new_echo_area ();
- close_echo_area ();
- }
-
- /* Create and display the modeline. */
- make_modeline ()
- {
- extern int info_buffer_len;
- int width = modeline_window.right - modeline_window.left;
-
- sprintf (modeline, "Info: (%s)%s, %d lines",
- current_info_file, current_info_node, nodelines);
- if (strnicmp
- (opsys_filename (current_info_file), last_loaded_info_file,
- strlen (last_loaded_info_file)) != 0)
- {
- sprintf (&modeline[strlen (modeline)], ", Subfile: %s",
- last_loaded_info_file);
- }
-
- if (strlen (modeline) < width)
- {
- int idx = strlen (modeline);
- while (idx != width)
- modeline[idx++] = '-';
- modeline[idx] = '\0';
- }
-
- if (strlen (modeline) > width)
- modeline[width] = '\0';
- push_window ();
- set_window (&modeline_window);
- goto_xy (the_window.left, the_window.top);
-
- if (terminal_inverse_begin)
- do_term (terminal_inverse_begin);
- print_string (modeline);
- if (terminal_inverse_begin)
- do_term (terminal_end_attributes);
-
- pop_window ();
- }
-
- boolean typing_out = false;
-
- /* Prepare to do output to the typeout window. If the
- typeout window is already open, ignore this clown. */
- open_typeout ()
- {
- if (typing_out)
- return;
-
- push_window ();
- set_window (&terminal_window);
- goto_xy (the_window.ch, the_window.cv);
- typing_out = window_bashed = true;
- }
-
- /* Close the currently open typeout window. */
- close_typeout ()
- {
- if (inhibit_output)
- inhibit_output = false;
- else
- {
- do { untyi_char = getc (stdin); }
- while (untyi_char == -1 && errno == EINTR);
-
- if (untyi_char == SPACE)
- untyi_char = 0;
- }
- pop_window ();
- typing_out = false;
- }
-
- char *
- xrealloc (pointer, bytes)
- char *pointer;
- int bytes;
- {
- char *temp;
-
- if (!pointer)
- temp = (char *)xmalloc (bytes);
- else
- temp = (char *)realloc (pointer, bytes);
-
- if (!temp)
- {
- fprintf (stderr, "Virtual memory exhausted\n");
- restore_io ();
- exit (2);
- }
- return (temp);
- }
-
- char *
- xmalloc (bytes)
- int bytes;
- {
- char *temp = (char *) malloc (bytes);
-
- if (!temp)
- {
- fprintf (stderr, "Virtual memory exhausted\n");
- restore_io ();
- exit (2);
- }
- return (temp);
- }
-