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