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