1 /* manpage.c - draws an interactive man page browser
2 Copyright (C) 1996-2017 Paul Sheer
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17 02111-1307, USA.
18 */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <my_string.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28
29 #include <X11/Intrinsic.h>
30 #include "lkeysym.h"
31
32 #include "stringtools.h"
33 #include "app_glob.c"
34
35 #include "coolwidget.h"
36 #include "coollocal.h"
37 #include "loadfile.h"
38
39 #include "mad.h"
40 #include "edit.h"
41 #include "editcmddef.h"
42
43 CWidget *CManpageDialog (Window in, int x, int y, int columns, int lines, const char *manpage);
44
45 /* must be a power of 2 */
46 #define NUM_HISTORY 16
47 static struct history {
48 char *text;
49 int line;
50 } history[NUM_HISTORY] =
51
52 {
53 {
54 0, 0
55 },
56 {
57 0, 0
58 },
59 {
60 0, 0
61 },
62 {
63 0, 0
64 },
65
66 {
67 0, 0
68 },
69 {
70 0, 0
71 },
72 {
73 0, 0
74 },
75 {
76 0, 0
77 },
78
79 {
80 0, 0
81 },
82 {
83 0, 0
84 },
85 {
86 0, 0
87 },
88 {
89 0, 0
90 },
91
92 {
93 0, 0
94 },
95 {
96 0, 0
97 },
98 {
99 0, 0
100 },
101 {
102 0, 0
103 }
104 };
105
106 static unsigned int history_current = 0;
107
108 #define my_is_letter(ch) (isalpha (ch) || ch == '\b' || ch == '_' || ch == '-')
109
check_prev_next(void)110 void check_prev_next (void)
111 {
112 if (history[history_current & (NUM_HISTORY - 1)].text)
113 CIdent ("mandisplayfile.next")->disabled = 0;
114 else {
115 CIdent ("mandisplayfile.next")->disabled = 1;
116 if (CGetFocus () == CIdent ("mandisplayfile.next")->winid) {
117 CFocus (CIdent ("mandisplayfile.text"));
118 CExpose ("mandisplayfile.next");
119 }
120 }
121 if (history[(history_current - 2) & (NUM_HISTORY - 1)].text)
122 CIdent ("mandisplayfile.prev")->disabled = 0;
123 else {
124 CIdent ("mandisplayfile.prev")->disabled = 1;
125 if (CGetFocus () == CIdent ("mandisplayfile.prev")->winid) {
126 CFocus (CIdent ("mandisplayfile.text"));
127 CExpose ("mandisplayfile.prev");
128 }
129 }
130 }
131
add_to_history(char * t)132 void add_to_history (char *t)
133 {
134 char **h;
135 history_current &= (NUM_HISTORY - 1);
136 if (history[history_current].text)
137 free (history[history_current].text);
138 h = &(history[(history_current + 1) & (NUM_HISTORY - 1)].text);
139 if (*h) {
140 free (*h);
141 *h = 0;
142 }
143 history[history_current++].text = (char *) strdup (t);
144 }
145
146 int mansearch_callback (CWidget * w, XEvent * x, CEvent * c);
147 int mansearchagain_callback (CWidget * w, XEvent * x, CEvent * c);
148
manpageclear_callback(CWidget * w,XEvent * x,CEvent * c)149 int manpageclear_callback (CWidget * w, XEvent * x, CEvent * c)
150 {
151 int i;
152 CDestroyWidget ("mandisplayfile");
153 for (i = 0; i < NUM_HISTORY; i++) {
154 if (history[i].text) {
155 free (history[i].text);
156 history[i].text = 0;
157 history[i].line = 0;
158 }
159 }
160 history_current = 0;
161 return 0;
162 }
163
164 int calc_text_pos2 (CWidget * w, long b, long *q, int l);
165
manpage_callback(CWidget * w,XEvent * x,CEvent * c)166 int manpage_callback (CWidget * w, XEvent * x, CEvent * c)
167 {
168 int p, q;
169 unsigned char m[128] = "";
170 char *t;
171 if (c->command == CK_Cancel || !strcmp (c->ident, "mandisplayfile.clear")
172 || !strcmp (c->ident, "mandisplayfile.done")) {
173 CDestroyWidget ("mandisplayfile");
174 return 0;
175 }
176 if (c->command == CK_Find) {
177 mansearch_callback (w, x, c);
178 }
179 if (c->command == CK_Find_Again) {
180 mansearchagain_callback (w, x, c);
181 }
182 if (c->double_click) {
183 long lq;
184 unsigned char *text = (unsigned char *) w->text;
185 q = strmovelines ((char *) text, w->current, c->yt - w->firstline, w->width);
186 calc_text_pos2 (w, q, &lq, c->x - 4);
187 p = q = lq;
188 if (my_is_letter (text[q])) {
189 while (--q >= 0)
190 if (!my_is_letter (text[q]))
191 break;
192 q++;
193 while (text[++p])
194 if (!my_is_letter (text[p]))
195 break;
196 strncpy ((char *) m, (char *) text + q, min (p - q, 127));
197 m[min (p - q, 127)] = 0;
198 t = str_strip_nroff ((char *) m, 0);
199 if (*t != '-')
200 CManpageDialog (0, 0, 0, 0, 0, t);
201 free (t);
202 }
203 }
204 check_prev_next ();
205 return 0;
206 }
207
record_line(void)208 void record_line (void)
209 {
210 CWidget *w;
211 if (!(w = CIdent ("mandisplayfile.text")))
212 return;
213 if (!history[(history_current - 1) & (NUM_HISTORY - 1)].text)
214 return;
215 history[(history_current - 1) & (NUM_HISTORY - 1)].line = w->firstline;
216 }
217
manpageprev_callback(CWidget * w,XEvent * x,CEvent * c)218 int manpageprev_callback (CWidget * w, XEvent * x, CEvent * c)
219 {
220 record_line ();
221 history_current--;
222 check_prev_next ();
223 CRedrawTextbox ("mandisplayfile.text", history[(history_current - 1) & (NUM_HISTORY - 1)].text, 1);
224 CSetTextboxPos (CIdent ("mandisplayfile.text"), TEXT_SET_LINE, history[(history_current - 1) & (NUM_HISTORY - 1)].line);
225 CFocus (CIdent ("mandisplayfile.text"));
226 return 0;
227 }
228
manpagenext_callback(CWidget * w,XEvent * x,CEvent * c)229 int manpagenext_callback (CWidget * w, XEvent * x, CEvent * c)
230 {
231 record_line ();
232 history_current++;
233 check_prev_next ();
234 CRedrawTextbox ("mandisplayfile.text", history[(history_current - 1) & (NUM_HISTORY - 1)].text, 1);
235 CSetTextboxPos (CIdent ("mandisplayfile.text"), TEXT_SET_LINE, history[(history_current - 1) & (NUM_HISTORY - 1)].line);
236 CFocus (CIdent ("mandisplayfile.text"));
237 return 0;
238 }
239
240 extern int replace_scanf;
241 extern int replace_regexp;
242 extern int replace_whole;
243 extern int replace_case;
244
text_get_byte(unsigned char * text,long index)245 int text_get_byte (unsigned char *text, long index)
246 {
247 return text[index];
248 }
249
Ctextboxsearch(CWidget * w,int again)250 void Ctextboxsearch (CWidget * w, int again)
251 {
252 static char *old = NULL;
253 char *exp = "";
254 int isfocussed = 0;
255
256 exp = old ? old : exp;
257 if (again) {
258 if (!old)
259 return;
260 exp = (char *) strdup (old);
261 } else {
262 isfocussed = (w->winid == CGetFocus ());
263 edit_search_replace_dialog (w->parentid, 20, 20, &exp, 0, 0, _(" Search "), 0);
264 }
265
266 if (exp) {
267 if (*exp) {
268 int len, l;
269 char *t;
270 long search_start;
271 if (old)
272 free (old);
273 old = (char *) strdup (exp);
274 /* here we run strip on everything from here
275 to the end of the file then search through
276 the stripped text */
277 search_start = strmovelines (w->text, w->current, 1, 32000);
278 t = str_strip_nroff (w->text + search_start, &l);
279 search_start = edit_find (0, (unsigned char *) exp, &len, l, (int (*)(void *, long)) text_get_byte, (void *) t, 0);
280 if (search_start == -3) {
281 CErrorDialog (w->mainid, 20, 20, _(" Error "), _(" Invalid regular expression. "));
282 } else if (search_start >= 0) {
283 l = strcountlines (t, 0, search_start, 32000) + 1 + w->firstline;
284 CSetTextboxPos (w, TEXT_SET_LINE, l);
285 CExpose (w->ident);
286 } else {
287 CErrorDialog (w->mainid, 20, 20, _(" Search "), _(" Search string not found. "));
288 }
289 free (t);
290 }
291 free (exp);
292 }
293 }
294
mansearch_callback(CWidget * w,XEvent * x,CEvent * c)295 int mansearch_callback (CWidget * w, XEvent * x, CEvent * c)
296 {
297 Ctextboxsearch (CIdent ("mandisplayfile.text"), 0);
298 CFocus (CIdent ("mandisplayfile.text"));
299 return 0;
300 }
301
mansearchagain_callback(CWidget * w,XEvent * x,CEvent * c)302 int mansearchagain_callback (CWidget * w, XEvent * x, CEvent * c)
303 {
304 Ctextboxsearch (CIdent ("mandisplayfile.text"), 1);
305 CFocus (CIdent ("mandisplayfile.text"));
306 return 0;
307 }
308
309 char *option_man_cmdline = MAN_CMD;
310
CManpageDialog(Window in,int x,int y,int columns,int lines,const char * manpage)311 CWidget *CManpageDialog (Window in, int x, int y, int columns, int lines, const char *manpage)
312 {
313 CWidget *res;
314 char *t;
315 char *argv[] =
316 {
317 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
318 };
319 int i = 0, man_pipe, fre = 1, line = 0;
320
321 record_line ();
322
323 if (manpage) {
324 Window win = 0;
325 CWidget *w;
326 char *p, *q, *r;
327 w = CIdent ("mandisplayfile");
328 if (w)
329 win = w->winid;
330 i = 0;
331 p = q = (char *) strdup (option_man_cmdline);
332 r = p;
333 for (i = 0; i < 32; i++) {
334 q = strchr (p, ' ');
335 if (strcmp (p, "%m"))
336 argv[i] = p;
337 else
338 argv[i] = (char *) manpage;
339 if (!q)
340 break;
341 *q = 0;
342 p = q + 1;
343 }
344 if (win)
345 CHourGlass (win);
346 CHourGlass (CFirstWindow);
347 if (triple_pipe_open (0, &man_pipe, 0, 1, argv[0], argv) <= 0) { /* "1" is to pipe both stderr AND stdout into man_pipe */
348 CErrorDialog (CFirstWindow, 20, 20, _(" Manual page "), _(" Fail trying to run man, check 'option_man_cmdline' in the file ~/.cedit/.cooledit.ini "));
349 if (win)
350 CUnHourGlass (win);
351 free (r);
352 return 0;
353 }
354 free (r);
355 t = read_pipe (man_pipe, 0);
356 close (man_pipe);
357 if (win)
358 CUnHourGlass (win);
359 CUnHourGlass (CFirstWindow);
360 } else {
361 if (!(t = history[(history_current - 1) & (NUM_HISTORY - 1)].text))
362 return 0;
363 line = history[(history_current - 1) & (NUM_HISTORY - 1)].line;
364 fre = 0;
365 }
366
367 if (!t) {
368 CErrorDialog (CFirstWindow, 20, 20, _(" Manual Page "), get_sys_error (_(" Error reading from pipe, check 'option_man_cmdline' in the file ~/.cedit/.cooledit.ini ")));
369 return 0;
370 } else if (*t) {
371 if (fre)
372 add_to_history (t);
373 if (CIdent ("mandisplayfile.text")) {
374 CRedrawTextbox ("mandisplayfile.text", t, 0);
375 } else {
376 Window win;
377 CWidget *w;
378 if (in) {
379 win = in;
380 } else {
381 win = CDrawMainWindow ("mandisplayfile", _(" Manual Page "));
382 CGetHintPos (&x, &y);
383 }
384 CPushFont ("editor", 0);
385 w = CDrawTextbox ("mandisplayfile.text", win,
386 x, y, columns * FONT_MEAN_WIDTH + 7,
387 lines * FONT_PIX_PER_LINE, line, 0, t,
388 TEXTBOX_MAN_PAGE | TEXTBOX_NO_CURSOR);
389 CPopFont ();
390 /* Toolhint */
391 CSetToolHint ("mandisplayfile.text", _("Double click on words to open new man pages"));
392 w->position |= POSITION_HEIGHT | POSITION_WIDTH;
393 (CIdent ("mandisplayfile.text.vsc"))->position = POSITION_HEIGHT | POSITION_RIGHT;
394 CGetHintPos (0, &y);
395 (CDrawButton ("mandisplayfile.prev", win, x, y, AUTO_WIDTH, AUTO_HEIGHT, _(" Previous ")))->position = POSITION_BOTTOM;
396 CGetHintPos (&x, 0);
397 (CDrawButton ("mandisplayfile.next", win, x, y, AUTO_WIDTH, AUTO_HEIGHT, _(" Next ")))->position = POSITION_BOTTOM;
398 CGetHintPos (&x, 0);
399 (CDrawButton ("mandisplayfile.search", win, x, y, AUTO_WIDTH, AUTO_HEIGHT, _(" Search ")))->position = POSITION_BOTTOM;
400 CGetHintPos (&x, 0);
401 (CDrawButton ("mandisplayfile.again", win, x, y, AUTO_WIDTH, AUTO_HEIGHT, _(" Again ")))->position = POSITION_BOTTOM;
402 if (!in) {
403 CGetHintPos (&x, 0);
404 (CDrawPixmapButton ("mandisplayfile.done", win, x, y, PIXMAP_BUTTON_TICK))->position = POSITION_BOTTOM;
405 CGetHintPos (&x, 0);
406 (CDrawPixmapButton ("mandisplayfile.clear", win, x, y, PIXMAP_BUTTON_CROSS))->position = POSITION_BOTTOM;
407 CSetSizeHintPos ("mandisplayfile");
408 CMapDialog ("mandisplayfile");
409 CSetWindowResizable ("mandisplayfile", FONT_MEAN_WIDTH * 15, FONT_PIX_PER_LINE * 15, 1600, 1200); /* minimum and maximum sizes */
410 }
411 CAddCallback ("mandisplayfile.done", manpage_callback);
412 CAddCallback ("mandisplayfile.clear", manpageclear_callback);
413 CAddCallback ("mandisplayfile.text", manpage_callback);
414 CAddCallback ("mandisplayfile.next", manpagenext_callback);
415 CAddCallback ("mandisplayfile.prev", manpageprev_callback);
416 CAddCallback ("mandisplayfile.search", mansearch_callback);
417 CAddCallback ("mandisplayfile.again", mansearchagain_callback);
418 }
419 check_prev_next ();
420 if (t && fre)
421 free (t);
422 } else {
423 CErrorDialog (CFirstWindow, 20, 20, _(" Manual page "), _(" Fail trying to popen man, check 'option_man_cmdline' in the file ~/.cedit/.cooledit.ini "));
424 return 0;
425 }
426 res = CIdent ("mandisplayfile");
427 if (res)
428 return res;
429 return CIdent ("mandisplayfile.text");
430 }
431