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