search.c revision 6d36ef34
1/* 2 3Copyright (c) 1987, 1988 X Consortium 4 5Permission is hereby granted, free of charge, to any person obtaining 6a copy of this software and associated documentation files (the 7"Software"), to deal in the Software without restriction, including 8without limitation the rights to use, copy, modify, merge, publish, 9distribute, sublicense, and/or sell copies of the Software, and to 10permit persons to whom the Software is furnished to do so, subject to 11the following conditions: 12 13The above copyright notice and this permission notice shall be included 14in all copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR 20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22OTHER DEALINGS IN THE SOFTWARE. 23 24Except as contained in this notice, the name of the X Consortium shall 25not be used in advertising or otherwise to promote the sale, use or 26other dealings in this Software without prior written authorization 27from the X Consortium. 28 29*/ 30 31 32#include "globals.h" 33#include "vendor.h" 34 35/* Map <CR> and control-M to goto beginning of file. */ 36 37#define SEARCHARGS 10 38 39static FILE *DoManualSearch(ManpageGlobals *man_globals, char *string); 40static int BEntrySearch(char *string, char **first, int number); 41 42/* Function Name: MakeSearchWidget 43 * Description: This Function Creates the Search Widget. 44 * Arguments: man_globals - the pseudo globals for this manpage. 45 * w - the widgets parent 46 * Returns: the search widget. 47 */ 48 49void 50MakeSearchWidget(ManpageGlobals * man_globals, Widget parent) 51{ 52 Widget dialog, command, text, cancel; 53 Arg arglist[2]; 54 Cardinal num_args = 0; 55 56 XtSetArg(arglist[0], XtNtransientFor, parent); 57 man_globals->search_widget = XtCreatePopupShell(SEARCHNAME, 58 transientShellWidgetClass, 59 parent, arglist, 1); 60 61 if (resources.clear_search_string) { 62 XtSetArg(arglist[0], XtNvalue, ""); 63 num_args++; 64 } 65 66 dialog = XtCreateManagedWidget(DIALOG, dialogWidgetClass, 67 man_globals->search_widget, 68 arglist, num_args); 69 70 if ((text = XtNameToWidget(dialog, "value")) == (Widget) NULL) 71 PopupWarning(NULL, "Could not find text widget in MakeSearchWidget."); 72 else 73 XtSetKeyboardFocus(dialog, text); 74 75 XawDialogAddButton(dialog, MANUALSEARCH, NULL, NULL); 76 XawDialogAddButton(dialog, APROPOSSEARCH, NULL, NULL); 77 XawDialogAddButton(dialog, CANCEL, NULL, NULL); 78 79/* 80 * This is a bit gross, but it get the cancel button underneath the 81 * others, and forms them up to the right size.. 82 */ 83 84 if (((command = XtNameToWidget(dialog, MANUALSEARCH)) == (Widget) NULL) || 85 ((cancel = XtNameToWidget(dialog, CANCEL)) == (Widget) NULL)) 86 PopupWarning(NULL, 87 "Could not find manual search widget in MakeSearchWidget."); 88 else { 89 static const char *half_size[] = { 90 MANUALSEARCH, APROPOSSEARCH, NULL 91 }; 92 static const char *full_size[] = { 93 "label", "value", CANCEL, NULL 94 }; 95 96 num_args = 0; 97 XtSetArg(arglist[num_args], XtNfromVert, command); 98 num_args++; 99 XtSetArg(arglist[num_args], XtNfromHoriz, NULL); 100 num_args++; 101 XtSetValues(cancel, arglist, num_args); 102 FormUpWidgets(dialog, full_size, half_size); 103 } 104 105} 106 107/* Function Name: SearchString 108 * Description: Returns the search string. 109 * Arguments: man_globals - the globals. 110 * Returns: the search string. 111 */ 112 113static char * 114SearchString(ManpageGlobals * man_globals) 115{ 116 Widget dialog; 117 118 dialog = XtNameToWidget(man_globals->search_widget, DIALOG); 119 if (dialog != NULL) 120 return (XawDialogGetValueString(dialog)); 121 122 PopupWarning(man_globals, 123 "Could not get the search string, no search will be preformed."); 124 return (NULL); 125} 126 127 128/* Function Name: DoSearch 129 * Description: This function performs a search for a man page or apropos 130 * search upon search string. 131 * Arguments: man_globals - the pseudo globals for this manpage. 132 * type - the type of search. 133 * Returns: none. 134 */ 135 136#define LOOKLINES 6 137 138/* 139 * Manual searches look through the list of manual pages for the right one 140 * with a binary search. 141 * 142 * Apropos searches still exec man -k. 143 * 144 * If nothing is found then I send a warning message to the user, and do 145 * nothing. 146 */ 147 148FILE * 149DoSearch(ManpageGlobals * man_globals, int type) 150{ 151 char cmdbuf[BUFSIZ], *mantmp, *manpath; 152 char tmp[BUFSIZ], path[BUFSIZ]; 153 char string_buf[BUFSIZ], cmp_str[BUFSIZ], error_buf[BUFSIZ]; 154 char *search_string = SearchString(man_globals); 155 FILE *file; 156 int fd; 157 int count; 158 Boolean flag; 159 160 if (search_string == NULL) 161 return (NULL); 162 163 /* If the string is empty or starts with a space then do not search */ 164 165 if (streq(search_string, "")) { 166 PopupWarning(man_globals, "Search string is empty."); 167 return (NULL); 168 } 169 170 if (strlen(search_string) >= BUFSIZ) { 171 PopupWarning(man_globals, "Search string too long."); 172 return (NULL); 173 } 174 if (search_string[0] == ' ') { 175 PopupWarning(man_globals, "First character cannot be a space."); 176 return (NULL); 177 } 178 179 if (type == APROPOS) { 180 char label[BUFSIZ]; 181 182 strcpy(tmp, MANTEMP); /* get a temp file. */ 183 fd = mkstemp(tmp); 184 if (fd < 0) { 185 PopupWarning(man_globals, "Cant create temp file"); 186 return NULL; 187 } 188 mantmp = tmp; 189 190 manpath = getenv("MANPATH"); 191 if (manpath == NULL || streq(manpath, "")) { 192#ifdef MANCONF 193 if (!ReadManConfig(path)) 194#endif 195 { 196 strcpy(path, SYSMANPATH); 197#ifdef LOCALMANPATH 198 strcat(path, ":"); 199 strcat(path, LOCALMANPATH); 200#endif 201 } 202 } 203 else { 204 strcpy(path, manpath); 205 } 206 207 snprintf(label, sizeof(label), 208 "Results of apropos search on: %s", search_string); 209 210#ifdef NO_MANPATH_SUPPORT /* not quite correct, but the best I can do. */ 211 snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, search_string, mantmp); 212#else 213 snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, path, search_string, 214 mantmp); 215#endif 216 217 if (system(cmdbuf) != 0) { /* execute search. */ 218 snprintf(error_buf, sizeof(error_buf), 219 "Something went wrong trying to run %s\n", cmdbuf); 220 PopupWarning(man_globals, error_buf); 221 } 222 223 if ((file = fdopen(fd, "r")) == NULL) 224 PrintError("lost temp file? out of temp space?"); 225 226/* 227 * Since we keep the FD open we can remove the file safely, this 228 * will keep extra files out of /tmp. 229 */ 230 231 remove(mantmp); 232 233 snprintf(string_buf, sizeof(string_buf), "%s: nothing appropriate", 234 search_string); 235 236 /* 237 * Check first LOOKLINES lines for "nothing appropriate". 238 */ 239 240 count = 0; 241 flag = FALSE; 242 while ((fgets(cmp_str, BUFSIZ, file) != NULL) && (count < LOOKLINES)) { 243 if (cmp_str[strlen(cmp_str) - 1] == '\n') /* strip off the '\n' */ 244 cmp_str[strlen(cmp_str) - 1] = '\0'; 245 246 if (streq(cmp_str, string_buf)) { 247 flag = TRUE; 248 break; 249 } 250 count++; 251 } 252 253 /* 254 * If the file is less than this number of lines then assume that there is 255 * nothing apropriate found. This does not confuse the apropos filter. 256 */ 257 258 if (flag) { 259 fclose(file); 260 file = NULL; 261 ChangeLabel(man_globals->label, string_buf); 262 return (NULL); 263 } 264 265 snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title), 266 "%s", label); 267 ChangeLabel(man_globals->label, label); 268 fseek(file, 0L, SEEK_SET); /* reset file to point at top. */ 269 } 270 else { /* MANUAL SEARCH */ 271 file = DoManualSearch(man_globals, search_string); 272 if (file == NULL) { 273 snprintf(string_buf, sizeof(string_buf), "No manual entry for %s.", 274 search_string); 275 ChangeLabel(man_globals->label, string_buf); 276 if (man_globals->label == NULL) 277 PopupWarning(man_globals, string_buf); 278 return (NULL); 279 } 280 } 281 282 if (resources.clear_search_string) { 283 Arg arglist[1]; 284 Widget dialog; 285 286 dialog = XtNameToWidget(man_globals->search_widget, DIALOG); 287 if (dialog == NULL) 288 PopupWarning(man_globals, "Could not clear the search string."); 289 290 XtSetArg(arglist[0], XtNvalue, ""); 291 XtSetValues(dialog, arglist, (Cardinal) 1); 292 } 293 294 return (file); 295} 296 297/* Function Name: DoManualSearch 298 * Description: performs a manual search. 299 * Arguments: man_globals - the manual page specific globals. 300 * Returns: the filename of the man page. 301 */ 302 303#define NO_ENTRY -100 304 305static FILE * 306DoManualSearch(ManpageGlobals * man_globals, char *string) 307{ 308 int e_num = NO_ENTRY; 309 int i; 310 311/* search current section first. */ 312 313 i = man_globals->current_directory; 314 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 315 316/* search other sections. */ 317 318 if (e_num == NO_ENTRY) { 319 i = 0; /* At the exit of the loop i needs to 320 be the one we used. */ 321 while (TRUE) { 322 if (i == man_globals->current_directory) 323 if (++i >= sections) 324 return (NULL); 325 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 326 if (e_num != NO_ENTRY) 327 break; 328 if (++i >= sections) 329 return (NULL); 330 } 331 332/* 333 * Manual page found in some other section, unhighlight the current one. 334 */ 335 if (man_globals->manpagewidgets.box != NULL) 336 XawListUnhighlight(man_globals->manpagewidgets. 337 box[man_globals->current_directory]); 338 } 339 else { 340 /* 341 * Highlight the element we are searching for if it is in the directory 342 * listing currently being shown. 343 */ 344 if (man_globals->manpagewidgets.box != NULL) 345 XawListHighlight(man_globals->manpagewidgets.box[i], e_num); 346 } 347 return (FindManualFile(man_globals, i, e_num)); 348} 349 350/* Function Name: BEntrySearch 351 * Description: binary search through entries. 352 * Arguments: string - the string to match. 353 * first - the first entry in the list. 354 * number - the number of entries. 355 * Returns: a pointer to the entry found. 356 */ 357 358static int 359BEntrySearch(char *string, char **first, int number) 360{ 361 int check, cmp, len_cmp, global_number; 362 char *head, *tail; 363 364 global_number = 0; 365 while (TRUE) { 366 367 if (number == 0) { 368 return (NO_ENTRY); /* didn't find it. */ 369 } 370 371 check = number / 2; 372 373 head = strrchr(first[global_number + check], '/'); 374 if (head == NULL) 375 PrintError("index failure in BEntrySearch"); 376 head++; 377 378 tail = strrchr(head, '.'); 379 if (tail == NULL) 380 /* not an error, some systems (e.g. sgi) have only a .z suffix */ 381 tail = head + strlen(head); 382 383 cmp = strncmp(string, head, (tail - head)); 384 len_cmp = strlen(string) - (int) (tail - head); 385 386 if (cmp == 0 && len_cmp == 0) { 387 return (global_number + check); 388 } 389 else if (cmp < 0 || ((cmp == 0) && (len_cmp < 0))) 390 number = check; 391 else { /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */ 392 393 global_number += (check + 1); 394 number -= (check + 1); 395 } 396 } 397} 398