/* @(#)thelp.c 17.1.1.1 (ES0-DMD) 01/25/02 17:48:04 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, MA 02139, USA. Corresponding concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .TYPE Module .IDENTIFICATION thelp.c .AUTHOR Francois Ochsenbein [ESO-IPG] .LANGUAGE C .KEYWORDS Hierarchical Help using TermWindows .ENVIRONMENT TermWindows .VERSION 1.0 13-May-1988: Creation .VERSION 1.1 07-Nov-1988: Possibility of an index with macro \HelpIndex .VERSION 1.2 05-Dec-1988: Check if \Help commented out... .VERSION 1.3 20-Jan-1989: th_set: allows NULL pointer without crash... .VERSION 1.4 03-Mar-1989: Corrected bug signaled by Jeannine: \Help is always triggered, even if it's commented... .VERSION 1.5 29-Mar-1989: Default extension for files = .tex .VERSION 1.6 23-May-1989: Modified documentation .VERSION 1.7 22-Jun-1989: Modified th_set: ONLY ADDRESSES are kept... .VERSION 1.8 05-Apr-1990: Modified PromptKey: The prompt "? for subtopics" appears only if subtopics do exist.. .VERSION 1.9 09-Sep-1990: Bug corrected in load_file: fname is saved in a independant location. .VERSION 1.91 21-Nov-1990: Routine th_setsep to define the list of separators between topics. .VERSION 2.0 04-Dec-1990: Allow Several Indexes to be Merged. (see th_merge) .VERSION 2.1 07-Jun-1991: Added th_loc routine .COMMENTS The module uses actions starting with `H' The \Help and \HelpLoad macros contain four parameters: \begin{TeX} \begin{enumerate} \item The level, a number starting from 0 up to 9 \item The topic, or filename for HelpLoad \item A short explanation, for survey purposes. \item The context specification, as a set of words separated by commas. \end{enumerate} A located topic starts {\em just after} the {\tt\b Help} found macro. \end{TeX} The \HelpIndex has 7 parameters: \begin{TeX} \begin{enumerate} \item The level, a number starting from 0 up to 9 \item The topic, or filename for HelpLoad \item A short explanation, for survey purposes. \item The context specification, as a set of words separated by commas. \item The name of the file where the help is included \item The starting position just following the \b Help \item The number of bytes to load. \end{enumerate} \end{TeX} --------------------------------------------------------------------------*/ #define DEBUG 0 /* For debugging only */ #define PM_LEVEL LEVEL_TX #define MAX_TOPIC 31 /* Maximal length for topic comparison */ #include #include #include #include #include #include #include #define reply_size 40 #define default_ext ".tex" #define FileDate(f) fi_date(NameFile(f,default_ext)) #define _REDISPLAY_ 10 #define _PROMPT_ 11 #define _SUMMARY_ 12 #define _BAD_INDEX_ -3 static int TheDoc = 0; /* DOC Folder */ static WINDOW *helpw = NULL_WINDOW; /* Small ^G Window */ static TeX *htex = NULL_PTR(TeX); TeX *tx_tex(); typedef struct { char *start; char *end; } PIECE; /* Piece of text to use with DisplayArray */ static PIECE loaded_file; /* Just loaded file */ typedef struct { char level; /* Level 0-9 */ char flag; /* '@' to load a file */ short ltopic; /* Proposed topic length*/ char *topic; /* The topic address */ char *got_topic; /* The got topic address*/ char *hpos; /* Where exactly is \Help */ char *pos; /* Where exactly in current piece */ int piece; /* Position in PIECE */ int ofile; /* Filenames Position */ long fpos; /* Position in File */ int ltext; /* Text length */ } TOPIC; typedef struct { int ofile; /* Filenames Position */ long fpos; /* Position in File */ int ltext; /* Text length */ char text[1]; /* Filename + Text */ } LOADED; static LOADED *loaded = NULL_PTR(LOADED); static TOPIC topics[10] = { /* Current topic to look for */ {'0', 0, 0, (char *)0, (char *)0, (char *)0, 0}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}}; static TOPIC topc; static char *located_help = (char *)0; /* Used by th_loc */ static char help_flag; /* Used for communication with th_act */ static char level_found; /* Used for communication with th_act */ static char reply[reply_size]; static char blank = ' '; static int subtopics = 0; /* Used for communication with EdTop */ static TWHELP help; /* Contains the Help definitions: windows + buffer */ static BUFFER *list; /* This buffer collects title + subtopics */ static BUFFER *filenames; /* This buffer collects filenames */ static char *tit3 = (char *)0; static int ltit3 = 0; static unsigned char sep_table[256]; static char *sep_top = (char *)0; /* Default Separators */ #define FINISH goto FIN #define HELP_MACRO "\\Help" #define HELP_INDEX "\\HelpIndex" #define default_title " Help" MONITOR(TXHELP); /*========================================================================== * Interfaces to Doc *==========================================================================*/ static int myOpenDocText(str,len) /*+++ .PURPOSE Interface to OpenDocText .RETURNS NOK if not completely displayed / OK otherwise ---*/ char *str; /* IN: TeX string to display */ int len; /* IN: Length of Text */ { CloseDoc(TheDoc); TheDoc = OpenDocText(help.w[1], str, len); RaiseWindow(help.w[1]); return(atLastDocPage(TheDoc)); } static int myOpenDocArray(str,n) /*+++ .PURPOSE Interface to OpenDocText .RETURNS NOK if not completely displayed / OK otherwise ---*/ char **str; /* IN: TeX strings to display */ int n; /* IN: How many strings */ { CloseDoc(TheDoc); TheDoc = OpenDocArray(help.w[1], str, n); RaiseWindow(help.w[1]); return(atLastDocPage(TheDoc)); } static int get_tit3() /*+++ .PURPOSE Get the 3rd parameter of Title .RETURNS Length of Text ---*/ { int l3; char *p; l3 = tex_getvparm(3); /* The short explanation */ p = (l3 > 0 ? htex->ap : ""); if (l3 < 0) l3 = 0; if (l3 >= ltit3) /* Must expand */ ltit3 = 1 + (l3|7), tit3 = MEM_EXP(char, tit3, ltit3); oscopy (tit3, p, l3); tit3[l3] = EOS; return(l3); } /*========================================================================== * act0 *==========================================================================*/ static int act0(str, len) /*+++ .PURPOSE Action Routine to execute definitions in a help file .RETURNS len (does nothing) .REMARKS ---*/ char *str; /* IN: Action string, should start with H */ int len; /* IN: Length of action string */ { return(len); } /*========================================================================== * CheckContext *==========================================================================*/ static int CheckContext() /*+++ .PURPOSE Checks if the context is OK .RETURNS OK / NOK .REMARKS Context is a string in help; if the 4th parameter of \Help includes something, the same string must be in help.context ---*/ { char *p; int status; status = OK; tex_getparm(4); /* Get 4th parameter */ if (*(htex->ap) == EOS) /* No 4th parameter => always valid */ FINISH; /* Check if 4th parameter includes context */ p = htex->ap + stritem(htex->ap, help.context, "\t, "); /* Separators may be , blank tab */ if (*p == EOS) status = NOK; FIN: return(status); } /*========================================================================== * th_act *==========================================================================*/ static int th_act(str, len) /*+++ .PURPOSE Action Routine to interpret the `\Help' Macros .RETURNS 0 (must stop) .REMARKS Context checking and \HelpIndex check. ---*/ char *str; /* IN: Action string, should start with H */ int len; /* IN: Length of action string */ { if (*str != 'H') FINISH; if (help.context) if (*help.context) if_not(CheckContext()) FINISH; tex_getparm(1); /* Indicates Level */ if ((help.indexed == 0) && (str[1] == ':')) FINISH; level_found = '0' + atoi(htex->ap); help_flag = str[1]; /* @ for HelpLoad */ tex_getparm(2); /* Get argument = Topic or File Name */ FIN: return(0); } /*========================================================================== * th_out *==========================================================================*/ static int th_out(str, len) /*+++ .PURPOSE Output Routine to interpret the `\Help' Macros .RETURNS 0 (must stop) .REMARKS ---*/ char *str; /* IN: Action string, should start with H */ int len; /* IN: Length of action string */ { return(0); /* Must stop, should NEVER be called !!! */ } /*==========================================================================*/ static int next_help(str, len) /*+++ .PURPOSE Locate next \Help .RETURNS Offset found .REMARKS Check if not in comment ... ---*/ char *str; /* IN: Text in which locate \Help */ int len; /* IN: Length of str */ { char *b, *be, *bc; int is_a_comment; b = str, be = b + len; while(b < be) { b += oscindex(b, be-b, HELP_MACRO, sizeof(HELP_MACRO)-1); if (b >= be) break; /* Check if it's not a comment... */ is_a_comment = FALSE; for (bc = b; (bc > str) && (*bc != '\n'); bc--) { if ((*bc == '%') && (*(bc-1) != '\\')) /* It's a comment */ { is_a_comment = TRUE; break; } } if (is_a_comment) b += sizeof(HELP_MACRO) -1; else break; } return(b - str); } /*========================================================================== * Help Merging *==========================================================================*/ static int which_help(str, len, topic) /*+++ .PURPOSE interpret the next Help as (level+Text) .RETURNS Offset found .REMARKS The actions th_act are assumed to be in effect. ---*/ char *str; /* IN: Text in which locate \Help */ int len; /* IN: Length of str */ char *topic; /* OUT: Level + Topic, assumed size MAX_TOPIC+1 */ { char *p, *pe; p = str; pe = p + len; *topic = 0; p += next_help(p, pe-p); if (p < pe) { tex_exec(htex, p, pe-p); *topic = level_found; strncopy(topic+1, MAX_TOPIC, htex->ap); } return(p - str); } static PIECE *MergeHelp(p1, p2) /*+++ .PURPOSE Merge two loaded helps into a single index .RETURNS The merged piece .REMARKS Works properly only for Sorted Indexed Helps. p1 and p2 are freed. ---*/ PIECE *p1; /* IN: First Help tree */ PIECE *p2; /* IN: Second Help tree */ { char *b1, *b2, *c1, *c2; char top1[MAX_TOPIC+1], top2[MAX_TOPIC+1]; int i; int (*old_act)(), (*old_out)(); static PIECE p; /* Allocate first the new Piece */ old_act = htex->action, old_out = htex->output; htex->action = th_act, htex->output = th_out; i = (p1->end - p1->start) + (p2->end - p2->start); p.start = MEM_GET(char, i); p.end = p.start; b1 = c1 = p1->start; b2 = c2 = p2->start; c2 += which_help(c2, p2->end - c2, top2); if (top2[0] != '0') FINISH; c2++; c2 += which_help(c2, p2->end - c2, top2); if (top2[0] != '1') FINISH; b2 = c2; /* Ignore 2nd help tree until first topic */ c1 += which_help(c1, p1->end - c1, top1); if (top1[0] != '0') FINISH; c1++; c1 += which_help(c1, p1->end - c1, top1); if (top1[0] != '1') FINISH; p.end += oscopy(p.end, b1, c1-b1); b1 = c1; while ( (c1 < p1->end) && (c2 < p2->end)) { i = top2[0] - top1[0]; if (i == 0) i = studiff(top1, top2); if (i <= 0) { /* top1 comes first */ ++c1; c1 += which_help(c1, p1->end - c1, top1); p.end += oscopy(p.end, b1, c1-b1); b1 = c1; } if (i >= 0) { /* top2 comes first */ ++c2; c2 += which_help(c2, p2->end - c2, top2); if (i) p.end += oscopy(p.end, b2, c2-b2); b2 = c2; } } FIN: p.end += oscopy(p.end, b1, p1->end - b1); /* Append rest */ p.end += oscopy(p.end, b2, p2->end - b2); /* Append rest */ MEM_FREE(p1->start); MEM_FREE(p2->start); htex->action = old_act, htex->output = old_out; return(&p); } /*========================================================================== * Reset anything at specified level *==========================================================================*/ static int Reset(lev) /*+++ .PURPOSE Reset the list of subtopics to specified level: title and starting point of subtopics. .RETURNS The level .REMARKS ---*/ int lev; /* IN: The current level */ { char *b; int i; /* First, reset the Title in the List buffer */ for (b = list->buf, i = 0; i <= lev; i++) b += oscloc(b, list->offset, ' ') + 1; list->offset = b - list->buf; list->used = list->offset; /* Second, reset starting point of subtopic */ i = lev+1; if (i <= 9) { topics[i].piece = topics[lev].piece; topics[i].pos = topics[lev].pos; topics[i].ltopic= 0; } return(lev); } /*========================================================================== * LocHelp *==========================================================================*/ static int LocHelp(t) /*+++ .PURPOSE Locate the next `\Help' macro .RETURNS Level (t->level) as a number / -1 if end found .REMARKS On return, t->pos AND t->hpos point to the text starting with \Help The context is checked. If the index is not loaded, \HelpIndex is ignored. ---*/ TOPIC *t; /* MOD: Starting search point */ { PIECE *p; char *st; int got, flag; int (*old_act)(), (*old_out)(); got = -1; t->got_topic = NULL_PTR(char); old_act = htex->action, old_out = htex->output; htex->action = th_act, htex->output = th_out; flag = 0; /* Indicates starting of a new piece */ while(1) /* Loop over pieces */ { if (t->piece >= help.buf->used) FINISH; p = (PIECE *)(help.buf->buf + t->piece); if (flag) t->pos = p->start; flag = 0; if (t->pos >= p->end) { t->piece += sizeof(PIECE); flag = 1; continue; } t->pos += next_help(t->pos, p->end - t->pos); if (t->pos >= p->end) continue; /* Not found */ /* Something starting with \Help found * Interpret with the TeX parser -- * but be sure it's not a comment! */ #if 0 for (st = t->pos; st > p->start; st--) if ( (*st == '\n') || (*st == '%')) break; if (*st == '%') /* It's a comment */ { t->pos += 1; continue; } #endif level_found = '\0'; tex_exec(htex, t->pos, p->end - t->pos); if (level_found) break; t->pos += 1; } /* Here if a \Help macro found: level is stored as level_found, * and the found topic / filename as htex->ap. */ t->level = level_found; t->flag = help_flag; t->hpos = t->pos; t->got_topic = htex->ap; got = level_found - '0'; FIN: htex->action = old_act, htex->output = old_out; return(got); } /*========================================================================== * Sorry (no help available...) *==========================================================================*/ static int Sorry(asked, exit_option) /*+++ .PURPOSE Display the "Sorry" message ... .RETURNS OK .REMARKS ---*/ char *asked; /* IN: What was asked for and not found */ int exit_option; /* IN: 1 to log error, 0 to output on window */ { static char *sp[] = {"Sorry, no such", NULL_PTR(char)}; int i; if (exit_option) ERR_ED_STRING("Topic not found: ", asked); else { sp[1] = help.title; ClearWindow(help.w[0]); SetAttr(help.w[0], _BOLD_|_BLINK_|_REVERSE_); for (i = 0; i < ITEMS(sp); i++) Put(help.w[0], sp[i]); SetAttr(help.w[0], _NORMAL_); WriteBinary(help.w[0], list->buf, list->offset); SetAttr(help.w[0], _UNDERSCORE_); Put(help.w[0], asked); SetAttr(help.w[0], _NORMAL_); Bell(); RaiseWindow(help.w[0]); } return(OK); } /*========================================================================== * BadIndex *==========================================================================*/ static int BadIndex() /*+++ .PURPOSE Display a message if index is too old. Execute the bad_index. .RETURNS _BAD_INDEX_ .REMARKS Replace \HelpIndex by \HelpLoad and replace arg. 2,3,4 by blanks ---*/ { int k; ERR_ED_STRING("File was modified: ", htex->ap); ClearWindow(help.w[0]); SetAttr(help.w[0], _BOLD_|_BLINK_|_REVERSE_); PutCentered(help.w[0], " Index Invalid "); SetAttr(help.w[0], _NORMAL_); RaiseWindow(help.w[0]); /* Modify \HelpIndex{0} to \HelpLoad{0} */ topc.piece = 0; topc.pos = ((PIECE *)(help.buf->buf))->start; LocHelp(&topc); oscopy(topc.pos+5, "Load ", 5); topc.pos += 1 + oscloc(topc.pos, 200, '{'); for (k=3; --k>=0; ) { topc.pos += oscloc(topc.pos, 200, '{'); *(topc.pos) = ' '; oscfill(topc.pos, oscloc(topc.pos, 200, '{'), ' '); } if (help.bad_index) oshcmd(help.bad_index, NULL_PTR(char), NULL_PTR(char), NULL_PTR(char)); return(_BAD_INDEX_); } /*========================================================================== * oFile *==========================================================================*/ static int oFile(filename) /*+++ .PURPOSE Save filename in `filenames' buffer. .RETURNS Position in `filenames' buffer where file name is saved. .REMARKS Check if filname already exists. The default extension is added. ---*/ char *filename; /* IN: File to save */ { int l, o; char *p, *complete_filename; complete_filename = NameFile(filename, default_ext); l = strlen(complete_filename) + 1; for (o=0; o < filenames->used; ) { p = filenames->buf + o; if (oscomp(p, complete_filename, l) == 0) break; o += 1 + strlen(p); } /* If not yet existing, add in buffer */ if (o >= filenames->used) { o = filenames->used; BUF_AppendItems(filenames, char, complete_filename, l); } return(o); } /*========================================================================== * load_file *==========================================================================*/ static int load_file(filename) /*+++ .PURPOSE Load a file in a new allocated piece of memory. This piece is detailed in the static loaded_file. .RETURNS --- OK (no remaining text) --- NOK (file not found) .REMARKS Execute definitions in the loaded file, until the first \Help. Default extension is .tex ---*/ char *filename; /* IN: File to load */ { char *b, *fname; int status, fid, lb; unsigned int l; long fs; int (*old_act)(), (*old_out)(); /* Open first the File. If fails, return */ status = NOK; fname = strsave(NameFile(filename, default_ext)); fs = fi_size(fname); /* Size of File */ if_not(fid = fi_open(fname, READ|RECORD_MODE)) FINISH; l = fs; if (l == 0) FINISH; /* Allocate memory to copy file */ if_not ( b = MEM_GET(char, l)) FINISH; loaded_file.start = b; loaded_file.end = b + l; /* Read file until first \Help */ for (fs=0; fi_gets(fid, b, loaded_file.end - b) > 0; fs = fi_tell(fid)) { if (*b == '%') continue; /* Comment line... */ lb = strlen(b); if (*(b + oscindex(b, lb, HELP_MACRO, sizeof(HELP_MACRO)-1))) break; b += lb; *(b++) = '\n'; } fi_close(fid); /* Execute the TeX definitions before the first \Help */ old_act = htex->action, old_out = htex->output; htex->action = act0, htex->output = act0; TeX_Execute(htex, loaded_file.start, b - loaded_file.start); htex->action = old_act, htex->output = old_out; /* Load the Remaining part of the File */ l = (loaded_file.end - loaded_file.start) - fs; MEM_FREE(loaded_file.start); b = MEM_GET(char, l); loaded_file.start = b; loaded_file.end = b + fi_load(fname, fs, b, l); status = OK; FIN: strfree(fname); /* Release the memory */ return(status); } /*========================================================================== * load_help *==========================================================================*/ static int load_help(filename, start_level) /*+++ .PURPOSE Load a new help file (triggered by \HelpLoad). It modifies the levels of the loaded file. .RETURNS OK / NOK (something failed) .REMARKS ---*/ char *filename; /* IN: File to load */ char start_level; /* IN: Lowest level */ { int status, i; char *b; status = NOK; if_not(load_file(filename)) FINISH; status = OK; if (start_level <= '0') FINISH; b = loaded_file.start; i = start_level - '0'; while (b < loaded_file.end) { b += next_help(b, loaded_file.end - b); b += oscspan(b, loaded_file.end - b, _ALPHA_|_PUNCT_|_SPACE_, main_ascii); /* Skip until digit */ if (b >= loaded_file.end) break; *b += i; /* Modify the Level */ } FIN: return(status); } /*========================================================================== * help_load *==========================================================================*/ static int help_load(t) /*+++ .PURPOSE Treats the `\HelpLoad{lev}{filename}' macro .RETURNS OK / NOK (something failed) .REMARKS Filename is given as t->got_topic, level as t->level ---*/ TOPIC *t; /* IN: The current topic */ { PIECE *p; char *b; int status, l; /* 1. The filename is in t->got_topic; load it */ status = NOK; b = t->pos + tex_tell(); /* Position just following the \Help */ t->flag = '.'; /* File is now loaded */ if_not(load_help(t->got_topic, t->level)) FINISH; /* 2. Allocate two new PIECE items in help: * - 1 for the new file * - 1 for the second part following the \HelpLoad * These items must be located where we are. */ help.buf->offset = t->piece; l = help.buf->used - help.buf->offset; /* Length of 2nd part */ if_not(BUF_AllocateItems(help.buf, PIECE, 2)) FINISH; p = BUF_ItemPosition(help.buf, PIECE); oscopy((char *)(p+2), (char *)p, l); /* Duplicates current piece */ /* 3. Modify the current length (stops just before \HelpLoad */ p->end = t->pos; /* 4. Insert the loaded file */ *++p = loaded_file; /* 5. Modify the 2nd part (starts just after \HelpLoad */ ++p; p->start = b; status = OK; FIN: return(status); } /*========================================================================== * LocTop *==========================================================================*/ static int LocTop(lev, opt, optl) /*+++ .PURPOSE Locate the topic of specified level .RETURNS Lev if found - another number otherwise .REMARKS Comparison is case insensitive. On return, the current text starts just after the \Help. ---*/ int lev; /* IN: Level to look for */ int opt; /* IN: 1 to add topic in title / 2 for Explanation */ int optl; /* IN: 1 to locate file with text / 0 otherwise */ { int i, size; long pos; for (topc = topics[lev]; (i = LocHelp(&topc)) >= lev; topc.pos += 1) { if (i > lev) continue; /* Check if a file was found. If yes, load it */ if (topc.flag == '@') { help_load(&topc); continue; } if (osccomp(topc.topic, topc.got_topic, topc.ltopic) == 0) break; /* Topic is found */ } if (i != lev) return(i); /* Not Found */ if (opt & 2) get_tit3(); if (opt & 1) { /* Modify the title */ list->used = list->offset; BUF_AppendString(list, topc.got_topic); BUF_AppendItem(list, char, &blank); list->offset = list->used ; } if ((topc.flag == ':') && (optl)) /* From Index: Load Parameters */ { tex_getparm(6); topc.fpos = atol(htex->ap); /* Start of file */ tex_getparm(7); topc.ltext = atoi(htex->ap); /* Length in file */ tex_getparm(5); topc.ofile = oFile(htex->ap); /* Filename */ } topc.pos += tex_tell(); /* Position after the \Help macro */ topics[lev] = topc; /* Copy found topic */ return(lev); } /*========================================================================== * GetSubTop *==========================================================================*/ static int GetSubTop(lev) /*+++ .PURPOSE Locate the subtopics of specified level .RETURNS Number of subtopics .REMARKS ---*/ int lev; /* IN: Level of Topic */ { int i, n, len; static char ed[] = "\\CheckCols{00}\05\01"; Reset(lev); BUF_AppendString(list,"\\vfill\n\\Rule\n"); i = lev+1; for (n = 0; LocTop(i, 0, 0) == i; n++) { len = strlen(topics[i].got_topic); ed[11] = '0' + len/10; ed[12] = '0' + len%10; BUF_AppendItems(list, char, ed, sizeof(ed)-1); BUF_AppendItems(list, char, topics[i].got_topic, len); BUF_AppendItems(list, char, "\05\02 \\tab", 7); } if (n == 0) list->used = list->offset; /* No subtopics */ else BUF_AppendItems(list, char, "\n\\SkipLine", 10); return(n); } /*========================================================================== * Prompt *==========================================================================*/ static WINDOW *CreateHelpWindow() /*+++ .PURPOSE Create the customized help window .RETURNS The window created .REMARKS ---*/ { WINDOW *helpw; #if 0 static char help_1[] = "Answer by non-ambiguous (sub)topic(s)\ separated with blanks, or the keys:\n\\SkipLine\n\\begin{table}{c p}{3}\ {\\bf?}&to redisplay previous topic\\\\\ {\\bf\\b}&to list a summary\\\\\ {\\bf Ret}&to return to previous topic\\\\ "; static char help_2[] = "{\\bf \\^A} & to Exit\\\\ "; static char help_3[] = "\ \\end{table}\n\\SkipLine\nTopic names are {\\Vu case insensitive}."; static char *help_text[] = { help_1, help_1+sizeof(help_1)-1, help_2, help_2+sizeof(help_2)-1, help_3, help_3+sizeof(help_3)-1 }; /* Create Help Window */ help_2[7] = '@' + GetControl(TW_cc_EOF); if (helpw = OpenWindow("Help Topics", 0, -29, -18, 29, _REVERSE_, _TITLE_|_BORDER_|_BORDER2_|_DISPLAY_, 0)) DisplayArray(helpw, help_text, ITEMS(help_text)/2); #else static char help_text[] = "\ Answer by non-ambiguous (sub)topics\n\ separated with blanks, or the keys\n\n\ \017^\04\016 to Exit\n\ \017Ret\016 to return to previous topic\n\ \017\\\016 to list a Summary\n\ \017?\016 to redisplay previous topic\n\ \017^B\016 to redisplay previous page\n\ \017.\016 to display one more line\n\n\ Topic names are \017case insensitive\016"; help_text[strloc(help_text, '\04')] = '@' | GetControl (TW_cc_EOF ); helpw = AdjustedWindow("Help on Help", help_text, _REVERSE_, _BORDER_|_TITLE_, _UPRIGHT_); #endif return(helpw); } static int Prompt1(opt) /*+++ .PURPOSE Prompt for Continuation .RETURNS -1 to stop / _SUMMARY_ to list the Summary (triggered by \) / _REDISPLAY_ to redisplay the parent topic / _PROMPT_ when just Return was entered / OK if a subtopic was typed in .REMARKS ---*/ int opt; /* IN: 1 if Bottom Reached / 2 for Exit with Q */ { int status, at_bottom, old_pos; static char specials[] = " \02\04\06\025.\r?\\"; /* Those stop input */ /* Special Keys are ^B ^D ^F ^U */ at_bottom = opt & 1; status = 0; old_pos = GetPosition(help.w[2]); while (status == 0) { SetPosition(help.w[2], old_pos); ClearRight(help.w[2]); if (opt & 2) { status = GetKey1(help.w[2], reply, specials); if (status > 0) status = 0; } else status = Gets1(help.w[2], reply, sizeof(reply), specials); if ((status == OK) && (reply[0] == EOS)) status = 0, reply[0] = ' '; if (status == 0) switch(reply[0]) { case '\\': status = _SUMMARY_; break; case '?': status = _REDISPLAY_; break; case '\r': case EOS: if (at_bottom) status = _PROMPT_; else reply[0] = ' '; break; } if (status) continue; status = MoreDoc(TheDoc, reply[0]); at_bottom = atLastDocPage(TheDoc); if (status == NOK) Bell(); if (status == OK) status = 0; } FIN: ClearWindow(help.w[2]); return(status == 0 ? OK : status); } static int Prompt(lev) /*+++ .PURPOSE Inquire for a Topic .RETURNS -1 to stop / _SUMMARY_ to list the Summary (triggered by \), _REDISPLAY_ to redisplay the parent topic. .REMARKS A special Help window is used, in case of Control-K ---*/ int lev; /* IN: Level / -1 for Continuation */ { int status; RaiseWindow(help.w[2]); ActiveWindow(help.w[2]); SetAttr(help.w[2], _REVERSE_); if (lev > 0) { WriteBinary(help.w[2], list->buf, list->offset); Put(help.w[2], "Subtopic ? "); } else Put(help.w[2], " Topic ? "); SetAttr(help.w[2], _NORMAL_); WriteBinary(help.w[2], &blank, 1); status = Prompt1(1); return(status); } static int PromptKey(opt) /*+++ .PURPOSE Prompt for Continuation .RETURNS -1 to stop / _SUMMARY_ to list the Summary (triggered by \), _REDISPLAY_ to redisplay the parent topic. .REMARKS ---*/ int opt; /* IN: non-zero to display the ? */ { int status, opt1; RaiseWindow(help.w[2]); ActiveWindow(help.w[2]); SetAttr(help.w[2], _REVERSE_); Put(help.w[2], " to continue"); Put(help.w[2], ", "); SetAttr(help.w[2], _REVERSE_|_BOLD_); opt1 = 0; /* Option for Prompt1 */ if (opt) { Put(help.w[2], "?"); SetAttr(help.w[2], _REVERSE_); Put(help.w[2], " for list of subtopics"); } else { Put(help.w[2], "q"); SetAttr(help.w[2], _REVERSE_); Put(help.w[2], " to quit"); opt1 = 2; /* Allow to Exit with Q */ } Put(help.w[2], " "); SetAttr(help.w[2], _NORMAL_); status = Prompt1(opt1); return(status); } /*========================================================================== * GetSummary *==========================================================================*/ static int GetSummary(lev) /*+++ .PURPOSE Prepare complete list of all subtopics .RETURNS OK .REMARKS ---*/ int lev; /* IN: Level of Topic */ { int i, l, l3, m; char *b, buf[80]; Reset(lev); topc = topics[lev]; BUF_AppendString(list, "\\begin{table}{ l p }{$$}"); m = 0; /* Size of first column */ while ( (i = LocHelp(&topc)) > lev) { if (topc.flag == '@') { help_load(&topc); continue; } l = (i-lev-1)*4; /* Length of first column */ b = buf + oscfill(buf, l, '~'); *(b++) = '{'; if (l == 0) b += strcopy(b, "\\bf"); *(b++) = '\05'; *(b++) = '\01'; /* begin{verbatim} */ BUF_AppendItems(list, char, buf, b-buf); BUF_AppendString(list, topc.got_topic); BUF_AppendItems(list, char, "\05\02}&", 4); l += 1 + strlen(topc.got_topic); m = MAX(m, l); l3 = get_tit3(); /* The short explanation */ if (l3 > 0) BUF_AppendItems(list, char, tit3, l3); BUF_AppendItems(list, char, "\\\\", sizeof("\\\\")-1); topc.pos += 1; } BUF_AppendString(list, "\n\\end{table}"); b = list->buf + list->offset; b += oscloc(b, 80, '$'); *(b++) = '0' + m/10, *(b++) = '0' + m%10; return(OK); } /*========================================================================== * EdTop *==========================================================================*/ static int EdTop(lev) /*+++ .PURPOSE Edit the found Topic and list of Subtopics .RETURNS _EOF_ / _INTERRUPT_ / _PROMPT_ / OK (something was typed) / _REDISPLAY_ (no subtopic, and ? was typed) / _SUMMARY_ .REMARKS The static subtopics contains in return the number of found subtopics. The relevant file is loaded if help is indexed. ---*/ int lev; /* IN: Level of Topic */ { PIECE *p; static char *as[4]; int status, n; /* Locate Text */ p = (PIECE *)(help.buf->buf + topics[lev].piece); /* If help is indexed, load the file */ if (topics[lev].flag == ':') { if ((loaded) && (loaded->fpos == topics[lev].fpos) && (loaded->ofile== topics[lev].ofile)) ; /* Topic already loaded */ else { loaded = (LOADED *)mm_expand(loaded, sizeof(LOADED)+ topics[lev].ltext); loaded->ofile = topics[lev].ofile; if (fi_date(filenames->buf + loaded->ofile) > help.date) return(BadIndex()); /* File was modified... */ loaded->fpos = topics[lev].fpos; loaded->ltext = topics[lev].ltext; as[0] = loaded->text; loaded->ltext = fi_load(filenames->buf + loaded->ofile, loaded->fpos, /* Pos in file */ as[0], loaded->ltext); as[1] = as[0] + loaded->ltext; } } else as[0] = topics[lev].pos, as[1] = p->end; /* Edit the title in the help.w[0] window */ ClearWindow(help.w[0]); SetAttr(help.w[0], _BOLD_), Put(help.w[0],help.title), Put(help.w[0],": "), SetAttr(help.w[0], _NORMAL_); if (help.flags & 2) /* Use Short Title */ ShowString(help.w[0], tit3); else WriteBinary(help.w[0], list->buf, list->offset); TouchWindow(help.w[0]); /* Display the Title */ /* Locate the Subtopics */ subtopics = GetSubTop(lev); /* Now, edit the text + list of subtopics */ as[2] = list->buf + list->offset, as[3] = list->buf + list->used; status = myOpenDocArray(as, 2); while(status == 0) { status = PromptKey(subtopics); if (status < 0) FINISH; switch(status) { case _REDISPLAY_ : /* Display subtopics */ if (subtopics == 0) { Bell(); FINISH;} status = myOpenDocText(as[2], as[3] - as[2]); break; case _PROMPT_ : /* Display Continuation */ break; case _SUMMARY_ : status = _SUMMARY_; FINISH; default: /* A text was typed ahead */ status = OK; FINISH; } RaiseWindow(help.w[1]); } status = _PROMPT_; FIN: return(status); } /*========================================================================== * EdSummary *==========================================================================*/ static int EdSummary(lev) /*+++ .PURPOSE Display the Summary .RETURNS _EOF_ / _INTERRUPT_ / _PROMPT_ / OK (something was typed) / _REDISPLAY_ (no subtopic, and ? was typed) .REMARKS ---*/ int lev; /* IN: Level of Topic */ { int status; /* First, edit the title in the help.w[0] window */ ClearWindow(help.w[0]); SetAttr(help.w[0], _BOLD_); Put(help.w[0], help.title); Put(help.w[0], " Summary:"); SetAttr(help.w[0], _NORMAL_); WriteBinary(help.w[0], list->buf, list->offset); TouchWindow(help.w[0]); /* Display the Title */ status = myOpenDocText(list->buf + list->offset, list->used - list->offset); while(status == 0) { status = PromptKey(1); if (status < 0) FINISH; switch(status) { case _REDISPLAY_ : /* Redisplay subtopics */ GetSubTop(lev); status = myOpenDocText(list->buf + list->offset, list->used - list->offset); break; case _PROMPT_ : /* Terminated (at bottom) */ break; case _SUMMARY_ : MoreDoc(TheDoc, 'g'); /* Display Top */ status = 0; break; default: /* A text was typed ahead */ status = OK; FINISH; } RaiseWindow(help.w[1]); } status = _PROMPT_; FIN: return(status); } /*************************************************************** * P U B L I C R O U T I N E S ****************************************************************/ /*========================================================================== * th_topsep *==========================================================================*/ int th_topsep(list) /*+++ .PURPOSE Define the list of topic separator characters .RETURNS OK .REMARKS Default is blank. ---*/ char *list; /* IN: Available characters as separators */ { strset(sep_table, list); sep_top = list; /* Tells it's initialized */ return(OK); } /*========================================================================== * th_init *==========================================================================*/ TWHELP *th_init(filename, wt, w, wd) /*+++ .PURPOSE Initialize Help procedure: definition of \Help macro, set the three windows. .RETURNS --- OK --- NOK (error of file doesn't exist) .REMARKS Default extension for filename = .tex ---*/ char *filename; /* IN: Name of help file */ WINDOW *wt; /* IN: Window for Help titles */ WINDOW *w; /* IN: Window for Help display */ WINDOW *wd; /* IN: Window for dialogue */ { TWHELP *h; int (*old_act)(), (*old_out)(); short int screen_size[2]; static char definitions[] = "\\def\\Help#4{\\action{H.}}\\def\\HelpLoad#4{\\action{H@}}\ \\def\\HelpIndex#7{\\action{H:}}\\def\\fromHelpFiles#1{}"; ENTER("*th_init"); TRACE_ED_STRING("Help file:", filename); /* Load the definitions */ if_not(htex) { htex = tx_tex(); if (!sep_top) th_topsep(" "); /* Topic Separators */ old_act = htex->action, old_out = htex->output; htex->action = act0, htex->output = act0; TeX_Execute(htex, definitions, sizeof(definitions)-1); htex->action = old_act, htex->output = old_out; list = BUF_Open(char, 256, 128); filenames = BUF_Open(char, 0, 128); tit3 = strsave(""); } /* Initialize the TWHELP structure */ h = NULL_PTR(TWHELP); help.w[0] = wt; help.w[1] = w; help.w[2] = wd; help.buf = NULL_PTR(BUFFER); help.date = FileDate(filename); help.title = default_title; help.context = NULL_PTR(char); help.bad_index = NULL_PTR(char); help.indexed = 0; /* Load Help file */ if_not (load_file(filename)) FINISH; if_not(help.buf = BUF_Open(PIECE, 1, 16)) /* Allocate buffer */ FINISH; /* Check if Help is Indexed */ if (oscomp(loaded_file.start, HELP_INDEX, sizeof(HELP_INDEX)-1) == 0) help.indexed = 1; BUF_AppendItem(help.buf, PIECE, &loaded_file); /* Add this piece to the buffer */ /* If windows are NULL, use defaults */ ScreenSize(screen_size); if_not(help.w[1]) /* No main Window for Help Display */ { help.w[1] = OpenWindow("$help", 0, 0, screen_size[0]-2, 0, _NORMAL_, _DISPLAY_, 0); CursorTo(help.w[1], 1, 0); SetAttr(help.w[1], _GRAPHICS_); Fill(help.w[1], RuleChar(_HORIZONTAL_), screen_size[1]); SetAttr(help.w[1], _NORMAL_); help.w[0] = OpenSubWindow(help.w[1], ".help", 0, 0, 1, 0, _NORMAL_, _DISPLAY_, 0); /* Title of Help Display */ help.w[1] = OpenSubWindow(help.w[1], "=help", 2, 0, 0, 0, _NORMAL_, _DISPLAY_, 0); } if_not(help.w[2]) /* No Dialogue Window for Help Display */ help.w[2] = OpenWindow("?help", -2, 0, 2, 0, _NORMAL_, 0, 5); h = MEM_GET(TWHELP, 1); if (h) *h = help; FIN: EXIT_PTR(TWHELP, h); } int th_merge(help, filename) /*+++ .PURPOSE Merge a Help with another one. The help must just have been opened, and the two help files MUST BE INDEXED. .RETURNS --- OK / NOK .REMARKS Default extension for filename = .tex ---*/ TWHELP *help; /* MOD: Main Help */ char *filename; /* IN: Name of help file */ { int status; BUFFER *b; PIECE *p; ENTER("th_merge"); status = NOK; if_not (help->indexed) { ERROR("Help not Indexed"); FINISH; } b = help->buf; if (b->used != sizeof(PIECE)) { ERROR("Help was used..."); FINISH; } if_not(load_file(filename)) FINISH; if (p = MergeHelp((PIECE *)(b->buf), &loaded_file)) { help->date = oshtime(); /* Be sure no Bad Index Error */ BUF_Clear(b); BUF_AppendItem(b, PIECE, p); status = OK; } FIN: EXIT(status); } /*========================================================================== * th_set *==========================================================================*/ char *th_set(helpin, context, opt) /*+++ .PURPOSE Defines the Help Context / Title / What to do if index too old .RETURNS The old Context. .REMARKS Only address is kept... Use therefore e.g. strsave before calling... ---*/ TWHELP *helpin; /* IN: The loaded Help */ char *context; /* IN: Context */ int opt; /* IN: 0 for Title / 1 for Context */ { char *old_context, **aa; int l, status; ENTER("*th_set"); /* Copy context to a new piece of memory */ old_context = NULL_PTR(char); switch(opt) { case 0: case 1: case 2: aa = &(helpin->title); break; default: ERR_ED_I("Bad option: ", opt); FINISH; } old_context = aa[opt]; aa[opt] = context; FIN: EXITp(old_context); } int th_oset(helpin, mask, opt) /*+++ .PURPOSE Define the Option Context .RETURNS The old Option Context. ---*/ TWHELP *helpin; /* IN: The loaded Help */ int mask; /* IN: Which option */ int opt; /* IN: 0 for Clear / 1 for Set */ { int old_option; old_option = helpin->flags & mask; if (opt) helpin->flags |= mask; else helpin->flags &= ~mask; return(old_option); } /*========================================================================== * th_help *==========================================================================*/ int th_help(helpin, topic, exit_option) /*+++ .PURPOSE Display help on `topic' .RETURNS OK / NOK (error reported) / EOF (the user typed EOF) / -3 (Bad Index) .REMARKS ---*/ TWHELP *helpin; /* IN: The loaded Help */ char *topic; /* IN: Topics (separated by blanks) to display */ int exit_option; /* IN: non-zero to exit immediately after \ display, -1 for locate only */ { char *b, x; int level, level_step, i, status; WINDOW *old_helpw; ENTER("th_help"); strfree(located_help), located_help = (char *)0; if (topic) { TRACE_ED_STRING("Topic: ", topic); i = strlen(topic); i = MIN(i, sizeof(reply)-1); oscopy(reply, topic, i); } else i = 0; reply[i] = EOS; if (!helpw) helpw = CreateHelpWindow(); old_helpw = help.w[2]->help; AttachHelpWindow(help.w[2],helpw); TheDoc = 0; help = *helpin; help.flags |= 1; /* Edit in Top Window */ status = 1; level = 0; /* Starting Level */ level_step = 1; /* Decrease by this amount for _PROMPT_ */ topics[0].piece = 0; topics[0].pos = ((PIECE *)(help.buf->buf))->start; BUF_Clear(filenames); LocTop(0, 0, 1); /* Find level 0 (start) */ if_not(exit_option) ClearWindow(help.w[2]); BUF_Clear(list); BUF_AppendItem(list, char, &blank); /* Initialize list buffer */ while ((status >= 0) && (level >= 0)) { Reset(level); if (level_step < 1) level_step = 1; switch(status) { case _REDISPLAY_: Reset(level); goto EDIT_TOP; case _PROMPT_: /* Get the topic */ status = Prompt(level); if (status == _PROMPT_) level-= level_step, level_step = 1; continue; case _SUMMARY_: /* List the Summary */ GetSummary(level); status = EdSummary(level); continue; } /* Locate topics separated by blanks */ TRACE_ED_STRING("Reply is: ", reply); level_step = 1; for (b = reply; *b; ) { i = strspan_(b, 1, sep_table); if (i) { if (*b != ' ') level_step += 1; b += i; } if_not(*b) break; Reset(level); if (++level > 9) level = 9; topics[level].topic = b; topics[level].ltopic = strscan_(b, 1, sep_table); if (LocTop(level, help.flags, 1) != level) { if (exit_option >= 0) Sorry(b, exit_option); if (exit_option) FINISH; level -= level_step, level_step = 1; status = _PROMPT_; break; } b += topics[level].ltopic; } if (status == _PROMPT_) continue; /* Edit located Topic */ EDIT_TOP: if (exit_option >= 0) status = EdTop(level); else { b = topics[level].pos; x = *b; *b = 0; located_help = strsave(topics[level].hpos); *b = x; } if (exit_option) FINISH; if (status < 0) FINISH; if (status == _REDISPLAY_) continue; if (subtopics == 0) level -= level_step, level_step = 1; } FIN: AttachHelpWindow(help.w[2],old_helpw); CloseDoc(TheDoc), TheDoc = 0; if (status >= 0) status = OK; else if (status == _BAD_INDEX_) helpin->indexed = 0; if (loaded) /* Free allocated memory if Index */ MEM_FREE(loaded), loaded = NULL_PTR(LOADED); EXIT(status); } /*========================================================================== * th_loc *==========================================================================*/ char *th_loc(helpin, topic, option) /*+++ .PURPOSE Retrieve the named topic .RETURNS String starting at the \Help keyword \ NULL .REMARKS ---*/ TWHELP *helpin; /* IN: The loaded Help */ char *topic; /* IN: Topics (separated by blanks) to display */ int option; /* For future use option */ { ENTER("*th_help"); strfree(located_help); located_help = (char *)0; if (th_help(helpin, topic, -1) != OK) strfree(located_help), located_help = (char *)0; EXITp(located_help); }