v05i021: Emacs Info Browsing Widget, Part01/02

Dan Heller argv at island.uu.net
Thu Nov 9 07:36:27 AEST 1989


Submitted-by: Jordan Hubbard <jkh at meepmepp.pcs.com>
Posting-number: Volume 5, Issue 21
Archive-name: eibw/part01

[from the README in posting #2]
This directory contains the sources for an "Emacs Info" browsing widget,
subclassed (conditionally [see below]) from the Athena Pane widget.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then feed it
# into a shell via "sh file" or similar.  To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
#		"End of archive 1 (of 2)."
# Contents:  info-widget info-widget/Info.c
# Wrapped by argv at island on Wed Nov  8 12:29:02 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test ! -d 'info-widget' ; then
    echo shar: Creating directory \"'info-widget'\"
    mkdir 'info-widget'
fi
if test -f 'info-widget/Info.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'info-widget/Info.c'\"
else
echo shar: Extracting \"'info-widget/Info.c'\" \(51392 characters\)
sed "s/^X//" >'info-widget/Info.c' <<'END_OF_FILE'
X#include <X11/IntrinsicP.h>
X#include <X11/Cardinals.h>
X#include <X11/XawMisc.h>
X#include <X11/StringDefs.h>
X#include <X11/Shell.h>
X#include <X11/Box.h>
X#include <X11/Label.h>
X#include <X11/Command.h>
X#include <X11/Viewport.h>
X#include <X11/List.h>
X#include <X11/AsciiText.h>
X
X#include <sys/stat.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <pwd.h>
X
X/* #include <X11/InfoP.h> */
X#include "InfoP.h"
X
X/*
X *
X *                     Copyright 1989
X *                    Jordan K. Hubbard
X *
X *                PCS Computer Systeme, Gm bH.
X *                   Munich, West Germany
X *
X *
X *  All rights reserved.
X *
X *  This is unsupported software and is subject to change without notice.
X *  the author makes no representations about the suitability of this software
X *  for any purpose. It is supplied "as is" without express or implied
X *  warranty.
X *
X *  Permission to use, copy, modify, and distribute this software and its
X *  documentation for any purpose and without fee is hereby granted, provided
X *  that the above copyright notice appear in all copies and that both that
X *  copyright notice and this permission notice appear in supporting
X *  documentation, and that the name of the author not be used in
X *  advertising or publicity pertaining to distribution of the software
X *  without specific, written prior permission.
X *
X */
X
X#define offset(name)	XtOffset(InfoWidget, info.name)
X
Xstatic XtResource resources[] = {
X     { XtNinfoHelp, XtCInfoHelp, XtRString, sizeof(String), offset(helpFile),
X	    XtRString, HELPFILE },
X     { XtNinfoPath, XtCInfoPath, XtRString, sizeof(String), offset(path),
X	    XtRString, XtDefaultInfoPath },
X     { XtNinfoFile, XtCInfoFile, XtRString, sizeof(String), offset(file),
X	    XtRString, XtDefaultInfoFile },
X     { XtNinfoNode, XtCInfoNode, XtRString, sizeof(String), offset(node),
X	    XtRString, XtDefaultInfoNode },
X     { XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t),
X	    offset(callback), XtRCallback, NULL},
X     { XtNprintCommand, XtCPrintCommand, XtRString, sizeof(String),
X	    offset(printCmd), XtRString, XtDefaultPrintCommand },
X};
X
X#undef offset
X
Xstatic void Initialize(), Destroy();
Xstatic Boolean SetValues();
X
Xstatic void NodeDir(), NodeNext(), NodePrev(), NodeUp(), NodeTop(),
X     NodeLast(),  NodeXRef(), NodeGoto(), NodeSearch(), NodeQuit(),
X     NodeMenuSelectByNumber(), NodePrint(), NodeHelp(), NodeTutorial();
X
Xstatic XtActionsRec actions[] =
X{
X     { "dir",		NodeDir },
X     { "next",		NodeNext },
X     { "prev",		NodePrev },
X     { "up",		NodeUp },
X     { "top",		NodeTop },
X     { "last",		NodeLast },
X     { "xref",		NodeXRef },
X     { "goto",		NodeGoto },
X     { "search",	NodeSearch },
X     { "menusel",	NodeMenuSelectByNumber },
X     { "print",		NodePrint },
X     { "quit",		NodeQuit },
X     { "tutorial",	NodeTutorial },
X     { "popupHelp",	NodeHelp },
X     { NULL,		NULL }
X};
X
Xstatic char deflTrans[] = "\
X<Key>1:		menusel(1)\n\
X<Key>2:		menusel(2)\n\
X<Key>3:		menusel(3)\n\
X<Key>4:		menusel(4)\n\
X<Key>5:		menusel(5)\n\
X<Key>6:		menusel(6)\n\
X<Key>7:		menusel(7)\n\
X<Key>8:		menusel(8)\n\
X<Key>9:		menusel(9)\n\
X<Key>Help:	popupHelp()\n\
X<Key>question:	popupHelp()\n\
XMeta<Key>P:	print()\n\
XNone<Key>d:	dir()\n\
XNone<Key>f:	xref()\n\
XNone<Key>g:	goto()\n\
XNone<Key>h:	tutorial()\n\
XNone<Key>l:	last()\n\
XNone<Key>m:	menusel(0)\n\
XNone<Key>n:	next()\n\
XNone<Key>p:	prev()\n\
XNone<Key>q:	quit()\n\
XNone<Key>s:	search()\n\
XNone<Key>t:	top()\n\
XNone<Key>u:	up()\n\
X";
X
X/* page movement translations for text areas */
Xstatic char textTrans[] = "\
XNone<Key>b:	beginning-of-file()\n\
X<Key>Home:	beginning-of-file()\n\
X<Key>Delete:	previous-page()\n\
X<Key>Prior:	previous-page()\n\
X<Key>Next:	next-page()\n\
X<Key>space:	next-page()\n\
X";
X
XInfoClassRec infoClassRec = {
X  { /* core fields */
X    /* superclass		*/	(WidgetClass) &panedClassRec,
X    /* class_name		*/	"Info",
X    /* widget_size		*/	sizeof(InfoRec),
X    /* class_initialize		*/	NULL,
X    /* class_part_initialize	*/	NULL,
X    /* class_inited		*/	False,
X    /* initialize		*/	Initialize,
X    /* initialize_hook		*/	NULL,
X    /* realize			*/	XtInheritRealize,
X    /* actions			*/	actions,
X    /* num_actions		*/	XtNumber(actions),
X    /* resources		*/	resources,
X    /* num_resources		*/	XtNumber(resources),
X    /* xrm_class		*/	NULLQUARK,
X    /* compress_motion		*/	True,
X    /* compress_exposure	*/	True,
X    /* compress_enterleave	*/	True,
X    /* visible_interest		*/	False,
X    /* destroy			*/	Destroy,
X    /* resize			*/	XtInheritResize,
X    /* expose			*/	XtInheritExpose,
X    /* set_values		*/	SetValues,
X    /* set_values_hook		*/	NULL,
X    /* set_values_almost	*/	XtInheritSetValuesAlmost,
X    /* get_values_hook		*/	NULL,
X    /* accept_focus		*/	XtInheritAcceptFocus,
X    /* version			*/	XtVersion,
X    /* callback_private		*/	NULL,
X    /* tm_table			*/	deflTrans,
X    /* query_geometry		*/	XtInheritQueryGeometry,
X    /* display_accelerator	*/	XtInheritDisplayAccelerator,
X    /* extension		*/	NULL
X  },
X  { /* composite_class fields	*/
X    /* geometry_manager		*/	XtInheritGeometryManager,
X    /* change_managed		*/	XtInheritChangeManaged,
X    /* insert_child		*/	XtInheritInsertChild,
X    /* delete_child		*/	XtInheritDeleteChild,
X    /* extension		*/	NULL
X  },
X  { /* constraint_class fields	*/
X    /* subresourses		*/	NULL,
X    /* subresource_count	*/	0,
X    /* constraint_size		*/	sizeof(InfoConstraintsRec),
X    /* initialize		*/	NULL,
X    /* destroy			*/	NULL,
X    /* set_values		*/	NULL,
X    /* extension		*/	NULL
X  },
X  { /* paned_class fields	*/
X    /* empty			*/	0
X  },
X  { /* info fields		*/
X    /* empty			*/	0
X  }
X};
X
XWidgetClass infoWidgetClass = (WidgetClass)&infoClassRec;
X
Xstatic int findNode();
Xstatic int iindex();
Xstatic int strcomp();
Xstatic int strncomp();
Xstatic void freezeSize();
Xstatic void parseIndirect();
Xstatic void message();
Xstatic void strccpy();
Xstatic void parseNode();
Xstatic void parseHeader();
Xstatic void parseMenu();
Xstatic void parseXRefs();
Xstatic void displayNode();
Xstatic void displayHeader();
Xstatic void showStatus();
Xstatic String getFile();
Xstatic String find_file();
Xstatic String offsetToString();
Xstatic String file_name();
Xstatic String eat_whitespace();
Xstatic String search();
Xstatic String search_back();
Xstatic String strconcat();
Xstatic String substr();
Xstatic String reverse();
Xstatic String get_arg();
Xstatic String trueName();
Xstatic String downcase();
Xstatic String strip_evil();
Xstatic Boolean getNode();
Xstatic Boolean parseTags();
Xstatic NodeInfo *pushNode();
Xstatic NodeInfo *popNode();
X
Xstatic void do_prev(), do_up(), do_next(), do_menu(),
X     do_xref(), do_menu_sel(), do_xref_sel(), do_goto(), do_search();
X
Xstatic XtCallbackRec cb[2];
X#define XtSetCbk(argarray, rtn, arg) \
X     cb[0].callback = rtn; \
X     cb[0].closure = (caddr_t)arg; \
X     XtSetArg(argarray, XtNcallback, cb)
X
Xstatic void Initialize(request, new)
XWidget request;
XWidget new;
X{
X     Arg args[10];
X     Cardinal i;
X     InfoWidget iw = (InfoWidget)new;
X     Widget box1, box2, vport, vport2;
X     char blanks[MAXSTR];
X     XtTranslations defltrans, texttrans;
X
X     /* parse the transation tables */
X     defltrans = XtParseTranslationTable(deflTrans);
X     texttrans = XtParseTranslationTable(textTrans);
X
X     /* create a blank filled string as a placeholder for certain labels */
X     for (i = 0; i < MAXSTR - 1; i++)
X	  blanks[i] = ' ';
X     blanks[i] = '\0';
X
X     /*
X      * Create top row of "main control" buttons and labels.
X      */
X     i = 0;
X     box1 = XtCreateManagedWidget("infoNodeBox", boxWidgetClass, new, args, i);
X
X     i = 0;
X     XtSetArg(args[i], XtNjustify, XtJustifyLeft);			i++;
X     iw->info.fileLabel = XtCreateManagedWidget("File: ", labelWidgetClass,
X						box1, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNjustify, XtJustifyLeft);			i++;
X     iw->info.nodeLabel = XtCreateManagedWidget("Node: ", labelWidgetClass,
X						box1, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     XtSetArg(args[i], XtNjustify, XtJustifyLeft);			i++;
X     XtSetCbk(args[i], do_prev, iw);					i++;
X     iw->info.prevCmd = XtCreateManagedWidget("Prev: ", commandWidgetClass,
X					      box1, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     XtSetArg(args[i], XtNjustify, XtJustifyLeft);			i++;
X     XtSetCbk(args[i], do_up, iw);					i++;
X     iw->info.upCmd = XtCreateManagedWidget("Up: ", commandWidgetClass,
X					    box1, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     XtSetArg(args[i], XtNjustify, XtJustifyLeft);			i++;
X     XtSetCbk(args[i], do_next, iw);					i++;
X     iw->info.nextCmd = XtCreateManagedWidget("Next: ", commandWidgetClass,
X					      box1, args, i);
X
X     /* Create the menu pane */
X     i = 0;
X     XtSetArg(args[i], XtNallowVert, True);				i++;
X     XtSetArg(args[i], XtNheight, 150);					i++;
X     vport = XtCreateManagedWidget("menuPort", viewportWidgetClass,
X				   new, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNpasteBuffer, True);				i++;
X     XtSetArg(args[i], XtNcolumnSpacing,  8);				i++;
X     XtSetCbk(args[i], do_menu_sel, iw);				i++;
X     iw->info.menuList = XtCreateManagedWidget("menuList", listWidgetClass,
X					       vport, args, i);
X
X     /*
X      * Create the text area for displaying node contents.
X      */
X     i = 0;
X     XtSetArg(args[i], XtNheight, 500);					i++;
X     XtSetArg(args[i], XtNtextOptions, (wordBreak | scrollVertical));	i++;
X     iw->info.nodeText = XtCreateManagedWidget("infoNode",
X					       asciiStringWidgetClass,
X					       new, args, i);
X     XtOverrideTranslations(iw->info.nodeText, defltrans);
X     XtOverrideTranslations(iw->info.nodeText, texttrans);
X
X
X     i = 0;
X     XtSetArg(args[i], XtNallowVert, True);				i++;
X     XtSetArg(args[i], XtNheight, 150);					i++;
X     vport2 = XtCreateManagedWidget("xrefPort", viewportWidgetClass,
X				    new, args, i);
X     /* Create the xref pane */
X     i = 0;
X     XtSetArg(args[i], XtNpasteBuffer, True);				i++;
X     XtSetArg(args[i], XtNdefaultColumns, 6);				i++;
X     XtSetArg(args[i], XtNcolumnSpacing,  8);				i++;
X     XtSetCbk(args[i], do_xref_sel, iw);				i++;
X     iw->info.xrefList = XtCreateManagedWidget("xrefList", listWidgetClass,
X					       vport2, args, i);
X
X     /*
X      * Create the bottom "auxilliary" command button group.
X      */
X     i = 0;
X     box2 = XtCreateManagedWidget("infoCmdBox2", boxWidgetClass,
X				  new, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     XtSetCbk(args[i], do_menu, iw);					i++;
X     iw->info.xrefCmd = XtCreateManagedWidget("menu", commandWidgetClass,
X					      box2, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     XtSetCbk(args[i], do_xref, iw);					i++;
X     iw->info.xrefCmd = XtCreateManagedWidget("xref", commandWidgetClass,
X					      box2, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     XtSetCbk(args[i], do_goto, iw);					i++;
X     iw->info.gotoCmd = XtCreateManagedWidget("goto", commandWidgetClass,
X					      box2, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     XtSetCbk(args[i], do_search, iw);					i++;
X     iw->info.searchCmd = XtCreateManagedWidget("search", commandWidgetClass,
X						box2, args, i);
X     i = 0;
X     bzero(iw->info.arg, ARGLEN);
X     XtSetArg(args[i], XtNtextOptions, (editable | resizeWidth));	i++;
X     XtSetArg(args[i], XtNeditType, XttextEdit);			i++;
X     XtSetArg(args[i], XtNstring, iw->info.arg);			i++;
X     XtSetArg(args[i], XtNlength, ARGLEN);				i++;
X     XtSetArg(args[i], XtNallowResize, True);				i++;
X     XtSetArg(args[i], XtNresize, True);				i++;
X     XtSetArg(args[i], XtNborderWidth, 2);				i++;
X     iw->info.argText = XtCreateManagedWidget("infoArg",
X					      asciiStringWidgetClass,
X					      box2, args, i);
X
X     /*
X      * Create the status and message area labels.
X      */
X
X     i = 0;
X     XtSetArg(args[i], XtNresize, False);				i++;
X     XtSetArg(args[i], XtNlabel, blanks);				i++;
X     XtSetArg(args[i], XtNborderWidth, 0);				i++;
X     iw->info.statusLabel = XtCreateManagedWidget("infoStatus",
X						  labelWidgetClass,
X						  new, args, i);
X     i = 0;
X     XtSetArg(args[i], XtNresize, False);				i++;
X     XtSetArg(args[i], XtNlabel, blanks);				i++;
X     XtSetArg(args[i], XtNborderWidth, 0);				i++;
X     iw->info.messageLabel = XtCreateManagedWidget("infoMessage",
X						   labelWidgetClass,
X						   new, args, i);
X
X     /* set the initial node information */
X     ZERO_TABLE(INDIRECT(iw));
X     ZERO_TABLE(TAGTABLE(iw));
X     DATA(iw) = NULL;
X     CURNODE(iw) = NULL;
X
X     /* decide who can get bigger */
X     XtPanedAllowResize(vport, True);
X     XtPanedAllowResize(vport2, True);
X     XtPanedAllowResize(iw->info.xrefList, True);
X
X     iw->info.file = XtNewString(iw->info.file);
X     iw->info.node = XtNewString(iw->info.node);
X     if (getNode(iw, iw->info.file, iw->info.node, NULL) == False)
X	  message(iw, "?Can't find initial file/node.");
X}
X
Xstatic void Destroy(w)
XWidget w;
X{
X     InfoWidget iw = (InfoWidget)w;
X
X     if (INDIRECT(iw).table)
X	  FREE_TAG_TABLE(INDIRECT(iw));
X     if (TAGTABLE(iw).table)
X	  FREE_TAG_TABLE(TAGTABLE(iw));
X     while (popNode(iw));	/* popNode will free contents */
X     /* free the last one */
X     if (CURNODE(iw)) {
X	  XtFree(CURNODE(iw)->file);
X	  XtFree(CURNODE(iw)->node);
X	  XtFree(CURNODE(iw));
X     }
X     if (iw->info.file)
X	  XtFree(iw->info.file);
X     if (iw->info.node)
X	  XtFree(iw->info.node);
X}
X
Xstatic Boolean SetValues(current, request, new)
XWidget current, request, new;
X{
X     InfoWidget cw = (InfoWidget)current;
X     InfoWidget nw = (InfoWidget)new;
X
X     if ((cw->info.file != nw->info.file ||
X	  strcomp(cw->info.file, nw->info.file)) ||
X	 (cw->info.node != nw->info.node ||
X	  strcomp(cw->info.node, nw->info.node))) {
X	      XtFree(cw->info.file);
X	      XtFree(cw->info.node);
X	      getNode(nw, nw->info.file, nw->info.node, NULL);
X	 }
X}
X
X/*****************************************************************************
X * Info file manipulation routines.                                          *
X *****************************************************************************/
X
X/* Here is the main guy. Handles all navigation within the info tree. */
Xstatic Boolean getNode(iw, file, node, pushTo)
XInfoWidget iw;
XString file, node;
XNodeInfo *pushTo;
X{
X     NodeInfo *cur;
X     int offset;
X     Boolean status = False;
X
X     if (node && index(node, '(') && index(node, ')')) {
X	  file = substr(node, iindex(node, '(') + 1,
X			iindex(node, ')') - 1);
X	  node = index(node, ')') + 1;
X     }
X     if (!node || !*node)
X	  node = "Top";
X
X     if (!file)
X	  file = iw->info.file;
X
X     if (!DATA(iw) || strcomp(file_name(file), file_name(iw->info.file))) {
X	  /* get a new file */
X	  if ((file = getFile(iw, file, False)) != NULL) {
X	       if (file && iw->info.file != file) {
X		    XtFree(iw->info.file);
X		    iw->info.file = XtNewString(file);
X	       }
X	       iw->info.subFile = NULL;
X	  }
X     }
X     else if (!strcomp(node, iw->info.node))
X	  return(True);	/* we're already there */
X     else {
X	  XtFree(iw->info.node);
X	  iw->info.node = XtNewString(node);
X     }
X     if (file && (offset = findNode(iw, node)) >= 0) {
X	  if (!pushTo) {
X	       cur = pushNode(iw, iw->info.file, iw->info.node, offset);
X	       parseNode(iw, cur, offset);
X	  }
X	  else
X	       cur = pushTo;
X	  displayNode(iw, cur);
X	  message(iw, NULL);
X	  showStatus(iw, cur);
X	  status = True;
X     }
X     else {
X	  /* Failed to get the new node, go back (but only once) */
X	  if (!pushTo && CURNODE(iw))
X	       getNode(iw, CURNODE(iw)->file, CURNODE(iw)->node, CURNODE(iw));
X     }
X     return(status);
X}
X
X/* Loads in file "name" and tag/indirect info, if any. */
Xstatic String getFile(iw, name, subfilep)
XInfoWidget iw;
XString name;
XBoolean subfilep;
X{
X     String ret;
X
X     FILE *fp;
X
X     ret = find_file(iw->info.path, name);
X     if (ret) {
X	  struct stat sb;
X
X	  if (!stat(ret, &sb) && (fp = fopen(ret, "r"))) {
X	       if (DATA(iw))
X		    XtFree(DATA(iw));
X	       DATA(iw) = XtMalloc(sb.st_size);
X	       if (fread(DATA(iw), 1, sb.st_size, fp) == sb.st_size) {
X		    fclose(fp);
X		    DATASIZE(iw) = sb.st_size;
X		    if (!subfilep) {
X			 Boolean needIndirect;
X
X			 needIndirect = parseTags(iw);
X			 parseIndirect(iw, needIndirect);
X		    }
X	       }
X	       else {
X		    message(iw, "?Read error on %s.", name);
X		    XtFree(DATA(iw));
X		    ret = NULL;
X	       }
X	  }
X	  else
X	       ret = NULL;
X     }
X     return(ret);
X}
X
X/* Look through tag table (and/or current buffer) for a node */
Xstatic int findNode(iw, name)
XInfoWidget iw;
XString name;
X{
X     ID_P i;
X     int offset = -1;
X     String s, srch;
X
X     /* A node name of "*" means the whole file */
X     if (!strcomp(name, "*"))
X	  return(0);
X
X     if (TAGTABLE(iw).table) {
X	  for (i = TAGTABLE(iw).table; I_NAME(*i); i++) {
X	       if (!strcomp(I_NAME(*i), name)) {
X		    offset = I_OFFSET(*i);
X		    break;
X	       }
X	  }
X	  /* if we found the tag and there's an indirect table, adjust */
X	  if (offset && INDIRECT(iw).table) {
X	       String sub;
X
X	       for (i = INDIRECT(iw).table; I_NAME(*i); i++) {
X		    if (I_OFFSET(*i) > offset)	/* got it */
X			 break;
X	       }
X	       sub = I_NAME(*(--i));
X	       if (strcomp(sub, iw->info.subFile)) {
X		    if (!getFile(iw, sub, True))
X			 return(0);
X		    else
X			 iw->info.subFile = sub;
X	       }
X	       offset -= I_OFFSET(*i);
X	       /* compensate for header */
X	       offset += HDRSIZE(iw);
X	  }
X     }
X     /*
X      * Now search forward for the node name. Note that this will
X      * work whether or not we found the tag in the tag table. Having
X      * found the tag only insures that we search a little less (personally,
X      * I think that this whole indirect/tag nonsense is a bad idea. If
X      * one could count on tags always being accurate, I wouldn't mind so
X      * much, but one can't. As it is, it's just a pain in the ass on fast VM
X      * systems where file size and search time aren't such significant issues
X      * for files <~600K. Think how much simpler this code would be if
X      * we didn't have to deal with all the indirect/tag crap).
X      */
X
X     s = START(iw) + offset;
X     /*
X      * since bogus tags can leave us *after* the node start as well as
X      * before it, we risk a little extra searching and back up to the
X      * closest node marker above. Es tut mir leid, but this is what you
X      * get with out-of-date tags!
X      */
X     while (s > START(iw) && *s != INFO_CHAR)
X	  --s;
X     srch = strconcat(NODE_TOKEN, name);
X     while (s) {
X	  if ((s = search(iw, s, END(iw), srch, True)) != NULL) {
X	       /* If not an exact match, keep looking */
X	       if (!index(NAME, *s))
X		    continue;
X	       offset = INTOFF(START(iw), s);
X	       /* found it, move to the beginning */ 
X	       while(START(iw)[offset - 1] != INFO_CHAR)
X		    offset--;
X	       s = NULL;
X	  }
X	  else
X	       offset = -1;
X     }
X     return(offset);
X}
X     
X/* Push a node onto the history list */
Xstatic NodeInfo *pushNode(iw, file, node, offset)
XInfoWidget iw;
XString file, node;
Xint offset;
X{
X     NodeInfo *tmp;
X
X     tmp = (NodeInfo *)XtMalloc(sizeof(NodeInfo));
X     tmp->file = XtNewString(file);
X     tmp->node = XtNewString(node);
X     tmp->start = offset;
X     tmp->nextNode = CURNODE(iw);
X     CURNODE(iw) = tmp;
X     return(tmp);
X}
X
X/* Pop a node off the history list */
Xstatic NodeInfo *popNode(iw)
XInfoWidget iw;
X{
X     NodeInfo *tmp = NULL;
X
X     if (CURNODE(iw) && CURNODE(iw)->nextNode) {
X	  tmp = CURNODE(iw)->nextNode;
X	  XtFree(CURNODE(iw)->file);
X	  XtFree(CURNODE(iw)->node);
X	  XtFree(CURNODE(iw));
X	  CURNODE(iw) = tmp;
X     }
X     return(tmp);
X}
X
X/* Parse out all the header/menu/xref information for a node. */
Xstatic void parseNode(iw, n, offset)
XInfoWidget iw;
XNodeInfo *n;
Xint offset;
X{
X     register int i;
X     register String start = START(iw) + offset;
X
X     /* was the whole file ("*") selected? */
X     if (offset == 0) {
X	  n->length = DATASIZE(iw);
X	  I_START(n->name) = I_LEN(n->name) = 0;
X	  I_START(n->prev) = I_LEN(n->prev) = 0;
X	  I_START(n->up) = I_LEN(n->up) = 0;
X	  I_START(n->next) = I_LEN(n->next) = 0;
X	  I_START(n->text) = 0;
X	  I_LEN(n->text) = n->length;
X	  return;
X     }
X     else {
X	  /* find the end of the node */
X	  n->length = 0;
X	  start = START(iw) + offset;
X	  while (start < END(iw) && *start != INFO_CHAR) {
X	       n->length++;
X	       start++;
X	  }
X     }
X     /* get the header */
X     parseHeader(iw, n);
X     /* get the menu items */
X     parseMenu(iw, n);
X     /* get the cross reference entries */
X     parseXRefs(iw, n);
X}
X
Xstatic void parseHeader(iw, n)
XInfoWidget iw;
XNodeInfo *n;
X{
X     String strpbrk(), tmp;
X
X     /* first, get the node name offset */
X     I_START(n->name) = INTOFF(START(iw), NSEARCH(iw, n, NODE_TOKEN));
X     I_LEN(n->name) = INTOFF(START(iw), strpbrk(START(iw) + I_START(n->name),
X					       NAME_END_TOKEN)) -
X						    I_START(n->name);
X
X     /* now the prev, if any */
X     if ((I_START(n->prev) = INTOFF(START(iw),
X				    NSEARCH(iw, n, PREV_TOKEN))) > 0)
X	  I_LEN(n->prev) = INTOFF(START(iw),
X				  strpbrk(START(iw) + I_START(n->prev),
X						    NAME_END_TOKEN)) -
X							 I_START(n->prev);
X     else
X	  I_LEN(n->prev) = I_START(n->prev) = 0;
X
X     /* and the up, if any */
X     if ((I_START(n->up) = INTOFF(START(iw),
X				  NSEARCH(iw, n, UP_TOKEN))) > 0)
X	  I_LEN(n->up) = INTOFF(START(iw), strpbrk(START(iw) + I_START(n->up),
X						  NAME_END_TOKEN)) -
X						       I_START(n->up);
X     else
X	  I_LEN(n->up) = I_START(n->up) = 0;
X
X     /* the next, if any */
X     if ((I_START(n->next) = INTOFF(START(iw),
X				    NSEARCH(iw, n, NEXT_TOKEN))) > 0)
X	  I_LEN(n->next) = INTOFF(START(iw),
X				  strpbrk(START(iw) + I_START(n->next),
X						    NAME_END_TOKEN)) -
X							 I_START(n->next);
X     else
X	  I_LEN(n->next) = I_START(n->next) = 0;
X
X     /* And finally skip over the header and set the text offset there */
X     tmp = START(iw) + I_START(n->name);
X     while (*tmp != '\n')
X	  tmp++;
X     I_START(n->text) = INTOFF(START(iw), tmp + 1);
X     I_LEN(n->text) = n->length - (I_START(n->text) - n->start);
X}
X
Xstatic void parseMenu(iw, n)
XInfoWidget iw;
XNodeInfo *n;
X{
X     register String mstart;
X
X     /* start clean */
X     ZERO_LIST(n->menu);
X
X     /* Does node have a menu? */
X     if ((mstart = NSEARCH(iw, n, MENU_TOKEN)) != NULL) {
X	  /* Initialize table and string list */
X	  ALLOC_LIST(n->menu);
X
X	  /* go looking for menu items */
X	  while (mstart = search(iw, mstart, NEND(iw, n), MENU_SEP_TOKEN,
X				 False)) {
X	       MAYBE_BUMP_LIST(n->menu);
X	       I_LEN(TPOS(n->menu.t)) = 0;
X	       I_START(TPOS(n->menu.t)) = INTOFF(START(iw), mstart);
X	       while (*(mstart++) != ':')
X		    I_LEN(TPOS(n->menu.t))++;
X	       /* save the menu name as a string */
X	       LPOS(n->menu) = XtMalloc(I_LEN(TPOS(n->menu.t)) + 1);
X	       strncpy(LPOS(n->menu), START(iw) + I_START(TPOS(n->menu.t)),
X		       I_LEN(TPOS(n->menu.t)));
X	       LPOS(n->menu)[I_LEN(TPOS(n->menu.t))] = '\0';
X	       strip_evil(LPOS(n->menu));
X	       /* Is the menu name not the node name? */
X	       if (*mstart != ':') {
X		    mstart = eat_whitespace(mstart);
X		    I_START(TPOS(n->menu.t)) = INTOFF(START(iw), mstart);
X		    I_LEN(TPOS(n->menu.t)) =
X			 INTOFF(START(iw), strpbrk(mstart, NAME_END_TOKEN)) -
X			      I_START(TPOS(n->menu.t));
X	       }
X	       INCP(n->menu.t);
X	  }
X	  ROUND_LIST(n->menu);
X     }
X}
X	  
Xstatic void parseXRefs(iw, n)
XInfoWidget iw;
XNodeInfo *n;
X{
X     register String nstart;
X
X     /* start clean */
X     ZERO_LIST(n->xref);
X
X     /* Do we have any cross-reference entries? */
X     if ((nstart = search(iw, NSTART(iw, n), NEND(iw, n), NOTE_TOKEN, True))
X	 != NULL) {
X	  ALLOC_LIST(n->xref);
X	  nstart = NSTART(iw, n);
X
X	  /* go looking for cross-references (including the first one) */
X	  while (nstart = search(iw, nstart, NEND(iw, n), NOTE_TOKEN, True)) {
X	       MAYBE_BUMP_LIST(n->xref);
X	       I_LEN(TPOS(n->xref.t)) = 0;
X	       I_START(TPOS(n->xref.t)) = INTOFF(START(iw), nstart);
X	       while (*(nstart++) != ':')
X		    I_LEN(TPOS(n->xref.t))++;
X	       /* save the note name as a string */
X	       LPOS(n->xref) = XtMalloc(I_LEN(TPOS(n->xref.t)) + 1);
X	       strncpy(LPOS(n->xref), START(iw) + I_START(TPOS(n->xref.t)),
X		       I_LEN(TPOS(n->xref.t)));
X	       LPOS(n->xref)[I_LEN(TPOS(n->xref.t))] = '\0';
X	       strip_evil(LPOS(n->xref));
X	       /* Is the note name not the first part? */
X	       if (*nstart != ':') {
X		    nstart = eat_whitespace(nstart + 1);
X		    I_START(TPOS(n->xref.t)) = INTOFF(START(iw), nstart);
X		    I_LEN(TPOS(n->xref.t)) =
X			 INTOFF(START(iw), strpbrk(nstart, NAME_END_TOKEN)) -
X			      I_START(TPOS(n->xref.t));
X	       }
X	       INCP(n->xref.t);
X	  }
X	  ROUND_LIST(n->xref);
X     }
X}	       
X
X/* Put the node information on the screen */
Xstatic void displayNode(iw, n)
XInfoWidget iw;
XNodeInfo *n;
X{
X     Arg args[5];
X     Cardinal i;
X     XtTextSource oldSource, newSource;
X
X     /* show the header */
X     displayHeader(iw, n);
X
X     /* show the menu */
X     XtListChange(iw->info.menuList, n->menu.l, IDX(n->menu.t), 0, True);
X
X     /* change the xref list */
X     XtPanedAllowResize(iw->info.xrefList, True);
X     XtListChange(iw->info.xrefList, n->xref.l, IDX(n->xref.t), 0, True);
X
X     /*
X      * Show the text. Note: I don't know why the XtNlength argument
X      * to the SourceCreate seems to have no effect on the last
X      * displayed position. I have to do an explicit XtTextSetLastPos()
X      * for some reason. Sigh.
X      */
X     oldSource = XtTextGetSource(iw->info.nodeText);
X
X     i = 0;
X     XtSetArg(args[i], XtNlength, I_LEN(n->text));
X     XtSetArg(args[i], XtNstring, START(iw) + I_START(n->text));	i++;
X     XtSetArg(args[i], XtNtextOptions, (wordBreak | scrollVertical));	i++;
X     newSource = XtStringSourceCreate(iw->info.nodeText, args, i);
X     XtTextDisableRedisplay(iw->info.nodeText);
X     XtTextSetSource(iw->info.nodeText, newSource, 0);
X     XtStringSourceDestroy(oldSource);
X     XtTextSetLastPos(iw->info.nodeText, I_LEN(n->text));
X     XtTextEnableRedisplay(iw->info.nodeText);
X}
X
X/* display the header information */
Xstatic void displayHeader(iw, n)
XInfoWidget iw;
XNodeInfo *n;
X{
X     Arg args[5];
X     Cardinal i;
X     String tmp;
X     int sensitive;
X
X     /* set the file name */
X     tmp = strconcat("File: ", file_name(iw->info.file));
X     i = 0;
X     XtSetArg(args[i], XtNlabel, tmp);					i++;
X     XtSetValues(iw->info.fileLabel, args, i);
X
X     /* set the node name */
X     i = 0;
X     if ((tmp = offsetToString(iw, n->name)) != NULL)
X	  sensitive = True;
X     else
X	  sensitive = False;
X     XtSetArg(args[i], XtNlabel, strconcat("Node: ", tmp));		i++;
X     XtSetArg(args[i], XtNsensitive, sensitive);			i++;
X     XtSetValues(iw->info.nodeLabel, args, i);
X
X     /* set the prev */
X     i = 0;
X     if ((tmp = offsetToString(iw, n->prev)) != NULL)
X	  sensitive = True;
X     else
X	  sensitive = False;
X     XtSetArg(args[i], XtNlabel, strconcat("Prev: ", tmp));		i++;
X     XtSetArg(args[i], XtNsensitive, sensitive);			i++;
X     XtSetValues(iw->info.prevCmd, args, i);
X
X     /* set the up */
X     i = 0;
X     if ((tmp = offsetToString(iw, n->up)) != NULL)
X	  sensitive = True;
X     else
X	  sensitive = False;
X     XtSetArg(args[i], XtNlabel, strconcat("Up: ", tmp));		i++;
X     XtSetArg(args[i], XtNsensitive, sensitive);			i++;
X     XtSetValues(iw->info.upCmd, args, i);
X
X     /* set the next */
X     i = 0;
X     if ((tmp = offsetToString(iw, n->next)) != NULL)
X	  sensitive = True;
X     else
X	  sensitive = False;
X     XtSetArg(args[i], XtNlabel, strconcat("Next: ", tmp));		i++;
X     XtSetArg(args[i], XtNsensitive, sensitive);			i++;
X     XtSetValues(iw->info.nextCmd, args, i);
X}
X
X/*
X * Look for tag table information in the current buffer. If tag table
X * is indirect, return True, else return false.
X */
Xstatic Boolean parseTags(iw)
XInfoWidget iw;
X{
X     String start, s1;
X     char tmp[MAXSTR];
X     Boolean indirect = False;
X     int i;
X
X     /*
X      * go back about 8 lines. I don't know if this will always back up
X      * past the end marker, but Emacs info seems to think so.
X      */
X     start = END(iw);
X     i = 0;
X     while (i < 8)
X	  if (*(--start) == '\n')
X	       i++;
X
X     start = search(iw, start, END(iw), TAGEND_TOKEN, True);
X     if (start && (start = search_back(iw, start, START(iw),
X				       TAGTABLE_TOKEN, True))) {
X	  ALLOC_TABLE(TAGTABLE(iw));
X	  /* we were searching backward so move over the token */
X	  start += strlen(TAGTABLE_TOKEN);
X	  if ((s1 = search(iw, start, start + strlen(ITAGTABLE_TOKEN) + 10,
X			   ITAGTABLE_TOKEN, True)) != NULL) {
X	       indirect = True;
X	       start = s1;
X	  }
X	  while ((start = search(iw, start, END(iw), NODE_TOKEN, False)) !=
X		 NULL) {
X	       MAYBE_BUMP_TABLE(TAGTABLE(iw));
X	       strccpy(tmp, start, DEL_CHAR);
X	       I_NAME(TPOS(TAGTABLE(iw))) = XtNewString(tmp);
X	       start += strlen(tmp) + 1;
X	       sscanf(start, "%d", &I_OFFSET(TPOS(TAGTABLE(iw))));
X	       INCP(TAGTABLE(iw));
X	  }
X	  ROUND_TABLE(TAGTABLE(iw));
X     }
X     else if (TAGTABLE(iw).table)
X	  FREE_TAG_TABLE(TAGTABLE(iw));
X     return(indirect);
X}
X
X/* Look for indirect file information in the current buffer */
Xstatic void parseIndirect(iw, needIndirect)
XInfoWidget iw;
XBoolean needIndirect;
X{
X     String start;
X     char tmp[MAXSTR], *s1;
X
X     if (start = search(iw, START(iw), END(iw), INDIRECT_TOKEN, True)) {
X	  /* move backwards looking for the INFO_CHAR */
X	  for (s1 = start; s1 >= START(iw) && *s1 != INFO_CHAR; s1--);
X	  if (s1 < START(iw)) {
X	       message(iw, "?Invalid indirect table for %s!", iw->info.file);
X	       return;
X	  }
X	  else
X	       HDRSIZE(iw) = INTOFF(START(iw), s1);
X	  ALLOC_TABLE(INDIRECT(iw));
X	  for (IDX(INDIRECT(iw)) = 0; *start != INFO_CHAR; INCP(INDIRECT(iw))){
X	       MAYBE_BUMP_TABLE(INDIRECT(iw));
X	       strccpy(tmp, start, ':');
X	       I_NAME(TPOS(INDIRECT(iw))) = XtNewString(tmp);
X	       start += strlen(tmp) + 1;
X	       sscanf(start, "%d", &I_OFFSET(TPOS(INDIRECT(iw))));
X	       start = index(start, '\n') + 1;
X	  }
X	  ROUND_TABLE(INDIRECT(iw));
X     }
X     else if (needIndirect)
X	  message(iw, "?Indirect table not found for %s! Evil!",
X		  iw->info.file);
X     else if (INDIRECT(iw).table)
X	  FREE_TAG_TABLE(INDIRECT(iw));
X}
X
X/*****************************************************************************
X * Text display functions.                                                   *
X *****************************************************************************/
X
X/* display a message in the message area */
Xstatic void message(iw, s, p1, p2, p3)
XInfoWidget iw;
XString s;
X{
X     char msgbuf[MAXSTR];
X     Arg args[5];
X     Cardinal i;
X
X     i = 0;
X     if (s) {
X	  sprintf(msgbuf, s, p1, p2, p3);
X	  XtSetArg(args[i], XtNlabel, msgbuf);	i++;
X	  XtSetValues(iw->info.messageLabel, args, i);
X	  XBell(XtDisplay(iw), 50);
X	  if (*s == '?')	/* a dire warning */
X	       XtWarning(msgbuf);
X     }
X     else {	/* clear the message area */
X	  XtSetArg(args[i], XtNlabel, " ");	i++;
X	  XtSetValues(iw->info.messageLabel, args, i);
X     }
X}
X
X/* display the current node/file */
Xstatic void showStatus(iw, n)
XInfoWidget iw;
XNodeInfo *n;
X{
X     char statbuf[MAXSTR];
X     Arg args[5];
X     Cardinal i;
X     String sub = iw->info.subFile;
X
X     sprintf(statbuf, "(%s)%s, %d characters%s", file_name(iw->info.file),
X	     iw->info.node, n->length,
X	     sub ? strconcat(", subfile: ", sub) : ".");
X     i = 0;
X     XtSetArg(args[i], XtNlabel, statbuf);	i++;
X     XtSetValues(iw->info.statusLabel, args, i);
X}
X
X/*****************************************************************************
X * Various callback/action routines                                          *
X *****************************************************************************/
X
Xstatic void NodeDir(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     if (getNode(iw, "dir", "Top", NULL) == False)
X	  message(iw, "?Yow! The directory seems to have disappeared!\n");
X}
X
Xstatic void NodeNext(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     do_next(NULL, iw, NULL);
X}
X
Xstatic void NodePrev(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     do_prev(NULL, iw, NULL);
X}
X
X
Xstatic void NodeUp(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     do_up(NULL, iw, NULL);
X}
X
Xstatic void NodeTop(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     if (getNode(iw, NULL, "Top", NULL) == False)
X	  message(iw, "?This node has no top! Bad joss!");
X}
X
Xstatic void NodeLast(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     NodeInfo *tmp;
X     InfoWidget iw = TOP_WIDGET(w);
X     
X     if ((tmp = popNode(iw)) != NULL) {
X	  if (getNode(iw, tmp->file, tmp->node, tmp) == False)
X	       message(iw, "?Can't pop back to last node!");
X     }
X     else
X	  message(iw, "No further history.");
X}
X
Xstatic void NodeXRef(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     do_xref(NULL, iw, NULL);
X}
X
Xstatic void NodeGoto(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     do_goto(NULL, iw, NULL);
X}
X
Xstatic void NodeSearch(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     do_search(NULL, iw, NULL);
X}
X
Xstatic void NodeQuit(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     if (XtHasCallbacks(iw, XtNcallback) != XtCallbackHasSome)
X	  message(iw, "Sorry, I just don't know how to quit.");
X     else
X	  XtCallCallbacks(iw, XtNcallback, NULL);
X}
X
Xstatic void NodeTutorial(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     InfoWidget iw = TOP_WIDGET(w);
X
X     if (getNode(iw, "info", "Help", NULL) == False)
X	  message(iw, "?Hmmm. I can't seem to find the info tutorial!");
X}
X
X/*
X * Seems there should be a better way of doing this. Methinks the
X * XtCallbackPopdown() stuff isn't general enough. Should be a way of
X * doing this (and only this).
X */
Xstatic void popdown(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     XtPopdown((Widget)client_data);
X}
X
Xstatic void NodeHelp(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     Cardinal i;
X     Arg args[5];
X     InfoWidget iw = TOP_WIDGET(w);
X     
X     if (!iw->info.helpPopup) {
X	  Widget hpane, htext;
X	  static XtCallbackRec cb[2];
X	  static char pophelp[] = "None<Key>q:	MenuPopdown(infoHelp)\n";
X
X	  /* create the help popup */
X	  i = 0;
X	  XtSetArg(args[i], XtNheight, 300);			i++;
X	  XtSetArg(args[i], XtNwidth, 400);			i++; 
X	  iw->info.helpPopup = XtCreatePopupShell("infoHelp",
X						  topLevelShellWidgetClass,
X						  iw, args, i);
X	  i = 0;
X	  hpane = XtCreateManagedWidget("helpPane", panedWidgetClass,
X					iw->info.helpPopup, args, i);
X	  i = 0;
X	  cb[0].callback = popdown;
X	  cb[0].closure = (caddr_t)iw->info.helpPopup;
X	  XtSetArg(args[i], XtNcallback, cb);			i++;
X	  XtCreateManagedWidget("Close", commandWidgetClass,
X				hpane, args, i);
X	  i = 0;
X	  XtSetArg(args[i], XtNfile, iw->info.helpFile);	i++;
X	  XtSetArg(args[i], XtNtextOptions, (wordBreak | scrollVertical)); i++;
X	  htext = XtCreateManagedWidget("helpText", asciiDiskWidgetClass,
X					hpane, args, i);
X	  XtOverrideTranslations(htext, XtParseTranslationTable(textTrans));
X	  XtOverrideTranslations(htext, XtParseTranslationTable(pophelp));
X     }
X     XtPopup(iw->info.helpPopup, XtGrabNonexclusive);
X}
X
Xstatic void NodeMenuSelectByNumber(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     int menunum;
X     int nitems;
X     InfoWidget iw = TOP_WIDGET(w);
X
X     nitems = IDX(CURNODE(iw)->menu.t);
X     menunum = atoi(*params);
X     /* menu number of zero means get menu from arg area */
X     if (!menunum)
X	  do_menu(NULL, iw, NULL);
X     else if (!nitems)
X	  message(iw, "No menu for this node.");
X     else if (menunum > nitems)
X	  message(iw, "There are only %d menu items.", nitems);
X     else {
X	  XtListHighlight(iw->info.menuList, menunum - 1);
X	  if (getNode(iw, NULL,
X		      offsetToString(iw,CURNODE(iw)->menu.t.table[menunum-1]),
X		      NULL) == False)
X	       message(iw, "?Can't find node for menu item #%s!", *params);
X     }
X}
X
Xstatic void NodePrint(w, event, params, num_params)
XWidget   w;
XXEvent   *event;
XString   *params;
XCardinal *num_params;
X{
X     String tmp;
X     FILE *out;
X     InfoWidget iw = TOP_WIDGET(w);
X
X     /* if you don't have this routine in your stdlib, make one up */
X     tmp = tmpnam(NULL);
X
X     if (!CURNODE(iw))
X	  message(iw, "?No current node?");
X     else if ((out = fopen(tmp, "w")) == NULL)
X	  message(iw, "?Can't open temporary file '%s'.", tmp);
X     else {
X	  String s1 = NSTART(iw, CURNODE(iw));
X	  String s2 = NEND(iw, CURNODE(iw));
X	  char syscmd[MAXSTR];
X	  int stat;
X
X	  message(iw, "Sending '%s' to the printer, please wait..",
X		  iw->info.node);
X
X	  while (s1 < s2) {
X	       fputc(*s1, out);
X	       s1++;
X	  }
X	  fclose(out);
X	  sprintf(syscmd, "%s %s", iw->info.printCmd, tmp);
X	  if ((stat = system(syscmd)) != 0)
X	       message(iw, "?'%s' failed with exit status %d. Help!",
X		       syscmd, stat);
X	  else
X	       message(iw, "Finished printing.");
X	  unlink(tmp);
X     }
X	   
X     
X}
X
Xstatic void do_prev(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     String tmp;
X
X     if ((tmp = offsetToString(iw, CURNODE(iw)->prev))) {
X	  if (getNode(iw, NULL, tmp, NULL) == False)
X	       message(iw, "?Can't find the previous (%s) for this node.",
X		       tmp);
X     }
X     else
X	  message(iw, "Node has no previous");
X}
X
Xstatic void do_up(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     String tmp;
X
X     if ((tmp = offsetToString(iw, CURNODE(iw)->up))) {
X	  if (getNode(iw, NULL, tmp, NULL) == False)
X	       message(iw, "?Can't find the up (%s) for this node.", tmp);
X     }
X     else
X	  message(iw, "Node has no up");
X}
X
Xstatic void do_next(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     String tmp;
X
X     if ((tmp = offsetToString(iw, CURNODE(iw)->next))) {
X	  if (getNode(iw, NULL, tmp, NULL) == False)
X	       message(iw, "?Can't find the next (%s) for this node.", tmp);
X     }
X     else
X	  message(iw, "Node has no next");
X}
X
Xstatic void do_xref(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     String tmp;
X
X     if ((tmp = get_arg(iw)) != NULL) {
X	  if ((tmp = trueName(iw, CURNODE(iw)->xref, tmp)) == NULL)
X	       message(iw, "No cross reference entry named '%s' in this node.",
X		       get_arg(iw));
X	  else if (getNode(iw, NULL, tmp, NULL) == False)
X	       message(iw, "?Can't find node for xref item '%s'!",
X		       get_arg(iw));
X     }
X     else
X	  message(iw, "You must supply the name of a cross-reference.");
X}
X
Xstatic void do_menu(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     String tmp;
X
X     if ((tmp = get_arg(iw)) != NULL) {
X	  if ((tmp = trueName(iw, CURNODE(iw)->menu, tmp)) == NULL)
X	       message(iw, "No menu entry named '%s' in this node.",
X		       get_arg(iw));
X	  else if (getNode(iw, NULL, tmp, NULL) == False)
X	       message(iw, "?Can't find node for menu item '%s'!",
X		       get_arg(iw));
X     }
X     else
X	  message(iw, "You must supply a menu entry name.");
X}
X
Xstatic void do_goto(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     String tmp;
X
X     if ((tmp = get_arg(iw)) != NULL) {
X	  if (getNode(iw, NULL, tmp, NULL) == False)
X	       message(iw, "Can't find a node named %s", tmp);
X     }
X     else
X	  message(iw, "You must supply the name of a node to go to.");
X}
X
X/*
X * Implement a somewhat simplistic search strategy. If file has an indirect
X * list, look for a match in the tag table (since just looking in the current
X * file probably wouldn't be very useful). If not, then search the current
X * file. If we're successful in either case, record the position (in the
X * tags table or the file) so that we don't hit it again right away.
X */
Xstatic void do_search(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     String tmp, s;
X     char name[MAXSTR];
X     static struct {
X	  String file;
X	  caddr_t pos;
X     } oldPos;
X
X     if ((tmp = get_arg(iw)) != NULL) {
X	  /* if remembered position is invalid, reset it */
X	  if (strcomp(oldPos.file, iw->info.file)) {
X	       oldPos.file = iw->info.file;
X	       oldPos.pos = NULL;
X	  }
X	  if (INDIRECT(iw).table) {
X	       ID_P i;
X	       int len = strlen(tmp);
X
X	       if (oldPos.pos)
X		    i = (ID_P)oldPos.pos;
X	       else
X		    i = TAGTABLE(iw).table;
X	       /* do a tags search */
X	       while (I_NAME(*i)) {
X		    if (!strncomp(I_NAME(*i), tmp, len))
X			 break;
X		    i++;
X	       }
X	       /* success? */
X	       if (I_NAME(*i)) {
X		    oldPos.pos = (caddr_t)(i + 1);
X		    if (getNode(iw, iw->info.file, I_NAME(*i), NULL) == False)
X			 message(iw, "?Can't find node for tag %s!",
X				 I_NAME(*i));
X	       }
X	       else {
X		    message(iw, "Tag search for '%s' failed.", tmp);
X		    oldPos.pos = NULL;
X	       }
X	  }
X	  else {
X	       if (oldPos.pos)
X		    s = (String)oldPos.pos;
X	       else
X		    s = START(iw);
X	       if ((s = search(iw, s, END(iw), 
X			       strconcat(NODE_TOKEN, tmp),
X			       True)) != NULL) {
X		    int i;
X
X		    oldPos.pos = (caddr_t)s;
X		    strcpy(name, tmp);
X		    i = strlen(name);
X		    while (!index(NAME, *s))
X			 name[i++] = *s++;
X		    name[i] = '\0';
X		    if (getNode(iw, iw->info.file, name, NULL) == False)
X			 message(iw, "?Can't find node name in search!");
X	       }
X	       else {
X		    message(iw, "Search for '%s' failed.", tmp);
X		    oldPos.pos = NULL;
X	       }
X	  }
X     }
X     else
X	  message(iw, "You must supply a search string.");
X}
X
X/* These two handle selections from the menu and xref lists */
X
Xstatic void do_menu_sel(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     XtListReturnStruct *rs = (XtListReturnStruct *)call_data;
X
X     if (getNode(iw, NULL, trueName(iw, CURNODE(iw)->menu, rs->string),
X		 NULL) == False)
X	  message(iw, "?Can't find node for menu item '%s'", rs->string);
X}
X
Xstatic void do_xref_sel(w, client_data, call_data)
XWidget w;
Xcaddr_t client_data;
Xcaddr_t call_data;
X{
X     InfoWidget iw = (InfoWidget)client_data;
X     XtListReturnStruct *rs = (XtListReturnStruct *)call_data;
X
X     if (getNode(iw, NULL, trueName(iw, CURNODE(iw)->xref, rs->string),
X		 NULL) == False)
X	  message(iw, "?Can't find node for cross reference '%s'", rs->string);
X}
X
X/*****************************************************************************
X * Utility functions.                                                        *
X *****************************************************************************/
X
X/*
X * Search for a file along a path, returning the complete path name if found.
X * This routine uses fopen() rather than access() to determine whether a file
X * exists (and is readable) because access()'s argument macros [X_OK, R_OK, ..]
X * tend to be in different places on different unix's and it's a pain to find
X * them reliably.
X */
Xstatic String find_file(path, name)
XString path, name;
X{
X     FILE *tmp = NULL;
X     String cp = path;
X     Boolean more_path = True;
X     static char dir[MAXPATHLEN];
X
X     dir[0] = '\0';
X
X     /* absolute path name? */
X     if (name[0] == '/') {
X	  if ((tmp = fopen(name, "r")) != NULL) {
X	       fclose(tmp);
X	       return(name);
X	  }
X	  else
X	       name = file_name(name);
X     }
X     while (!tmp && more_path) {
X          if ((cp = index(path, ':')) != NULL) {
X               strncpy(dir, path, cp - path);
X	       dir[cp - path] = '\0';
X               strcat(dir, "/");
X               path = cp + 1;
X          }
X          else {
X               strcpy(dir, path);
X               strcat(dir, "/");
X               more_path = False;
X          }
X          strcat(dir, name);
X          tmp = fopen(dir, "r");
X	  if (!tmp)	/* if we failed, try again in lower case */
X	       tmp = fopen(downcase(dir), "r");
X     }
X     if (tmp)
X          fclose(tmp);
X     if (dir[0])
X          return(dir);
X     else
X          return(NULL);
X}
X
X/* return the file part of a path name */
Xstatic String file_name(s)
XString s;
X{
X     int i = strlen(s);
X
X     while (i) {
X	  if (s[i - 1] == '/')
X	       return(s + i);
X	  i--;
X     }
X     return(s);
X}
X
X/* strip evil tab/formfeed/newline chars from a string (replacing w/blanks) */
Xstatic String strip_evil(s)
XString s;
X{
X     if (s) {
X	  while (*s) {
X	       if (index(WHITE, *s))
X		    *s = ' ';
X	       s++;
X	  }
X     }
X     return(s);
X}
X
X/* Convert from an offset ID to a string. */
Xstatic String offsetToString(iw, blk)
XInfoWidget iw;
XID blk;
X{
X     static char ret[MAXSTR];
X
X     if (I_LEN(blk) != 0) {
X	  strncpy(ret, START(iw) + I_START(blk), I_LEN(blk));
X	  ret[I_LEN(blk)] = '\0';
X	  return(ret);
X     }
X     else
X	  return(NULL);
X}
X
X/* chew through white space */
Xstatic String eat_whitespace(s)
XString s;
X{
X     while (index(WHITE, *s))
X	  s++;
X     return(s);
X}
X
X/* Get the argument area as a string */
Xstatic String get_arg(iw)
XInfoWidget iw;
X{
X     int len;
X     static char ret[ARGLEN];
X
X     XtTextSetInsertionPoint(iw->info.argText, 9999);
X     len = XtTextGetInsertionPoint(iw->info.argText);
X
X     if (len) {
X	  strncpy(ret, iw->info.arg, len);
X	  ret[len] = '\0';
X	  return(ret);
X     }
X     else
X	  return(NULL);
X}
X
X/* look up the actual name of a list item */
Xstatic String trueName(iw, lst, name)
XInfoWidget iw;
XIDList lst;
XString name;
X{
X     int i;
X
X     for (i = 0; i < lst.t.idx; i++)
X	  if (!strcomp(lst.l[i], name))
X	       return(offsetToString(iw, lst.t.table[i]));
X     return(NULL);
X}
X		      
X/* Search for a string. */
Xstatic String search(iw, start, end, str, igncase)
XInfoWidget iw;
Xregister String start, end, str;
XBoolean igncase;
X{
X     register String ind = str;
X     register String stop = str + strlen(str);
X     register int comp;
X
X     while (start < end) {
X	  if (!igncase)
X	       comp = (*start == *ind);
X	  else
X	       comp = (tolower(*start) == tolower(*ind));
X	  if (!comp) {
X	       if (ind != str)
X		    ind = str;
X	       else
X		    start++;
X	  }
X	  else {
X	       start++;
X	       if (++ind == stop)
X		    return(start);
X	  }
X     }
X     return(NULL);
X}
X
X/*
X * Like search(), but in the reverse direction.
X */
Xstatic String search_back(iw, start, end, str, igncase)
XInfoWidget iw;
Xregister String start, end, str;
XBoolean igncase;
X{
X     register String ind;
X     register String stop;
X     register int comp;
X
X     ind = str = reverse(str);
X     stop = ind + strlen(ind);
X
X     while (start > end) {
X	  if (!igncase)
X	       comp = (*start == *ind);
X	  else
X	       comp = (tolower(*start) == tolower(*ind));
X	  if (!comp) {
X	       if (ind != str)
X		    ind = str;
X	       else
X		    start--;
X	  }
X	  else {
X	       start--;
X	       if (++ind == stop)
X		    return(start);
X	  }
X     }
X     return(NULL);
X}
X
X/*
X * Safe and sane strcmp. Deals with null pointer for either arg and ignores
X * case.
X */
Xstatic int strcomp(s1, s2)
Xregister String s1, s2;
X{
X     if (s1 && s2) {
X	  if (strlen(s1) != strlen(s2))
X	      return(-1);
X
X	  while (*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
X	       s1++, s2++;
X	  if (!*s1 && !*s2)
X	       return(0);
X	  else if (*s1 < *s2)
X	       return (-1);
X	  else
X	       return(1);
X     }
X     else if (!s1 && !s2)
X          return(0);
X     else if (!s1 && s2)
X          return(-1);
X     else
X          return(1);
X}
X
X/* like above, but stops after n characters */
Xstatic int strncomp(s1, s2, n)
Xregister String s1, s2;
Xint n;
X{
X     register String s3 = s2 + n;
X
X     if (s1 && s2) {
X	  while (*s1 && *s2 && (tolower(*s1) == tolower(*s2)) && (s2 < s3))
X	       s1++, s2++;
X	  if (!*s1 && !*s2 || s2 == s3)
X	       return(0);
X	  else if (*s1 < *s2)
X	       return (-1);
X	  else
X	       return(1);
X     }
X     else if (!s1 && !s2)
X          return(0);
X     else if (!s1 && s2)
X          return(-1);
X     else
X          return(1);
X}
X
X/* Copy s2 to s1 up to (but not including) character c */
Xstatic void strccpy(s1, s2, c)
Xregister String s1, s2;
Xregister char c;
X{
X     while (*s2 && *s2 != c)
X	  *(s1++) = *(s2++);
X     *s1 = '\0';
X}
X
X/*
X * Return integer subscript of character 'c' in string 's'.
X * (why doesn't this already exist in a library somewhere?).
X */
Xstatic int iindex(s, c)
Xregister char *s, c;
X{
X     register char *cp;
X
X     if (!s)
X          return(-1);
X     cp = index(s, c);
X     if (cp)
X          return(cp - s);
X     else
X          return(-1);
X}
X
Xstatic String substr(s, p1, p2)
Xregister String s;
Xregister int p1, p2;
X{
X     static char ret[MAXSTR];
X     register int i = 0;
X
X     if (p1 > p2) {
X	  sprintf(ret, "substr: start %d, end %d. start must be <= end",
X		  p1, p2);
X	  XtWarning(ret);
X          return(NULL);
X     }
X     if (p2 - p1 > MAXSTR) {
X          sprintf(ret, "substr: end - start is > max len of %d", MAXSTR);
X	  XtWarning(ret);
X          return(NULL);
X     }
X     while (p1 <= p2)
X          ret[i++] = s[p1++];
X     ret[i] = '\0';
X     return(ret);
X}
X
X/*
X * Safely concatenate two strings into static area, returning pointer to
X * result.
X */
Xstatic String strconcat(s1, s2)
Xregister String s1, s2;
X{
X     static char ret[MAXSTR];
X     int len1;
X
X     if (s1) {
X	  if ((len1 = strlen(s1)) >= MAXSTR) {
X	       sprintf(ret, "strconcat: length of s1 > MAX (%d)", MAXSTR);
X	       XtWarning(ret);
X	       return(NULL);
X	  }
X	  else
X	       strcpy(ret, s1);
X	  if (s2) {
X	       if (len1 + strlen(s2) > MAXSTR) {
X		    sprintf(ret, "strconcat: length of s1 + s2 is > MAX (%d)",
X			    MAXSTR);
X		    XtWarning(ret);
X	       }
X	       else
X		    strcat(ret, s2);
X	  }
X	  return(ret);
X     }
X     else
X	  return(NULL);
X}
X
X/* reverse a string so that a simple reverse search may be done on it */
Xstatic String reverse(s)
XString s;
X{
X     static char ret[MAXSTR];
X     int i, len;
X
X     if ((len = strlen(s)) > MAXSTR) {
X	  sprintf(ret, "reverse: string too long to reverse. MAX is %d",
X		  MAXSTR);
X	  XtWarning(ret);
X	  return(NULL);
X     }
X     else {
X	  i = 0;
X	  while (len)
X	       ret[i++] = s[--len];
X	  ret[i] = '\0';
X	  return(ret);
X     }
X}
X
X/* convert a string to lower case */
Xstatic String downcase(s)
XString s;
X{
X     String orig = s;
X
X     if (s)
X	  while (*s) {
X	       *s = tolower(*s);
X	       s++;
X	  }
X     return(orig);
X}
END_OF_FILE
if test 51392 -ne `wc -c <'info-widget/Info.c'`; then
    echo shar: \"'info-widget/Info.c'\" unpacked with wrong size!
fi
# end of 'info-widget/Info.c'
fi
echo shar: End of archive 1 \(of 2\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked both archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0



More information about the Comp.sources.x mailing list