1 /*
2 
3     Minimum Profit - A Text Editor
4 
5     ttcdt <dev@triptico.com> et al.
6 
7     This software is released into the public domain.
8     NO WARRANTY. See file LICENSE for details.
9 
10 */
11 
12 #include "config.h"
13 
14 #include <stdio.h>
15 #include <wchar.h>
16 #include <wctype.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #include "mpdm.h"
21 #include "mpsl.h"
22 
23 #include "mp.h"
24 
25 /** data **/
26 
27 /* exit requested? */
28 int mp_exit_requested = 0;
29 
30 /** private data for drawing syntax-highlighted text **/
31 
32 struct drw_1_info {
33     mpdm_t txt;                 /* the document */
34     mpdm_t syntax;              /* syntax highlight information */
35     mpdm_t colors;              /* color definitions (for attributes) */
36     mpdm_t word_color_func;     /* word color function (just for detection) */
37     mpdm_t last_search;         /* last search regex */
38     int normal_attr;            /* normal attr */
39     int cursor_attr;            /* cursor attr */
40     int n_lines;                /* number of processed lines */
41     int p_lines;                /* number of prereaded lines */
42     int t_lines;                /* total lines in document */
43     int vx;                     /* first visible column */
44     int vy;                     /* first visible line */
45     int tx;                     /* horizontal window size */
46     int ty;                     /* vertical window size */
47     int tab_size;               /* tabulator size */
48     int mod;                    /* modify count */
49     int preread_lines;          /* lines to pre-read (for synhi blocks) */
50     int mark_eol;               /* mark end of lines */
51     int redraw;                 /* redraw trigger */
52     int xoffset;                /* # of columns reserved for line numbers */
53     int double_page;            /* # of columns where double page activates */
54     wchar_t wc_tab;             /* char for marking tabs */
55     wchar_t wc_formfeed;        /* char for marking form feeds */
56     wchar_t wc_unknown;         /* char for marking unknown characters */
57     wchar_t wc_wordwrap;        /* char for marking word wraps */
58     wchar_t wc_m_dash;          /* char for the m-dash */
59 };
60 
61 struct drw_1_info drw_1;
62 struct drw_1_info drw_1_o;
63 
64 static struct {
65     int x;                      /* cursor x */
66     int y;                      /* cursor y */
67     int *offsets;               /* offsets of lines */
68     char *attrs;                /* attributes */
69     int visible;                /* offset to the first visible character */
70     int cursor;                 /* offset to cursor */
71     wchar_t *ptr;               /* pointer to joined data */
72     int size;                   /* size of joined data */
73     int matchparen_offset;      /* offset to matched paren */
74     int matchparen_o_attr;      /* original attribute there */
75     int cursor_o_attr;          /* original attribute under cursor */
76     mpdm_t v;                   /* the data */
77     mpdm_t old;                 /* the previously generated array */
78     int mark_offset;            /* offset to the marked block */
79     int mark_size;              /* size of mark_o_attr */
80     char *mark_o_attr;          /* saved attributes for the mark */
81 
82     wchar_t *cmap;
83     int *amap;
84     int *vx2x;
85     int *vy2y;
86     int mx;
87     int my;
88 } drw_2;
89 
90 /** code **/
91 
92 #define MP_REAL_TAB_SIZE(x) (drw_1.tab_size - ((x) % drw_1.tab_size))
93 
drw_wcwidth(int x,wchar_t c)94 static int drw_wcwidth(int x, wchar_t c)
95 /* returns the wcwidth of c, or the tab spaces for
96    the x column if it's a tab */
97 {
98     int r;
99 
100     switch (c) {
101     case L'\n':
102         r = 1;
103         break;
104 
105     case L'\t':
106         r = MP_REAL_TAB_SIZE(x);
107         break;
108 
109     default:
110         r = 1;
111         break;
112     }
113 
114     return r < 0 ? 1 : r;
115 }
116 
117 
drw_vx2x(mpdm_t str,int vx)118 int drw_vx2x(mpdm_t str, int vx)
119 /* returns the character in str that is on column vx */
120 {
121     const wchar_t *ptr = str->data;
122     int n, x;
123 
124     for (n = x = 0; n < vx && ptr[x] != L'\0'; x++)
125         n += drw_wcwidth(n, ptr[x]);
126 
127     return x;
128 }
129 
130 
drw_x2vx(mpdm_t str,int x)131 int drw_x2vx(mpdm_t str, int x)
132 /* returns the column where the character at offset x seems to be */
133 {
134     const wchar_t *ptr;
135     int n, vx = 0;
136 
137     if (str) {
138         ptr = str->data;
139         for (n = vx = 0; n < x && ptr[n] != L'\0'; n++)
140             vx += drw_wcwidth(vx, ptr[n]);
141     }
142 
143     return vx;
144 }
145 
146 
drw_line_offset(int l)147 static int drw_line_offset(int l)
148 /* returns the offset into v for line number l */
149 {
150     return drw_2.offsets[l - drw_1.vy + drw_1.p_lines];
151 }
152 
153 
drw_adjust_y(int y,int * vy,int ty)154 static int drw_adjust_y(int y, int *vy, int ty)
155 /* adjusts the visual y position */
156 {
157     int t = *vy;
158 
159     if (*vy < 0)
160         *vy = 0;
161 
162     /* is y above the first visible line? */
163     if (y < *vy)
164         *vy = y;
165 
166     /* is y below the last visible line? */
167     if (y > *vy + (ty - 2))
168         *vy = y - (ty - 2);
169 
170     return t != *vy;
171 }
172 
173 
drw_adjust_x(int x,int y,int * vx,int tx,wchar_t * ptr)174 static int drw_adjust_x(int x, int y, int *vx, int tx, wchar_t * ptr)
175 /* adjust the visual x position */
176 {
177     int n, m;
178     int t = *vx;
179 
180     /* calculate the column for the cursor position */
181     for (n = m = 0; n < x; n++, ptr++)
182         m += drw_wcwidth(m, *ptr);
183 
184     /* if new cursor column is nearer the leftmost column, set */
185     if (m < *vx)
186         *vx = m;
187 
188     /* if new cursor column is further the rightmost column, set */
189     if (m > *vx + (tx - 1))
190         *vx = m - (tx - 1);
191 
192     return t != *vx;
193 }
194 
195 
drw_get_attr(wchar_t * color_name)196 static int drw_get_attr(wchar_t * color_name)
197 /* returns the attribute number for a color */
198 {
199     mpdm_t v;
200     int attr = 0;
201 
202     if ((v = mpdm_get_wcs(drw_1.colors, color_name)) != NULL)
203         attr = mpdm_ival(mpdm_get_wcs(v, L"attr"));
204 
205     return attr;
206 }
207 
208 
drw_prepare(mpdm_t doc)209 static int drw_prepare(mpdm_t doc)
210 /* prepares the document for screen drawing */
211 {
212     mpdm_t window   = mpdm_ref(mpdm_get_wcs(MP, L"window"));
213     mpdm_t config   = mpdm_ref(mpdm_get_wcs(MP, L"config"));
214     mpdm_t txt      = mpdm_ref(mpdm_get_wcs(doc, L"txt"));
215     mpdm_t lines    = mpdm_ref(mpdm_get_wcs(txt, L"lines"));
216     int x           = mpdm_ival(mpdm_get_wcs(txt, L"x"));
217     int y           = mpdm_ival(mpdm_get_wcs(txt, L"y"));
218     int n;
219     mpdm_t v;
220     wchar_t *ptr;
221     int ret = 1;
222 
223     mpdm_store(&drw_1.txt,             txt);
224     mpdm_store(&drw_1.syntax,          mpdm_get_wcs(doc, L"syntax"));
225     mpdm_store(&drw_1.colors,          mpdm_get_wcs(MP, L"colors"));
226     mpdm_store(&drw_1.word_color_func, mpdm_get_wcs(MP, L"word_color_func"));
227     mpdm_store(&drw_1.last_search,     mpdm_get_wcs(MP, L"last_search"));
228 
229     drw_1.vx            = mpdm_ival(mpdm_get_wcs(txt, L"vx"));
230     drw_1.vy            = mpdm_ival(mpdm_get_wcs(txt, L"vy"));
231     drw_1.tx            = mpdm_ival(mpdm_get_wcs(window, L"tx"));
232     drw_1.ty            = mpdm_ival(mpdm_get_wcs(window, L"ty"));
233     drw_1.tab_size      = mpdm_ival(mpdm_get_wcs(config, L"tab_size"));
234     drw_1.mod           = mpdm_ival(mpdm_get_wcs(txt, L"mod"));
235     drw_1.mark_eol      = mpdm_ival(mpdm_get_wcs(config, L"mark_eol"));
236     drw_1.t_lines       = mpdm_size(lines);
237 
238     /* get preread_lines from the syntax definition */
239     drw_1.preread_lines = mpdm_ival(mpdm_get_wcs(drw_1.syntax, L"preread_lines"));
240 
241     /* if it's 0, get it from the configuration */
242     if (drw_1.preread_lines == 0)
243         drw_1.preread_lines = mpdm_ival(mpdm_get_wcs(config, L"preread_lines"));
244 
245     n = mpdm_ival(mpdm_get_wcs(config, L"double_page"));
246 
247     if (n && drw_1.tx > n) {
248         /* if the usable screen is wider than the
249            double page setting, activate it */
250         drw_1.tx /= 2;
251         drw_1.ty *= 2;
252         drw_1.double_page = 1;
253     }
254     else
255         drw_1.double_page = 0;
256 
257     /* adjust the visual y coordinate */
258     if (drw_adjust_y(y, &drw_1.vy, drw_1.ty))
259         mpdm_set_wcs(txt, MPDM_I(drw_1.vy), L"vy");
260 
261     /* adjust the visual x coordinate */
262     if (drw_adjust_x(x, y, &drw_1.vx, drw_1.tx, mpdm_string(mpdm_get_i(lines, y))))
263         mpdm_set_wcs(txt, MPDM_I(drw_1.vx), L"vx");
264 
265     /* get the maximum prereadable lines */
266     drw_1.p_lines = drw_1.vy > drw_1.preread_lines ? drw_1.preread_lines : drw_1.vy;
267 
268     /* maximum lines */
269     drw_1.n_lines = drw_1.ty + drw_1.p_lines;
270 
271     /* get the most used attributes */
272     drw_1.normal_attr = drw_get_attr(L"normal");
273     drw_1.cursor_attr = drw_get_attr(L"cursor");
274 
275     drw_2.x = x;
276     drw_2.y = y;
277 
278     /* redraw trigger */
279     drw_1.redraw = mpdm_ival(mpdm_get_wcs(MP, L"redraw_counter"));
280 
281     /* calculate number of lines reserved for line numbers */
282     n = mpdm_ival(mpdm_get_wcs(config, L"show_line_numbers"));
283 
284     if (n) {
285         char tmp[32];
286         sprintf(tmp, " %d ", (int) mpdm_size(lines));
287         drw_1.xoffset = strlen(tmp);
288     }
289     else
290         drw_1.xoffset = 0;
291 
292     mpdm_set_wcs(MP, MPDM_I(drw_1.xoffset), L"xoffset");
293 
294     /* placeholder Unicode characters */
295     n = mpdm_ival(mpdm_get_wcs(config, L"use_unicode"));
296     v = mpdm_get_i(mpdm_get_wcs(MP, L"unicode_tbl"), n);
297 
298     if ((ptr = mpdm_string(mpdm_get_wcs(v, L"tab"))) != NULL)
299         drw_1.wc_tab = *ptr;
300     if ((ptr = mpdm_string(mpdm_get_wcs(v, L"formfeed"))) != NULL)
301         drw_1.wc_formfeed = *ptr;
302     if ((ptr = mpdm_string(mpdm_get_wcs(v, L"wordwrap"))) != NULL)
303         drw_1.wc_wordwrap = *ptr;
304     if ((ptr = mpdm_string(mpdm_get_wcs(v, L"m_dash"))) != NULL)
305         drw_1.wc_m_dash = *ptr;
306     if ((ptr = mpdm_string(mpdm_get_wcs(v, L"unknown"))) != NULL)
307         drw_1.wc_unknown = *ptr;
308 
309     /* compare drw_1 with drw_1_o; if they are the same,
310        no more expensive calculations on drw_2 are needed */
311     if (memcmp(&drw_1, &drw_1_o, sizeof(drw_1)) != 0) {
312 
313         /* different; store now */
314         memcpy(&drw_1_o, &drw_1, sizeof(drw_1_o));
315 
316         /* alloc space for line offsets */
317         drw_2.offsets = realloc(drw_2.offsets, drw_1.n_lines * sizeof(int));
318 
319         drw_2.ptr = NULL;
320         drw_2.size = 0;
321 
322         /* add first line */
323         drw_2.ptr = mpdm_pokev(drw_2.ptr, &drw_2.size,
324                         mpdm_get_i(lines, drw_1.vy - drw_1.p_lines));
325 
326         /* first line start at 0 */
327         drw_2.offsets[0] = 0;
328 
329         /* add the following lines and store their offsets */
330         for (n = 1; n < drw_1.n_lines; n++) {
331             /* add the separator */
332             drw_2.ptr = mpdm_pokews(drw_2.ptr, &drw_2.size, L"\n");
333 
334             /* this line starts here */
335             drw_2.offsets[n] = drw_2.size;
336 
337             /* now add it */
338             drw_2.ptr = mpdm_pokev(drw_2.ptr, &drw_2.size,
339                             mpdm_get_i(lines, n + drw_1.vy - drw_1.p_lines));
340         }
341 
342         drw_2.ptr = mpdm_poke(drw_2.ptr, &drw_2.size, L"", 1, sizeof(wchar_t));
343         drw_2.size--;
344 
345         /* now create a value */
346         mpdm_store(&drw_2.v, MPDM_ENS(drw_2.ptr, drw_2.size));
347 
348         /* alloc and init space for the attributes */
349         drw_2.attrs = realloc(drw_2.attrs, drw_2.size + 1);
350         memset(drw_2.attrs, drw_1.normal_attr, drw_2.size + 1);
351 
352         drw_2.visible = drw_line_offset(drw_1.vy);
353 
354         /* (re)create the maps */
355         n = (drw_1.tx + 1) * drw_1.ty;
356         drw_2.cmap  = realloc(drw_2.cmap, n * sizeof(wchar_t));
357         drw_2.amap  = realloc(drw_2.amap, n * sizeof(int));
358         drw_2.vx2x  = realloc(drw_2.vx2x, n * sizeof(int));
359         drw_2.vy2y  = realloc(drw_2.vy2y, n * sizeof(int));
360 
361         drw_2.mx = drw_2.my = -1;
362     }
363     else
364         /* drw_1 didn't change */
365         ret = 0;
366 
367     mpdm_unref(lines);
368     mpdm_unref(txt);
369     mpdm_unref(config);
370     mpdm_unref(window);
371 
372     return ret;
373 }
374 
375 
drw_fill_attr(int attr,int offset,int size)376 static int drw_fill_attr(int attr, int offset, int size)
377 /* fill an attribute */
378 {
379     if (attr != -1 && offset + size < drw_2.size + 1)
380         memset(drw_2.attrs + offset, attr, size);
381 
382     return offset + size;
383 }
384 
385 
drw_fill_attr_regex(int attr)386 static int drw_fill_attr_regex(int attr)
387 /* fills with an attribute the last regex match */
388 {
389     return drw_fill_attr(attr, mpdm_regex_offset, mpdm_regex_size);
390 }
391 
392 
drw_words(void)393 static void drw_words(void)
394 /* fills the attributes for separate words */
395 {
396     mpdm_t wcolor, func;
397     int o = drw_2.visible;
398 
399     wcolor = mpdm_get_wcs(MP, L"word_color");
400     func   = drw_1.word_color_func;
401 
402     if (mpdm_count(wcolor) || func) {
403         while (drw_2.ptr[o]) {
404             int ws;
405 
406             /* skip non-alpha characters */
407             while (drw_2.ptr[o] && !iswalpha(drw_2.ptr[o]))
408                 o++;
409 
410             ws = o;
411 
412             /* count alpha characters */
413             while(iswalpha(drw_2.ptr[o]))
414                 o++;
415 
416             if (ws != o) {
417                 int attr = -1;
418                 mpdm_t w, v;
419 
420                 /* create a word */
421                 w = mpdm_ref(MPDM_NS(&drw_2.ptr[ws], o - ws));
422 
423                 if ((v = mpdm_get(wcolor, w)) != NULL)
424                     attr = mpdm_ival(v);
425                 else
426                 if (func != NULL)
427                     attr = mpdm_ival(mpdm_exec_1(func, w, NULL));
428 
429                 drw_fill_attr(attr, ws, o - ws);
430 
431                 mpdm_unref(w);
432             }
433         }
434     }
435 }
436 
437 
drw_multiline_regex(mpdm_t a,int attr)438 static void drw_multiline_regex(mpdm_t a, int attr)
439 /* sets the attribute to all matching (possibly multiline) regexes */
440 {
441     int n;
442 
443     mpdm_ref(a);
444 
445     for (n = 0; n < mpdm_size(a); n++) {
446         mpdm_t r = mpdm_get_i(a, n);
447         int o = 0;
448 
449         /* if the regex is an array, it's a pair of
450            'match from this' / 'match until this' */
451         if (mpdm_type(r) == MPDM_TYPE_ARRAY) {
452             mpdm_t rs = mpdm_get_i(r, 0);
453             mpdm_t re = mpdm_get_i(r, 1);
454 
455             while (!mpdm_is_null(mpdm_regex(drw_2.v, rs, o))) {
456                 int s;
457 
458                 /* fill the matched part */
459                 o = drw_fill_attr_regex(attr);
460 
461                 /* try to match the end */
462                 if (!mpdm_is_null(mpdm_regex(drw_2.v, re, o))) {
463                     /* found; fill the attribute
464                        to the end of the match */
465                     s = mpdm_regex_size + (mpdm_regex_offset - o);
466                 }
467                 else {
468                     /* not found; fill to the end
469                        of the document */
470                     s = drw_2.size - o;
471                 }
472 
473                 /* fill to there */
474                 o = drw_fill_attr(attr, o, s);
475             }
476         }
477         else {
478             /* must be a scalar */
479 
480             if (*mpdm_string(r) == L'%') {
481                 /* it's a sscanf() expression */
482                 mpdm_t v;
483 
484                 while ((v = mpdm_ref(mpdm_sscanf(drw_2.v, r, o)))
485                        && mpdm_size(v) == 2) {
486                     int i = mpdm_ival(mpdm_get_i(v, 0));
487                     int s = mpdm_ival(mpdm_get_i(v, 1)) - i;
488 
489                     o = drw_fill_attr(attr, i, s);
490 
491                     mpdm_unref(v);
492                 }
493             }
494             else {
495                 /* it's a regex */
496                 /* while the regex matches, fill attributes */
497                 while (!mpdm_is_null(mpdm_regex(drw_2.v, r, o)) && mpdm_regex_size)
498                     o = drw_fill_attr_regex(attr);
499             }
500         }
501     }
502 
503     mpdm_unref(a);
504 }
505 
506 
drw_blocks(void)507 static void drw_blocks(void)
508 /* fill attributes for multiline blocks */
509 {
510     mpdm_t defs;
511 
512     if (drw_1.syntax && (defs = mpdm_get_wcs(drw_1.syntax, L"defs"))) {
513         int n;
514 
515         for (n = 0; n < mpdm_size(defs); n += 2) {
516             mpdm_t attr;
517             mpdm_t list;
518 
519             /* get the attribute */
520             attr = mpdm_get_i(defs, n);
521             attr = mpdm_get(drw_1.colors, attr);
522             attr = mpdm_get_wcs(attr, L"attr");
523 
524             /* get the list for this word color */
525             list = mpdm_get_i(defs, n + 1);
526 
527             drw_multiline_regex(list, mpdm_ival(attr));
528         }
529     }
530 }
531 
532 
drw_selection(void)533 static void drw_selection(void)
534 /* draws the selected block, if any */
535 {
536     mpdm_t mark;
537     int bx, by, ex, ey, vertical;
538     int so, eo;
539     int mby, mey;
540     int line_offset, next_line_offset;
541     int y;
542     int len;
543     int attr;
544 
545     /* no mark? return */
546     if ((mark = mpdm_get_wcs(drw_1.txt, L"mark")) == NULL)
547         return;
548 
549     bx = mpdm_ival(mpdm_get_wcs(mark, L"bx"));
550     by = mpdm_ival(mpdm_get_wcs(mark, L"by"));
551     ex = mpdm_ival(mpdm_get_wcs(mark, L"ex"));
552     ey = mpdm_ival(mpdm_get_wcs(mark, L"ey"));
553     vertical = mpdm_ival(mpdm_get_wcs(mark, L"vertical"));
554 
555     /* if block is not visible, return */
556     if (ey < drw_1.vy || by >= drw_1.vy + drw_1.ty)
557         return;
558 
559     so = by < drw_1.vy ? drw_2.visible : drw_line_offset(by) + bx;
560     eo = ey >= drw_1.vy + drw_1.ty ? drw_2.size : drw_line_offset(ey) + ex;
561 
562     /* alloc space and save the attributes being destroyed */
563     drw_2.mark_offset = so;
564     drw_2.mark_size = eo - so + 1;
565     drw_2.mark_o_attr = malloc(eo - so + 1);
566     memcpy(drw_2.mark_o_attr, &drw_2.attrs[so], eo - so + 1);
567 
568     if (vertical == 0) {
569         /* normal selection */
570         drw_fill_attr(drw_get_attr(L"selection"), so, eo - so);
571     }
572     else {
573         /* vertical selection */
574         mby = by < drw_1.vy ? drw_1.vy : by;
575         mey = ey >= drw_1.vy + drw_1.ty ? drw_1.vy + drw_1.ty : ey;
576         line_offset = drw_line_offset(mby);
577         attr = drw_get_attr(L"selection");
578         for (y = mby; y <= mey; y++) {
579             next_line_offset = drw_line_offset(y + 1);
580             len = next_line_offset - line_offset - 1;
581             so = bx > len ? -1 : bx;
582             eo = ex > len ? len : ex;
583 
584             if (so >= 0 && eo >= so)
585                 drw_fill_attr(attr, line_offset + so, eo - so + 1);
586 
587             line_offset = next_line_offset;
588         }
589     }
590 }
591 
592 
drw_search_hit(void)593 static void drw_search_hit(void)
594 /* colorize the search hit, if any */
595 {
596     if (drw_1.last_search != NULL) {
597         mpdm_t l = MPDM_A(0);
598 
599         mpdm_set_i(l, drw_1.last_search, 0);
600         drw_multiline_regex(l, drw_get_attr(L"search"));
601     }
602 }
603 
604 
drw_cursor(void)605 static void drw_cursor(void)
606 /* fill the attribute for the cursor */
607 {
608     /* calculate the cursor offset */
609     drw_2.cursor = drw_line_offset(drw_2.y) + drw_2.x;
610 
611     drw_2.cursor_o_attr = drw_2.attrs[drw_2.cursor];
612     drw_fill_attr(drw_1.cursor_attr, drw_2.cursor, 1);
613 }
614 
615 
drw_matching_paren(void)616 static void drw_matching_paren(void)
617 /* highlights the matching paren */
618 {
619     int o = drw_2.cursor;
620     int i = 0;
621     wchar_t c;
622 
623     /* by default, no offset has been found */
624     drw_2.matchparen_offset = -1;
625 
626     /* find the opposite and the increment (direction) */
627     switch (drw_2.ptr[o]) {
628     case L'(':
629         c = L')';
630         i = 1;
631         break;
632     case L'{':
633         c = L'}';
634         i = 1;
635         break;
636     case L'[':
637         c = L']';
638         i = 1;
639         break;
640     case L')':
641         c = L'(';
642         i = -1;
643         break;
644     case L'}':
645         c = L'{';
646         i = -1;
647         break;
648     case L']':
649         c = L'[';
650         i = -1;
651         break;
652     }
653 
654     /* if a direction is set, do the searching */
655     if (i) {
656         wchar_t s = drw_2.ptr[o];
657         int m = 0;
658         int l = i == -1 ? drw_2.visible - 1 : drw_2.size;
659 
660         while (o != l) {
661             if (drw_2.ptr[o] == s) {
662                 /* found the same */
663                 m++;
664             }
665             else if (drw_2.ptr[o] == c) {
666                 /* found the opposite */
667                 if (--m == 0) {
668                     /* found! fill and exit */
669                     drw_2.matchparen_offset = o;
670                     drw_2.matchparen_o_attr = drw_2.attrs[o];
671                     drw_fill_attr(drw_get_attr(L"matching"), o, 1);
672                     break;
673                 }
674             }
675 
676             o += i;
677         }
678     }
679 }
680 
681 
drw_push_pair(mpdm_t l,int i,int a,wchar_t * tmp)682 static mpdm_t drw_push_pair(mpdm_t l, int i, int a, wchar_t * tmp)
683 /* pushes a pair of attribute / string into l */
684 {
685     /* create the array, if doesn't exist yet */
686     if (l == NULL)
687         l = MPDM_A(0);
688 
689     /* finish the string */
690     tmp[i] = L'\0';
691 
692     /* special magic: if the attribute is the
693        one of the cursor and the string is more than
694        one character, create two strings; the
695        cursor is over a tab */
696     if (a == drw_1.cursor_attr && i > 1) {
697         mpdm_push(l, MPDM_I(a));
698         mpdm_push(l, MPDM_NS(tmp, 1));
699 
700         /* the rest of the string has the original attr */
701         a = drw_2.cursor_o_attr;
702 
703         /* one char less */
704         tmp[i - 1] = L'\0';
705     }
706 
707     /* store the attribute and the string */
708     mpdm_push(l, MPDM_I(a));
709     mpdm_push(l, MPDM_S(tmp));
710 
711     return l;
712 }
713 
714 
drw_char(wchar_t c,wchar_t pc,int n)715 static wchar_t drw_char(wchar_t c, wchar_t pc, int n)
716 /* does possible conversions to the char about to be printed */
717 {
718     /* real tab */
719     if (n == 0 && c == L'\t')
720         c = drw_1.wc_tab;
721 
722     /* soft hyphen */
723     if (c == L'\xad')
724         c = drw_1.wc_wordwrap;  /* cedilla */
725 
726     /* m-dash */
727     if (c == L'\x2014')
728         c = drw_1.wc_m_dash;
729 
730     if (drw_1.mark_eol) {
731         if (c == L'\t')
732             c = L'\xb7';    /* middledot */
733         else
734         if (c == L'\n' || c == L'\0') {
735             if (pc != L'\xad')
736                 c = L'\xb6';    /* pilcrow */
737             else
738                 c = L' ';
739         }
740     }
741     else {
742         if (c == L'\t' || c == L'\n' || c == L'\0')
743             c = L' ';
744     }
745 
746     /* replace form feed with visual representation and
747        the rest of control codes with the Unicode replace char */
748     if (c == L'\f')
749         c = drw_1.wc_formfeed;
750     else
751     if (c < L' ')
752         c = drw_1.wc_unknown;
753 
754     return c;
755 }
756 
757 
drw_map_1(int mx,int my,wchar_t c,int a,int x,int y)758 static void drw_map_1(int mx, int my, wchar_t c, int a, int x, int y)
759 {
760     int o = mx + my * (drw_1.tx + 1);
761 
762     drw_2.cmap[o] = c;
763     drw_2.amap[o] = a;
764     drw_2.vx2x[o] = x;
765     drw_2.vy2y[o] = y;
766 
767     if (a == drw_1.cursor_attr) {
768         drw_2.mx = mx;
769         drw_2.my = my;
770     }
771 }
772 
773 
vpos2pos(int mx,int my,int * x,int * y)774 static void vpos2pos(int mx, int my, int *x, int *y)
775 {
776     if (my < 0) {
777         /* above top margin: pick previous line */
778         *x = mx;
779         *y = drw_1.vy - 2;
780     }
781     else
782     if (my > drw_1.ty - 1) {
783         /* below bottom margin: pick next line */
784         *x = mx;
785         *y = drw_1.vy + drw_1.ty;
786     }
787     else {
788         /* in range: pick from the map */
789         int o = mx + my * (drw_1.tx + 1);
790 
791         *x = drw_2.vx2x[o];
792         *y = drw_2.vy2y[o];
793     }
794 }
795 
796 
drw_remap_truncate(void)797 static void drw_remap_truncate(void)
798 {
799     int i = drw_2.offsets[drw_1.p_lines];
800     int mx, my;
801     int x, y;
802 
803     x = 0;
804     y = drw_1.vy;
805 
806     for (my = 0; my < drw_1.ty; my++) {
807         wchar_t c, pc = 0;
808         int ax = 0;
809         mx = 0;
810 
811         do {
812             c = drw_2.ptr[i];
813             int t = drw_wcwidth(mx, c);
814             int n;
815 
816             if (c == '\0')
817                 break;
818 
819             for (n = 0; n < t && mx < drw_1.tx; n++) {
820                 if (ax >= drw_1.vx)
821                     drw_map_1(mx++, my, drw_char(c, pc, n), drw_2.attrs[i], x, y);
822                 ax++;
823             }
824 
825             i++;
826 
827             if (c == '\n')
828                 break;
829 
830             x++;
831             pc = c;
832 
833         } while (mx < drw_1.tx);
834 
835         while (mx < drw_1.tx)
836             drw_map_1(mx++, my, '.', -1, x, y);
837 
838         while (c != '\n' && c != '\0')
839             c = drw_2.ptr[i++];
840 
841         if (c == '\n') {
842             x = 0;
843             y++;
844         }
845     }
846 }
847 
848 
drw_double_page(void)849 static void drw_double_page(void)
850 /* recompose a double page char-mapped buffer */
851 {
852     if (drw_1.double_page) {
853         int n, m;
854         wchar_t *dp_cmap = drw_2.cmap;
855         int *dp_amap     = drw_2.amap;
856         int *dp_vx2x     = drw_2.vx2x;
857         int *dp_vy2y     = drw_2.vy2y;
858         int o = 0;
859 
860         n = (drw_1.tx + 1) * drw_1.ty;
861         drw_2.cmap  = calloc(n, sizeof(wchar_t));
862         drw_2.amap  = calloc(n, sizeof(int));
863         drw_2.vx2x  = calloc(n, sizeof(int));
864         drw_2.vy2y  = calloc(n, sizeof(int));
865 
866         for (n = 0; n < drw_1.ty / 2; n++) {
867             int i;
868 
869             /* copy first column */
870             i = n * (drw_1.tx + 1);
871 
872             for (m = 0; m < drw_1.tx && dp_amap[i] != -1; m++) {
873                 drw_2.amap[o] = dp_amap[i];
874                 drw_2.cmap[o] = dp_cmap[i];
875                 drw_2.vx2x[o] = dp_vx2x[i];
876                 drw_2.vy2y[o] = dp_vy2y[i];
877 
878                 i++;
879                 o++;
880             }
881 
882             /* fill up to next column with spaces */
883             for (; m < drw_1.tx; m++) {
884                 drw_2.amap[o] = drw_1.normal_attr;
885                 drw_2.cmap[o] = L' ';
886                 drw_2.vx2x[o] = dp_vx2x[i];
887                 drw_2.vy2y[o] = dp_vy2y[i];
888 
889                 o++;
890             }
891 
892             /* put the column separator */
893             drw_2.amap[o] = drw_1.normal_attr;
894             drw_2.cmap[o] = L'\x2502';
895             o++;
896 
897             /* copy the second column */
898             i = (n - 1 + drw_1.ty / 2) * (drw_1.tx + 1);
899 
900             for (m = 0; m < drw_1.tx; m++) {
901                 drw_2.amap[o] = dp_amap[i];
902                 drw_2.cmap[o] = dp_cmap[i];
903                 drw_2.vx2x[o] = dp_vx2x[i];
904                 drw_2.vy2y[o] = dp_vy2y[i];
905 
906                 i++;
907                 o++;
908             }
909         }
910 
911         /* restore size */
912         drw_1.tx *= 2;
913         drw_1.ty /= 2;
914 
915         free(dp_cmap);
916         free(dp_amap);
917     }
918 }
919 
920 
drw_remap_to_array(void)921 static mpdm_t drw_remap_to_array(void)
922 /* converts the char-mapped buffer to arrays of attr, string per line */
923 {
924     mpdm_t r = MPDM_A(0);
925     wchar_t *line = malloc((drw_1.tx + 1) * sizeof(wchar_t));
926     int my;
927     mpdm_t fmt = NULL;
928 
929     if (drw_1.xoffset) {
930         fmt = mpdm_ref(mpdm_strcat(MPDM_S(L" %"),
931             mpdm_strcat_wcs(MPDM_I(drw_1.xoffset - 2), L"d ")));
932     }
933 
934     for (my = 0; my < drw_1.ty; my++) {
935         mpdm_t l = MPDM_A(0);
936         int o = my * (drw_1.tx + 1);
937         int mx = 0;
938 
939         if (drw_1.xoffset && drw_1.vy + my < drw_1.t_lines) {
940             mpdm_push(l, MPDM_I(drw_1.normal_attr));
941             mpdm_push(l, mpdm_fmt(fmt, MPDM_I(drw_1.vy + 1 + my)));
942         }
943 
944         while (mx < drw_1.tx && drw_2.amap[o] != -1) {
945             int i = 0;
946             int a = drw_2.amap[o];
947 
948             while (a == drw_2.amap[o] && mx++ < drw_1.tx)
949                 line[i++] = drw_2.cmap[o++];
950 
951             line[i] = L'\0';
952 
953             l = drw_push_pair(l, i, a, line);
954         }
955 
956         mpdm_push(r, l);
957     }
958 
959     free(line);
960 
961     mpdm_unref(fmt);
962 
963     return r;
964 }
965 
966 
drw_optimize_array(mpdm_t a,int optimize)967 static mpdm_t drw_optimize_array(mpdm_t a, int optimize)
968 /* optimizes the array, NULLifying all lines that are the same as the last time */
969 {
970     mpdm_t o = drw_2.old;
971     mpdm_t r = a;
972 
973     mpdm_ref(a);
974 
975     if (optimize && o != NULL) {
976         int n = 0;
977 
978         /* creates a copy */
979         r = mpdm_clone(a);
980 
981         mpdm_ref(r);
982 
983         /* compare each array */
984         while (n < mpdm_size(o) && n < mpdm_size(r)) {
985             /* if both lines are equal, optimize out */
986             if (mpdm_cmp(mpdm_get_i(o, n), mpdm_get_i(r, n)) == 0)
987                 mpdm_set_i(r, NULL, n);
988 
989             n++;
990         }
991 
992         mpdm_unrefnd(r);
993     }
994 
995     mpdm_store(&drw_2.old, a);
996 
997     mpdm_unref(a);
998 
999     return r;
1000 }
1001 
1002 
drw_restore_attrs(void)1003 static void drw_restore_attrs(void)
1004 /* restored the patched attrs */
1005 {
1006     /* matching paren, if any */
1007     if (drw_2.matchparen_offset != -1)
1008         drw_fill_attr(drw_2.matchparen_o_attr, drw_2.matchparen_offset, 1);
1009 
1010     /* cursor */
1011     drw_fill_attr(drw_2.cursor_o_attr, drw_2.cursor, 1);
1012 
1013     /* marked block, if any */
1014     if (drw_2.mark_o_attr != NULL) {
1015         memcpy(&drw_2.attrs[drw_2.mark_offset], drw_2.mark_o_attr,
1016                drw_2.mark_size);
1017 
1018         free(drw_2.mark_o_attr);
1019         drw_2.mark_o_attr = NULL;
1020     }
1021 }
1022 
1023 
drw_draw(mpdm_t doc,int optimize)1024 static mpdm_t drw_draw(mpdm_t doc, int optimize)
1025 /* main document drawing function: takes a document and returns an array of
1026    arrays of attribute / string pairs */
1027 {
1028     mpdm_t r = NULL, w;
1029 
1030     if (drw_prepare(doc)) {
1031         /* colorize separate words */
1032         drw_words();
1033 
1034         /* colorize multiline blocks */
1035         drw_blocks();
1036     }
1037 
1038     /* now set the marked block (if any) */
1039     drw_selection();
1040 
1041     /* colorize the search hit */
1042     drw_search_hit();
1043 
1044     /* the cursor */
1045     drw_cursor();
1046 
1047     /* highlight the matching paren */
1048     drw_matching_paren();
1049 
1050     /* convert to an array of string / atribute pairs */
1051     drw_remap_truncate();
1052 
1053     drw_double_page();
1054 
1055     r = drw_remap_to_array();
1056 
1057     /* optimize */
1058     r = drw_optimize_array(r, optimize);
1059 
1060     /* restore the patched attrs */
1061     drw_restore_attrs();
1062 
1063     w = mpdm_get_wcs(MP, L"window");
1064     mpdm_set_wcs(w, MPDM_I(drw_2.mx), L"mx");
1065     mpdm_set_wcs(w, MPDM_I(drw_2.my), L"my");
1066 
1067     return r;
1068 }
1069 
1070 
1071 /** interface **/
1072 
mp_draw(mpdm_t doc,int optimize)1073 mpdm_t mp_draw(mpdm_t doc, int optimize)
1074 /* main generic drawing function for drivers */
1075 {
1076     mpdm_t f, r = NULL;
1077     static mpdm_t d = NULL;
1078 
1079     if (doc != d) {
1080         optimize = 0;
1081         mpdm_store(&d, doc);
1082     }
1083 
1084     if ((f = mpdm_get_wcs(doc, L"render")) != NULL) {
1085         /* create a context to contain the object itself
1086            (i.e. call as a method) */
1087         mpdm_t ctxt = MPDM_A(0);
1088 
1089         mpdm_push(ctxt, doc);
1090         r = mpdm_exec_2(f, doc, MPDM_I(optimize), ctxt);
1091     }
1092 
1093     return r;
1094 }
1095 
1096 
1097 #define THR_SPEED_STEP  10
1098 #define THR_MAX_SPEED   7
1099 
mp_keypress_throttle(int keydown)1100 int mp_keypress_throttle(int keydown)
1101 /* processes key acceleration and throttle */
1102 {
1103 #ifdef USE_THROTTLE
1104     static int keydowns = 0;
1105     static int seq = 0;
1106     int redraw = 0;
1107 
1108     if (keydown) {
1109         int speed;
1110 
1111         /* as keydowns accumulate, speed increases, which is the number
1112            of cycles the redraw will be skipped (up to a maximum) */
1113         if ((speed = 1 + (++keydowns / THR_SPEED_STEP)) > THR_MAX_SPEED)
1114             speed = THR_MAX_SPEED;
1115 
1116         if (++seq % speed == 0)
1117             redraw = 1;
1118     }
1119     else {
1120         if (keydowns > 1)
1121             redraw = 1;
1122 
1123         keydowns = 0;
1124     }
1125 
1126     return redraw;
1127 
1128 #else /* USE_THROTTLE */
1129     return 1;
1130 #endif /* USE_THROTTLE */
1131 }
1132 
1133 
mp_active(void)1134 mpdm_t mp_active(void)
1135 /* interface to mp.active() */
1136 {
1137     return mpdm_exec(mpdm_get_wcs(MP, L"active"), NULL, NULL);
1138 }
1139 
1140 
mp_process_action(mpdm_t action)1141 void mp_process_action(mpdm_t action)
1142 /* interface to mp.process_action() */
1143 {
1144     mpdm_void(mpdm_exec_1(mpdm_get_wcs(MP, L"process_action"), action, NULL));
1145 }
1146 
1147 
mp_process_event(mpdm_t keycode)1148 void mp_process_event(mpdm_t keycode)
1149 /* interface to mp.process_event() */
1150 {
1151     mpdm_void(mpdm_exec_1(mpdm_get_wcs(MP, L"process_event"), keycode, NULL));
1152 }
1153 
1154 
mp_set_y(mpdm_t doc,int y)1155 void mp_set_y(mpdm_t doc, int y)
1156 /* interface to mp.set_y() */
1157 {
1158     mpdm_void(mpdm_exec_2(mpdm_get_wcs(doc, L"set_y"), doc, MPDM_I(y), NULL));
1159 }
1160 
1161 
mp_build_status_line(void)1162 mpdm_t mp_build_status_line(void)
1163 /* interface to mp.build_status_line() */
1164 {
1165     return mpdm_exec(mpdm_get_wcs(MP, L"build_status_line"), NULL, NULL);
1166 }
1167 
1168 
mp_get_history(mpdm_t key)1169 mpdm_t mp_get_history(mpdm_t key)
1170 /* interface to mp.get_history() */
1171 {
1172     return mpdm_exec_1(mpdm_get_wcs(MP, L"get_history"), key, NULL);
1173 }
1174 
1175 
mp_get_doc_names(void)1176 mpdm_t mp_get_doc_names(void)
1177 /* interface to mp.get_doc_names() */
1178 {
1179     return mpdm_exec(mpdm_get_wcs(MP, L"get_doc_names"), NULL, NULL);
1180 }
1181 
1182 
mp_menu_label(mpdm_t action)1183 mpdm_t mp_menu_label(mpdm_t action)
1184 /* interface to mp.menu_label() */
1185 {
1186     return mpdm_exec_1(mpdm_get_wcs(MP, L"menu_label"), action, NULL);
1187 }
1188 
1189 
mp_c_exit(mpdm_t args,mpdm_t ctxt)1190 mpdm_t mp_c_exit(mpdm_t args, mpdm_t ctxt)
1191 /* exit the editor (set mp_exit_requested) */
1192 {
1193     mp_exit_requested = 1;
1194 
1195     return NULL;
1196 }
1197 
1198 
mp_c_exit_requested(mpdm_t args,mpdm_t ctxt)1199 static mpdm_t mp_c_exit_requested(mpdm_t args, mpdm_t ctxt)
1200 /* returns the value of the mp_exit_requested variable */
1201 {
1202     return MPDM_I(mp_exit_requested);
1203 }
1204 
1205 
mp_c_render(mpdm_t args,mpdm_t ctxt)1206 mpdm_t mp_c_render(mpdm_t args, mpdm_t ctxt)
1207 {
1208     return drw_draw(mpdm_get_i(args, 0), mpdm_ival(mpdm_get_i(args, 1)));
1209 }
1210 
1211 
mp_c_vx2x(mpdm_t args,mpdm_t ctxt)1212 mpdm_t mp_c_vx2x(mpdm_t args, mpdm_t ctxt)
1213 /* interface to drw_vx2x() */
1214 {
1215     return MPDM_I(drw_vx2x(mpdm_get_i(args, 0), mpdm_ival(mpdm_get_i(args, 1))));
1216 }
1217 
1218 
mp_c_x2vx(mpdm_t args,mpdm_t ctxt)1219 mpdm_t mp_c_x2vx(mpdm_t args, mpdm_t ctxt)
1220 /* interface to drw_x2vx() */
1221 {
1222     return MPDM_I(drw_x2vx(mpdm_get_i(args, 0), mpdm_ival(mpdm_get_i(args, 1))));
1223 }
1224 
1225 
mp_c_vpos2pos(mpdm_t args,mpdm_t ctxt)1226 mpdm_t mp_c_vpos2pos(mpdm_t args, mpdm_t ctxt)
1227 {
1228     mpdm_t r = MPDM_A(2);
1229     int x = mpdm_ival(mpdm_get_i(args, 0));
1230     int y = mpdm_ival(mpdm_get_i(args, 1));
1231 
1232     vpos2pos(x, y, &x, &y);
1233 
1234     mpdm_set_i(r, MPDM_I(x), 0);
1235     mpdm_set_i(r, MPDM_I(y), 1);
1236 
1237     return r;
1238 }
1239 
1240 
mp_c_search_hex(mpdm_t args,mpdm_t ctxt)1241 mpdm_t mp_c_search_hex(mpdm_t args, mpdm_t ctxt)
1242 /* search the hex string str in the file */
1243 {
1244     mpdm_t fd = mpdm_get_i(args, 0);
1245     mpdm_t str = mpdm_get_i(args, 1);
1246     FILE *f = mpdm_get_filehandle(fd);
1247     wchar_t *s = mpdm_string(str);
1248     int n = 0;
1249     unsigned char *ptr;
1250     off_t o;
1251     int found = 0;
1252 
1253     /* parse str into a binary buffer */
1254     ptr = malloc(wcslen(s) + 1);
1255     while (s[0] && s[1]) {
1256         char tmp[3];
1257         int c;
1258 
1259         tmp[0] = (char)s[0];
1260         tmp[1] = (char)s[1];
1261         sscanf(tmp, "%02x", &c);
1262 
1263         ptr[n++] = (unsigned char) c;
1264         s += 2;
1265     }
1266     ptr[n] = 0;
1267 
1268     /* start searching */
1269     o = ftell(f);
1270     while (!found && !feof(f)) {
1271         int c;
1272 
1273         fseek(f, o, 0);
1274         n = 0;
1275 
1276         while (!found && (c = fgetc(f)) != EOF) {
1277             if (c == ptr[n]) {
1278                 n++;
1279 
1280                 if (ptr[n] == '\0')
1281                     found = 1;
1282             }
1283             else {
1284                 o++;
1285                 break;
1286             }
1287         }
1288     }
1289 
1290     if (found)
1291         fseek(f, o, 0);
1292 
1293     free(ptr);
1294 
1295     return MPDM_I(found);
1296 }
1297 
1298 
mp_c_get_offset(mpdm_t args,mpdm_t ctxt)1299 mpdm_t mp_c_get_offset(mpdm_t args, mpdm_t ctxt)
1300 /* gets the character offset at lines[y][0] */
1301 {
1302     mpdm_t lines = mpdm_get_i(args, 0);
1303     int y = mpdm_ival(mpdm_get_i(args, 1));
1304     int n, o = 0;
1305 
1306     for (n = 0; n < y; n++) {
1307         mpdm_t v = mpdm_get_i(lines, n);
1308         wchar_t *ptr = mpdm_string(v);
1309         int z = wcslen(ptr);
1310 
1311         /* count 1 less if it ends in soft-hyphen, 1 more if not */
1312         z += (z && ptr[z - 1] == 0xad) ? -1 : 1;
1313 
1314         o += z;
1315     }
1316 
1317     return MPDM_I(o);
1318 }
1319 
1320 
mp_c_set_offset(mpdm_t args,mpdm_t ctxt)1321 mpdm_t mp_c_set_offset(mpdm_t args, mpdm_t ctxt)
1322 /* gets the x, y position of offset */
1323 {
1324     mpdm_t lines = mpdm_get_i(args, 0);
1325     int o = mpdm_ival(mpdm_get_i(args, 1));
1326     int n, y = 0;
1327     mpdm_t r;
1328 
1329     for (n = 0; n < mpdm_size(lines); n++) {
1330         mpdm_t v = mpdm_get_i(lines, n);
1331         wchar_t *ptr = mpdm_string(v);
1332         int z = wcslen(ptr);
1333 
1334         /* count 1 less if it ends in soft-hyphen, 1 more if not */
1335         z += (z && ptr[z - 1] == 0xad) ? -1 : 1;
1336 
1337         if (o <= z)
1338             break;
1339 
1340         o -= z;
1341         y++;
1342     }
1343 
1344     r = MPDM_A(2);
1345     mpdm_set_i(r, MPDM_I(o), 0);
1346     mpdm_set_i(r, MPDM_I(y), 1);
1347 
1348     return r;
1349 }
1350 
1351 
mp_c_vw_unwrap(mpdm_t args,mpdm_t ctxt)1352 mpdm_t mp_c_vw_unwrap(mpdm_t args, mpdm_t ctxt)
1353 /* unwraps lines wrapped by soft-hyphens */
1354 {
1355     mpdm_t lines = mpdm_get_i(args, 0);
1356     mpdm_t v = mpdm_get_i(args, 1);
1357     int y, o1;
1358 
1359     /* y not set? wrap the full array */
1360     if (v == NULL) {
1361         y  = 0;
1362         o1 = 0;
1363     }
1364     else {
1365         y  = mpdm_ival(v);
1366         o1 = 1;
1367     }
1368 
1369     while (y < mpdm_size(lines)) {
1370         wchar_t *ptr;
1371         int z;
1372 
1373         v = mpdm_get_i(lines, y);
1374 
1375         /* while this line ends in a soft-hyphen... */
1376         while ((ptr = mpdm_string(v)) && (z = wcslen(ptr)) > 1 && ptr[z - 1] == L'\xad') {
1377             /* join this line (without the soft-hyphen) with next line */
1378             v = mpdm_strcat(MPDM_NS(ptr, z - 1), mpdm_get_i(lines, y + 1));
1379 
1380             /* store in the array */
1381             mpdm_set_i(lines, v, y);
1382 
1383             /* delete next line */
1384             mpdm_del_i(lines, y + 1);
1385         }
1386 
1387         /* only one? done */
1388         if (o1)
1389             break;
1390 
1391         y++;
1392     }
1393 
1394     return lines;
1395 }
1396 
1397 
mp_c_vw_wrap(mpdm_t args,mpdm_t ctxt)1398 mpdm_t mp_c_vw_wrap(mpdm_t args, mpdm_t ctxt)
1399 /* rewrap lines to max */
1400 {
1401     mpdm_t lines = mpdm_get_i(args, 0);
1402     int max = mpdm_ival(mpdm_get_i(args, 1));
1403     mpdm_t v = mpdm_get_i(args, 2);
1404     int y, o1;
1405 
1406     /* convert args from lines, max, y to lines, y */
1407     mpdm_del_i(args, 1);
1408 
1409     /* unwrap first */
1410     mp_c_vw_unwrap(args, ctxt);
1411 
1412     /* y not set? wrap the full array */
1413     if (v == NULL) {
1414         y  = 0;
1415         o1 = 0;
1416     }
1417     else {
1418         y  = mpdm_ival(v);
1419         o1 = 1;
1420     }
1421 
1422     while (y < mpdm_size(lines)) {
1423         wchar_t *ptr;
1424 
1425         v = mpdm_get_i(lines, y);
1426 
1427         /* while this line is longer than max... */
1428         while ((ptr = mpdm_string(v)) && wcslen(ptr) > max) {
1429             mpdm_t w;
1430             int m = max;
1431             wchar_t *ptr2;
1432 
1433             /* wrap by word */
1434             while (m > 0 && ptr[m] == L' ')
1435                 m--;
1436             while (m > 0 && ptr[m] != L' ')
1437                 m--;
1438 
1439             if (m == 0)
1440                 m = max;
1441 
1442             /* build the first part */
1443             ptr2 = calloc((m + 3), sizeof(wchar_t));
1444             wcsncpy(ptr2, ptr, m + 1);
1445             ptr2[m + 1] = L'\xad';
1446             w = MPDM_ENS(ptr2, m + 2);
1447 
1448             /* build the second part */
1449             v = MPDM_S(&ptr[m + 1]);
1450 
1451             /* set the first part */
1452             mpdm_set_i(lines, w, y);
1453 
1454             /* insert the second part */
1455             y++;
1456             mpdm_ins(lines, v, y);
1457         }
1458 
1459         /* only one? done */
1460         if (o1)
1461             break;
1462 
1463         y++;
1464     }
1465 
1466     return lines;
1467 }
1468 
1469 
find_in_embedded_arch(mpdm_t args,mpdm_t ctxt)1470 static mpdm_t find_in_embedded_arch(mpdm_t args, mpdm_t ctxt)
1471 /* searches for embedded MPSL code */
1472 {
1473     return mpdm_read_arch_mem_s(mpdm_get_i(args, 0), ARCH_START, ARCH_END);
1474 }
1475 
1476 
ni_drv_startup(mpdm_t v)1477 mpdm_t ni_drv_startup(mpdm_t v)
1478 {
1479     return NULL;
1480 }
1481 
1482 
ni_drv_detect(int * argc,char *** argv)1483 int ni_drv_detect(int *argc, char ***argv)
1484 {
1485     int n, ret = 0;
1486 
1487     for (n = 0; n < *argc; n++) {
1488         if (strcmp(argv[0][n], "-ni") == 0 || strcmp(argv[0][n], "-F") == 0)
1489             ret = 1;
1490     }
1491 
1492     if (ret) {
1493         mpdm_t drv;
1494 
1495         drv = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"mp_drv");
1496         mpdm_set_wcs(drv, MPDM_S(L"ni"), L"id");
1497         mpdm_set_wcs(drv, MPDM_X(ni_drv_startup), L"startup");
1498     }
1499 
1500     return ret;
1501 }
1502 
1503 
mp_startup(int argc,char * argv[])1504 void mp_startup(int argc, char *argv[])
1505 {
1506     mpdm_t INC;
1507     char *ptr;
1508     mpdm_t mp_c;
1509 
1510     mpdm_startup();
1511     mpdm_set_wcs(mpdm_root(), MPDM_MBS(CONFOPT_APPNAME), L"APPID");
1512 
1513     mpsl_startup();
1514 
1515     /* reset the structures */
1516     memset(&drw_1, '\0', sizeof(drw_1));
1517     memset(&drw_1_o, '\0', sizeof(drw_1_o));
1518 
1519     /* set an initial value for drw_1.tab_size:
1520        drw_wcwidth() may be called before drw_1 being filled
1521        by drw_prepare() (when opening files from the command
1522        line and mp.config.visual_wrap == 1) */
1523     drw_1.tab_size = 1;
1524 
1525     /* new mp_c namespace (C interface) */
1526     mp_c = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"mp_c");
1527 
1528     /* version */
1529     mpdm_set_wcs(mp_c, MPDM_S(L"" VERSION),         L"VERSION");
1530     mpdm_set_wcs(mp_c, MPDM_X(mp_c_x2vx),           L"x2vx");
1531     mpdm_set_wcs(mp_c, MPDM_X(mp_c_vx2x),           L"vx2x");
1532     mpdm_set_wcs(mp_c, MPDM_X(mp_c_vpos2pos),       L"vpos2pos");
1533     mpdm_set_wcs(mp_c, MPDM_X(mp_c_exit),           L"exit");
1534     mpdm_set_wcs(mp_c, MPDM_X(mp_c_exit_requested), L"exit_requested");
1535     mpdm_set_wcs(mp_c, MPDM_X(mp_c_render),         L"render");
1536     mpdm_set_wcs(mp_c, MPDM_X(mp_c_search_hex),     L"search_hex");
1537     mpdm_set_wcs(mp_c, MPDM_X(mp_c_get_offset),     L"get_offset");
1538     mpdm_set_wcs(mp_c, MPDM_X(mp_c_set_offset),     L"set_offset");
1539     mpdm_set_wcs(mp_c, MPDM_X(mp_c_vw_unwrap),      L"vw_unwrap");
1540     mpdm_set_wcs(mp_c, MPDM_X(mp_c_vw_wrap),        L"vw_wrap");
1541 
1542     /* creates the INC (executable path) array */
1543     INC = mpdm_set_wcs(mpdm_root(), MPDM_A(0), L"INC");
1544 
1545     /* if the MP_LIBRARY_PATH environment variable is set,
1546        put it before anything else */
1547     if ((ptr = getenv("MP_LIBRARY_PATH")) != NULL)
1548         mpdm_push(INC, MPDM_MBS(ptr));
1549 
1550     /* add code library as embedded archive */
1551     mpdm_push(INC, MPDM_X(find_in_embedded_arch));
1552 
1553     /* add code library as externally installed zip */
1554     mpdm_push(INC, mpdm_strcat_wcs(
1555         mpdm_get_wcs(mpdm_root(), L"APPDIR"), L"/mp.zip"));
1556 
1557     /* add code library as externally installed tar */
1558     mpdm_push(INC, mpdm_strcat_wcs(
1559         mpdm_get_wcs(mpdm_root(), L"APPDIR"), L"/mp.tar"));
1560 
1561     if (!ni_drv_detect(&argc, &argv) && !TRY_DRIVERS()) {
1562         printf("No usable driver found; exiting.\n");
1563         exit(1);
1564     }
1565 
1566     mpsl_argv(argc, argv);
1567 }
1568 
1569 
mp_mpsl(void)1570 void mp_mpsl(void)
1571 {
1572     mpdm_t e;
1573 
1574     mpdm_void(mpsl_eval(MPDM_S(L"load('mp_core.mpsl');"), NULL, NULL));
1575 
1576     if ((e = mpdm_get_wcs(mpdm_root(), L"ERROR")) != NULL) {
1577         mpdm_write_wcs(stdout, mpdm_string(e));
1578         printf("\n");
1579     }
1580 }
1581 
1582 
mp_shutdown(void)1583 void mp_shutdown(void)
1584 {
1585     /* unref pending values */
1586     mpdm_unref(drw_1.txt);
1587     mpdm_unref(drw_2.v);
1588     mpdm_unref(drw_2.old);
1589 
1590 #ifdef DEBUG_CLEANUP
1591     mpdm_unref(mpdm_root());
1592 #endif
1593 
1594     mpsl_shutdown();
1595 }
1596 
1597 
main(int argc,char * argv[])1598 int main(int argc, char *argv[])
1599 {
1600     mp_startup(argc, argv);
1601 
1602     mp_mpsl();
1603 
1604     mp_shutdown();
1605 
1606     return 0;
1607 }
1608