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