another update from CVS HEAD, for QA
[alioth/jupp.git] / help.c
1 /*
2  *      Help system
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *              (C) 2001 Marek 'Marx' Grac
6  *
7  *      This file is part of JOE (Joe's Own Editor)
8  */
9 #include "config.h"
10 #include "types.h"
11
12 __RCSID("$MirOS: contrib/code/jupp/help.c,v 1.15 2017/12/08 03:24:15 tg Exp $");
13
14 #include <stdlib.h>
15 #include <string.h>
16
17 #ifdef HAVE_BSD_STRING_H
18 #include <bsd/string.h>
19 #endif
20
21 #include "blocks.h"
22 #include "builtin.h"
23 #include "help.h"
24 #include "scrn.h"
25 #include "utils.h"
26 #include "vs.h"
27 #include "utf8.h"
28 #include "w.h"
29
30 #define NOT_ENOUGH_MEMORY -11
31
32 struct help *help_actual = NULL;                        /* actual help screen */
33
34 /*
35  * Process help file
36  * Returns 0 if the help file was succefully processed
37  *        -1 if the help file couldn't be opened
38  *        NOT_ENOUGH_MEMORY if there is not enough memory
39  */
40 int
41 help_init(const unsigned char *filename)
42 {
43         JFILE *fd;                                      /* help file */
44         unsigned char buf[1024];                        /* input buffer */
45
46         struct help *tmp;
47         unsigned int bfl;                               /* buffer length */
48         unsigned int hlpsiz, hlpbsz;                    /* number of used/allocated bytes for tmp->text */
49         unsigned char *tempbuf;
50
51         if (!(fd = jfopen((const char *)filename, "r")))/* open the help file */
52                 return -1;                              /* return if we couldn't open the file */
53
54         fprintf(stderr, "Processing '%s'...", filename);
55         fflush(stderr);
56
57         while (jfgets((char *)buf, sizeof(buf), fd)) {
58                 if (buf[0] == '{') {                    /* start of help screen */
59                         if (!(tmp = malloc(sizeof(struct help)))) {
60                                 return NOT_ENOUGH_MEMORY;
61                         }
62
63                         tmp->text = NULL;
64                         tmp->lines = 0;
65                         hlpsiz = 0;
66                         hlpbsz = 0;
67                         tmp->name = vsncpy(NULL, 0, sz(buf + 1) - 1);
68
69                         while ((jfgets((char *)buf, sizeof(buf), fd)) && (buf[0] != '}')) {
70                                 bfl = strlen((char *)buf);
71                                 if (hlpsiz + bfl > hlpbsz) {
72                                         if (tmp->text) {
73                                                 tempbuf = realloc(tmp->text, hlpbsz + bfl + 1024);
74                                                 if (!tempbuf) {
75                                                         free(tmp->text);
76                                                         free(tmp);
77                                                         return NOT_ENOUGH_MEMORY;
78                                                 } else {
79                                                         tmp->text = tempbuf;
80                                                 }
81                                         } else {
82                                                 tmp->text = malloc(bfl + 1024);
83                                                 if (!tmp->text) {
84                                                         free(tmp);
85                                                         return NOT_ENOUGH_MEMORY;
86                                                 } else {
87                                                         tmp->text[0] = 0;
88                                                 }
89                                         }
90                                         hlpbsz += bfl + 1024;
91                                 }
92                                 strlcpy((char *)(tmp->text + hlpsiz), (char *)buf, 1024);
93                                 hlpsiz += bfl;
94                                 ++tmp->lines;
95                         }
96                         if (buf[0] == '}') {            /* set new help screen as actual one */
97                                 tmp->prev = help_actual;
98                                 tmp->next = NULL;
99                                 if (help_actual) {
100                                         help_actual->next = tmp;
101                                 }
102                                 help_actual = tmp;
103                         } else {
104                                 fprintf(stderr, "\nHelp file '%s' is not properly ended with } on new line.\n", filename);
105                                 fprintf(stderr, "Do you want to accept incomplete help screen (y/n)?");
106                                 fflush(stderr);
107                                 if (fgets((char *)buf, 8, stdin) == NULL ||
108                                     (buf[0] | 0x20) != 'y') {
109                                         free(tmp->text);
110                                         free(tmp);
111                                         return 0;
112                                 } else {
113                                         tmp->prev = help_actual;
114                                         tmp->next = NULL;
115                                         if (help_actual) {
116                                                 help_actual->next = tmp;
117                                         }
118                                         help_actual = tmp;
119                                 }
120                         }
121                 }
122         }
123         jfclose(fd);                                    /* close help file */
124
125         fprintf(stderr, "done\n");
126
127         while (help_actual && help_actual->prev) {      /* move to first help screen */
128                 help_actual = help_actual->prev;
129         }
130
131         return 0;
132 }
133
134 /*
135  * Find context help - find help entry with the same name
136  */
137
138 struct help *
139 find_context_help(const unsigned char *name)
140 {
141         struct help *tmp = help_actual;
142
143         while (tmp->prev != NULL)       /* find the first help entry */
144                 tmp = tmp->prev;
145
146         while (tmp != NULL && strcmp(tmp->name, name) != 0)
147                 tmp = tmp->next;
148
149         return tmp;
150 }
151
152 /*
153  * Display help text
154  */
155 void
156 help_display(SCREEN *t)
157 {
158         unsigned char *str;
159         int y, x, z;
160         int atr = 0;
161
162         if (help_actual) {
163                 str = help_actual->text;
164         } else {
165                 str = NULL;
166         }
167
168         for (y = skiptop; y != t->wind; ++y) {
169                 if (t->t->updtab[y]) {
170                         unsigned char *start = str;
171                         int width=0;
172                         int nspans=0;
173                         int spanwidth;
174                         int spancount=0;
175                         int spanextra;
176                         /* First pass: count no. springs \| and determine minimum width */
177                         while(*str && *str!='\n')
178                                 if (*str++ == '\\')
179                                         switch(*str) {
180                                                 case 'i':
181                                                 case 'I':
182                                                 case 'u':
183                                                 case 'U':
184                                                 case 'd':
185                                                 case 'D':
186                                                 case 'b':
187                                                 case 'B':
188                                                 case 'f':
189                                                 case 'F':
190                                                         ++str;
191                                                         break;
192                                                 case '|':
193                                                         ++str;
194                                                         ++nspans;
195                                                         break;
196                                                 case 0:
197                                                         break;
198                                                 default:
199                                                         ++str;
200                                                         ++width;
201                                         }
202                                 else
203                                         ++width;
204                         str = start;
205                         /* Now calculate span width */
206                         if (width >= t->w - 1 || nspans==0) {
207                                 spanwidth = 0;
208                                 spanextra = nspans;
209                         } else {
210                                 spanwidth = ((t->w - 1) - width)/nspans;
211                                 spanextra = nspans - ((t->w - 1) - width - nspans*spanwidth);
212                         }
213                         /* Second pass: display text */
214                         for (x = 0; x != t->w - 1; ++x) {
215                                 if (*str == '\n' || !*str) {
216                                         if (eraeol(t->t, x, y)) {
217                                                 return;
218                                         } else {
219                                                 break;
220                                         }
221                                 } else {
222                                         if (*str == '\\') {
223                                                 switch (*++str) {
224                                                 case '|':
225                                                         ++str;
226                                                         for (z=0;z!=spanwidth;++z)
227                                                                 outatr(locale_map,t->t,t->t->scrn+x+y*t->w+z,t->t->attr+x+y*t->w+z,x+z,y,' ',atr);
228                                                         if (spancount++ >= spanextra) {
229                                                                 outatr(locale_map,t->t,t->t->scrn+x+y*t->w+z,t->t->attr+x+y*t->w+z,x+z,y,' ',atr);
230                                                                 ++z;
231                                                         }
232                                                         x += z-1;
233                                                         continue;
234                                                 case 'i':
235                                                 case 'I':
236                                                         atr ^= INVERSE;
237                                                         ++str;
238                                                         --x;
239                                                         continue;
240                                                 case 'u':
241                                                 case 'U':
242                                                         atr ^= UNDERLINE;
243                                                         ++str;
244                                                         --x;
245                                                         continue;
246                                                 case 'd':
247                                                 case 'D':
248                                                         atr ^= DIM;
249                                                         ++str;
250                                                         --x;
251                                                         continue;
252                                                 case 'b':
253                                                 case 'B':
254                                                         atr ^= BOLD;
255                                                         ++str;
256                                                         --x;
257                                                         continue;
258                                                 case 'f':
259                                                 case 'F':
260                                                         atr ^= BLINK;
261                                                         ++str;
262                                                         --x;
263                                                         continue;
264                                                 case 0:
265                                                         --x;
266                                                         continue;
267                                                 }
268                                         }
269                                         outatr_help(t->t,
270                                             t->t->scrn + x + y * t->w,
271                                             t->t->attr + x + y * t->w,
272                                             x, y, *str++, atr);
273                                 }
274                         }
275                         atr = 0;
276                         t->t->updtab[y] = 0;
277                 }
278
279                 while (*str && *str != '\n')
280                         ++str;
281                 if (*str == '\n')
282                         ++str;
283         }
284 }
285
286 /*
287  * Show help screen
288  */
289 int
290 help_on(SCREEN *t)
291 {
292         if (help_actual) {
293                 t->wind = help_actual->lines + skiptop;
294                 if ((t->h - t->wind) < FITHEIGHT) {
295                         t->wind = t->h - FITHEIGHT;
296                 }
297                 if (t->wind < 0) {
298                         t->wind = skiptop;
299                         return -1;
300                 }
301                 wfit(t);
302                 msetI(t->t->updtab + skiptop, 1, t->wind);
303                 return 0;
304         } else {
305                 return -1;
306         }
307 }
308
309 /*
310  * Hide help screen
311  */
312 void
313 help_off(SCREEN *t)
314 {
315         t->wind = skiptop;
316         wfit(t);
317 }
318
319 /*
320  * Show/hide current help screen
321  */
322 int
323 u_help(BASE *base)
324 {
325         W *w = base->parent;
326         struct help *new_help;
327
328         if (w->huh && (new_help = find_context_help(w->huh)) != NULL) {
329                 if (help_actual != new_help) {
330                         if (w->t->wind != skiptop)
331                                 help_off(w->t);
332                         help_actual = new_help;         /* prepare context help */
333                 }
334         }
335         if (w->t->wind == skiptop) {
336                 return help_on(w->t);                   /* help screen is hidden, so show the actual one */
337         } else {
338                 help_off(w->t);                         /* hide actual help screen */
339                 return 0;
340         }
341 }
342
343 /*
344  * Show next help screen (if it is possible)
345  */
346 int
347 u_help_next(BASE *base)
348 {
349         W *w = base->parent;
350
351         if (help_actual && help_actual->next) {         /* is there any next help screen? */
352                 if (w->t->wind != skiptop) {
353                         help_off(w->t);                 /* if help screen was visible, then hide it */
354                 }
355                 help_actual = help_actual->next;        /* change to next help screen */
356                 return help_on(w->t);                   /* show actual help screen */
357         } else {
358                 return -1;
359         }
360 }
361
362 /*
363  * Show previous help screen (if it is possible)
364  */
365 int
366 u_help_prev(BASE *base)
367 {
368         W *w = base->parent;
369
370         if (help_actual && help_actual->prev) {         /* is there any previous help screen? */
371                 if (w->t->wind != skiptop)
372                         help_off(w->t);                 /* if help screen was visible, then hide it */
373                 help_actual = help_actual->prev;        /* change to previous help screen */
374                 return help_on(w->t);                   /* show actual help screen */
375         } else {
376                 return -1;
377         }
378 }