11abf7346Smrg/* 21abf7346Smrg 31abf7346SmrgCopyright (c) 1987, 1988 X Consortium 41abf7346Smrg 51abf7346SmrgPermission is hereby granted, free of charge, to any person obtaining 61abf7346Smrga copy of this software and associated documentation files (the 71abf7346Smrg"Software"), to deal in the Software without restriction, including 81abf7346Smrgwithout limitation the rights to use, copy, modify, merge, publish, 91abf7346Smrgdistribute, sublicense, and/or sell copies of the Software, and to 101abf7346Smrgpermit persons to whom the Software is furnished to do so, subject to 111abf7346Smrgthe following conditions: 121abf7346Smrg 131abf7346SmrgThe above copyright notice and this permission notice shall be included 141abf7346Smrgin all copies or substantial portions of the Software. 151abf7346Smrg 161abf7346SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 171abf7346SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 181abf7346SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 191abf7346SmrgIN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR 201abf7346SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 211abf7346SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 221abf7346SmrgOTHER DEALINGS IN THE SOFTWARE. 231abf7346Smrg 241abf7346SmrgExcept as contained in this notice, the name of the X Consortium shall 251abf7346Smrgnot be used in advertising or otherwise to promote the sale, use or 261abf7346Smrgother dealings in this Software without prior written authorization 271abf7346Smrgfrom the X Consortium. 281abf7346Smrg 291abf7346Smrg*/ 301abf7346Smrg 31b55195beSchristos#ifdef HAVE_CONFIG_H 32b55195beSchristos# include "config.h" 33b55195beSchristos#endif 341abf7346Smrg 351abf7346Smrg#include "globals.h" 361abf7346Smrg#include "vendor.h" 37b55195beSchristos#include <X11/Xos.h> /* sys/types.h and unistd.h included in here */ 38b55195beSchristos#include <sys/stat.h> 391abf7346Smrg 406d36ef34Smrg/* Map <CR> and control-M to goto beginning of file. */ 411abf7346Smrg 421abf7346Smrg#define SEARCHARGS 10 431abf7346Smrg 446d36ef34Smrgstatic FILE *DoManualSearch(ManpageGlobals *man_globals, char *string); 456d36ef34Smrgstatic int BEntrySearch(char *string, char **first, int number); 461abf7346Smrg 471abf7346Smrg/* Function Name: MakeSearchWidget 481abf7346Smrg * Description: This Function Creates the Search Widget. 496d36ef34Smrg * Arguments: man_globals - the pseudo globals for this manpage. 501abf7346Smrg * w - the widgets parent 511abf7346Smrg * Returns: the search widget. 521abf7346Smrg */ 531abf7346Smrg 541abf7346Smrgvoid 551abf7346SmrgMakeSearchWidget(ManpageGlobals * man_globals, Widget parent) 561abf7346Smrg{ 576d36ef34Smrg Widget dialog, command, text, cancel; 586d36ef34Smrg Arg arglist[2]; 596d36ef34Smrg Cardinal num_args = 0; 606d36ef34Smrg 616d36ef34Smrg XtSetArg(arglist[0], XtNtransientFor, parent); 626d36ef34Smrg man_globals->search_widget = XtCreatePopupShell(SEARCHNAME, 636d36ef34Smrg transientShellWidgetClass, 646d36ef34Smrg parent, arglist, 1); 656d36ef34Smrg 666d36ef34Smrg if (resources.clear_search_string) { 676d36ef34Smrg XtSetArg(arglist[0], XtNvalue, ""); 686d36ef34Smrg num_args++; 696d36ef34Smrg } 701abf7346Smrg 716d36ef34Smrg dialog = XtCreateManagedWidget(DIALOG, dialogWidgetClass, 726d36ef34Smrg man_globals->search_widget, 736d36ef34Smrg arglist, num_args); 741abf7346Smrg 756d36ef34Smrg if ((text = XtNameToWidget(dialog, "value")) == (Widget) NULL) 766d36ef34Smrg PopupWarning(NULL, "Could not find text widget in MakeSearchWidget."); 776d36ef34Smrg else 786d36ef34Smrg XtSetKeyboardFocus(dialog, text); 791abf7346Smrg 806d36ef34Smrg XawDialogAddButton(dialog, MANUALSEARCH, NULL, NULL); 816d36ef34Smrg XawDialogAddButton(dialog, APROPOSSEARCH, NULL, NULL); 826d36ef34Smrg XawDialogAddButton(dialog, CANCEL, NULL, NULL); 831abf7346Smrg 841abf7346Smrg/* 856d36ef34Smrg * This is a bit gross, but it get the cancel button underneath the 861abf7346Smrg * others, and forms them up to the right size.. 871abf7346Smrg */ 881abf7346Smrg 896d36ef34Smrg if (((command = XtNameToWidget(dialog, MANUALSEARCH)) == (Widget) NULL) || 906d36ef34Smrg ((cancel = XtNameToWidget(dialog, CANCEL)) == (Widget) NULL)) 916d36ef34Smrg PopupWarning(NULL, 926d36ef34Smrg "Could not find manual search widget in MakeSearchWidget."); 936d36ef34Smrg else { 946d36ef34Smrg static const char *half_size[] = { 956d36ef34Smrg MANUALSEARCH, APROPOSSEARCH, NULL 966d36ef34Smrg }; 976d36ef34Smrg static const char *full_size[] = { 986d36ef34Smrg "label", "value", CANCEL, NULL 996d36ef34Smrg }; 1006d36ef34Smrg 1016d36ef34Smrg num_args = 0; 1026d36ef34Smrg XtSetArg(arglist[num_args], XtNfromVert, command); 1036d36ef34Smrg num_args++; 1046d36ef34Smrg XtSetArg(arglist[num_args], XtNfromHoriz, NULL); 1056d36ef34Smrg num_args++; 1066d36ef34Smrg XtSetValues(cancel, arglist, num_args); 1076d36ef34Smrg FormUpWidgets(dialog, full_size, half_size); 1086d36ef34Smrg } 1091abf7346Smrg 1101abf7346Smrg} 1111abf7346Smrg 1121abf7346Smrg/* Function Name: SearchString 1131abf7346Smrg * Description: Returns the search string. 1141abf7346Smrg * Arguments: man_globals - the globals. 1151abf7346Smrg * Returns: the search string. 1161abf7346Smrg */ 1171abf7346Smrg 1181abf7346Smrgstatic char * 1196d36ef34SmrgSearchString(ManpageGlobals * man_globals) 1201abf7346Smrg{ 1216d36ef34Smrg Widget dialog; 1221abf7346Smrg 1236d36ef34Smrg dialog = XtNameToWidget(man_globals->search_widget, DIALOG); 1246d36ef34Smrg if (dialog != NULL) 1256d36ef34Smrg return (XawDialogGetValueString(dialog)); 1261abf7346Smrg 1276d36ef34Smrg PopupWarning(man_globals, 128da4a0041Smrg "Could not get the search string, no search will be performed."); 1296d36ef34Smrg return (NULL); 1301abf7346Smrg} 1311abf7346Smrg 1326d36ef34Smrg 1331abf7346Smrg/* Function Name: DoSearch 1341abf7346Smrg * Description: This function performs a search for a man page or apropos 1351abf7346Smrg * search upon search string. 1366d36ef34Smrg * Arguments: man_globals - the pseudo globals for this manpage. 1371abf7346Smrg * type - the type of search. 1381abf7346Smrg * Returns: none. 1391abf7346Smrg */ 1401abf7346Smrg 1411abf7346Smrg#define LOOKLINES 6 1421abf7346Smrg 1436d36ef34Smrg/* 1441abf7346Smrg * Manual searches look through the list of manual pages for the right one 1451abf7346Smrg * with a binary search. 1461abf7346Smrg * 1471abf7346Smrg * Apropos searches still exec man -k. 1481abf7346Smrg * 1491abf7346Smrg * If nothing is found then I send a warning message to the user, and do 1501abf7346Smrg * nothing. 1511abf7346Smrg */ 1521abf7346Smrg 1531abf7346SmrgFILE * 1541abf7346SmrgDoSearch(ManpageGlobals * man_globals, int type) 1551abf7346Smrg{ 1566d36ef34Smrg char cmdbuf[BUFSIZ], *mantmp, *manpath; 1576d36ef34Smrg char tmp[BUFSIZ], path[BUFSIZ]; 1586d36ef34Smrg char string_buf[BUFSIZ], cmp_str[BUFSIZ], error_buf[BUFSIZ]; 1596d36ef34Smrg char *search_string = SearchString(man_globals); 1606d36ef34Smrg FILE *file; 1613231ecdcSchristos int fd, omask; 1626d36ef34Smrg int count; 1636d36ef34Smrg Boolean flag; 1646d36ef34Smrg 1656d36ef34Smrg if (search_string == NULL) 1666d36ef34Smrg return (NULL); 1676d36ef34Smrg 1686d36ef34Smrg /* If the string is empty or starts with a space then do not search */ 1696d36ef34Smrg 1706d36ef34Smrg if (streq(search_string, "")) { 1716d36ef34Smrg PopupWarning(man_globals, "Search string is empty."); 1726d36ef34Smrg return (NULL); 1736d36ef34Smrg } 1746d36ef34Smrg 1756d36ef34Smrg if (strlen(search_string) >= BUFSIZ) { 1766d36ef34Smrg PopupWarning(man_globals, "Search string too long."); 1776d36ef34Smrg return (NULL); 1786d36ef34Smrg } 1796d36ef34Smrg if (search_string[0] == ' ') { 1806d36ef34Smrg PopupWarning(man_globals, "First character cannot be a space."); 1816d36ef34Smrg return (NULL); 1821abf7346Smrg } 1831abf7346Smrg 1846d36ef34Smrg if (type == APROPOS) { 1856d36ef34Smrg char label[BUFSIZ]; 1866d36ef34Smrg 1876d36ef34Smrg strcpy(tmp, MANTEMP); /* get a temp file. */ 1883231ecdcSchristos omask = umask(077); 1896d36ef34Smrg fd = mkstemp(tmp); 1903231ecdcSchristos umask(omask); 1916d36ef34Smrg if (fd < 0) { 1926448b35cSmrg PopupWarning(man_globals, "Can't create temp file"); 1936d36ef34Smrg return NULL; 1946d36ef34Smrg } 1956d36ef34Smrg mantmp = tmp; 1966d36ef34Smrg 1976d36ef34Smrg manpath = getenv("MANPATH"); 1986d36ef34Smrg if (manpath == NULL || streq(manpath, "")) { 1991abf7346Smrg#ifdef MANCONF 2006d36ef34Smrg if (!ReadManConfig(path)) 2011abf7346Smrg#endif 2026d36ef34Smrg { 2036d36ef34Smrg strcpy(path, SYSMANPATH); 2041abf7346Smrg#ifdef LOCALMANPATH 2056d36ef34Smrg strcat(path, ":"); 2066d36ef34Smrg strcat(path, LOCALMANPATH); 2071abf7346Smrg#endif 2086d36ef34Smrg } 2096d36ef34Smrg } 2106d36ef34Smrg else { 2116d36ef34Smrg strcpy(path, manpath); 2126d36ef34Smrg } 2131abf7346Smrg 2146d36ef34Smrg snprintf(label, sizeof(label), 2156d36ef34Smrg "Results of apropos search on: %s", search_string); 2161abf7346Smrg 2176d36ef34Smrg snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, path, search_string, 2186d36ef34Smrg mantmp); 2191abf7346Smrg 2206d36ef34Smrg if (system(cmdbuf) != 0) { /* execute search. */ 2216d36ef34Smrg snprintf(error_buf, sizeof(error_buf), 2226d36ef34Smrg "Something went wrong trying to run %s\n", cmdbuf); 2236d36ef34Smrg PopupWarning(man_globals, error_buf); 2246d36ef34Smrg } 2251abf7346Smrg 2266d36ef34Smrg if ((file = fdopen(fd, "r")) == NULL) 2276d36ef34Smrg PrintError("lost temp file? out of temp space?"); 2281abf7346Smrg 2296d36ef34Smrg/* 2306d36ef34Smrg * Since we keep the FD open we can remove the file safely, this 2316d36ef34Smrg * will keep extra files out of /tmp. 2321abf7346Smrg */ 2331abf7346Smrg 2346d36ef34Smrg remove(mantmp); 2356d36ef34Smrg 2366d36ef34Smrg snprintf(string_buf, sizeof(string_buf), "%s: nothing appropriate", 2376d36ef34Smrg search_string); 2386d36ef34Smrg 2396d36ef34Smrg /* 2406d36ef34Smrg * Check first LOOKLINES lines for "nothing appropriate". 2416d36ef34Smrg */ 2426d36ef34Smrg 2436d36ef34Smrg count = 0; 2446d36ef34Smrg flag = FALSE; 2456d36ef34Smrg while ((fgets(cmp_str, BUFSIZ, file) != NULL) && (count < LOOKLINES)) { 246da4a0041Smrg size_t len = strlen(cmp_str); 247da4a0041Smrg 248da4a0041Smrg if (len > 0 && cmp_str[len - 1] == '\n') /* strip off the '\n' */ 249da4a0041Smrg cmp_str[len - 1] = '\0'; 2506d36ef34Smrg 2516d36ef34Smrg if (streq(cmp_str, string_buf)) { 2526d36ef34Smrg flag = TRUE; 2536d36ef34Smrg break; 2546d36ef34Smrg } 2556d36ef34Smrg count++; 2566d36ef34Smrg } 2576d36ef34Smrg 2586d36ef34Smrg /* 2596d36ef34Smrg * If the file is less than this number of lines then assume that there is 260da4a0041Smrg * nothing appropriate found. This does not confuse the apropos filter. 2616d36ef34Smrg */ 2626d36ef34Smrg 2636d36ef34Smrg if (flag) { 2646d36ef34Smrg fclose(file); 2656d36ef34Smrg file = NULL; 2666d36ef34Smrg ChangeLabel(man_globals->label, string_buf); 2676d36ef34Smrg return (NULL); 2686d36ef34Smrg } 2696d36ef34Smrg 2706d36ef34Smrg snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title), 2716d36ef34Smrg "%s", label); 2726d36ef34Smrg ChangeLabel(man_globals->label, label); 2736d36ef34Smrg fseek(file, 0L, SEEK_SET); /* reset file to point at top. */ 2741abf7346Smrg } 2756d36ef34Smrg else { /* MANUAL SEARCH */ 2766d36ef34Smrg file = DoManualSearch(man_globals, search_string); 2776d36ef34Smrg if (file == NULL) { 2786d36ef34Smrg snprintf(string_buf, sizeof(string_buf), "No manual entry for %s.", 2796d36ef34Smrg search_string); 2806d36ef34Smrg ChangeLabel(man_globals->label, string_buf); 2816d36ef34Smrg if (man_globals->label == NULL) 2826d36ef34Smrg PopupWarning(man_globals, string_buf); 2836d36ef34Smrg return (NULL); 2846d36ef34Smrg } 2851abf7346Smrg } 2861abf7346Smrg 2876d36ef34Smrg if (resources.clear_search_string) { 2886d36ef34Smrg Arg arglist[1]; 2896d36ef34Smrg Widget dialog; 2901abf7346Smrg 2916d36ef34Smrg dialog = XtNameToWidget(man_globals->search_widget, DIALOG); 2926d36ef34Smrg if (dialog == NULL) 2936d36ef34Smrg PopupWarning(man_globals, "Could not clear the search string."); 2941abf7346Smrg 2956d36ef34Smrg XtSetArg(arglist[0], XtNvalue, ""); 2966d36ef34Smrg XtSetValues(dialog, arglist, (Cardinal) 1); 2976d36ef34Smrg } 2981abf7346Smrg 2996d36ef34Smrg return (file); 3001abf7346Smrg} 3011abf7346Smrg 3021abf7346Smrg/* Function Name: DoManualSearch 3031abf7346Smrg * Description: performs a manual search. 3041abf7346Smrg * Arguments: man_globals - the manual page specific globals. 3051abf7346Smrg * Returns: the filename of the man page. 3061abf7346Smrg */ 3071abf7346Smrg 3081abf7346Smrg#define NO_ENTRY -100 3091abf7346Smrg 3106d36ef34Smrgstatic FILE * 3116d36ef34SmrgDoManualSearch(ManpageGlobals * man_globals, char *string) 3121abf7346Smrg{ 3136d36ef34Smrg int e_num = NO_ENTRY; 3146d36ef34Smrg int i; 3151abf7346Smrg 3161abf7346Smrg/* search current section first. */ 3176d36ef34Smrg 3186d36ef34Smrg i = man_globals->current_directory; 3196d36ef34Smrg e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 3201abf7346Smrg 3211abf7346Smrg/* search other sections. */ 3221abf7346Smrg 3236d36ef34Smrg if (e_num == NO_ENTRY) { 3246d36ef34Smrg i = 0; /* At the exit of the loop i needs to 3256d36ef34Smrg be the one we used. */ 3266d36ef34Smrg while (TRUE) { 3276d36ef34Smrg if (i == man_globals->current_directory) 3286d36ef34Smrg if (++i >= sections) 3296d36ef34Smrg return (NULL); 3306d36ef34Smrg e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 3316d36ef34Smrg if (e_num != NO_ENTRY) 3326d36ef34Smrg break; 3336d36ef34Smrg if (++i >= sections) 3346d36ef34Smrg return (NULL); 3356d36ef34Smrg } 3361abf7346Smrg 3371abf7346Smrg/* 3381abf7346Smrg * Manual page found in some other section, unhighlight the current one. 3391abf7346Smrg */ 3406d36ef34Smrg if (man_globals->manpagewidgets.box != NULL) 3416d36ef34Smrg XawListUnhighlight(man_globals->manpagewidgets. 3426d36ef34Smrg box[man_globals->current_directory]); 3436d36ef34Smrg } 3446d36ef34Smrg else { 3456d36ef34Smrg /* 3466d36ef34Smrg * Highlight the element we are searching for if it is in the directory 3476d36ef34Smrg * listing currently being shown. 3486d36ef34Smrg */ 3496d36ef34Smrg if (man_globals->manpagewidgets.box != NULL) 3506d36ef34Smrg XawListHighlight(man_globals->manpagewidgets.box[i], e_num); 3516d36ef34Smrg } 3526d36ef34Smrg return (FindManualFile(man_globals, i, e_num)); 3531abf7346Smrg} 3541abf7346Smrg 3551abf7346Smrg/* Function Name: BEntrySearch 3561abf7346Smrg * Description: binary search through entries. 3571abf7346Smrg * Arguments: string - the string to match. 3581abf7346Smrg * first - the first entry in the list. 3591abf7346Smrg * number - the number of entries. 3601abf7346Smrg * Returns: a pointer to the entry found. 3611abf7346Smrg */ 3621abf7346Smrg 3631abf7346Smrgstatic int 3646d36ef34SmrgBEntrySearch(char *string, char **first, int number) 3651abf7346Smrg{ 3666d36ef34Smrg int check, cmp, len_cmp, global_number; 3676d36ef34Smrg char *head, *tail; 3681abf7346Smrg 3696d36ef34Smrg global_number = 0; 3706d36ef34Smrg while (TRUE) { 3711abf7346Smrg 3726d36ef34Smrg if (number == 0) { 3736d36ef34Smrg return (NO_ENTRY); /* didn't find it. */ 3746d36ef34Smrg } 3751abf7346Smrg 3766d36ef34Smrg check = number / 2; 3771abf7346Smrg 3786d36ef34Smrg head = strrchr(first[global_number + check], '/'); 3796d36ef34Smrg if (head == NULL) 3806d36ef34Smrg PrintError("index failure in BEntrySearch"); 3816d36ef34Smrg head++; 3821abf7346Smrg 3836d36ef34Smrg tail = strrchr(head, '.'); 3846d36ef34Smrg if (tail == NULL) 3856d36ef34Smrg /* not an error, some systems (e.g. sgi) have only a .z suffix */ 3866d36ef34Smrg tail = head + strlen(head); 3876d36ef34Smrg 3886d36ef34Smrg cmp = strncmp(string, head, (tail - head)); 3896d36ef34Smrg len_cmp = strlen(string) - (int) (tail - head); 3906d36ef34Smrg 3916d36ef34Smrg if (cmp == 0 && len_cmp == 0) { 3926d36ef34Smrg return (global_number + check); 3936d36ef34Smrg } 3946d36ef34Smrg else if (cmp < 0 || ((cmp == 0) && (len_cmp < 0))) 3956d36ef34Smrg number = check; 3966d36ef34Smrg else { /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */ 3976d36ef34Smrg 3986d36ef34Smrg global_number += (check + 1); 3996d36ef34Smrg number -= (check + 1); 4006d36ef34Smrg } 4011abf7346Smrg } 4021abf7346Smrg} 403