misc.c revision 5e248323
1/* $XConsortium: misc.c,v 1.31 94/12/16 21:36:53 gildea Exp $ */
2/* $XdotOrg: xc/programs/xman/misc.c,v 1.6 2004/09/02 08:40:33 kem Exp $ */
3/*
4
5Copyright (c) 1987, 1988  X Consortium
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be included
16in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
22OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24OTHER DEALINGS IN THE SOFTWARE.
25
26Except as contained in this notice, the name of the X Consortium shall
27not be used in advertising or otherwise to promote the sale, use or
28other dealings in this Software without prior written authorization
29from the X Consortium.
30
31*/
32/* $XFree86: xc/programs/xman/misc.c,v 1.10 2003/08/02 17:35:48 herrb Exp $ */
33
34/*
35 * xman - X window system manual page display program.
36 * Author:    Chris D. Peterson, MIT Project Athena
37 * Created:   October 27, 1987
38 */
39
40#ifdef HAVE_CONFIG_H
41# include "config.h"
42#endif
43
44#include "globals.h"
45#include "vendor.h"
46#include <X11/Xos.h> 		/* sys/types.h and unistd.h included in here */
47#include <sys/stat.h>
48#include <errno.h>
49#include <X11/Xaw/Dialog.h>
50#include <X11/Shell.h>
51
52static FILE * Uncompress(ManpageGlobals * man_globals, char * filename);
53#ifndef HAS_MKSTEMP
54static Boolean UncompressNamed(ManpageGlobals * man_globals, char * filename,
55			       char * output);
56static Boolean UncompressUnformatted(ManpageGlobals * man_globals,
57				     char * entry, char * filename);
58#else
59static Boolean UncompressNamed(ManpageGlobals * man_globals, char * filename,
60			       char * output, FILE ** output_fd);
61static Boolean UncompressUnformatted(ManpageGlobals * man_globals,
62				     char * entry, char * filename,
63				     FILE **file);
64#endif
65#ifdef HANDLE_ROFFSEQ
66static Boolean ConstructCommand(char * cmdbuf, char * path, char * filename, char * tempfile);
67#endif
68
69#if defined(ISC) || defined(__SCO__) || defined(__UNIXWARE__)
70static char *uncompress_format = NULL;
71static char *uncompress_formats[] =
72      {  UNCOMPRESS_FORMAT_1,
73         UNCOMPRESS_FORMAT_2,
74         UNCOMPRESS_FORMAT_3
75      };
76#endif
77
78/*	Function Name: PopupWarning
79 *	Description: This function pops upa warning message.
80 *	Arguments: string - the specific warning string.
81 *	Returns: none
82 */
83
84static Widget warnShell, warnDialog;
85
86static void
87PopdownWarning(Widget w, XtPointer client, XtPointer call)
88{
89  XtPopdown((Widget)client);
90}
91
92void
93PopupWarning(ManpageGlobals * man_globals, const char * string)
94{
95  int n;
96  Arg wargs[3];
97  Dimension topX, topY;
98  char buffer[BUFSIZ];
99  Boolean hasPosition;
100
101  snprintf( buffer, sizeof(buffer), "Xman Warning: %s", string);
102  hasPosition = FALSE;
103  if (top)
104  {
105    n=0;
106    XtSetArg(wargs[n], XtNx, &topX); n++;
107    XtSetArg(wargs[n], XtNy, &topY); n++;
108    XtGetValues(top, wargs, n);
109    hasPosition = TRUE;
110  }
111
112  if (man_globals != NULL)
113    ChangeLabel(man_globals->label, buffer);
114  if (man_globals->label == NULL) {
115    n=0;
116    if (hasPosition)
117    {
118      XtSetArg(wargs[n], XtNx, topX); n++;
119      XtSetArg(wargs[n], XtNy, topY); n++;
120    }
121    XtSetArg(wargs[n], XtNtransientFor, top); n++;
122    warnShell = XtCreatePopupShell("warnShell", transientShellWidgetClass,
123				   initial_widget, wargs, n);
124    XtSetArg(wargs[0], XtNlabel, buffer);
125    warnDialog = XtCreateManagedWidget("warnDialog", dialogWidgetClass,
126				       warnShell, wargs, 1);
127    XawDialogAddButton(warnDialog, "dismiss", PopdownWarning,
128		       (XtPointer)warnShell);
129    XtRealizeWidget(warnShell);
130    Popup(warnShell, XtGrabNone);
131  }
132}
133
134/*	Function Name: PrintError
135 *	Description: This Function prints an error message and exits.
136 *	Arguments: string - the specific message.
137 *	Returns: none. - exits tho.
138 */
139
140void
141PrintError(char * string)
142{
143  fprintf(stderr,"Xman Error: %s\n",string);
144  exit(EXIT_FAILURE);
145}
146
147/*	Function Name: OpenFile
148 *	Description: Assignes a file to the manpage.
149 *	Arguments: man_globals - global structure.
150 *                 file        - the file pointer.
151 *	Returns: none
152 */
153
154void
155OpenFile(ManpageGlobals * man_globals, FILE * file)
156{
157  Arg arglist[1];
158  Cardinal num_args = 0;
159
160  if (man_globals->curr_file) {
161#if 0 /* Ownership rules need to be fixed first */
162    fclose(man_globals->curr_file);
163#endif
164  }
165  man_globals->curr_file = file;
166
167  XtSetArg(arglist[num_args], XtNfile, man_globals->curr_file); num_args++;
168  XtSetValues(man_globals->manpagewidgets.manpage, arglist, num_args);
169}
170
171
172/*	Function Name: FindManualFile
173 *	Description: Opens the manual page file given the entry information.
174 *	Arguments: man_globals - the globals info for this manpage.
175 *                 section_num - section number of the man page.
176 *                 entry_num   - entry number of the man page.
177 *	Returns: fp - the file pointer
178 *
179 * NOTES:
180 *
181 * If there is a uncompressed section it will look there for uncompresed
182 * manual pages first and then for individually compressed file in the
183 * uncompressed section.
184 *
185 * If there is a compressed directory then it will also look there for
186 * the manual pages.
187 *
188 * If both of these fail then it will attempt to format the manual page.
189 */
190
191FILE *
192FindManualFile(ManpageGlobals * man_globals, int section_num, int entry_num)
193{
194  FILE * file;
195  char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], *temp;
196  char filename[BUFSIZ];
197  char * entry = manual[section_num].entries[entry_num];
198  int len_cat = strlen(CAT);
199#if defined(ISC) || defined(__SCO__) || defined(__UNIXWARE__)
200  int i;
201#endif
202
203  temp = CreateManpageName(entry, 0, 0);
204  snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title),
205    "The current manual page is: %s.", temp);
206  XtFree(temp);
207
208  ParseEntry(entry, path, section, page);
209
210/*
211 * Look for uncompressed files first.
212 */
213#if defined(__OpenBSD__) || defined(__NetBSD__)
214  /* look in machine subdir first */
215  snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s", path, CAT,
216	  section + len_cat, MACHINE, page);
217  if ( (file = fopen(filename,"r")) != NULL)
218    return(file);
219#endif
220
221  snprintf(filename, sizeof(filename), "%s/%s%s/%s",
222    path, CAT, section + len_cat, page);
223  if ( (file = fopen(filename,"r")) != NULL)
224    return(file);
225
226/*
227 * Then for compressed files in an uncompressed directory.
228 */
229
230#if !defined(ISC) && !defined(__UNIXWARE__)
231#if defined(__OpenBSD__) || defined(__NetBSD__)
232  /* look in machine subdir first */
233  snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT,
234	  section + len_cat, MACHINE, page, COMPRESSION_EXTENSION);
235  if ( (file = Uncompress(man_globals, filename)) != NULL)
236    return(file);
237#endif
238  snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
239	  section + len_cat, page, COMPRESSION_EXTENSION);
240  if ( (file = Uncompress(man_globals, filename)) != NULL)
241    return(file);
242#ifdef GZIP_EXTENSION
243  else {
244#if defined(__OpenBSD__) || defined(__NetBSD__)
245      /* look in machine subdir first */
246      snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT,
247	      section + len_cat, MACHINE, page, GZIP_EXTENSION);
248      if ( (file = Uncompress(man_globals, filename)) != NULL)
249	  return(file);
250#endif
251    snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
252	    section + len_cat, page, GZIP_EXTENSION);
253    if ( (file = Uncompress(man_globals, filename)) != NULL)
254      return(file);
255  }
256#endif
257#ifdef BZIP2_EXTENSION
258#if defined(__OpenBSD__) || defined(__NetBSD__)
259      /* look in machine subdir first */
260      snprintf(filename, sizeof(filename), "%s/%s%s/%s/%s.%s", path, CAT,
261	      section + len_cat, MACHINE, page, BZIP2_EXTENSION);
262      if ( (file = Uncompress(man_globals, filename)) != NULL)
263	  return(file);
264#endif
265  {
266    snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
267	    section + len_cat, page, BZIP2_EXTENSION);
268    if ( (file = Uncompress(man_globals, filename)) != NULL)
269      return(file);
270  }
271#endif
272#ifdef LZMA_EXTENSION
273  {
274    snprintf(filename, sizeof(filename), "%s/%s%s/%s.%s", path, CAT,
275	    section + len_cat, page, LZMA_EXTENSION);
276    if ( (file = Uncompress(man_globals, filename)) != NULL)
277      return(file);
278  }
279#endif
280#else
281  for(i = 0; i < strlen(COMPRESSION_EXTENSIONS); i++) {
282      snprintf(filename, sizeof(filename), "%s/%s%s/%s.%c", path, CAT,
283            section + len_cat, page, COMPRESSION_EXTENSIONS[i]);
284      uncompress_format = uncompress_formats[i];
285#ifdef DEBUG
286      printf("Trying .%c ...\n", COMPRESSION_EXTENSIONS[i]);
287#endif
288      if ( (file = Uncompress(man_globals, filename)) != NULL)
289	return(file);
290  }
291#endif
292
293/*
294 * And lastly files in a compressed directory.
295 *
296 * The directory is not actually compressed it is just named man#.Z
297 * and all files in it are compressed without the .Z extension.
298 * HP does it this way (really :-).
299 */
300
301  snprintf(filename, sizeof(filename), "%s/%s%s.%s/%s", path, CAT,
302	   section + len_cat, COMPRESSION_EXTENSION, page);
303  if ( (file = Uncompress(man_globals, filename)) != NULL)
304    return(file);
305/*
306 * We did not find any preformatted manual pages, try to format it.
307 */
308
309  return(Format(man_globals, entry));
310}
311
312/*	Function Namecompress
313 *	Description: This function will attempt to find a compressed man
314 *                   page and uncompress it.
315 *	Arguments: man_globals - the psuedo global info.
316 *                 filename - name of file to uncompress.
317 *	Returns:; a pointer to the file or NULL.
318 */
319
320static FILE *
321Uncompress(ManpageGlobals * man_globals, char * filename)
322{
323  char tmp_file[BUFSIZ];
324  FILE * file;
325
326#ifndef HAS_MKSTEMP
327  if ( !UncompressNamed(man_globals, filename, tmp_file) )
328    return(NULL);
329
330  else if ((file = fopen(tmp_file, "r")) == NULL) {
331      PopupWarning(man_globals, "Something went wrong in retrieving the "
332		   "uncompressed manual page try cleaning up /tmp.");
333  }
334#else
335  if (!UncompressNamed(man_globals, filename, tmp_file, &file)) {
336      PopupWarning(man_globals, "Something went wrong in retrieving the "
337		   "uncompressed manual page try cleaning up /tmp.");
338      return(NULL);
339  }
340#endif
341
342  unlink(tmp_file);		/* remove name in tree, it will remain
343				   until we close the fd, however. */
344  return(file);
345}
346
347/*	Function Name: UncompressNamed
348 *	Description: This function will attempt to find a compressed man
349 *                   page and uncompress it.
350 *	Arguments: man_globals - the psuedo global info.
351 *                 filename - name of file to uncompress.
352 * RETURNED        output - the file name output (must be an allocated string).
353 *	Returns:; TRUE if the file was found.
354 */
355
356#ifndef HAS_MKSTEMP
357static Boolean
358UncompressNamed(ManpageGlobals * man_globals, char * filename, char * output)
359#else
360static Boolean
361UncompressNamed(ManpageGlobals * man_globals, char * filename, char * output,
362		FILE ** output_fd)
363#endif
364{
365  char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ];
366  struct stat junk;
367#ifdef HAS_MKSTEMP
368  int fd;
369#endif
370
371  if (stat(filename, &junk) != 0) { /* Check for existance of the file. */
372    if (errno != ENOENT) {
373      snprintf(error_buf, sizeof(error_buf),
374	       "Error while stating file %s, errno = %d", filename, errno);
375      PopupWarning(man_globals, error_buf);
376    }
377    return(FALSE);
378  }
379
380/*
381 * Using stdin is necessary to fool zcat since we cannot guarentee
382 * the .Z extension.
383 */
384
385  strcpy(tmp, MANTEMP);		/* get a temp file. */
386#ifndef HAS_MKSTEMP
387  (void) mktemp(tmp);
388#else
389  fd = mkstemp(tmp);
390  if (fd < 0) {
391      PopupWarning(man_globals, "Error creating a temp file");
392      return FALSE;
393  }
394  *output_fd = fdopen(fd, "r");
395#endif
396  strcpy(output, tmp);
397
398#ifdef GZIP_EXTENSION
399  if (streq(filename + strlen(filename) - strlen(GZIP_EXTENSION),
400	    GZIP_EXTENSION))
401    snprintf(cmdbuf, sizeof(cmdbuf), GUNZIP_FORMAT, filename, output);
402  else
403#endif
404#ifdef BZIP2_EXTENSION
405  if (streq(filename + strlen(filename) - strlen(BZIP2_EXTENSION),
406	    BZIP2_EXTENSION))
407    snprintf(cmdbuf, sizeof(cmdbuf), BUNZIP2_FORMAT, filename, output);
408  else
409#endif
410#ifdef LZMA_EXTENSION
411  if (streq(filename + strlen(filename) - strlen(LZMA_EXTENSION),
412	    LZMA_EXTENSION))
413    snprintf(cmdbuf, sizeof(cmdbuf), UNLZMA_FORMAT, filename, output);
414  else
415#endif
416  snprintf(cmdbuf, sizeof(cmdbuf), UNCOMPRESS_FORMAT, filename, output);
417  if(system(cmdbuf) == 0) 	/* execute search. */
418    return(TRUE);
419
420  snprintf(error_buf, sizeof(error_buf),
421	   "Error while uncompressing, command was: %s", cmdbuf);
422  PopupWarning(man_globals, error_buf);
423  return(FALSE);
424}
425
426#if defined(SMAN) && defined(SFORMAT)
427/*	Function Name: SgmlToRoffNamed
428 *	Description: This function will attempt to find an SGML man
429 *                   page and convert it to roff format.
430 *	Arguments: man_globals - the psuedo global info.
431 *                 filename - name of file to uncompress.
432 * RETURNED        output - the file name output (must be an allocated string).
433 *	Returns:; TRUE if the file was found.
434 */
435
436#ifndef HAS_MKSTEMP
437static Boolean
438SgmlToRoffNamed(ManpageGlobals * man_globals, char * filename, char * output)
439#else
440static Boolean
441SgmlToRoffNamed(ManpageGlobals * man_globals, char * filename, char * output,
442		FILE ** output_fd)
443#endif
444{
445  char tmp[BUFSIZ], cmdbuf[BUFSIZ], error_buf[BUFSIZ];
446  struct stat junk;
447#ifdef HAS_MKSTEMP
448  int fd;
449#endif
450
451  if (stat(filename, &junk) != 0) { /* Check for existance of the file. */
452    if (errno != ENOENT) {
453      snprintf(error_buf, sizeof(error_buf),
454	       "Error while stating file %s, errno = %d", filename, errno);
455      PopupWarning(man_globals, error_buf);
456    }
457    return(FALSE);
458  }
459
460  strcpy(tmp, MANTEMP);		/* get a temp file. */
461#ifndef HAS_MKSTEMP
462  (void) mktemp(tmp);
463#else
464  fd = mkstemp(tmp);
465  if (fd < 0) {
466      PopupWarning(man_globals, "Error creating a temp file");
467      return FALSE;
468  }
469  *output_fd = fdopen(fd, "r");
470#endif
471  strcpy(output, tmp);
472
473  snprintf(cmdbuf, sizeof(cmdbuf), "%s %s > %s", SFORMAT, filename, output);
474  if(system(cmdbuf) == 0) 	/* execute search. */
475    return(TRUE);
476
477  snprintf(error_buf, sizeof(error_buf),
478	   "Error while converting from sgml, command was: %s", cmdbuf);
479  PopupWarning(man_globals, error_buf);
480  return(FALSE);
481}
482#endif /* defined (SMAN) && defined(SFORMAT) */
483
484/*	Function Name: Format
485 *	Description: This funtion formats the manual pages and interfaces
486 *                   with the user.
487 *	Arguments: man_globals - the psuedo globals
488 *                 file - the file pointer to use and return
489 *                 entry - the current entry struct.
490 *                 current_box - The current directory being displayed.
491 *	Returns: none.
492 */
493
494/* ARGSUSED */
495
496FILE *
497Format(ManpageGlobals * man_globals, char * entry)
498{
499  FILE * file = NULL;
500#ifdef HAS_MKSTEMP
501  int fd;
502#endif
503  Widget manpage = man_globals->manpagewidgets.manpage;
504  char cmdbuf[BUFSIZ], tmp[BUFSIZ], filename[BUFSIZ], error_buf[BUFSIZ];
505  char path[BUFSIZ], sect[BUFSIZ];
506  XEvent event;
507  Position x,y;			/* location to pop up the
508				   "would you like to save" widget. */
509
510#ifndef HAS_MKSTEMP
511  if ( !UncompressUnformatted(man_globals, entry, filename) ) {
512#else
513  if ( !UncompressUnformatted(man_globals, entry, filename, &file) ) {
514#endif
515    /* We Really could not find it, this should never happen, yea right. */
516    snprintf(error_buf, sizeof(error_buf),
517	     "Could not open manual page, %s", entry);
518    PopupWarning(man_globals, error_buf);
519    XtPopdown( XtParent(man_globals->standby) );
520    return(NULL);
521  }
522
523#ifndef HAS_MKSTEMP
524  if ((file = fopen(filename, "r")) != NULL) {
525#else
526  if (file != NULL) {
527#endif
528    char line[BUFSIZ];
529
530    if (fgets(line, sizeof(line), file) != NULL) {
531	if (strncmp(line, ".so ", 4) == 0) {
532	  line[strlen(line) - 1] = '\0';
533	  fclose(file);
534	  unlink(filename);
535	  if (line[4] != '/') {
536	    char *ptr = NULL;
537
538	    strcpy(tmp, entry);
539	    if ((ptr = rindex(tmp, '/')) != NULL) {
540	      *ptr = '\0';
541	      if ((ptr = rindex(tmp, '/')) != NULL)
542		ptr[1] = '\0';
543	    }
544	  }
545	  else
546	    *tmp = '\0';
547	  snprintf(filename, sizeof(filename), "%s%s", tmp, line + 4);
548
549	  return (Format(man_globals, filename));
550	}
551    }
552    fclose(file);
553  }
554
555  Popup(XtParent(man_globals->standby), XtGrabExclusive);
556  while ( !XCheckTypedWindowEvent(XtDisplay(man_globals->standby),
557				  XtWindow(man_globals->standby),
558				  Expose, &event) );
559  XtDispatchEvent( &event );
560  XFlush(XtDisplay(man_globals->standby));
561
562  strcpy(tmp,MANTEMP);		          /* Get a temp file. */
563#ifndef HAS_MKSTEMP
564  (void) mktemp(tmp);
565#else
566  fd = mkstemp(tmp);
567  file = fdopen(fd, "r");
568#endif
569  strcpy(man_globals->tempfile, tmp);
570
571  ParseEntry(entry, path, sect, NULL);
572
573#ifndef HANDLE_ROFFSEQ
574#ifndef HAS_MKSTEMP
575  snprintf(cmdbuf, sizeof(cmdbuf), "cd %s ; %s %s %s > %s %s", path, TBL,
576	  filename, FORMAT, man_globals->tempfile, "2> /dev/null");
577#else
578  snprintf(cmdbuf, sizeof(cmdbuf), "cd %s ; %s %s %s >> %s %s", path, TBL,
579	  filename, FORMAT, man_globals->tempfile, "2> /dev/null");
580#endif
581#else
582  /* Handle more flexible way of specifying the formatting pipeline */
583  if (! ConstructCommand(cmdbuf, path, filename, man_globals->tempfile)) {
584     PopupWarning(man_globals, "Constructed command was too long!");
585     file = NULL;
586  }
587  else
588#endif /* HANDLE_ROFFSEQ */
589
590  if(system(cmdbuf) != 0) {	/* execute search. */
591    snprintf(error_buf, sizeof(error_buf),
592	    "Something went wrong trying to run the command: %s", cmdbuf);
593    PopupWarning(man_globals, error_buf);
594    file = NULL;
595  }
596  else {
597#ifndef HAS_MKSTEMP
598    if ((file = fopen(man_globals->tempfile,"r")) == NULL) {
599      PopupWarning(man_globals, "Something went wrong in retrieving the "
600		   "temp file, try cleaning up /tmp");
601    }
602    else {
603#endif
604
605      XtPopdown( XtParent(man_globals->standby) );
606
607      if ( (man_globals->save == NULL) ||
608	   (man_globals->manpagewidgets.manpage == NULL) )
609	unlink(man_globals->tempfile);
610      else {
611	char * ptr, catdir[BUFSIZ];
612
613	/*
614	 * If the catdir is writeable then ask the user if he/she wants to
615	 * write the man page to it.
616	 */
617
618	strcpy(catdir, man_globals->save_file);
619	if ( (ptr = rindex(catdir, '/')) != NULL) {
620	  *ptr = '\0';
621
622	  if ( access(catdir, W_OK) != 0 )
623	    unlink(man_globals->tempfile);
624	  else {
625	    x = (Position) Width(man_globals->manpagewidgets.manpage)/2;
626	    y = (Position) Height(man_globals->manpagewidgets.manpage)/2;
627	    XtTranslateCoords(manpage, x, y, &x, &y);
628	    PositionCenter( man_globals->save, (int) x, (int) y, 0, 0, 0, 0);
629	    XtPopup( man_globals->save, XtGrabExclusive);
630	  }
631	}
632	else
633	  unlink(man_globals->tempfile);
634      }
635#ifndef HAS_MKSTEMP
636    }
637#endif
638  }
639
640 /*
641  * If the original was compressed or in another format, delete temporary file.
642  */
643  if (man_globals->deletetempfile)
644    unlink(filename);
645
646  return(file);
647}
648
649#ifdef HANDLE_ROFFSEQ
650/*      Function Name: ConstructCommand
651 *      Description: Constructs the pipeline of commands necessary to format
652 *                   a manual page.
653 *      Arguments: cmdbuf - the buffer into which to write the command
654 *                 path - the directory in which the original man page resides
655 *                 filename - the (uncompressed) manpage source file
656 *                 tempfile - the name of a temporary file to direct the final
657 *                  output of the pipeline into
658 *      Returns: TRUE if the command fit into the buffer, FALSE if it would
659 *               be too long (more than BUFSIZ characters)
660 */
661static Boolean
662ConstructCommand(cmdbuf, path, filename, tempfile)
663   char *cmdbuf, *path, *filename, *tempfile;
664{
665   /* The original code did the following to produce a command line:
666    *   sprintf(cmdbuf,"cd %s ; %s %s %s > %s %s", path, TBL,
667    *      filename, FORMAT, man_globals->tempfile, "2> /dev/null");
668    * We are more flexible and follow more or less the algorithm used
669    * by the Linux man command:
670    *  + Obtain a string of letters from the following sources in order
671    *    of preference:
672    *    + a command line option (not implemented in xman; it's probably not
673    *      useful)
674    *    + the first line of the manpage source, if it is of the form:
675    *      '\" <string>
676    *    + the MANROFFSEQ environment variable
677    *    + a default string; this is "".
678    *  + Interpret the string as a pipeline of filters:
679    *    + e = eqn   g = grap   p = pic   t = tbl   v = vgrind   r = refer
680    *  + zsoelim is always run as the first preprocessor in any case.
681    *
682    * Strictly speaking we should save a catpage iff the string comes
683    * from the file or is the default.
684    *
685    * You'll notice that we format a man page into ASCII text output and then
686    * attempt to interpret things like L^HL as bold and so forth. This
687    * is so obviously the Wrong Thing it's untrue.
688    */
689   char *c = cmdbuf;           /* current posn in buffer */
690   int left = BUFSIZ;          /* space left in buffer */
691   int used;
692   char *fmt;
693   FILE *file;
694   char fmtbuf[128];
695   int gotfmt = 0;             /* set to 1 if we got a directive from source */
696   char fname[PATH_MAX];
697#ifdef __UNIXOS2__
698   int i;
699#endif
700
701   fmt = NULL;
702   /* If you have a command line option that gives a setting for fmt,
703      set it here. */
704
705   if (!fmt) {
706      /* This is the tricky bit: extract a format string from the source file
707       * Annoyingly, filename might be relative or absolute. We cheat and
708       * use system to get the thing to a known absoute filename.
709       */
710      if (filename[0] == '/') {
711         snprintf(fname, sizeof(fname), "%s", filename);
712      } else {
713         snprintf(fname, sizeof(fname), "%s/%s", path, filename);
714      }
715      if ((file = fopen(fname, "r")) &&
716          (fgets(fmtbuf, sizeof(fmtbuf), file)) &&
717          (!memcmp(fmtbuf, "'\\\" ", 4))) {
718                              /* that's squote-backslash-dquote-space */
719         int len;
720         fmt = fmtbuf + 3;
721         len = strlen(fmt);
722         if (len && (fmt[len-1] == '\n')) {
723            fmt[len-1] = 0;
724            gotfmt++;
725         }
726      }
727      if (!gotfmt)                                /* not there or some error */
728      {
729         fmt = getenv("MANROFFSEQ");
730      }
731   }
732
733   if (!fmt)
734   {
735      fmt = DEFAULT_MANROFFSEQ;
736   }
737
738
739   /* Start with the first fixed part of the command line */
740#ifdef __UNIXOS2__
741   for (i = 0; i < strlen(path); i++) {
742     if (path[i] == '/')
743       path[i] = '\\';
744   }
745   used = snprintf(c, left, "cd %s & %s %s ", path, ZSOELIM, filename);
746#else
747   used = snprintf(c, left, "cd %s; %s %s ", path, ZSOELIM, filename);
748#endif
749   left -= used;
750   c += used;
751   if (left <= 1)
752      return (FALSE);
753
754   /* Now add preprocessors of the form '| processor' */
755   for ( ; *fmt; fmt++)
756   {
757      char *filter;
758      switch (*fmt)
759      {
760         case 'e':
761            filter = EQN;
762            break;
763         case 'g':
764            filter = GRAP;
765            break;
766         case 'p':
767            filter = ROFF_PIC;
768            break;
769         case 't':
770            filter = TBL;
771            break;
772         case 'v':
773            filter = VGRIND;
774            break;
775         case 'r':
776            filter = REFER;
777            break;
778         default:
779            filter = NULL;
780            break;
781      }
782      if (filter)
783      {
784         used = snprintf(c, left, " | %s ", filter);
785         left -= used;
786         c += used;
787         if (left <= 1)
788            return (FALSE);
789      }
790   }
791
792   /* Now add the fixed trailing part 'formatprog > tempfile 2> /dev/null' */
793#ifdef __UNIXOS2__
794   used = snprintf(c, left, " | %s > %s 2>NUL", FORMAT, tempfile);
795#else
796#ifndef HAS_MKSTEMP
797   used = snprintf(c, left, " | %s > %s 2>/dev/null", FORMAT, tempfile);
798#else
799   used = snprintf(c, left, " | %s >> %s 2>/dev/null", FORMAT, tempfile);
800#endif
801#endif /* __UNIXOS2__ */
802   left -= used;
803   if (left <= 1)
804      return (FALSE);
805
806   return (TRUE);
807}
808#endif /* HANDLE_ROFFSEQ */
809
810/*	Function Name: UncompressUnformatted
811 *	Description: Finds an uncompressed unformatted manual page.
812 *	Arguments: man_globals - the psuedo global structure.
813 *                 entry - the manual page entry.
814 * RETURNED        filename - location to put the name of the file.
815 *	Returns: TRUE if the file was found.
816 */
817
818static Boolean
819#ifndef HAS_MKSTEMP
820UncompressUnformatted(ManpageGlobals * man_globals, char * entry,
821		      char * filename)
822#else
823UncompressUnformatted(ManpageGlobals * man_globals, char * entry,
824		      char * filename, FILE **file)
825#endif
826{
827  char path[BUFSIZ], page[BUFSIZ], section[BUFSIZ], input[BUFSIZ];
828  int len_cat = strlen(CAT), len_man = strlen(MAN);
829#if defined(SMAN) && defined(SFORMAT)
830  int len_sman = strlen(SMAN);
831#endif
832
833  ParseEntry(entry, path, section, page);
834
835  man_globals->bzip2 = FALSE;
836  man_globals->lzma = FALSE;
837
838#if defined(__OpenBSD__) || defined(__NetBSD__)
839  /*
840   * look for uncompressed file in machine subdir first
841   */
842  snprintf(filename, BUFSIZ, "%s/%s%s/%s/%s", path, MAN,
843	  section + len_cat, MACHINE, page);
844  if ( access( filename, R_OK ) == 0 ) {
845    man_globals->compress = FALSE;
846    man_globals->gzip = FALSE;
847    man_globals->deletetempfile = FALSE;
848    snprintf(man_globals->save_file, sizeof(man_globals->save_file),
849    	     "%s/%s%s/%s/%s", path, CAT, section + len_cat, MACHINE, page);
850    return(TRUE);
851  }
852 /*
853  * Then for compressed files in an uncompressed directory.
854  */
855  snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION);
856#ifndef HAS_MKSTEMP
857  if ( UncompressNamed(man_globals, input, filename) ) {
858#else
859  if ( UncompressNamed(man_globals, input, filename, file) ) {
860#endif
861    man_globals->compress = TRUE;
862    man_globals->deletetempfile = TRUE;
863    snprintf(man_globals->save_file, sizeof(man_globals->save_file),
864	     "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
865	     COMPRESSION_EXTENSION);
866    return(TRUE);
867  }
868#ifdef GZIP_EXTENSION
869  else {
870    snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION);
871#ifndef HAS_MKSTEMP
872    if ( UncompressNamed(man_globals, input, filename) ) {
873#else
874    if ( UncompressNamed(man_globals, input, filename, file) ) {
875#endif
876      man_globals->compress = TRUE;
877      man_globals->gzip = TRUE;
878      man_globals->deletetempfile = TRUE;
879      snprintf(man_globals->save_file, sizeof(man_globals->save_file),
880	       "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
881	       GZIP_EXTENSION);
882      return(TRUE);
883    }
884  }
885#endif /* GZIP_EXTENSION */
886#endif /* __OpenBSD__ || __NetBSD__ */
887
888#ifdef BZIP2_EXTENSION
889 {
890    snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION);
891#ifndef HAS_MKSTEMP
892    if ( UncompressNamed(man_globals, input, filename) ) {
893#else
894    if ( UncompressNamed(man_globals, input, filename, file) ) {
895#endif
896      man_globals->compress = TRUE;
897      man_globals->gzip = FALSE;
898      man_globals->bzip2 = TRUE;
899      snprintf(man_globals->save_file, sizeof(man_globals->save_file),
900	       "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
901	       BZIP2_EXTENSION);
902      return(TRUE);
903    }
904  }
905#endif /* BZIP2_EXTENSION */
906
907#ifdef LZMA_EXTENSION
908 {
909    snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION);
910#ifndef HAS_MKSTEMP
911    if ( UncompressNamed(man_globals, input, filename) ) {
912#else
913    if ( UncompressNamed(man_globals, input, filename, file) ) {
914#endif
915      man_globals->compress = TRUE;
916      man_globals->gzip = FALSE;
917      man_globals->lzma = TRUE;
918      snprintf(man_globals->save_file, sizeof(man_globals->save_file),
919	       "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
920	       LZMA_EXTENSION);
921      return(TRUE);
922    }
923  }
924#endif /* LZMA_EXTENSION */
925
926/*
927 * Look for uncompressed file first.
928 */
929
930  snprintf(filename, BUFSIZ, "%s/%s%s/%s", path, MAN, section + len_man, page);
931  if ( access( filename, R_OK ) == 0 ) {
932    man_globals->compress = FALSE;
933    man_globals->gzip = FALSE;
934    man_globals->deletetempfile = FALSE;
935    snprintf(man_globals->save_file, sizeof(man_globals->save_file),
936	     "%s/%s%s/%s", path, CAT, section + len_cat, page);
937    return(TRUE);
938  }
939
940#if defined(SMAN) && defined(SFORMAT)
941 /*
942  * Look for uncompressed sgml file next.
943  */
944
945  snprintf(input, BUFSIZ, "%s/%s%s/%s", path, SMAN, section + len_sman, page);
946#ifndef HAS_MKSTEMP
947  if ( SgmlToRoffNamed(man_globals, input, filename) ) {
948#else
949  if ( SgmlToRoffNamed(man_globals, input, filename, file) ) {
950#endif
951    man_globals->compress = FALSE;
952    man_globals->gzip = FALSE;
953    man_globals->deletetempfile = TRUE;
954    snprintf(man_globals->save_file, sizeof(man_globals->save_file),
955            "%s/%s%s/%s", path, CAT, section + len_cat, page);
956    return(TRUE);
957  }
958#endif
959
960/*
961 * Then for compressed files in an uncompressed directory.
962 */
963
964  snprintf(input, sizeof(input), "%s.%s", filename, COMPRESSION_EXTENSION);
965#ifndef HAS_MKSTEMP
966  if ( UncompressNamed(man_globals, input, filename) ) {
967#else
968  if ( UncompressNamed(man_globals, input, filename, file) ) {
969#endif
970    man_globals->compress = TRUE;
971    man_globals->deletetempfile = TRUE;
972    snprintf(man_globals->save_file, sizeof(man_globals->save_file),
973	     "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
974	     COMPRESSION_EXTENSION);
975    return(TRUE);
976  }
977#ifdef GZIP_EXTENSION
978  else {
979    snprintf(input, sizeof(input), "%s.%s", filename, GZIP_EXTENSION);
980#ifndef HAS_MKSTEMP
981    if ( UncompressNamed(man_globals, input, filename) ) {
982#else
983    if ( UncompressNamed(man_globals, input, filename, file) ) {
984#endif
985      man_globals->compress = TRUE;
986      man_globals->gzip = TRUE;
987      man_globals->deletetempfile = TRUE;
988      snprintf(man_globals->save_file, sizeof(man_globals->save_file),
989	       "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
990	       GZIP_EXTENSION);
991      return(TRUE);
992    }
993  }
994#endif
995
996#ifdef BZIP2_EXTENSION
997  {
998    snprintf(input, sizeof(input), "%s.%s", filename, BZIP2_EXTENSION);
999#ifndef HAS_MKSTEMP
1000    if ( UncompressNamed(man_globals, input, filename) ) {
1001#else
1002    if ( UncompressNamed(man_globals, input, filename, file) ) {
1003#endif
1004      man_globals->compress = TRUE;
1005      man_globals->gzip = TRUE;
1006      snprintf(man_globals->save_file, sizeof(man_globals->save_file),
1007	       "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
1008	       BZIP2_EXTENSION);
1009      return(TRUE);
1010    }
1011  }
1012#endif
1013
1014#ifdef LZMA_EXTENSION
1015  {
1016    snprintf(input, sizeof(input), "%s.%s", filename, LZMA_EXTENSION);
1017#ifndef HAS_MKSTEMP
1018    if ( UncompressNamed(man_globals, input, filename) ) {
1019#else
1020    if ( UncompressNamed(man_globals, input, filename, file) ) {
1021#endif
1022      man_globals->compress = TRUE;
1023      man_globals->lzma = TRUE;
1024      snprintf(man_globals->save_file, sizeof(man_globals->save_file),
1025	       "%s/%s%s/%s.%s", path, CAT, section + len_cat, page,
1026	       LZMA_EXTENSION);
1027      return(TRUE);
1028    }
1029  }
1030#endif
1031
1032/*
1033 * And lastly files in a compressed directory.
1034 */
1035
1036  snprintf(input, sizeof(input), "%s/%s%s.%s/%s", path,
1037	  MAN, section + len_man, COMPRESSION_EXTENSION, page);
1038#ifndef HAS_MKSTEMP
1039  if ( UncompressNamed(man_globals, input, filename) ) {
1040#else
1041  if ( UncompressNamed(man_globals, input, filename, file) ) {
1042#endif
1043    man_globals->compress = TRUE;
1044    man_globals->deletetempfile = TRUE;
1045    snprintf(man_globals->save_file, sizeof(man_globals->save_file),
1046	     "%s/%s%s.%s/%s", path, CAT, section + len_cat,
1047    	     COMPRESSION_EXTENSION, page);
1048    return(TRUE);
1049  }
1050  return(FALSE);
1051}
1052
1053/*	Function Name: AddCursor
1054 *	Description: This function adds the cursor to the window.
1055 *	Arguments: w - the widget to add the cursor to.
1056 *                 cursor - the cursor to add to this widget.
1057 *	Returns: none
1058 */
1059
1060void
1061AddCursor(Widget w, Cursor cursor)
1062{
1063  XColor colors[2];
1064  Arg args[10];
1065  Cardinal num_args = 0;
1066  Colormap c_map;
1067
1068  if (!XtIsRealized(w)) {
1069    PopupWarning(NULL, "Widget is not realized, no cursor added.\n");
1070    return;
1071  }
1072
1073  XtSetArg( args[num_args], XtNcolormap, &c_map); num_args++;
1074  XtGetValues( w, args, num_args);
1075
1076  colors[0].pixel = resources.cursors.fg_color;
1077  colors[1].pixel = resources.cursors.bg_color;
1078
1079  XQueryColors (XtDisplay(w), c_map, colors, 2);
1080  XRecolorCursor(XtDisplay(w), cursor, colors, colors+1);
1081  XDefineCursor(XtDisplay(w),XtWindow(w),cursor);
1082}
1083
1084/*	Function Name: ChangeLabel
1085 *	Description: This function changes the label field of the
1086 *                   given widget to the string in str.
1087 *	Arguments: w - the widget.
1088 *                 str - the string to change the label to.
1089 *	Returns: none
1090 */
1091
1092void
1093ChangeLabel(Widget w, char * str)
1094{
1095  Arg arglist[3];		/* An argument list. */
1096
1097  if (w == NULL) return;
1098
1099  XtSetArg(arglist[0], XtNlabel, str);
1100
1101/* shouldn't really have to do this. */
1102  XtSetArg(arglist[1], XtNwidth, 0);
1103  XtSetArg(arglist[2], XtNheight, 0);
1104
1105  XtSetValues(w, arglist, (Cardinal) 1);
1106}
1107
1108/*
1109 * In an ideal world this would be part of the XToolkit, and I would not
1110 * have to do it, but such is life sometimes.  Perhaps in X11R3.
1111 */
1112
1113/*	Function Name: PositionCenter
1114 *	Description: This function positions the given widgets center
1115 *                   in the following location.
1116 *	Arguments: widget - the widget widget to postion
1117 *                 x,y - The location for the center of the widget
1118 *                 above - number of pixels above center to locate this widget
1119 *                 left - number of pixels left of center to locate this widget
1120 *                 h_space, v_space - how close to get to the edges of the
1121 *                                    parent window.
1122 *	Returns: none
1123 *      Note:  This should only be used with a popup widget that has override
1124 *             redirect set.
1125 */
1126
1127void
1128PositionCenter(Widget widget, int x, int y, int above, int left, int v_space, int h_space)
1129{
1130  Arg wargs[2];
1131  int x_temp,y_temp;		/* location of the new window. */
1132  int parent_height,parent_width; /* Height and width of the parent widget or
1133				   the root window if it has no parent. */
1134
1135  x_temp = x - left - Width(widget) / 2 + BorderWidth(widget);
1136  y_temp = y - above -  Height(widget) / 2 + BorderWidth(widget);
1137
1138  parent_height = HeightOfScreen(XtScreen(widget));
1139  parent_width = WidthOfScreen(XtScreen(widget));
1140
1141/*
1142 * Check to make sure that all edges are within the viewable part of the
1143 * root window, and if not then force them to be.
1144 */
1145
1146  if (x_temp < h_space)
1147    x_temp = v_space;
1148  if (y_temp < v_space)
1149    (y_temp = 2);
1150
1151  if ( y_temp + Height(widget) + v_space > parent_height )
1152      y_temp = parent_height - Height(widget) - v_space;
1153
1154  if ( x_temp + Width(widget) + h_space > parent_width )
1155      x_temp = parent_width - Width(widget) - h_space;
1156
1157  XtSetArg(wargs[0], XtNx, x_temp);
1158  XtSetArg(wargs[1], XtNy, y_temp);
1159  XtSetValues(widget, wargs, 2);
1160}
1161
1162/*	Function Name: ParseEntry(entry, path, sect, page)
1163 *	Description: Parses the manual pages entry filenames.
1164 *	Arguments: str - the full path name.
1165 *                 path - the path name.      RETURNED
1166 *                 sect - the section name.   RETURNED
1167 *                 page - the page name.      RETURNED
1168 *	Returns: none.
1169 */
1170
1171void
1172ParseEntry(char *entry, char *path, char *sect, char *page)
1173{
1174  char *c, temp[BUFSIZ];
1175
1176  strcpy(temp, entry);
1177
1178  c = rindex(temp, '/');
1179  if (c == NULL)
1180    PrintError("index failure in ParseEntry.");
1181  *c++ = '\0';
1182  if (page != NULL)
1183    strcpy(page, c);
1184
1185  c = rindex(temp, '/');
1186  if (c == NULL)
1187    PrintError("index failure in ParseEntry.");
1188  *c++ = '\0';
1189#if defined(SFORMAT) && defined(SMAN)
1190  /* sgmltoroff sometimes puts an extra ./ in the path to .so entries */
1191  if (strcmp(c, ".") == 0) {
1192      c = rindex(temp, '/');
1193      if (c == NULL)
1194	  PrintError("index failure in ParseEntry.");
1195      *c++ = '\0';
1196  }
1197#endif
1198#if defined(__OpenBSD__) || defined(__NetBSD__)
1199  /* Skip machine subdirectory if present */
1200  if (strcmp(c, MACHINE) == 0) {
1201      c = rindex(temp, '/');
1202      if (c == NULL)
1203	  PrintError("index failure in ParseEntry.");
1204      *c++ = '\0';
1205  }
1206#endif
1207  if (sect != NULL)
1208    strcpy(sect, c);
1209
1210  if (path != NULL)
1211    strcpy(path, temp);
1212}
1213
1214/*      Function Name: GetGlobals
1215 *      Description: Gets the psuedo globals associated with the
1216 *                   manpage associated with this widget.
1217 *      Arguments: w - a widget in the manpage.
1218 *      Returns: the psuedo globals.
1219 *      Notes: initial_widget is a globals variable.
1220 *             manglobals_context is a global variable.
1221 */
1222
1223ManpageGlobals *
1224GetGlobals(Widget w)
1225{
1226  Widget temp;
1227  caddr_t data;
1228
1229  while ( (temp = XtParent(w)) != initial_widget && (temp != NULL))
1230    w = temp;
1231
1232  if (temp == NULL)
1233    XtAppError(XtWidgetToApplicationContext(w),
1234	       "Xman: Could not locate widget in tree, exiting");
1235
1236  if (XFindContext(XtDisplay(w), XtWindow(w),
1237		   manglobals_context, &data) != XCSUCCESS)
1238    XtAppError(XtWidgetToApplicationContext(w),
1239	       "Xman: Could not find global data, exiting");
1240
1241  return( (ManpageGlobals *) data);
1242}
1243
1244/*      Function Name: SaveGlobals
1245 *      Description: Saves the psuedo globals on the widget passed
1246 *                   to this function, although GetGlobals assumes that
1247 *                   the data is associated with the popup child of topBox.
1248 *      Arguments: w - the widget to associate the data with.
1249 *                 globals - data to associate with this widget.
1250 *      Returns: none.
1251 *      Notes: WIDGET MUST BE REALIZED.
1252 *             manglobals_context is a global variable.
1253 */
1254
1255void
1256SaveGlobals(Widget w, ManpageGlobals * globals)
1257{
1258  if (XSaveContext(XtDisplay(w), XtWindow(w), manglobals_context,
1259		   (caddr_t) globals) != XCSUCCESS)
1260    XtAppError(XtWidgetToApplicationContext(w),
1261	       "Xman: Could not save global data, are you out of memory?");
1262}
1263
1264/*      Function Name: RemoveGlobals
1265 *      Description: Removes the psuedo globals from the widget passed
1266 *                   to this function.
1267 *      Arguments: w - the widget to remove the data from.
1268 *      Returns: none.
1269 *      Notes: WIDGET MUST BE REALIZED.
1270 *             manglobals_context is a global variable.
1271 */
1272
1273void
1274RemoveGlobals(Widget w)
1275{
1276  if (XDeleteContext(XtDisplay(w), XtWindow(w),
1277		     manglobals_context) != XCSUCCESS)
1278    XtAppError(XtWidgetToApplicationContext(w),
1279	       "Xman: Could not remove global data?");
1280}
1281