[comp.sources.x] v06i033: X11R3 xman changes to run under HP-UX, Patch1

evgabb@uunet.UU.NET (Rob Gabbard) (03/15/90)

Submitted-by: sdrc!evgabb@uunet.UU.NET (Rob Gabbard)
Posting-number: Volume 6, Issue 33
Archive-name: xman/patch1

Since I got alot of requests for my HP changes to xman I decided just to post 
them to comp.sources.x.  The shar file below contains the only 4 files I 
changed from the X11R3 version. Chris Peterson sent me a message saying
that the R4 version now supports this format as well as a number of other new
formats (way to go Chris!). So, if you're using R4, don't apply these.
When you compile these on an HP, make sure to use -Dhpux on the compile line
as that is how the changes are picked up. You should also modify defs.h as
to the location of the help file.  I'm not sure if the Apropos option will
work or not. Our sys admin hasn't run catman here so I took a stab at what I
thought might work. If anyone finds out it does need changes, let me know.

-Rob Gabbard @ SDRC (uunet!sdrc!evgabb)

---------------------------------- cut here ------------------------------
#!/bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Rob Gabbard <evgabb@hp2> on Tue Mar 13 09:27:10 1990
#
# This archive contains:
#	defs.h		man.c		misc.c		search.c	
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - defs.h
cat >defs.h <<'@EOF'
/*
 * xman - X window system manual page display program.
 *
 * $XConsortium: defs.h,v 1.7 89/01/06 18:41:55 kit Exp $
 * $Athena: defs.h,v 4.8 89/01/06 15:56:19 kit Exp $
 *
 * Copyright 1987, 1988 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Author:    Chris D. Peterson, MIT Project Athena
 * Created:   October 22, 1987
 */

#ifndef HELPFILE
#define HELPFILE "/u/evgabb/X/xman/xman.help" /* name of the default helpfile. */ 
#endif

#ifdef hpux
#define NUMSECTIONS 7
#else
#define NUMSECTIONS 8
#endif

/* The default cursors */

#define XMAN_CURSOR "left_ptr"		/* Top level cursor. */
#define HELP_CURSOR "left_ptr"	        /* The help cursor. */
#define MANPAGE_CURSOR "left_ptr"	/* The manpage cursor. */
#define SEARCH_ENTRY_CURSOR "question_arrow"	/* The search text widget
						   cursor. */
/* The default fonts */

#ifdef ATHENA
#define MANPAGE_NORMAL   "fixed"
#define MANPAGE_BOLD     "helvetica-bold12"
#define MANPAGE_ITALIC   "helvetica-boldoblique12"
#define DIRECTORY_NORMAL "fixed"
#else
#define MANPAGE_NORMAL   "*-new century schoolbook-medium-r-normal--*-120-*"
#define MANPAGE_BOLD     "*-new century schoolbook-bold-r-normal--*-120-*"
#define MANPAGE_ITALIC   "*-new century schoolbook-bold-i-normal--*-120-*"
#define DIRECTORY_NORMAL "fixed"
#endif ATHENA

#define TOPBOXNAME  "topBox"	/* Name of the Top Box. */
#define MANNAME "manualBrowser"	/* name for each manual page widget. */
#define POPUPNAME "xmanCommands" /* The name of the popup menu */
#define SPOPUPNAME "xmanSections" /* The name of the section popup name. */
#define SEARCHNAME "xmanSearch" /* The name for the search widget. */
#define HELPNAME  "help"	/* The name of the help widget. */
#define DIRECTORY_NAME "directory" /* name of the directory widget. */
#define MANUALPAGE "manualpage"	/* name of the Scrollbyline widget that
				 contains the man page. */

/* definitions of string to use for show both and show one. */

#define SHOW_BOTH "Show Both Screens"
#define SHOW_ONE "Show One Screen"

/* 
 * Things will not look right if you change these names to make 
 * MANUALSEARCH longer APROPOSSEARCH, see search.c for details.
 */

#define MANUALSEARCH "Manual Page"
#define APROPOSSEARCH "Apropos"

#define MANUAL 0
#define APROPOS 1

#define INIT_SEARCH_STRING "xman"     /* Intial search string. */
#define SEARCH_STRING_LENGTH 30
#define NO_SECTION_DEFAULTS ("no default sections")
/*
 * The command filters for the manual and apropos searches.
 */

#define APROPOSFILTER ("man -k %s | pr -h Apropos > %s")
#define MANUALCOMMAND "man"
#ifdef macII
#define FORMAT "pcat"	/* The format command. */
#else
#define FORMAT "nroff -man"	/* The format command. */
#endif

#define CANCEL "Cancel"

#define DEFAULT_WIDTH 500	/* The default width of xman. */
#define MAXSECT 62		/* The maximum number of sections.
				   one for each of (1-9) & (a-z) & (A-Z) */
#ifdef sun
#define MAXENTRY 2000		/* The maximum number of entries in one 
				   section, on a sun. */
#else
#define MAXENTRY 2000		/* The maximum number of entries in one 
				   section, on other machines. */
#endif

#define NLINES  66		/* This is the number of lines to wait until
				   we boldify the line again, this allows 
				   me to bold the first line of each page.*/

#define INITIAL_DIR 0		/* The Initial Directory displayed. */

#define LMAN 3			/* Name and length of the man and cat dirs. */
#define MAN "man"
#define LCAT 3
#define CAT "cat"

#define SEARCHDIR  MAN
#define LSEARCHDIR LMAN		/* The directories to search we are making 
				 the assumption that the manual directories 
				 are more complete that the cat directories. 
				 but you can change it if you like. */

#define COPY "cp"		/* The unix copy command.  */

#define BACKSPACE 010		/* I doubt you would want to change this. */
#define MANDESC "mandesc"	/* Name of the mandesc files.  */
#ifdef macII
#define MANDIR "/usr/catman/u_man:/usr/catman/a_man"	/* The default manual page directory. */
#else
#define MANDIR "/usr/man"	/* The default manual page directory. */
#endif

#define INDENT 15
#define TYP20STR "MMMMMMMMMMMMMMMMMMMM"

#define FILE_SAVE "Yes"
#define CANCEL_FILE_SAVE "No"
#define MANTEMP "/tmp/xmanXXXXXX"

/* 
 * function defintions 
 */

/* Standard library function definitions. */

char * mktemp(), * getenv(), * malloc();
void exit();

/* Toolkit standard definitions. */

void XtResizeWidget(), XtMoveWidget();

/* buttons.c */

void MakeTopMenuWidget(), CreateManpage(), StartManpage();
void CreateManpageWidget(), MakeSaveWidgets(), WriteLabel();
void MakeTopPopUpWidget(),MakeDirPopUpWidget(), MakeDirectoryBox();
char * CreateManpageName();

/* handler.c */

void DirectoryHandler(),SearchCallback(),PopUpMenu(),SaveCallback();
void TopCallback(),TopPopUpCallback(),DirPopUpCallback();
void PopDown(),ManpageButtonPress(), GotoManpage();

/* help.c */

void PopupHelp();
Boolean MakeHelpWidget(), OpenHelpfile();

/* main.c */

void main(),Quit();

/* man.c */

int Man();
char * StrAlloc();

/* menu.c is self contained */

/* misc.c */

void PrintError(),PrintWarning(),KillManpage(), ChangeLabel();
void RemovePixmaps(),PositionCenter(),AddCursor(),ParseEntry();
FILE *FindFilename(),*Format(), *OpenEntryFile();

/* pages.c */

void InitManpage();
void PrintManpage();
Boolean Boldify();

/* search */

void MakeSearchWidget();
FILE * DoSearch();

/* tkfunctions.c */

int Width(), Height(), BorderWidth();
Widget PopupChild(), Child();
char * Name();
Boolean MakeLong();
@EOF

chmod 770 defs.h

echo x - man.c
cat >man.c <<'@EOF'
/*
 * xman - X window system manual page display program.
 *
 * $XConsortium: man.c,v 1.3 89/01/06 18:42:14 kit Exp $
 *
 * Copyright 1987, 1988 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Author:    Chris D. Peterson, MIT Project Athena
 * Created:   August 10, 1987
 */

#if ( !defined(lint) && !defined(SABER))
  static char rcs_version[] = "$Athena: man.c,v 4.5 88/12/19 13:47:35 kit Exp $";
#endif

#include "globals.h"

static char error_buf[BUFSIZ];		/* The buffer for error messages. */

static void SortList(), ReadMandescFile(), SortAndRemove(), InitManual();
static void AddToCurrentSection(), AddNewSection(), AddStandardSections();
static int CmpEntryLabel();

#define SECT_ERROR -1
#define streq(a, b)        ( strcmp((a), (b)) == 0 )

typedef struct _SectionList {
  struct _SectionList * next;	/* link to next elem in list. */
  char * label, *directory;	/* The label and directory that this 
				   section represents. */
  Boolean standard;		/* Is this one of the standard sections? */
} SectionList;

/*	Function Name: Man
 *	Description: Builds a list of all manual directories and files.
 *	Arguments: none. 
 *	Returns: the number of manual sections.
 */

int
Man()
{
  SectionList *list = NULL;
  char *ptr, manpath[BUFSIZ], *path, *current_label;
  int sect;

/* 
 * Get the environment variable MANPATH, and if it doesn't exist then back
 * up to MANDIR.
 */

  ptr = getenv("MANPATH");
  if (ptr == NULL || streq(ptr , "") )
    ptr = MANDIR;
  strcpy(manpath, ptr);

/*
 * Get the list of manual directories in the users MANPATH that we should
 * open to look for manual pages.  The ``mandesc'' file is read here.
 */

  for ( path = manpath ; (ptr = index(path , ':')) != NULL ; path = ++ptr) { 
    *ptr = '\0';
    ReadMandescFile(&list, path);
  }
  ReadMandescFile(&list, path);

  SortList(&list);

  sect = 0;
  InitManual( &(manual[sect]), list->label );
  current_label = NULL;
  while ( list != NULL ) {
    SectionList * old_list;

    if ( current_label == NULL || streq(list->label, current_label) )
      AddToCurrentSection( &(manual[sect]), list->directory);
    else {
      if (manual[sect].nentries == 0) {	/* empty section, re-use it. */
	free(manual[sect].blabel);
	manual[sect].blabel = list->label;
      }
      else {
	if ( ++sect >= MAXSECT ) {
	  sprintf(error_buf, "%s %s", "Too many manual sections, recompile",
		  "with a larger value for MAXSECT.");
	  PrintError(error_buf);
	}
	InitManual( &(manual[sect]), list->label );
      }
      AddToCurrentSection( &(manual[sect]), list->directory);
    }
    /* Save label to see if it matches next entry. */
    current_label = list->label; 
    old_list = list;
    list = list->next;
    free(old_list);		/* free what you allocate. */
  }
  if (manual[sect].nentries != 0)
    sect++;			/* don't forget that last section. */
  
  SortAndRemove(manual, sect);

#ifdef notdef			/* dump info. */
  DumpManual(sect);
#endif
  return(sect);		/* return the number of man sections. */
}    

/*	Function Name: SortList
 *	Description: Sorts the list of sections to search.
 *	Arguments: list - a pointer to the list to sort.
 *	Returns: a sorted list.
 *
 * This is the most complicated part of the entire operation.
 * all sections with the same label must by right next to each other,
 * but the sections that are in the standard list have to come first.
 */

static void
SortList(list)
SectionList ** list;
{
  SectionList * local;
  SectionList *head, *last, *inner, *old;
  
  if (*list == NULL)
    PrintError("No manual sections to read, exiting.");

/* 
 * First step 
 * 
 * Look for standard list items, and more them to the top of the list.
 */

  last = NULL;			/* keep Saber happy. */
  for ( local = *list ; local->next != NULL ; local = local->next) {
    if ( local->standard ) {
      if ( local == *list )	/* top element is already standard. */
	break;
      head = local;

      /* Find end of standard block */
      for ( ; (local->next != NULL) && (local->standard) 
	   ; old = local, local = local->next); 

      last->next = old->next; /* Move the block. */
      old->next = *list;
      *list = head;

      break;			/* First step accomplished. */
    }
    last = local;
  }

/*
 *  Second step
 *
 *  Move items with duplicate labels right next to each other.
 */

  local = *list;
  for ( local = *list ; local->next != NULL ; local = local->next) {
    inner = local->next;
    while ( inner != NULL) {
      if ( streq(inner->label, local->label) && (inner != local->next)) {
	last->next = inner->next; /* Move it to directly follow local. */
	inner->next = local->next;
	local->next = inner;
	inner = last;		/* just so that we keep marching down the
				   tree (this keeps us from looping). */
      }
      last = inner;
      inner = inner->next;
    }
  }
}	

/*	Function Name: ReadMandescFile
 *	Description: Reads the mandesc file, and adds more sections as 
 *                   nescessary.
 *	Arguments: path - path name if the current search directory.
 *                 section_list - pointer to the list of sections.
 *	Returns: TRUE in we should use default sections
 */
  
static void
ReadMandescFile( section_list, path )
SectionList ** section_list;
char * path;
{
  char mandesc_file[BUFSIZ];	/* full path to the mandesc file. */
  FILE * descfile;
  char string[BUFSIZ], local_file[BUFSIZ];
  Boolean use_defaults = TRUE;

  sprintf(mandesc_file, "%s/%s", path, MANDESC);
  if ( (descfile = fopen(mandesc_file, "r")) != NULL) {
    while ( fgets(string, BUFSIZ, descfile) != NULL) {
      string[strlen(string)-1] = '\0';        /* Strip off the CR. */

      if ( streq(string, NO_SECTION_DEFAULTS) ) {
	use_defaults = FALSE;
	continue;
      }

#ifdef hpux
      sprintf(local_file, "%s%c.Z", MAN, string[0]);
#else
      sprintf(local_file, "%s%c", MAN, string[0]);
#endif
      AddNewSection(section_list, path, local_file, (string + 1), FALSE );
    }
    fclose(descfile);
  }
  if (use_defaults)
    AddStandardSections(section_list, path);
}

/*	Function Name: AddStandardSections
 *	Description: Adds all the standard sections to the list for this path.
 *	Arguments: list - a pointer to the section list.
 *                 path - the path to these standard sections.
 *	Returns: none.
 */

static void
AddStandardSections(list, path)
SectionList **list;
char * path;
{

  static char * names[] = {
    "User Commands       (1)",
    "System Calls        (2)",
    "Subroutines         (3)",
    "Devices             (4)",
    "File Formats        (5)",
    "Games               (6)",
    "Miscellaneous       (7)",
#ifdef hpux
    "Sys. Administration (1m)",
#else
    "Sys. Administration (8)",
#endif
    "Local               (l)",
    "New                 (n)",
    "Old                 (o)",
    };
  register int i;
  char file[BUFSIZ];

  for (i = 1 ; i <= NUMSECTIONS ; i++) {
#ifdef hpux
    sprintf(file, "%s%d.Z", MAN, i);
#else
    sprintf(file, "%s%d", MAN, i);
#endif
    AddNewSection(list, path, file, names[i-1], TRUE);
  }
  i--;
#ifdef hpux
  sprintf(file, "%s1m.Z", MAN);
  AddNewSection(list, path, file, names[i++], TRUE);
  sprintf(file, "%sl.Z", MAN);
  AddNewSection(list, path, file, names[i++], TRUE);
  sprintf(file, "%sn.Z", MAN);
  AddNewSection(list, path, file, names[i++], TRUE);
  sprintf(file, "%so.Z", MAN);
  AddNewSection(list, path, file, names[i], TRUE);
#else
  sprintf(file, "%sl", MAN);
  AddNewSection(list, path, file, names[i++], TRUE);
  sprintf(file, "%sn", MAN);
  AddNewSection(list, path, file, names[i++], TRUE);
  sprintf(file, "%so", MAN);
  AddNewSection(list, path, file, names[i], TRUE);
#endif
}

/*	Function Name: AddNewSection
 *	Description: Adds the new section onto the current section list.
 *	Arguments: list - pointer to the section list.
 *                 path - the path to the current manual section.
 *                 file - the file to save.
 *                 label - the current section label.
 *                 standard - one of the standard labels?
 *	Returns: none.
 */

static void
AddNewSection(list, path, file, label, standard)
SectionList **list;
char * path, * label, * file;
Boolean standard;
{
  SectionList * local_list, * end;
  char full_path[BUFSIZ];

/* Allocate a new list element */

  if ( (local_list = (SectionList *) malloc(sizeof(SectionList)) ) == NULL)
    PrintError("Could not allocate Memory in AddNewSection.");

  if (*list != NULL) {
    for ( end = *list ; end->next != NULL ; end = end->next );
    end->next = local_list;
  }
  else 
    *list = local_list;

  local_list->next = NULL;
  local_list->label = StrAlloc(label);
  sprintf(full_path, "%s/%s", path, file);
  local_list->directory = StrAlloc(full_path);
  local_list->standard = standard;
}  

/*	Function Name: AddToCurrentSection
 *	Description: This function gets the names of the manual page
 *                   directories, then closes the directory.
 *	Arguments:  local_manual - a pointer to a manual pages structure.
 *                  path - the path to this directory.
 *	Returns: FALSE if directory could not be opened.
 */

static void
AddToCurrentSection(local_manual, path)
Manual * local_manual;
char * path;
{
  DIR * dir;
  register struct direct *dp;
  register int nentries;
  char full_name[BUFSIZ];

  if((dir = opendir(path)) == NULL) {	
#ifdef DEBUG
    sprintf(error_buf,"Can't open directory %s", path);
    PrintWarning(NULL, error_buf);
#endif DEBUG
    return;
  }
  
  nentries = local_manual->nentries;

  while( (dp = readdir(dir)) != NULL ) {
    char * name = dp->d_name;
    if( (name[0] == '.') || (index(name, '.') == NULL) ) 
      continue;
    if ( nentries >= MAXENTRY ) {
      sprintf(error_buf, "%s %s %s", "Too many manual pages in directory",
	      path, "recompile with A larger value for MAXENTRY.");
      PrintError(error_buf);
    }
    sprintf(full_name, "%s/%s", path, name);
    local_manual->entries[nentries++] = StrAlloc(full_name);
  }
  local_manual->nentries = nentries;
}

/*	Function Name: SortAndRemove
 *	Description: This function sorts all the entry names and
 *                   then removes all the duplicate entries.
 *	Arguments: manual - a pointer to the manual structure.
 *                 number - the number of manual sections.
 *	Returns: an improved manual stucure
 */

static void
SortAndRemove(manual, number)
Manual *manual;
int number;
{
  int i;
  char *l1, *l2;

  for ( i = 0; i < number; i++) { /* sort each section */
    register int j = 0;
    Manual * man = &(manual[i]);

#ifdef DEBUG
  printf("sorting section %d - %s\n", i, man->blabel);
#endif DEBUG

    qsort(man->entries, man->nentries, sizeof( char * ), CmpEntryLabel);

#ifdef DEBUG
    printf("removing from section %d.\n", i);
#endif DEBUG

    if ( (l1 = rindex(man->entries[j], '/')) == NULL)
      PrintError("Internal error while removing duplicate manual pages.");
    j++;

    while (j < (man->nentries - 1) ) {
      l2 = l1;
      if ( (l1 = rindex(man->entries[j], '/')) == NULL)
	PrintError("Internal error while removing duplicate manual pages.");
      if ( streq(l1, l2) ) {
	register int k;
	for( k = j; k < (man->nentries); k++)
	  man->entries[k - 1] = man->entries[k];
	(man->nentries)--;
      }
      else
	j++;
    }
  }
}

/*	Function Name: CmpEntryLabel - used in qsort().
 *	Description: compares to elements by using their labels.
 *	Arguments: e1, e2 - two items to compare.
 *	Returns: an integer >, < or = 0.
 */

static int 
CmpEntryLabel(e1, e2) 
char **e1, **e2;
{
  char *l1, *l2;

/*
 * What we really want to compare is the actual names of the manual pages,
 * and not the full path names.
 */

  if ( (l1 = rindex(*e1, '/')) == NULL)
    PrintError("Internal error while sorting manual pages.");
  if ( (l2 = rindex(*e2, '/')) == NULL)
    PrintError("Internal error while sorting manual pages.");
  return( strcmp(l1, l2) );
}

/*	Function Name: StrAlloc
 *	Description: this function allocates memory for a character string
 *      pointed to by sp and returns its new pointer.
 *	Arguments: sp - a pointer to the string that needs memory.
 *	Returns: a pointer to this string, that is now safely allocated.
 */

char *
StrAlloc(sp) char *sp;
{
  char *ret;

  if((ret = (char *) malloc(strlen(sp)+1)) == NULL) {
    sprintf(error_buf,"Out of memory");
    PrintError(error_buf);
  }
  strcpy(ret,sp);
  return(ret);
}

/*	Function Name: InitManual
 *	Description: Initializes this manual section.
 *	Arguments: l_manual - local copy of the manual structure.
 *                 label - the button label for this section.
 *	Returns: none.
 */

static void
InitManual(l_manual, label)
Manual * l_manual;
char * label;
{
  bzero( l_manual, sizeof(Manual) );	        /* clear it. */
  l_manual->blabel = label;	                /* set label. */
  l_manual->entries = (char **) malloc( sizeof(char *) * MAXENTRY);
  if (l_manual->entries == NULL)
	PrintError("Could not allocate memory in InitManual().");
}
  
#if defined(DEBUG)

/*	Function Name: DumpManual
 *	Description: Debugging function that dumps the entire manual page
 *                   structure.
 *	Arguments: number - the number of sections.
 *	Returns: none.
 */

DumpManual(number)
{
  register int i,j;
  
  for ( i = 0; i < number; i++) {
    printf("label: %s\n", manual[i].blabel);
    for (j = 0; j < manual[i].nentries; j++) 
      printf("%s\n", manual[i].entries[j]);
  }
}

#endif DEBUG
@EOF

chmod 770 man.c

echo x - misc.c
cat >misc.c <<'@EOF'
/*
 * xman - X window system manual page display program.
 *
 * $XConsortium: misc.c,v 1.4 89/01/06 18:42:24 kit Exp $
 *
 * Copyright 1987, 1988 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Author:    Chris D. Peterson, MIT Project Athena
 * Created:   October 27, 1987
 */

#if ( !defined(lint) && !defined(SABER))
  static char rcs_version[] = "$Athena: misc.c,v 4.6 88/12/19 13:48:01 kit Exp $";
#endif

#include "globals.h"

/*
 * It would be very nice if these would pop up their own windows for 
 * error messages, whould anyone like to implement this???
 */

/*	Function Name: PrintWarning
 *	Description: This function prints a warning message to stderr.
 *	Arguments: string - the specific warning string.
 *	Returns: none
 */

void
PrintWarning(man_globals, string)
ManpageGlobals * man_globals;
char * string;
{
  char buffer[BUFSIZ];

  sprintf( buffer, "Xman Warning: %s", string);

  if (man_globals != NULL) 
    ChangeLabel(man_globals->label, buffer);

  fprintf(stderr, "%s\n", buffer);
}

/*	Function Name: PrintError
 *	Description: This Function prints an error message and exits.
 *	Arguments: string - the specific message.
 *	Returns: none. - exits tho.
 */

void
PrintError(string)
char * string;
{
  fprintf(stderr,"Xman Error: %s\n",string);
#ifdef DEBUG
  fprintf(stderr,"\n\nbye,bye\n\n\n\n\nsniff...\n");
#endif
  exit(42);
}

/*	Function Name: FindFilename
 *	Description: Opens the entry file given the entry struct.
 *	Arguments: man_globals - the globals info for this manpage.
 *                 entry - the structure containg the info on the file to open.
 *	Returns: fp - the file pointer
 */

FILE *
FindFilename(man_globals, entry)
ManpageGlobals * man_globals;
char * entry;
{
  FILE * file;
  char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], *temp;

  temp = CreateManpageName(entry);
  sprintf(man_globals->manpage_title, "The current manual page is: %s.", temp);
  free(temp);
  
  ParseEntry(entry, path, section, page);
  sprintf(man_globals->filename, "%s/%s%c.Z/%s", path, CAT, section[LCAT], page);

/* if we find the formatted manpage then return it */

  if ( (file = fopen(man_globals->filename,"r")) != NULL)
    return(file);

  return(Format(man_globals, entry));
}

/*	Function Name: Format
 *	Description: This funtion formats the manual pages and interfaces
 *                   with the user.
 *	Arguments: man_globals - the psuedo globals
 *                 file - the file pointer to use and return
 *                 entry - the current entry struct.
 *                 current_box - The current directory being displayed. 
 *	Returns: none.
 */

/* ARGSUSED */

FILE *
Format(man_globals, entry)
ManpageGlobals * man_globals; 
char * entry;
{
  FILE * file;
  Widget w = man_globals->manpagewidgets.directory;
  char cmdbuf[BUFSIZ], tmp[BUFSIZ], catdir[BUFSIZ];
  char path[BUFSIZ], section[BUFSIZ], error_buf[BUFSIZ];

  Position x,y;			/* location to pop up whould you 
				   like to save widget. */

  strcpy(tmp,MANTEMP);		/* get a temp file. */
  strcpy(man_globals->tmpfile,mktemp(tmp));

/*
 * Replace with XtPopupSync when this becomes avaliable. 
 * This section of code does not work, and I have not got time
 * to f*ck with it.
 */

/*
  PopUpMenu(w,NULL,NULL);
  while ( !XCheckTypedWindowEvent(XtDisplay(w), 
				 XtWindow(man_globals->standby1), 
				 Expose, &event) );
  XtDispatchEvent( &event );
  while ( !XCheckTypedWindowEvent(XtDisplay(w), 
				 XtWindow(man_globals->standby2), 
				 Expose, &event) );
  XtDispatchEvent( &event );
  XFlush(XtDisplay(w));
*/
/* End replacement. */

  if ( (file = fopen( entry , "r")) == NULL) {
    /* We Really could not find it, this should never happen, yea right. */
    sprintf(error_buf, "Could open manual page file, %s", entry);
    PrintWarning(man_globals, error_buf);
    return(NULL);
  }

  ParseEntry(entry, path, section, NULL);

#ifdef macII
  sprintf(cmdbuf,
        "cd %s;/usr/bin/pcat %s | /usr/bin/col | /usr/bin/ul -t dumb > %s %s",
	path, entry, man_globals->tmpfile, "2> /dev/null");
#else
#ifdef hpux
  sprintf(cmdbuf,"cd %s ; cat %s | zcat | %s > %s %s", path,
	  entry, FORMAT, man_globals->tmpfile, "2> /dev/null");
#else
  sprintf(cmdbuf,"cd %s ; %s %s > %s %s", path,
	  FORMAT, entry, man_globals->tmpfile, "2> /dev/null");
#endif /* HP */
#endif

  if(system(cmdbuf) != 0) {	/* execute search. */
    sprintf(error_buf,
	    "Something went wrong trying to run the command: %s", cmdbuf);
    PrintWarning(man_globals, error_buf);
    return(NULL);
  }

  if ((file = fopen(man_globals->tmpfile,"r")) == NULL) {  
    sprintf(error_buf, "Something went wrong in retrieving the temp file, %s",
	    "Try cleaning up /tmp");
    PrintWarning(man_globals, error_buf);
    return(NULL);
  }

/* if the catdir is writeable the ask the user if he/she wants to
   write the man page to it. */

  sprintf(catdir,"%s/%s%c", path, CAT, section[LCAT]);
  
  if( (access(catdir,W_OK)) == 0)  {
    x = Width(man_globals->manpagewidgets.manpage)/2;
    y = Height(man_globals->manpagewidgets.manpage)/2;
    XtTranslateCoords(man_globals->manpagewidgets.manpage, x, y, &x, &y);
    PositionCenter( PopupChild(man_globals->manpagewidgets.manpage, 0),
		   (int) x, (int) y,0,0,0,0);
    XtPopup( PopupChild(man_globals->manpagewidgets.manpage, 0),
	    XtGrabExclusive);
  }
  XtPopdown( PopupChild(w, 0) );
  
  return(file);
}

/*	Function Name: KillManpage
 *	Description: This function kills off a manpage display and cleans up
 *                   after it.
 *	Arguments: man_globals - the psuedo global structure.
 *	Returns: none.
 */

void
KillManpage(man_globals)
ManpageGlobals * man_globals;
{
  if (man_globals->This_Manpage != help_widget) {
    XtDestroyWidget(man_globals->This_Manpage);
    free(man_globals);
    man_pages_shown--;
  }
  else
    XtPopdown(help_widget);
    
}

/*	Function Name: AddCursor
 *	Description: This function adds the cursor to the window.
 *	Arguments: w - the widget to add the cursor to.
 *                 cursor - the cursor to add to this widget.
 *	Returns: none
 */

void
AddCursor(w,cursor)
Widget w;
Cursor cursor;
{

  if (!XtIsRealized(w)) {
    PrintWarning(NULL, "Widget is not realized, no cursor added.\n");
    return;
  }
  XDefineCursor(XtDisplay(w),XtWindow(w),cursor);
}

/*	Function Name: ChangeLabel
 *	Description: This function changes the label field of the
 *                   given widget to the string in str.
 *	Arguments: w - the widget.
 *                 str - the string to change the label to.
 *	Returns: none
 */

void
ChangeLabel(w,str)
Widget w;
char * str;
{
  Arg arglist[3];		/* An argument list. */

  XtSetArg(arglist[0], XtNlabel, str);

/* shouldn't really have to do this. */
  XtSetArg(arglist[1], XtNwidth, 0);
  XtSetArg(arglist[2], XtNheight, 0);

  XtSetValues(w, arglist, (Cardinal) 1);
}

/*
 * In an ideal world this would be part of the XToolkit, and I would not
 * have to do it, but such is life sometimes.  Perhaps in X11R3.
 */

/*	Function Name: PositionCenter
 *	Description: This fuction positions the given widgets center
 *                   in the following location.
 *	Arguments: widget - the widget widget to postion
 *                 x,y - The location for the center of the widget
 *                 above - number of pixels above center to locate this widget
 *                 left - number of pixels left of center to locate this widget
 *                 h_space, v_space - how close to get to the edges of the
 *                                    parent window.
 *	Returns: none
 *      Note:  This should only be used with a popup widget that has override
 *             redirect set.
 */

void
PositionCenter(widget,x,y,above,left,v_space,h_space)
Widget widget;
int x,y,above,left;
int h_space,v_space;
{
  int x_temp,y_temp;		/* location of the new window. */
  int parent_height,parent_width; /* Height and width of the parent widget or
				   the root window if it has no parent. */

  x_temp = x - left - Width(widget) / 2 + BorderWidth(widget);
  y_temp = y - above -  Height(widget) / 2 + BorderWidth(widget);

  parent_height = HeightOfScreen(XtScreen(widget));
  parent_width = WidthOfScreen(XtScreen(widget));

/*
 * Check to make sure that all edges are within the viewable part of the
 * root window, and if not then force them to be.
 */

  if (x_temp < h_space) 
    x_temp = v_space;
  if (y_temp < v_space)
    (y_temp = 2);

  if ( y_temp + Height(widget) + v_space > parent_height )
      y_temp = parent_height - Height(widget) - v_space; 

  if ( x_temp + Width(widget) + h_space > parent_width )
      x_temp = parent_width - Width(widget) - h_space; 

  XtMoveWidget(widget,x_temp,y_temp);
}  

/*	Function Name: ParseEntry(entry, path, sect, page)
 *	Description: Parses the manual pages entry filenames.
 *	Arguments: str - the full path name.
 *                 path - the path name.      RETURNED
 *                 sect - the section name.   RETURNED
 *                 page - the page name.      RETURNED
 *	Returns: none.
 */

void
ParseEntry(entry, path, sect, page)
char *entry, *path, *page, *sect;
{
  char *c, temp[BUFSIZ];

  strcpy(temp, entry);

  c = rindex(temp, '/');
  if (c == NULL) 
    PrintError("index failure in ParseEntry.");
  *c++ = '\0';
  if (page != NULL)
    strcpy(page, c);

  c = rindex(temp, '/');
  if (c == NULL) 
    PrintError("index failure in ParseEntry.");
  *c++ = '\0';
  if (sect != NULL)
    strcpy(sect, c);

  if (path != NULL)
    strcpy(path, temp);
}
@EOF

chmod 770 misc.c

echo x - search.c
cat >search.c <<'@EOF'
/*
 * xman - X window system manual page display program.
 *
 * $XConsortium: search.c,v 1.3 89/01/06 18:42:28 kit Exp $
 * $oHeader: search.c,v 4.0 88/08/31 22:13:19 kit Exp $
 *
 * Copyright 1987, 1988 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Author:    Chris D. Peterson, MIT Project Athena
 * Created:   November 3, 1987
 */

#if ( !defined(lint) && !defined(SABER))
  static char rcs_version[] = "$Athena: search.c,v 4.7 89/01/06 15:59:02 kit Exp $";
#endif

#include "globals.h"

/* Map <CR> and control-M to goto begining of file. */

char search_trans_table[] =
"<Key>0xff0d:   beginning-of-file() \n\
Ctrl<Key>m:    beginning-of-file()";

#define SEARCHARGS 10

FILE * DoManualSearch();
static int BEntrySearch();

/*	Function Name: MakeSearchWidget
 *	Description: This Function Creates the Search Widget.
 *	Arguments: man_globals - the pseudo globas for this manpage.
 *                 w - the widgets parent
 *	Returns: the search widget.
 */

void
MakeSearchWidget(man_globals,parent)
ManpageGlobals * man_globals;
Widget parent;
{
  Widget label,box,command,text;	/* the widgets for various items. */
  Widget popup_shell;		/* the pop up widget's shell. */
  Arg arglist[SEARCHARGS];	/* The arglist and number of args. */
  Cardinal num_args;
  Dimension width, height;	/* The width of the cancel and text button. */
  Dimension h_space;
  XtGeometryResult answer;	/* the answer for the resize request. */
  XtTranslations translations;	/* The translation table to add to the text
				   widget.*/

  num_args = 0;
  XtSetArg(arglist[num_args], XtNallowShellResize, TRUE);
  num_args++;

  popup_shell = XtCreatePopupShell(SEARCHNAME, transientShellWidgetClass, 
				   parent, arglist, num_args);

  box = XtCreateWidget("box",boxWidgetClass, popup_shell,NULL, (Cardinal) 0);

  num_args = 0;
  XtSetArg(arglist[num_args], XtNborderWidth, 0);
  num_args++;

  label = XtCreateWidget("Type string to search for",
			 labelWidgetClass,box,arglist,num_args);
  XtManageChild(label);

  num_args = 0;

  XtSetArg(arglist[num_args], XtNeditType, XttextEdit);
  num_args++;

  strcpy(man_globals->search_string, INIT_SEARCH_STRING);

  XtSetArg(arglist[num_args], XtNstring, man_globals->search_string);
  num_args++;

/* length the user is allowed to make the search string. */

  XtSetArg(arglist[num_args], XtNlength, SEARCH_STRING_LENGTH);
  num_args++;
  XtSetArg(arglist[num_args], XtNcursor, resources.cursors.search_entry);
  num_args++;

  text = XtCreateWidget("textWidgetSearch",asciiStringWidgetClass,
			box, arglist, num_args);
  XtManageChild(text);
  translations = XtParseTranslationTable(search_trans_table);
  XtOverrideTranslations(text, translations);

  man_globals->text_widget = text;
  XtSetKeyboardFocus(box, text);

/* 
 * I am playing a few games here, Make sure that the button with the longest
 * name comes first, otherwise you may lose some text.
 */

  num_args = 0;
  command = XtCreateManagedWidget(MANUALSEARCH,commandWidgetClass,box,
			   arglist, num_args);
  XtAddCallback(command, XtNcallback, SearchCallback, (caddr_t) man_globals);

  num_args = 0;
  XtSetArg(arglist[num_args], XtNwidth, &width); 
  num_args++;
  XtGetValues(command,arglist,num_args);

  num_args = 0;
  XtSetArg(arglist[num_args], XtNwidth, width); 
  num_args++;

  command = XtCreateManagedWidget(APROPOSSEARCH,commandWidgetClass,box,
			   arglist, num_args);
  XtAddCallback(command, XtNcallback, SearchCallback, (caddr_t) man_globals);
  
  XtSetArg(arglist[0], XtNhSpace, &h_space);
  XtGetValues(box, arglist, (Cardinal) 1);

/*
 * The width of the cancel button is 2 command buttons + 2 border_widths
 * + the h_space of the button. 
 */
 
  width = 2 * Width(command);
  width += 2 * BorderWidth(command);
  width += h_space;

  num_args = 0;			
  XtSetArg(arglist[num_args], XtNwidth, width); 
  num_args++;

  command = XtCreateManagedWidget(CANCEL, commandWidgetClass, box,
				  arglist, num_args);
  XtAddCallback(command, XtNcallback, SearchCallback, (caddr_t) man_globals);

/* 
 * Make the text widget 1 line high.
 * 
 * NOTE: This assumes that the text widget uses the same font as the command 
 * widgets.
 */

  height = Height(command);

  answer = XtMakeResizeRequest(text, width, height, &width, &height);
    
  switch(answer) {
  case XtGeometryYes:
  case XtGeometryNo:
    break;
  case XtGeometryAlmost:
    (void) XtMakeResizeRequest(text, width, height, &width, &height);
  }

  XtManageChild(box);
  XtRealizeWidget(popup_shell);
  AddCursor(popup_shell,resources.cursors.search_entry);
}

/*	Function Name: DoSearch
 *	Description: This function performs a search for a man page or apropos
 *                   search upon search string.
 *	Arguments: man_globals - the pseudo globas for this manpage.
 *                 type - the type of search.
 *	Returns: none.
 */

#define LOOKLINES 6

/* 
 * All I do here is to check the string to be sure that we like it, and
 * Then exec the commend via a system() call, the information for a manual
 * search is all in memory, but I was too lazy to write a function to look
 * for it.  If you want one, please write it and send it to me.
 * If nothing is found then I send a warning message to the user, and do
 * nothing.
 */

FILE *
DoSearch(man_globals,type)
ManpageGlobals * man_globals;
int type;
{
  char cmdbuf[BUFSIZ],*mantmp,*manpath;
  char tmp[BUFSIZ],path[BUFSIZ];
  char string_buf[BUFSIZ],*cmp_str;
  char error_buf[BUFSIZ],label[BUFSIZ];
  FILE * file;
  int i,count;
  Boolean flag;

  /* if there was a CR ignore it and every thing after it */

  for (i = 0; i <= strlen(man_globals->search_string);i++) {
    if (man_globals->search_string[i] == '\n')
      man_globals->search_string[i] = '\0';
  }

  /* If the string is empty or starts with a space then do not search */

  if (!strcmp(man_globals->search_string,"") || 
      (man_globals->search_string[0] == ' ')) {
    PrintWarning(man_globals, "You want me to search for what???");
    return(NULL);
  }

  strcpy(tmp,MANTEMP);		/* get a temp file. */
  mantmp = mktemp(tmp);

  /* set the command */

  manpath=getenv("MANPATH");
  if (manpath == NULL || strcmp(manpath,"") == 0)
    strcpy(path,MANDIR);
  else
    strcpy(path,manpath);

  if (type == APROPOS) {
#ifdef hpux
    sprintf(cmdbuf, APROPOSFILTER, man_globals->search_string, mantmp);
#else
    sprintf(cmdbuf, APROPOSFILTER, path, man_globals->search_string, mantmp);
#endif
    sprintf(label,"Results of apropos search on: %s",
	    man_globals->search_string);

    if(system(cmdbuf) != 0) {	/* execute search. */
      sprintf(error_buf,"Something went wrong trying to run %s\n",cmdbuf);
      PrintError(error_buf);
    }

    if((file = fopen(mantmp,"r")) == NULL)
      PrintError("lost temp file? out of temp space?");

    sprintf(string_buf,"%s: nothing appropriate",man_globals->search_string);

    /*
     * Check first LOOKLINES lines for "nothing appropriate".
     */
  
    count = 0;
    cmp_str = "foo";
    flag = FALSE;
    while ( cmp_str != NULL && count <= LOOKLINES ) {
      fgets(cmp_str, 80, file);
      /* strip off the '\n' */
      for (i = 0; i <= strlen(cmp_str);i++) {
	if (cmp_str[i] == '\n')
	  cmp_str[i] = '\0';
      }
      if (!strcmp(cmp_str,string_buf)) {
	flag = TRUE;
	count += LOOKLINES;	/* we've matched finish loop fast. */
      }
      count++;
    }

    /*
     * If the file is less than this number of lines then assume that there is
     * nothing apropriate found. This does not confuse the apropos filter.
     */

    if (flag) {
      fclose(file);
      file = NULL;
      ChangeLabel(man_globals->label,string_buf);
      return(NULL);
    }
  
    strcpy(man_globals->manpage_title,label);
    ChangeLabel(man_globals->label,label);
    fseek(file, 0L, 0);		/* reset file to point at top. */
  }
  else {			/* MANUAL SEACH */
    file = DoManualSearch(man_globals);
    if (file == NULL) {
      sprintf(string_buf,"No manual entry for %s.",man_globals->search_string);
      ChangeLabel(man_globals->label,string_buf);
      return(NULL);
    }
  }
  return(file);
}

/*	Function Name: DoManualSearch
 *	Description: performs a manual search.
 *	Arguments: man_globals - the manual page specific globals.
 *	Returns: the filename of the man page.
 */

#define NO_ENTRY -100

FILE * 
DoManualSearch(man_globals)
ManpageGlobals *man_globals;
{
  char *string;
  int e_num = NO_ENTRY;
  int i, initial_entry;

/* search current section first. */
  
  string = man_globals->search_string;
  i = man_globals->current_directory;
  e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);

/* search other sections. */

  if (e_num == NO_ENTRY) {
    i = -1;			/* At the exit of the loop i needs to
				   be the one we used. */
    do {
      i++;
      if (i == man_globals->current_directory) i++;
      e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
    } while ( (i < sections) && (e_num == NO_ENTRY) );
    if (e_num == NO_ENTRY)
      return(NULL);
/*
 * Manual page found in some other section, unhighlight the current one.
 */
    XtListUnhighlight(
            man_globals->manpagewidgets.box[man_globals->current_directory]);
  }
  else {
    /* 
     * Highlight the element we are searching for if it is in the directory
     * listing currently being shown.
     */
   
    XtListHighlight(man_globals->manpagewidgets.box[i], e_num);
  }
  return(FindFilename(man_globals, manual[i].entries[e_num]));
}

/*	Function Name: BEntrySearch
 *	Description: binary search through entries.
 *	Arguments: string - the string to match.
 *                 first - the first entry in the list.
 *                 number - the number of entries.
 *	Returns: a pointer to the entry found.
 */

static int
BEntrySearch(string, first, number)
char * string;
char ** first;
int number;
{
  int check, cmp, len_cmp, global_number;
  char *head, *tail;
  
  global_number = 0;
  while (TRUE) {

    if (number == 0) {
      return(NO_ENTRY);		/* didn't find it. */
    }

    check = number/2;

    head = rindex(first[ global_number + check ], '/');
    if (head == NULL) 
      PrintError("index failure in BEntrySearch");
    head++;

    tail = rindex(head, '.');
    if (tail == NULL) 
      PrintError("index failure in BEntrySearch");

    cmp = strncmp(string, head, (tail - head));
    len_cmp = strlen(string) - (int) (tail - head);

    if ( cmp == 0 && len_cmp == 0) {
      return(global_number + check);
    }
    else if ( cmp < 0 || ((cmp == 0) && (len_cmp < 0)) ) 
      number = check;
    else /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */ {
      global_number += (check + 1);
      number -= ( check + 1 );
    }
  }
}
@EOF

chmod 770 search.c

exit 0
-- 
                                   ________   _________    _______    ________
                                  / _______|  |  ____  \  |  ___  \  / _______|
Rob Gabbard (uunet!sdrc!evgabb)   | |______   | |    \  \ | |   \ | | /
Technical Development Engineer    \_______ \  | |     | | | |___/ / | |
Structural Dynamics Research Corp. ______| |  | |____/  / |  ____ \ | \_______
#include <std/disclaimer.h>       |________/  |________/  |_|    |_| \________|

dan
-----------------------------------------------------------
		    O'Reilly && Associates
		argv@sun.com / argv@ora.com
	   632 Petaluma Ave, Sebastopol, CA 95472 
     800-338-NUTS, in CA: 800-533-NUTS, FAX 707-829-0104
    Opinions expressed reflect those of the author only.