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