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, "Can't 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 snprintf(cmdbuf, sizeof(cmdbuf), APROPOS_FORMAT, path, search_string, 218 mantmp); 219 220 if (system(cmdbuf) != 0) { /* execute search. */ 221 snprintf(error_buf, sizeof(error_buf), 222 "Something went wrong trying to run %s\n", cmdbuf); 223 PopupWarning(man_globals, error_buf); 224 } 225 226 if ((file = fdopen(fd, "r")) == NULL) 227 PrintError("lost temp file? out of temp space?"); 228 229/* 230 * Since we keep the FD open we can remove the file safely, this 231 * will keep extra files out of /tmp. 232 */ 233 234 remove(mantmp); 235 236 snprintf(string_buf, sizeof(string_buf), "%s: nothing appropriate", 237 search_string); 238 239 /* 240 * Check first LOOKLINES lines for "nothing appropriate". 241 */ 242 243 count = 0; 244 flag = FALSE; 245 while ((fgets(cmp_str, BUFSIZ, file) != NULL) && (count < LOOKLINES)) { 246 size_t len = strlen(cmp_str); 247 248 if (len > 0 && cmp_str[len - 1] == '\n') /* strip off the '\n' */ 249 cmp_str[len - 1] = '\0'; 250 251 if (streq(cmp_str, string_buf)) { 252 flag = TRUE; 253 break; 254 } 255 count++; 256 } 257 258 /* 259 * If the file is less than this number of lines then assume that there is 260 * nothing appropriate found. This does not confuse the apropos filter. 261 */ 262 263 if (flag) { 264 fclose(file); 265 file = NULL; 266 ChangeLabel(man_globals->label, string_buf); 267 return (NULL); 268 } 269 270 snprintf(man_globals->manpage_title, sizeof(man_globals->manpage_title), 271 "%s", label); 272 ChangeLabel(man_globals->label, label); 273 fseek(file, 0L, SEEK_SET); /* reset file to point at top. */ 274 } 275 else { /* MANUAL SEARCH */ 276 file = DoManualSearch(man_globals, search_string); 277 if (file == NULL) { 278 snprintf(string_buf, sizeof(string_buf), "No manual entry for %s.", 279 search_string); 280 ChangeLabel(man_globals->label, string_buf); 281 if (man_globals->label == NULL) 282 PopupWarning(man_globals, string_buf); 283 return (NULL); 284 } 285 } 286 287 if (resources.clear_search_string) { 288 Arg arglist[1]; 289 Widget dialog; 290 291 dialog = XtNameToWidget(man_globals->search_widget, DIALOG); 292 if (dialog == NULL) 293 PopupWarning(man_globals, "Could not clear the search string."); 294 295 XtSetArg(arglist[0], XtNvalue, ""); 296 XtSetValues(dialog, arglist, (Cardinal) 1); 297 } 298 299 return (file); 300} 301 302/* Function Name: DoManualSearch 303 * Description: performs a manual search. 304 * Arguments: man_globals - the manual page specific globals. 305 * Returns: the filename of the man page. 306 */ 307 308#define NO_ENTRY -100 309 310static FILE * 311DoManualSearch(ManpageGlobals * man_globals, char *string) 312{ 313 int e_num = NO_ENTRY; 314 int i; 315 316/* search current section first. */ 317 318 i = man_globals->current_directory; 319 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 320 321/* search other sections. */ 322 323 if (e_num == NO_ENTRY) { 324 i = 0; /* At the exit of the loop i needs to 325 be the one we used. */ 326 while (TRUE) { 327 if (i == man_globals->current_directory) 328 if (++i >= sections) 329 return (NULL); 330 e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries); 331 if (e_num != NO_ENTRY) 332 break; 333 if (++i >= sections) 334 return (NULL); 335 } 336 337/* 338 * Manual page found in some other section, unhighlight the current one. 339 */ 340 if (man_globals->manpagewidgets.box != NULL) 341 XawListUnhighlight(man_globals->manpagewidgets. 342 box[man_globals->current_directory]); 343 } 344 else { 345 /* 346 * Highlight the element we are searching for if it is in the directory 347 * listing currently being shown. 348 */ 349 if (man_globals->manpagewidgets.box != NULL) 350 XawListHighlight(man_globals->manpagewidgets.box[i], e_num); 351 } 352 return (FindManualFile(man_globals, i, e_num)); 353} 354 355/* Function Name: BEntrySearch 356 * Description: binary search through entries. 357 * Arguments: string - the string to match. 358 * first - the first entry in the list. 359 * number - the number of entries. 360 * Returns: a pointer to the entry found. 361 */ 362 363static int 364BEntrySearch(char *string, char **first, int number) 365{ 366 int check, cmp, len_cmp, global_number; 367 char *head, *tail; 368 369 global_number = 0; 370 while (TRUE) { 371 372 if (number == 0) { 373 return (NO_ENTRY); /* didn't find it. */ 374 } 375 376 check = number / 2; 377 378 head = strrchr(first[global_number + check], '/'); 379 if (head == NULL) 380 PrintError("index failure in BEntrySearch"); 381 head++; 382 383 tail = strrchr(head, '.'); 384 if (tail == NULL) 385 /* not an error, some systems (e.g. sgi) have only a .z suffix */ 386 tail = head + strlen(head); 387 388 cmp = strncmp(string, head, (tail - head)); 389 len_cmp = strlen(string) - (int) (tail - head); 390 391 if (cmp == 0 && len_cmp == 0) { 392 return (global_number + check); 393 } 394 else if (cmp < 0 || ((cmp == 0) && (len_cmp < 0))) 395 number = check; 396 else { /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */ 397 398 global_number += (check + 1); 399 number -= (check + 1); 400 } 401 } 402} 403