/*************************************************************************** * Pinfo is a ncurses based lynx style info documentation browser * * Copyright (C) 1999 Przemek Borys <pborys@dione.ids.pl> * Copyright (C) 2005 Bas Zoetekouw <bas@debian.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA ***************************************************************************/ #include "common_includes.h" #include <ctype.h> #include <sys/stat.h> #include <stdlib.h> #define HTTPSECTION 100 #define FTPSECTION 101 #define MAILSECTION 102 /* check if a char is a hyphen character */ int ishyphen(unsigned char ch); /* load manual */ void loadmanual(FILE * id); /* handle keyboard */ int manualwork(); void rescan_selected(); /* scan for potential link to select on viewed manual page */ /* self explanatory */ void showmanualscreen(); /* mvaddstr with bold/italic */ void mvaddstr_manual(int y, int x, char *str); /* adds highlights to a painted screen */ void add_highlights(); /* strips line from formatting characters */ void strip_manual(char *buf); /* * Initialize links in a line . Links are entries of form reference(section), * and are stored in `manuallinks' var, described bellow. */ void man_initializelinks(char *line, int carry); int is_in_manlinks(char *in, char *find); void printmanual(char **Message, long Lines); /* line by line stored manual */ char **manual = 0; /* number of lines in manual */ unsigned ManualLines = 0; int selected = -1; /* number of selected link(offset in 'manuallinks', bellow) */ unsigned manualpos = 0; /* number of the first line, which is painted on screen */ unsigned manualcol = 0; /* the first displayed column of manpage-- for moving the screen left/right */ int manual_aftersearch = 0; /* this is set if man page is now after search operation */ int manwidthChanged = 0; /* this flag indicates whether the env variable $MANWIDTH was changed by pinfo */ typedef struct { /* name of a manual */ char name[128]; /* section */ char sect[32]; /* what was last selected on this page */ int selected; /* what was the last manualpos */ int pos; } manhistory; /* * type for the `lastread' history entries, when viewing * man pages. */ /* manual lastread history */ manhistory *manualhistory = 0; /* length of the above table - 1 */ int manualhistorylength = 0; /* this structure describes a hyperlink in manual viewer */ typedef struct { /* struct for hypertext references */ unsigned int line; /* line of the manpage, where the reference is */ /* column of that line */ unsigned int col; /* name of the reference */ char *name; /* section of the reference */ char section[32]; int section_mark; /* determine whether there is a hyphen above */ int carry; } manuallink; /* a set of manual references of man page */ manuallink *manuallinks = 0; /* number of found manual references in man page */ unsigned ManualLinks = 0; /* semaphore for checking if it's a history(left arrow) call */ int historical = 0; void /* free buffers allocated by current man page */ manual_free_buffers() { unsigned int i; /* first free previously allocated memory */ /* for the manual itself... */ if (manual) { for (i = 0; i <= ManualLines; i++) { xfree(manual[i]); } xfree(manual); manual = 0; ManualLines = 0; } /* ...and for the list of manual hypertext */ if (manuallinks) { /* links */ for (i = 0; i < ManualLinks; i++) { xfree(manuallinks[i].name); } xfree(manuallinks); manuallinks = 0; ManualLinks = 0; selected = -1; } } /* initialize history variables for manual pages. */ void set_initial_history(char *name) { int len = strlen(name), i; char *name1 = strdup(name); /* one object of array */ manualhistory = xmalloc(sizeof(manhistory)); /* filter trailing spaces */ while ((len > 1) &&(isspace(name1[len - 1]))) { name1[len - 1] = 0; len--; } i = len; /* find the beginning of the last token */ for (i = len - 1;(i > 0) &&(!isspace(name1[i])); i--); /* if we've found space, then we move to the first nonspace character */ if (i > 0) i++; /* filename->name */ strcpy(manualhistory[0].name, &name1[i]); /* section unknown */ strcpy(manualhistory[0].sect, ""); /* selected unknown */ manualhistory[0].selected = -1; /* pos=0 */ manualhistory[0].pos = 0; free(name1); } /* construct man name; take care about carry */ void construct_manualname(char *buf, int which) { if (!manuallinks[which].carry) { /* workaround for names starting with '(' */ if (manuallinks[which].name[0] == '(') strcpy(buf, manuallinks[which].name + 1); else strcpy(buf, manuallinks[which].name); return; } else { /* normal manual reference */ if (manuallinks[which].section_mark < HTTPSECTION) { char *base = xmalloc(1024); char *ptr; int tmppos; strncpy(base, manual[manuallinks[which].line - 1],1023); strip_manual(base); ptr = base + strlen(base) - 3; while (((isalpha(*ptr)) ||(*ptr == '.') ||(*ptr == '_')) &&(ptr > base)) ptr--; /* workaround for man pages with leading '(' see svgalib man pages */ if (*ptr == '(') ptr++; strcpy(buf, ptr); tmppos = strlen(buf); if (tmppos > 1) buf[tmppos - 2] = 0; strcat(buf, manuallinks[which].name); xfree(base); } /* url reference */ else { char *base = xmalloc(1024); char *ptr, *eptr; int namelen = strlen(manuallinks[which].name); strcpy(base, manual[manuallinks[which].line + 1]); strip_manual(base); ptr = base; /* skip whitespace */ while (isspace(*ptr)) ptr++; eptr = findurlend(ptr); *eptr = 0; strcpy(buf, manuallinks[which].name); /* cut the hyphen */ buf[namelen - 1] = 0; strcat(buf, ptr); xfree(base); } } } /* this is something like main() function for the manual viewer code. */ int handlemanual(char *name) { int return_value = 0; struct stat statbuf; FILE *id; char manualname[256]; char cmd[256]; char *raw_tempfilename = 0; char *apropos_tempfilename = 0; if (tmpfilename1) { unlink(tmpfilename1); xfree(tmpfilename1); } tmpfilename1 = make_tempfile(); #ifdef getmaxyx init_curses(); /* if ncurses, get maxx and maxy */ getmaxyx(stdscr, maxy, maxx); myendwin(); if ((!getenv("MANWIDTH")) ||(manwidthChanged)) { /* set MANWIDTH environment variable */ static char tmp[24]; snprintf(tmp, 24, "MANWIDTH=%d", maxx); putenv(tmp); manwidthChanged = 1; } #else /* otherwise hardcode 80x25... */ maxx = 80; maxy = 25; #endif /* getmaxyx */ #ifdef NIETS /**************************************************************************** * Ignore macros part: BEGIN * * PS: Siewca: I still expect that you'll isolate it to a single procedure * * Description(by PB): This code opens a manpage file, and filters it from * * dangerous macros. The output is put into a temporary file, which is then * * used as the `name' filename argument of this(handlemanual) procedure. * * There is a stored variable raw_tempfilename to allow unlinking this temp * * file after usage * ****************************************************************************/ FILE *source; char **ignored_entries; char location[256]; char line[1025]; char *end, *prev; size_t macroline_size; int ignored_items = 0, i = 0; char zipped = 0; /* if the pointer is non-null */ if (ignoredmacros) /* if there are some macros */ if (*ignoredmacros && strlen(ignoredmacros)) { /* that should be ignored */ *location = '\0'; /* we need to know the path */ snprintf(cmd, 255, "man -W %s %s", ManOptions, name); id = popen(cmd, "r"); if (!id) { printf(_("Error: Cannot call man command.\n")); return 1; } fflush(id); fgets(location, 255, id); pclose(id); if (*location == '\0') { printf(_("Error: No manual page found either.\n")); if (use_apropos) { printf(_("Apropos pages:\n")); snprintf(cmd, 255, "apropos %s|cat %s", name, StderrRedirection); system(cmd); } return 1; } ignored_items++; prev = ignoredmacros; /* counting items */ while ((end = strchr(prev, ':'))) { ignored_items++; prev = end + 1; } ignored_entries =(char **) xmalloc(ignored_items * sizeof(char **)); ignored_entries[0] = ignoredmacros; prev = ignoredmacros; i = 0; /* creating pointers */ while ((end = strchr(prev, ':'))) { *end = '\0'; prev = end + 1; i++; ignored_entries[i] = prev; } /* removing newline */ if ((prev = rindex(location, '\n'))) *prev = '\0'; /* checking if it's compressed */ prev = index(location, '\0'); if ((strlen(location)) > 3 &&((*(prev - 1) == 'Z' && *(prev - 2) == '.') ||(*(prev - 1) == 'z' && *(prev - 2) == 'g' && *(prev - 3) == '.') ) ) { if (verbose) printf("%s %s\n", _("Calling gunzip for"), location); snprintf(cmd, 255, "gunzip -c %s", location); source = popen(cmd, "r"); zipped = 1; if (!source) { printf(_("Couldn't call gunzip.\n")); return 1; } } else /* from cmd output */ source = fopen(location, "r"); name = make_tempfile(); raw_tempfilename = name; id = fopen(name, "w"); /* we read until eof */ while (!feof(source)) { if (fgets(line, 1024, source) == NULL) line[0] = '\0'; /* macro starts with a dot*/ if (line[0] != '.' ||(strlen(line)) <(size_t) 2) { fprintf(id, "%s", line); continue; } else while (i >= 0) { macroline_size = strlen(ignored_entries[i]); if (strlen(line + 1) < macroline_size) macroline_size = strlen(line + 1); if ((strncmp(ignored_entries[i], line + 1, macroline_size)) == 0 &&(*(line + 1 +(int) macroline_size) == ' ' || *(line + 1 +(int) macroline_size) == '\n' || *(line + 1 +(int) macroline_size) == '\t')) { if (quote_ignored) { if ((prev = rindex(line, '\n'))) *prev = '\0'; sprintf(cmd, "\n.br\n.nf\n[ [pinfo] - %s: %.42s", _("IGNORING"), line); if ((strlen(line)) >(size_t) 42) strcat(cmd, "(...)]\n.fi\n"); else strcat(cmd, " ]\n.fi\n"); } else { sprintf(cmd, ".\\\" removed macro: %.42s", line); if ((strlen(line)) >(size_t) 42) strcat(cmd, "(...)"); } strcpy(line, cmd); break; } i--; } fprintf(id, "%s", line); i = ignored_items - 1; } /* while (!feof(source)) */ if (zipped) pclose(source); else fclose(source); fclose(id); free(ignored_entries); } /* if (ignored_macros... */ /**************************************************************************** * Ignore macros part: END * ****************************************************************************/ #endif if (!plain_apropos) snprintf(cmd, 255, "man %s %s %s > %s", ManOptions, name, StderrRedirection, tmpfilename1); if ((plain_apropos) ||(system(cmd) != 0)) { if (!plain_apropos) { unlink(tmpfilename1); printf(_("Error: No manual page found\n")); } plain_apropos = 0; if (use_apropos) { printf(_("Calling apropos \n")); apropos_tempfilename = make_tempfile(); snprintf(cmd, 255, "apropos %s > %s", name, apropos_tempfilename); if (system(cmd) != 0) { printf(_("Nothing appropiate\n")); unlink(apropos_tempfilename); return 1; } id = fopen(apropos_tempfilename, "r"); } else return 1; } else id = fopen(tmpfilename1, "r"); init_curses(); set_initial_history(name); /* load manual to memory */ loadmanual(id); fclose(id); do { /* manualwork handles all actions when viewing man page */ return_value = manualwork(); #ifdef getmaxyx /* if ncurses, get maxx and maxy */ getmaxyx(stdscr, maxy, maxx); if ((!getenv("MANWIDTH")) ||(manwidthChanged)) { /* set MANWIDTH environment variable */ static char tmp[24]; snprintf(tmp, 24, "MANWIDTH=%d", maxx); putenv(tmp); manwidthChanged = 1; } #endif manual_aftersearch = 0; /* -1 is quit key */ if (return_value != -1) { if (tmpfilename2) { unlink(tmpfilename2); xfree(tmpfilename2); } tmpfilename2 = make_tempfile(); /* * key_back is not pressed; and return_value is an offset to * manuallinks */ if (return_value != -2) { construct_manualname(manualname, return_value); snprintf(cmd, 255, "man %s %s %s %s > %s", ManOptions, manuallinks[return_value].section, manualname, StderrRedirection, tmpfilename2); } else /* key_back was pressed */ { manualhistorylength--; if (manualhistorylength == 0 && apropos_tempfilename) { id = fopen(apropos_tempfilename, "r"); loadmanual(id); fclose(id); continue; } if (manualhistory[manualhistorylength].sect[0] == 0) snprintf(cmd, 255, "man %s %s %s > %s", ManOptions, manualhistory[manualhistorylength].name, StderrRedirection, tmpfilename2); else snprintf(cmd, 255, "man %s %s %s %s > %s", ManOptions, manualhistory[manualhistorylength].sect, manualhistory[manualhistorylength].name, StderrRedirection, tmpfilename2); /* * flag to make sure, that * manualwork will refresh the variables manualpos and selected * when going back to this page */ historical = 1; } system(cmd); stat(tmpfilename2, &statbuf); if (statbuf.st_size > 0) { snprintf(cmd, 255, "mv %s %s", tmpfilename2, tmpfilename1); /* create tmp file containing man page */ system(cmd); /* open man page */ id = fopen(tmpfilename1, "r"); if (id != NULL) { /* now we create history entry for new page */ if (!historical) { manualhistorylength++; manualhistory = xrealloc(manualhistory,(manualhistorylength + 2) * sizeof(manhistory)); /* * we can write so since this code applies * only when it's not a history call */ strcpy(manualhistory[manualhistorylength].name, manualname); strcpy(manualhistory[manualhistorylength].sect, manuallinks[return_value].section); } /* loading manual page and its defaults... */ loadmanual(id); fclose(id); /* continuing with creation of history */ if (!historical) { manualhistory[manualhistorylength].pos = manualpos; manualhistory[manualhistorylength].selected = selected; } else historical = 0; } else return_value = -1; } } } while (return_value != -1); if (apropos_tempfilename) unlink(apropos_tempfilename); /* we were using temporary */ if (raw_tempfilename) unlink(raw_tempfilename); /* raw-manpage for scaning */ return 0; } void /* loads manual from given filedescriptor */ loadmanual(FILE * id) { char prevlinechar = 0; /* tmp variable, set after reading first nonempty line of input */ int cutheader = 0; int carryflag = 0; manualpos = 0; manual_free_buffers(); manual = xmalloc(sizeof(char *)); manuallinks = xmalloc(sizeof(manuallinks)); manual[ManualLines] = xmalloc(1024); /* we read until eof */ while (!feof(id)) { char *tmp; /* * it happens sometimes, that the last line is weird * and causes sigsegvs by not entering anything to buffer, what * confuses strlen */ if (fgets(manual[ManualLines], 1024, id) == NULL) manual[ManualLines][0] = 0; if (cutheader) { if (strcmp(manual[cutheader], manual[ManualLines]) == 0) { manual[ManualLines][0] = '\n'; manual[ManualLines][1] = 0; } } if (FilterB7) { char *filter_pos = index(manual[ManualLines], 0xb7); if (filter_pos) *filter_pos = 'o'; } if (CutManHeaders) if (!cutheader) { if (strlen(manual[ManualLines]) > 1) { cutheader = ManualLines; } } if ((CutEmptyManLines) &&((manual[ManualLines][0]) == '\n') && (prevlinechar == '\n')) ; /* do nothing :)) */ else { int manlinelen = strlen(manual[ManualLines]); manual[ManualLines] = xrealloc(manual[ManualLines], manlinelen + 10); /* temporary variable for determining hypertextuality of fields */ tmp = xmalloc(manlinelen + 10); strcpy(tmp, manual[ManualLines]); /* remove formatting chars */ strip_manual(tmp); man_initializelinks(tmp, carryflag); carryflag = 0; if (manlinelen > 1) if (ishyphen(manual[ManualLines][manlinelen - 2])) carryflag = 1; /* free temporary buffer */ xfree(tmp); prevlinechar = manual[ManualLines][0]; /* increase the number of man lines */ ManualLines++; /* * and realloc manual to add an empty space for * next entry of manual line */ manual = xrealloc(manual,(ManualLines + 5) * sizeof(char *)); manual[ManualLines] = xmalloc(1024); } } } int compare_manuallink(const void *a, const void *b) { return ((manuallink *) a)->col -((manuallink *) b)->col; } void sort_manuallinks_from_current_line(long startlink, long endlink) { qsort(manuallinks + startlink, endlink - startlink, sizeof(manuallink), compare_manuallink); } /* initializes hyperlinks in manual */ void man_initializelinks(char *tmp, int carry) { /* set tmpcnt to the trailing zero of tmp */ int tmpcnt = strlen(tmp) + 1; char *mylink = tmp; char *urlstart, *urlend; long initialManualLinks = ManualLinks; int i, b; /****************************************************************************** * handle url refrences * *****************************************************************************/ urlend = tmp; while ((urlstart = strstr(urlend, "http://")) != NULL) { /* always successfull */ urlend = findurlend(urlstart); manuallinks = xrealloc(manuallinks, sizeof(manuallink) *(ManualLinks + 3)); manuallinks[ManualLinks].line = ManualLines; manuallinks[ManualLinks].col = width_of_string(tmp, urlstart - tmp); strcpy(manuallinks[ManualLinks].section, "HTTPSECTION"); manuallinks[ManualLinks].section_mark = HTTPSECTION; manuallinks[ManualLinks].name = xmalloc(urlend - urlstart + 10); strncpy(manuallinks[ManualLinks].name, urlstart, urlend - urlstart); manuallinks[ManualLinks].name[urlend - urlstart] = 0; if (ishyphen(manuallinks[ManualLinks].name[urlend - urlstart - 1])) manuallinks[ManualLinks].carry = 1; else manuallinks[ManualLinks].carry = 0; ManualLinks++; } urlend = tmp; while ((urlstart = strstr(urlend, "ftp://")) != NULL) { /* always successfull */ urlend = findurlend(urlstart); manuallinks = xrealloc(manuallinks, sizeof(manuallink) *(ManualLinks + 3)); manuallinks[ManualLinks].line = ManualLines; manuallinks[ManualLinks].col = width_of_string(tmp, urlstart - tmp); strcpy(manuallinks[ManualLinks].section, "FTPSECTION"); manuallinks[ManualLinks].section_mark = FTPSECTION; manuallinks[ManualLinks].name = xmalloc(urlend - urlstart + 10); strncpy(manuallinks[ManualLinks].name, urlstart, urlend - urlstart); manuallinks[ManualLinks].name[urlend - urlstart] = 0; if (ishyphen(manuallinks[ManualLinks].name[urlend - urlstart - 1])) manuallinks[ManualLinks].carry = 1; else manuallinks[ManualLinks].carry = 0; ManualLinks++; } urlend = tmp; while ((urlstart = findemailstart(urlend)) != NULL) { /* always successfull */ urlend = findurlend(urlstart); manuallinks = xrealloc(manuallinks, sizeof(manuallink) *(ManualLinks + 3)); manuallinks[ManualLinks].line = ManualLines; manuallinks[ManualLinks].col = width_of_string(tmp, urlstart - tmp); strcpy(manuallinks[ManualLinks].section, "MAILSECTION"); manuallinks[ManualLinks].section_mark = MAILSECTION; manuallinks[ManualLinks].name = xmalloc(urlend - urlstart + 10); strncpy(manuallinks[ManualLinks].name, urlstart, urlend - urlstart); manuallinks[ManualLinks].name[urlend - urlstart] = 0; if (ishyphen(manuallinks[ManualLinks].name[urlend - urlstart - 1])) manuallinks[ManualLinks].carry = 1; else manuallinks[ManualLinks].carry = 0; /* there should be a dot in e-mail domain */ if (strchr(manuallinks[ManualLinks].name, '.') != NULL) ManualLinks++; } /****************************************************************************** * handle normal manual refrences -- reference(section) * ******************************************************************************/ do { /* we look for '(', since manual link */ mylink = strchr(mylink, '('); /* has form of 'blah(x)' */ if (mylink != NULL) { char *temp; /* look for the closing bracket */ if ((temp = strchr(mylink, ')'))) { char *p_t1, *p_t; p_t = p_t1 = xmalloc((strlen(mylink) + 10) * sizeof(char)); for (++mylink; mylink != temp; *p_t++ = *mylink++); *p_t = '\0'; mylink -=(strlen(p_t1) + sizeof(char)); if ((!strchr(p_t1, '(')) &&(!is_in_manlinks(manlinks, p_t1))) { char tempchar; int breakpos, cols_before_link; i = mylink - tmp - 1; if (i < 0) i++; for (; i > 0; --i) { if (!isspace(tmp[i])) /* ignore spaces between linkname and '(x)' */ break; } /* we'll put zero on the last non-textual character of link */ breakpos = i + 1; /* but remember the cleared char for the future */ tempchar = tmp[breakpos]; tmp[breakpos] = 0; /* * scan to the first space sign or to 0 -- that means go to * the beginning of the scanned token */ for (i = breakpos; i > 0; --i) { if (isspace(tmp[i])) { i++; break; } } /* now we have needed string in i..breakpos. We need now to * realloc the * manuallinks table to make free space for new entry */ /* calculate the number of columns in front of the link */ cols_before_link = width_of_string(tmp, i-1); /* a small check */ if (!((use_apropos) &&(manualhistorylength == 0))) { /* * In English: if the name of the link is the name of * the current page and the section of the link is the * current section or if we don't know the current * section, then... */ if ((!strcasecmp(&tmp[i], manualhistory[manualhistorylength].name)) &&((!strcasecmp(p_t1, manualhistory[manualhistorylength].sect)) ||(manualhistory[manualhistorylength].sect[0] == 0) ||(!strcmp(manualhistory[manualhistorylength].sect, " ")))) break; } manuallinks = xrealloc(manuallinks, sizeof(manuallink) *(ManualLinks + 3)); manuallinks[ManualLinks].line = ManualLines; manuallinks[ManualLinks].col = cols_before_link + 1; if (LongManualLinks) { for (b = 1; mylink[b] != ')'; b++) manuallinks[ManualLinks].section[b - 1] = tolower(mylink[b]); manuallinks[ManualLinks].section[b - 1] = 0; } else { manuallinks[ManualLinks].section[0] = mylink[1]; manuallinks[ManualLinks].section[1] = 0; } manuallinks[ManualLinks].section_mark = 0; manuallinks[ManualLinks].name = xmalloc((breakpos - i) + 10); strcpy(manuallinks[ManualLinks].name, tmp + i); tmp[breakpos] = tempchar; /* check whether this is a carry'ed entry(i.e. in the * previous line there was `-' at end, and this is the * first word of this line */ for (b = i - 1; b >= 0; b--) { if (b > 0) if (!isspace(tmp[b])) break; } if (b >= 0) manuallinks[ManualLinks].carry = 0; else manuallinks[ManualLinks].carry = carry; /* increase the number of entries */ ManualLinks++; } /*... if (in man links) */ xfree((void *) p_t1); } } if (mylink) mylink++; if (mylink >(tmp + tmpcnt)) { break; } } /* do this line until strchr() won't find a '(' in string */ while (mylink != NULL); if (initialManualLinks != ManualLinks) sort_manuallinks_from_current_line(initialManualLinks, ManualLinks); } /* viewer function. Handles keyboard actions--main event loop */ int manualwork() { /* for user's shell commands */ FILE *mypipe; /* a temporary buffer */ char *token; /* key, which contains the value entered by user */ int key = 0; /* tmp values */ int selectedchanged; int statusline = FREE; #ifdef getmaxyx /* if ncurses, get maxx and maxy */ getmaxyx(stdscr, maxy, maxx); if ((!getenv("MANWIDTH")) ||(manwidthChanged)) { /* set MANWIDTH environment variable */ static char tmp[24]; snprintf(tmp, 24, "MANWIDTH=%d", maxx); putenv(tmp); manwidthChanged = 1; } #else maxx = 80; /* otherwise hardcode 80x25... */ maxy = 25; #endif /* getmaxyx */ /* get manualpos from history. it is set in handlemanual() */ manualpos = manualhistory[manualhistorylength].pos; /* if there was a valid selected entry, apply it */ if (manualhistory[manualhistorylength].selected != -1) selected = manualhistory[manualhistorylength].selected; else /* otherwise scan for selected on currently viewed page */ rescan_selected(); /* clean screen */ erase(); /* user events loop. finish when key_quit */ while (1) { /* make getch not wait for user */ nodelay(stdscr, TRUE); /* action -- return ERR */ key = pinfo_getch(); /* if there was nothing in buffer */ if (key == ERR) { /* then show screen */ if (statusline == FREE) showmanualscreen(); wrefresh(stdscr); waitforgetch(); key = pinfo_getch(); } nodelay(stdscr, FALSE); statusline = FREE; if (winchanged) { handlewinch(); winchanged = 0; key = pinfo_getch(); } /************************ keyboard handling **********************************/ if (key > 0) { if ((key == keys.print_1) || (key == keys.print_2)) { if (yesno(_("Are you sure you want to print?"), 0)) printmanual(manual, ManualLines); } /*====================================================*/ if ((key == keys.goto_1) || (key == keys.goto_2)) { manuallinks = xrealloc(manuallinks,(ManualLinks + 1) *(sizeof(manuallink) + 3)); /* get user's value */ attrset(bottomline); move(maxy - 1, 0); echo(); curs_set(1); manuallinks[ManualLinks].name = getstring(_("Enter manual name: ")); curs_set(0); noecho(); move(maxy - 1, 0); #ifdef HAVE_BKGDSET bkgdset(' ' | bottomline); clrtoeol(); bkgdset(0); #else myclrtoeol(); #endif attrset(normal); manuallinks[ManualLinks].carry = 0; manuallinks[ManualLinks].section_mark = 0; strcpy(manuallinks[ManualLinks].section, " "); manuallinks[ManualLinks].line = -1; manuallinks[ManualLinks].col = -1; ManualLinks++; return ManualLinks - 1; } /*====================================================*/ if ((key == keys.goline_1) || (key == keys.goline_2)) { long newpos; /* get user's value */ attrset(bottomline); move(maxy - 1, 0); echo(); curs_set(1); token = getstring(_("Enter line: ")); curs_set(0); noecho(); move(maxy - 1, 0); #ifdef HAVE_BKGDSET bkgdset(' ' | bottomline); clrtoeol(); bkgdset(0); #else myclrtoeol(); #endif attrset(normal); /* convert string to long. careful with nondigit strings. */ if (token) { int digit_val = 1; for (unsigned i = 0; token[i] != 0; i++) { if (!isdigit(token[i])) digit_val = 0; } /* move cursor position */ if (digit_val) { newpos = atol(token); newpos -=(maxy - 1); if ((newpos >= 0) &&(newpos < ManualLines -(maxy - 2))) manualpos = newpos; else if (newpos > 0) manualpos = ManualLines -(maxy - 2); else manualpos = 0; } xfree(token); token = 0; } } /*=====================================================*/ if ((key == keys.shellfeed_1) || (key == keys.shellfeed_2)) { /* get command name */ curs_set(1); attrset(bottomline); move(maxy - 1, 0); echo(); /* get users cmd */ token = getstring(_("Enter command: ")); noecho(); move(maxy - 1, 0); #ifdef HAVE_BKGDSET bkgdset(' ' | bottomline); clrtoeol(); bkgdset(0); #else myclrtoeol(); #endif attrset(normal); myendwin(); system("clear"); /* open mypipe */ mypipe = popen(token, "w"); if (mypipe != NULL) { /* and flush the msg to stdin */ for (unsigned i = 0; i < ManualLines; i++) fprintf(mypipe, "%s", manual[i]); pclose(mypipe); } getchar(); doupdate(); curs_set(0); } /*=====================================================*/ if ((key == keys.refresh_1) || (key == keys.refresh_2)) { myendwin(); doupdate(); refresh(); curs_set(0); } /*=====================================================*/ /* search in current node */ if ((key == keys.search_1) || (key == keys.search_2)) { int success = 0; /* procedure of getting regexp string */ move(maxy - 1, 0); attrset(bottomline); echo(); curs_set(1); /* * searchagain handler. see keys.totalsearch at mainfunction.c * for comments */ if (!searchagain.search) { token = getstring(_("Enter regular expression: ")); strcpy(searchagain.lastsearch, token); searchagain.type = key; } else { token = xmalloc(strlen(searchagain.lastsearch) + 1); strcpy(token, searchagain.lastsearch); searchagain.search = 0; } /* end of searchagain handler */ if (strlen(token) == 0) { xfree(token); goto skip_search; } curs_set(0); noecho(); move(maxy - 1, 0); #ifdef HAVE_BKGDSET bkgdset(' ' | bottomline); clrtoeol(); bkgdset(0); #else myclrtoeol(); #endif attrset(normal); /* compile regexp expression */ if (pinfo_re_comp(token) != 0) { /* We're not in a search! */ aftersearch = 0; /* print error message */ attrset(bottomline); mymvhline(maxy - 1, 0, ' ', maxx); move(maxy - 1, 0); printw(_("Invalid regular expression;")); printw(" "); printw(_("Press any key to continue...")); getch(); goto skip_search; } /* and search for it in all subsequential lines */ for (unsigned i = manualpos + 1; i < ManualLines - 1; i++) { char *tmp; tmp = xmalloc(strlen(manual[i]) + strlen(manual[i + 1]) + 10); /* * glue two following lines together, to find expres- sions * split up into two lines */ strcpy(tmp, manual[i]); strcat(tmp, manual[i + 1]); strip_manual(tmp); /* execute search */ if (pinfo_re_exec(tmp)) { /* if found, enter here... */ success = 1; strcpy(tmp, manual[i + 1]); strip_manual(tmp); /* * if it was found in the second line of the glued * expression. */ if (pinfo_re_exec(tmp)) manualpos = i + 1; else manualpos = i; xfree(tmp); break; } xfree(tmp); } xfree(token); rescan_selected(); if (!success) { attrset(bottomline); mvaddstr(maxy - 1, 0, _("Search string not found...")); statusline = LOCKED; } manual_aftersearch = 1; } /*=====================================================*/ /* search again */ /* see mainfunction.c for comments */ if ((key == keys.search_again_1) || (key == keys.search_again_2)) { if (searchagain.type != 0) { searchagain.search = 1; ungetch(searchagain.type); } } skip_search: /*=====================================================*/ if ((key == keys.twoup_1) || (key == keys.twoup_2)) { ungetch(keys.up_1); ungetch(keys.up_1); } /*=====================================================*/ if ((key == keys.up_1) || (key == keys.up_2)) { selectedchanged = 0; /* if there are links at all */ if (selected != -1) { /* if one is selected */ if (selected > 0) /* * scan for a next visible one, which is above the * current. */ for (int i = selected - 1; i >= 0; i--) { if ((manuallinks[i].line >= manualpos) && (manuallinks[i].line < manualpos +(maxy - 1))) { selected = i; selectedchanged = 1; break; } } } /* if new link not found */ if (!selectedchanged) { /* move one position up */ if (manualpos >= 1) manualpos--; /* and scan for selected again :) */ for (unsigned i = 0; i < ManualLinks; i++) { if (manuallinks[i].line == manualpos) { selected = i; break; } } } } /*=====================================================*/ if ((key == keys.end_1) || (key == keys.end_2)) { if (ManualLines < maxy - 1) manualpos = 0; else manualpos = ManualLines -(maxy - 1); selected = ManualLinks - 1; } /*=====================================================*/ if ((key == keys.nextnode_1) || (key == keys.nextnode_2)) { for (unsigned i = manualpos + 1; i < ManualLines; i++) { if (manual[i][1] == 8) { manualpos = i; break; } } } /*=====================================================*/ if ((key == keys.prevnode_1) || (key == keys.prevnode_2)) { for (unsigned i = manualpos - 1; i > 0; i--) { if (manual[i][1] == 8) { manualpos = i; break; } } } /*=====================================================*/ if ((key == keys.pgdn_1) || (key == keys.pgdn_2)) { if (manualpos +(maxy - 2) < ManualLines -(maxy - 1)) { manualpos +=(maxy - 2); rescan_selected(); } else if (ManualLines -(maxy - 1) >= 1) { manualpos = ManualLines -(maxy - 1); selected = ManualLinks - 1; } else { manualpos = 0; selected = ManualLinks - 1; } } /*=====================================================*/ if ((key == keys.home_1) || (key == keys.home_2)) { manualpos = 0; rescan_selected(); } /*=====================================================*/ if ((key == keys.pgup_1) | (key == keys.pgup_2)) { if (manualpos >(maxy - 1)) manualpos -=(maxy - 1); else manualpos = 0; rescan_selected(); } /*=====================================================*/ /* top+bottom line \|/ */ /* see keys.up for comments */ if ((key == keys.twodown_1) || (key == keys.twodown_2)) { ungetch(keys.down_1); ungetch(keys.down_1); } /*=====================================================*/ /* top+bottom line \|/ */ /* see keys.up for comments */ if ((key == keys.down_1) || (key == keys.down_2)) { selectedchanged = 0; for (unsigned i = selected + 1; i < ManualLinks; i++) { if ((manuallinks[i].line >= manualpos) && (manuallinks[i].line < manualpos +(maxy - 2))) { selected = i; selectedchanged = 1; break; } } if (!selectedchanged) { if (manualpos < ManualLines -(maxy - 1)) manualpos++; for (unsigned i = selected + 1; i < ManualLinks; i++) { if ((manuallinks[i].line >= manualpos) && (manuallinks[i].line < manualpos +(maxy - 2))) { selected = i; selectedchanged = 1; break; } } } } /*=====================================================*/ if ((key == keys.back_1) || (key == keys.back_2)) { if (manualhistorylength) return -2; } /*=====================================================*/ if ((key == keys.followlink_1) || (key == keys.followlink_2)) { manualhistory[manualhistorylength].pos = manualpos; manualhistory[manualhistorylength].selected = selected; if (selected >= 0) if ((manuallinks[selected].line >= manualpos) && (manuallinks[selected].line < manualpos +(maxy - 1))) { if (!strncmp(manuallinks[selected].section, "HTTPSECTION", 11)) { int buflen; char *tempbuf = xmalloc(1024); strcpy(tempbuf, httpviewer); strcat(tempbuf, " "); buflen = strlen(tempbuf); construct_manualname(tempbuf + buflen, selected); myendwin(); system(tempbuf); doupdate(); xfree(tempbuf); } else if (!strncmp(manuallinks[selected].section, "FTPSECTION", 10)) { int buflen; char *tempbuf = xmalloc(1024); strcpy(tempbuf, ftpviewer); strcat(tempbuf, " "); buflen = strlen(tempbuf); construct_manualname(tempbuf + buflen, selected); myendwin(); system(tempbuf); doupdate(); xfree(tempbuf); } else if (!strncmp(manuallinks[selected].section, "MAILSECTION", 11)) { int buflen; char *tempbuf = xmalloc(1024); strcpy(tempbuf, maileditor); strcat(tempbuf, " "); buflen = strlen(tempbuf); construct_manualname(tempbuf + buflen, selected); myendwin(); system(tempbuf); doupdate(); xfree(tempbuf); } else { return selected; } } } /*=====================================================*/ if ((key==keys.left_1)||(key==keys.left_2)) if (manualcol>0) manualcol--; if ((key==keys.right_1)||(key==keys.right_2)) manualcol++; /*=====================================================*/ /********* end of keyboard handling *********************/ /********* mouse handler ********************************/ #ifdef CURSES_MOUSE if (key == KEY_MOUSE) { MEVENT mouse; int done = 0; getmouse(&mouse); if (mouse.x<0 || mouse.y<0) /* should never happen, according to curses docs */ continue; /* copy to unsigned vars to avoid all kinds of signed/unsigned comparison unpleasantness below */ unsigned mouse_x = mouse.x; unsigned mouse_y = mouse.x; if (mouse.bstate == BUTTON1_CLICKED) { if ((mouse_y > 0) &&(mouse_y < maxy - 1)) { for (int i = selected; i >= 0; i--) { if (manuallinks[i].line == mouse_y + manualpos - 1) { if (manuallinks[i].col <= mouse_x - 1) { if (manuallinks[i].col + strlen(manuallinks[i].name) >= mouse_x - 1) { selected = i; done = 1; break; } } } } if (!done) for (unsigned i = selected; i < ManualLinks; i++) { if (manuallinks[i].line == mouse_y + manualpos - 1) { if (manuallinks[i].col <= mouse_x - 1) { if (manuallinks[i].col + strlen(manuallinks[i].name) >= mouse_x - 1) { selected = i; done = 1; break; } } } } } /* end: mouse not on top/bottom line */ if (mouse_y == 0) ungetch(keys.up_1); if (mouse_y == maxy - 1) ungetch(keys.down_1); } /* end: button_clicked */ if (mouse.bstate == BUTTON1_DOUBLE_CLICKED) { if ((mouse_y > 0) &&(mouse_y < maxy - 1)) { for (int i = selected; i >= 0; i--) { if (manuallinks[i].line == mouse_y + manualpos - 1) { if (manuallinks[i].col <= mouse_x - 1) { if (manuallinks[i].col + strlen(manuallinks[i].name) >= mouse_x - 1) { selected = i; done = 1; break; } } } } if (!done) for (unsigned i = selected; i < ManualLinks; i++) { if (manuallinks[i].line == mouse_y + manualpos - 1) { if (manuallinks[i].col <= mouse_x - 1) { if (manuallinks[i].col + strlen(manuallinks[i].name) >= mouse_x - 1) { selected = i; done = 1; break; } } } } if (done) ungetch(keys.followlink_1); } /* end: mouse not at top/bottom line */ if (mouse_y == 0) ungetch(keys.pgup_1); if (mouse_y == maxy - 1) ungetch(keys.pgdn_1); } /* end: button doubleclicked */ } #endif /* CURSES_MOUSE */ /*****************************************************************************/ } if ((key == keys.quit_2) ||(key == keys.quit_1)) { if (!ConfirmQuit) break; else { if (yesno(_("Are you sure you want to quit?"), QuitConfirmDefault)) break; } } } closeprogram(); return -1; } void /* scan for some hyperlink, available on current screen */ rescan_selected() { for (unsigned i = 0; i < ManualLinks; i++) { if ((manuallinks[i].line >= manualpos) && (manuallinks[i].line < manualpos +(maxy - 1))) { selected = i; break; } } } /* * calculate from which to start displaying of manual line *man. Skip `mancol' * columns. But remember, that *man contains also nonprinteble characters for * boldface etc. */ char *getmancolumn(char *man, int mancol) { if (mancol==0) return man; while (mancol>0) { if (*(man+1) == 8) man+=3; else man++; mancol--; } return man; } /* show the currently visible part of manpage */ void showmanualscreen() { #ifdef getmaxyx /* refresh maxy, maxx values */ getmaxyx(stdscr, maxy, maxx); #endif attrset(normal); /* print all visible text lines */ for (unsigned i = manualpos;(i < manualpos +(maxy - 2)) &&(i < ManualLines); i++) { size_t len = strlen(manual[i]); if (len) manual[i][len - 1] = ' '; /* if we have something to display */ if (len>manualcol) mvaddstr_manual((i - manualpos) + 1, 0, getmancolumn(manual[i],manualcol)); else /* otherwise, just clear the line to eol */ { move((i - manualpos) + 1, 0); bkgdset(' ' | normal); clrtoeol(); } if (len) manual[i][len - 1] = '\n'; } #ifdef HAVE_BKGDSET bkgdset(' ' | normal); #endif /* and clear to bottom */ clrtobot(); #ifdef HAVE_BKGDSET bkgdset(0); #endif attrset(normal); /* add highlights */ add_highlights(); /* draw bottomline with user informations */ attrset(bottomline); mymvhline(0, 0, ' ', maxx); mymvhline(maxy - 1, 0, ' ', maxx); move(maxy - 1, 0); if (((manualpos + maxy) < ManualLines) &&(ManualLines > maxy - 2)) printw(_("Viewing line %d/%d, %d%%"),(manualpos - 1 + maxy), ManualLines,((manualpos - 1 + maxy) * 100) / ManualLines); else printw(_("Viewing line %d/%d, 100%%"), ManualLines, ManualLines); move(maxy - 1, 0); attrset(normal); } void /* print a manual line */ mvaddstr_manual(int y, int x, char *str) { int i, j, len = strlen(str); static char strippedline[1024]; if ((h_regexp_num) ||(manual_aftersearch)) { memcpy(strippedline, str, len + 1); strip_manual(strippedline); } move(y, x); for (i = 0; i < len; i++) { if ((i > 0) &&(i < len - 1)) { /* handle bold highlight */ if ((str[i] == 8) &&(str[i - 1] == '_')) { attrset(manualbold); addch(str[i] & 0xff); addch(str[i + 1] & 0xff); attrset(normal); i++; goto label_skip_other; } /* * if it wasn't bold, check italic, before default, unhighlighted * line will be painted. We can do it only if i<len-3. */ else if (i < len - 3) goto label_check_italic; else /* unhighlighted */ { addch(str[i] & 0xff); goto label_skip_other; } } /* italic highlight */ if (i < len - 3) { label_check_italic: if ((str[i + 1] == 8) &&(str[i + 2] == str[i])) { attrset(manualitalic); addch(str[i] & 0xff); i += 2; attrset(normal); } else { addch(str[i] & 0xff); } } label_skip_other:; } #ifdef HAVE_BKGDSET bkgdset(' ' | normal); clrtoeol(); bkgdset(0); #else myclrtoeol(); #endif attrset(normal); #ifndef ___DONT_USE_REGEXP_SEARCH___ if ((h_regexp_num) ||(manual_aftersearch)) { regmatch_t pmatch[1]; int maxregexp = manual_aftersearch ? h_regexp_num + 1 : h_regexp_num; /* if it is after search, then we have user defined regexps+ a searched regexp to highlight */ for (j = 0; j < maxregexp; j++) { char *tmpstr = strippedline; while (!regexec(&h_regexp[j], tmpstr, 1, pmatch, 0)) { int n = pmatch[0].rm_eo - pmatch[0].rm_so; int rx = pmatch[0].rm_so + tmpstr - strippedline; int curY, curX; char tmpchr; getyx(stdscr, curY, curX); tmpchr = strippedline[rx + n]; strippedline[rx + n] = 0; attrset(searchhighlight); mvaddstr(y, rx, strippedline + rx); attrset(normal); strippedline[rx + n] = tmpchr; tmpstr = tmpstr + pmatch[0].rm_eo; move(curY, curX); } } } #endif } /* add hyperobject highlights */ void add_highlights() { int i; /* scan through the visible objects */ for (i = 0; (unsigned) i < ManualLinks; i++) { /* if the object is on the current screen */ if ((manuallinks[i].line >= manualpos) && (manuallinks[i].line < manualpos +(maxy - 2))) { /* if it's a simple man link */ if (manuallinks[i].section_mark < HTTPSECTION) { if (i == selected) attrset(noteselected); else attrset(note); /* if it's a link split into two lines */ if (manuallinks[i].carry == 1) { int x, y, ltline = manuallinks[i].line - 1; /* find the line, where starts the split link */ char *tmpstr = strdup(manual[ltline]); size_t ltlinelen; char *newlinemark; /* remove boldfaces&italics */ strip_manual(tmpstr); /* calculate the length of this line */ ltlinelen = strlen(tmpstr); /* set this var to the last character of this line(to an '\n')*/ newlinemark = tmpstr + ltlinelen - 1; getyx(stdscr, y, x); if (y > 2) { #define TestCh tmpstr[ltlinelen] /* skip \n, -, and the at least one char... */ if (ltlinelen > 2) ltlinelen -= 3; /* * positon ltlinelen to the beginning of the link to be * highlighted */ while ((isalpha(TestCh)) ||(TestCh == '.') ||(TestCh == '_')) ltlinelen--; *newlinemark = 0; /* OK, link horizontally fits into screen */ if (ltlinelen>manualcol) mvaddstr(manuallinks[i].line - manualpos + 1 - 1, ltlinelen-manualcol, &tmpstr[ltlinelen]); /* * we cut here a part of the link, and draw only what's * visible on screen */ else if (ltlinelen+strlen(&tmpstr[ltlinelen])>manualcol) mvaddstr(manuallinks[i].line - manualpos + 1 - 1, ltlinelen-manualcol, &tmpstr[manualcol]); *newlinemark = '\n'; #undef TestCh } xfree(tmpstr); move(y, x); } } else { if (i == selected) attrset(urlselected); else attrset(url); if (manuallinks[i].carry == 1) { int ltline = manuallinks[i].line + 1; /* * the split part to find is lying down * to the line defined in manlinks(line+1) */ char *tmpstr = strdup(manual[ltline]); char *wsk = tmpstr, *wskend; strip_manual(tmpstr); /* skip spaces */ while (isspace(*wsk)) wsk++; /* find the end of url */ wskend = findurlend(wsk); /* add end of string, and print */ *wskend = 0; if (wsk-tmpstr<manualcol) mvaddstr(manuallinks[i].line - manualpos + 2, wsk - tmpstr - manualcol, wsk); else if (wskend-tmpstr<manualcol) mvaddstr(manuallinks[i].line - manualpos + 2, 0, wsk+manualcol); } } if (manuallinks[i].col>manualcol) mvaddstr(1 + manuallinks[i].line - manualpos, manuallinks[i].col - manualcol, manuallinks[i].name); else if (manuallinks[i].col+strlen(manuallinks[i].name)>manualcol) mvaddstr(1 + manuallinks[i].line - manualpos, 0, manuallinks[i].name+(manualcol-manuallinks[i].col)); attrset(normal); } } } /* all variables passed here must have, say 10 bytes of overrun buffer */ void strip_manual(char *buf) { int i, tmpcnt = 0; /* in general, tmp buffer will hold a line stripped from highlight marks */ for (i = 0; buf[i] != 0; i++) { /* so we strip the line from "'_',0x8" -- bold marks */ if ((buf[i] == '_') &&(buf[i + 1] == 8)) { buf[tmpcnt++] = buf[i + 2]; i += 2; } /* and 0x8 -- italic marks */ else if ((buf[i + 1] == 8) &&(buf[i + 2] == buf[i])) { buf[tmpcnt++] = buf[i]; i += 2; } else /* else we don't do anything */ buf[tmpcnt++] = buf[i]; } buf[tmpcnt] = 0; } /* * checks if a construction, which looks like hyperlink, belongs to the allowed * manual sections. */ int is_in_manlinks(char *in, char *find) { char *copy, *token; const char delimiters[] = ":"; copy = strdup(in); if ((strcmp(find,(token = strtok(copy, delimiters))) != 0)) { while ((token = strtok(NULL, delimiters))) { #ifdef HAVE_STRCASECMP if (!strcasecmp(token, find)) #else if (!strcmp(token, find)) #endif { xfree((void *) copy); return 0; } } xfree((void *) copy); return 1; } else { xfree((void *) copy); return 0; } } void printmanual(char **Message, long Lines) { /* printer fd */ FILE *prnFD; int i; prnFD = popen(printutility, "w"); /* scan through all lines */ for (i = 0; i < Lines; i++) { fprintf(prnFD, "\r%s", Message[i]); } pclose(prnFD); } int ishyphen(unsigned char ch) { if ((ch == '-') ||(ch == SOFT_HYPHEN)) return 1; return 0; }