1 /*
2 This file is part of MOST.
3
4 Copyright (c) 1991, 1999, 2002, 2005-2018, 2019 John E. Davis
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc., 675
18 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 #include "config.h"
21
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <ctype.h>
26 #include <slang.h>
27 #include "jdmacros.h"
28
29 #include "most.h"
30 #include "line.h"
31 #include "window.h"
32 #include "display.h"
33
34 int Most_Tab_Width = 8;
35
36 int Most_Selective_Display = 0;
37 int Most_Show_Wrap_Marker = 1;
38
39 #define IS_BYTE_PRINTABLE(b) \
40 ((((b) >= ' ') && ((b) < 0x7F)) \
41 || ((Most_UTF8_Mode == 0) && ((b) >= SLsmg_Display_Eight_Bit)))
42
43 /* take 16 binary characters and put them in displayable form */
binary_format_line(unsigned char * beg,unsigned char * end,char * buf)44 static void binary_format_line (unsigned char *beg, unsigned char *end,
45 char *buf)
46 {
47 unsigned char *b;
48 char *s, *s1;
49 unsigned char ch;
50 int count;
51
52 count = 0;
53 b = beg;
54 s = buf;
55
56 while (b < end)
57 {
58 if (count == 4)
59 {
60 *s++ = ' ';
61 count = 0;
62 }
63 count++;
64
65 ch = *b++;
66
67 if ((Most_V_Opt == 0)
68 || (ch & 0x80))
69 {
70 sprintf (s, "%02X", ch);
71 s += 2;
72 continue;
73 }
74
75 if ((ch >= ' ') && (ch < 0x7F))
76 {
77 *s++ = ' ';
78 *s++ = (char) ch;
79 continue;
80 }
81
82 *s++ = '^';
83 if (ch < ' ') ch += '@';
84 else ch = '?';
85 *s++ = ch;
86 }
87
88 s1 = buf + (9 * 4) + 4;
89 while (s < s1)
90 *s++ = ' ';
91
92 b = beg;
93 while (b < end)
94 {
95 ch = *b++;
96 if (IS_BYTE_PRINTABLE(ch))
97 {
98 *s++ = ch;
99 continue;
100 }
101 *s++ = '.';
102 }
103 *s = 0;
104 }
105
output_binary_formatted_line(void)106 static void output_binary_formatted_line (void)
107 {
108 unsigned char *beg, *end;
109 char buf[256];
110
111 beg = Most_Beg + Most_C_Offset;
112 end = beg + 16;
113
114 if (end > Most_Eob) end = Most_Eob;
115
116 sprintf (buf, "0x%08lX: ", (unsigned long) Most_C_Offset);
117 binary_format_line (beg, end, buf + 12);
118 SLsmg_write_string (buf);
119 SLsmg_erase_eol ();
120 }
121
122 /* Here *begp points to the char after \e.
123 * The general escape sequence parsed here is assumed to look like:
124 * \e[ XX ; ... m
125 * If 30 <= XX <= 37, then it specifies the foreground color
126 * If 40 <= XX <= 47, then a background color is specified
127 * If 0 <= XX <= 8, then an attribute (e.g, 8) is specified.
128 * These numbers will be encoded as:
129 * offset + (FG-30 + 8*(BG-40 + 9*attribute))
130 */
most_parse_color_escape(unsigned char ** begp,unsigned char * end,int * colorp)131 int most_parse_color_escape (unsigned char **begp, unsigned char *end, int *colorp)
132 {
133 unsigned char *beg = *begp;
134 int fg = 38, bg = 48, at = 0;
135 int xx;
136
137 if ((beg >= end) || (*beg != '['))
138 return -1;
139
140 beg++; /* skip [ */
141 #if 1
142 if ((beg < end) && (*beg == 'K'))
143 {
144 if (colorp != NULL) *colorp = -1;
145 *begp = beg + 1;
146 return 0;
147 }
148 #endif
149
150 while (1)
151 {
152 xx = 0;
153 while ((beg < end) && isdigit (*beg))
154 {
155 xx = xx*10 + (*beg - '0');
156 beg++;
157 }
158 if ((xx >= 0) && (xx <= 8))
159 at = xx;
160 else if ((xx >= 20) && (xx <= 28))
161 xx = 0;
162 else if ((xx >= 30) && (xx <= 37))
163 fg = xx;
164 else if ((xx >= 40) && (xx <= 47))
165 bg = xx;
166 else return -1;
167
168 if ((beg < end) && (*beg == ';'))
169 {
170 beg++;
171 continue;
172 }
173
174 if ((beg < end) && ((*beg == 'm') || (*beg == ']')))
175 {
176 *begp = beg + 1;
177 if (colorp != NULL)
178 {
179 if ((fg != 38) || (bg != 48))
180 xx = ((fg-30) + 9*((bg-40) + 9*at));
181 if (xx != 0)
182 xx += MOST_EMBEDDED_COLOR_OFFSET;
183 *colorp = xx;
184 }
185 return 0;
186 }
187 return -1;
188 }
189 }
190
191 typedef struct
192 {
193 unsigned char *bytes;
194 unsigned char byte; /* used if bytes is NULL */
195 unsigned int len;
196 int color;
197 }
198 Multibyte_Cell_Type;
199
most_analyse_line(unsigned char * begg,unsigned char * endd,Multibyte_Cell_Type * cells,unsigned int num_cols,int * start_colorp)200 static int most_analyse_line (unsigned char *begg, unsigned char *endd,
201 Multibyte_Cell_Type *cells, unsigned int num_cols, int *start_colorp)
202 {
203 unsigned char *beg, *end;
204 unsigned int min_col, max_col, prev_width;
205 unsigned int col, max_col_reached;
206 int default_attr;
207 Multibyte_Cell_Type *cell, *max_cell;
208
209 beg = begg;
210 end = endd;
211 col = max_col_reached = 0;
212 cell = cells;
213 max_cell = cell;
214 min_col = Most_Column - 1;
215 max_col = min_col + num_cols;
216
217 default_attr = *start_colorp;
218 prev_width = 1;
219 while (beg < end)
220 {
221 int attr = default_attr;
222 unsigned char ch;
223 unsigned char *pch = beg++;
224 char buf[16];
225
226 if ('\n' == (ch = *pch))
227 break;
228
229 if ((ch == '\r') && (Most_V_Opt == 0))
230 {
231 if (col > max_col_reached) max_col_reached = col;
232 col = 0;
233 prev_width = 1;
234 continue;
235 }
236
237 if ((ch == '\b') && (Most_V_Opt == 0))
238 {
239 if (col > max_col_reached) max_col_reached = col;
240 if (col < prev_width)
241 col = 0;
242 else
243 col -= prev_width;
244 continue;
245 }
246
247 if (col < max_col_reached) /* overstrike */
248 {
249 attr = MOST_BOLD_COLOR;
250 if ((col >= min_col) && (col < max_col))
251 {
252 cell = cells + (col-min_col);
253 if (cell->bytes[0] == '_')
254 attr = MOST_ULINE_COLOR;
255 else if (ch == '_')
256 {
257 cell->color = MOST_ULINE_COLOR;
258 col++;
259 continue;
260 }
261 }
262 /* drop */
263 }
264
265 if (IS_BYTE_PRINTABLE(ch))
266 {
267 if ((col >= min_col) && (col < max_col))
268 {
269 cell = cells + (col-min_col);
270 cell->bytes = pch;
271 cell->len = 1;
272 cell->color = attr;
273 if (cell >= max_cell)
274 max_cell = cell + 1;
275 }
276 col++;
277 prev_width = 1;
278 continue;
279 }
280
281 if ((ch == '\t') && (Most_T_Opt == 0) && (Most_Tab_Width))
282 {
283 int nspaces = Most_Tab_Width * (col/Most_Tab_Width + 1) - col;
284 prev_width = nspaces;
285 while (nspaces > 0)
286 {
287 if ((col >= min_col) && (col < max_col))
288 {
289 cell = cells + (col-min_col);
290 cell->bytes = &cell->byte;
291 cell->byte = ' ';
292 cell->color = attr;
293 cell->len = 1;
294 if (cell >= max_cell)
295 max_cell = cell + 1;
296 }
297 col++;
298 nspaces--;
299 }
300 continue;
301 }
302 #if 1
303 if ((ch == 033) && (Most_V_Opt == 0))
304 {
305 int color;
306 if (0 == most_parse_color_escape (&beg, end, &color))
307 {
308 if (color != -1) default_attr = color;
309 continue;
310 }
311 /* drop */
312 }
313 #endif
314
315 if (ch & 0x80)
316 {
317 SLwchar_Type wch;
318 if ((Most_UTF8_Mode)
319 && (NULL != SLutf8_decode (pch, end, &wch, NULL)))
320 {
321 int width = SLwchar_wcwidth (wch);
322 beg = SLutf8_skip_chars (pch, end, 1, NULL, 1);
323
324 prev_width = width;
325 if (width == 0)
326 {
327 col--;
328 if ((col >= min_col) && (col < max_col))
329 {
330 cell = cells + (col-min_col);
331 cell->len += beg-pch;
332 }
333 col++;
334 continue;
335 }
336
337 if ((col >= min_col) && (col < max_col))
338 {
339 cell = cells + (col-min_col);
340 cell->bytes = pch;
341 cell->color = attr;
342 cell->len = beg - pch;
343 if (cell >= max_cell)
344 max_cell = cell + 1;
345 }
346 col++;
347 if (width > 1)
348 {
349 if ((col >= min_col) && (col < max_col))
350 {
351 cell = cells + (col-min_col);
352 cell->bytes = pch;
353 cell->color = attr;
354 cell->len = 0;
355 if (cell >= max_cell)
356 max_cell = cell + 1;
357 }
358 col++;
359 }
360 continue;
361 }
362
363 /* Otherwise, this displays as <XX> and takes up 4 character cells */
364 sprintf (buf, "<%02X>", (unsigned int) ch);
365 prev_width = 4;
366 /* drop */
367 }
368 else
369 {
370 /* Otherwise we have a Ctrl-char displayed as ^X */
371 if (ch == 0x7F) ch = '?';
372 else ch += '@';
373
374 sprintf (buf, "^%c", ch);
375 prev_width = 2;
376 }
377
378 pch = (unsigned char *)buf;
379 while (*pch)
380 {
381 if ((col >= min_col) && (col < max_col))
382 {
383 cell = cells + (col-min_col);
384 cell->bytes = &cell->byte;
385 cell->byte = *pch;
386 cell->color = attr;
387 cell->len = 1;
388 if (cell >= max_cell)
389 max_cell = cell + 1;
390 }
391 col++;
392 pch++;
393 }
394 }
395
396 if (col < max_col_reached)
397 col = max_col_reached;
398 else
399 max_col_reached = col;
400
401 /* Now add "..." if selective display. To do that, the next line needs to
402 * be dealt with to determine whether or not it will be hidden.
403 */
404 if (Most_Selective_Display
405 && (Most_W_Opt == 0)
406 && (beg < Most_Eob)
407 && ((col >= min_col) && (col < max_col)))
408 {
409 if (*beg == '\n') beg++;
410
411 while ((beg < Most_Eob)
412 && ((*beg == ' ') || (*beg == '\t') || (*beg == '\r')))
413 beg++;
414
415 if ((beg >= Most_Eob) || (*beg == '\n')
416 || (most_apparant_distance(beg) >= Most_Selective_Display))
417 {
418 max_col_reached = col + 3;
419 while (col < max_col_reached)
420 {
421 if (col < max_col)
422 {
423 cell = cells + (col-min_col);
424 cell->bytes = &cell->byte;
425 cell->byte = '.';
426 cell->color = 0;
427 cell->len = 1;
428 if (cell >= max_cell)
429 max_cell = cell + 1;
430 }
431 col++;
432 }
433 }
434 }
435 *start_colorp = default_attr;
436 return max_cell - cells;
437 }
438
display_cells(Multibyte_Cell_Type * cell,unsigned int n,char dollar)439 static void display_cells (Multibyte_Cell_Type *cell, unsigned int n, char dollar)
440 {
441 Multibyte_Cell_Type *cell_max;
442 int last_color = -1;
443
444 cell_max = cell + n;
445 while (cell < cell_max)
446 {
447 if (last_color != cell->color)
448 {
449 last_color = cell->color;
450 SLsmg_set_color (last_color);
451 }
452 SLsmg_write_chars (cell->bytes, cell->bytes + cell->len);
453 cell++;
454 }
455
456 if (last_color != 0)
457 SLsmg_set_color (0);
458
459 SLsmg_erase_eol ();
460 if (dollar)
461 {
462 SLsmg_gotorc (SLsmg_get_row (), SLtt_Screen_Cols-1);
463 SLsmg_write_nchars (&dollar, 1);
464 }
465 }
466
most_display_line(int reset)467 void most_display_line (int reset)
468 {
469 unsigned char *beg, *end;
470 unsigned char dollar;
471 static Multibyte_Cell_Type *cells;
472 static unsigned int num_cells;
473 unsigned int screen_cols;
474 unsigned int num_cells_set;
475 static int last_color = 0; /* used for a line that wrapped */
476
477 if (Most_B_Opt)
478 {
479 output_binary_formatted_line ();
480 return;
481 }
482
483 screen_cols = SLtt_Screen_Cols;
484 if (num_cells != screen_cols + 1)
485 {
486 num_cells = screen_cols + 1;
487
488 SLfree ((char *) cells);
489 if (NULL == (cells = (Multibyte_Cell_Type *)SLcalloc (num_cells, sizeof (Multibyte_Cell_Type))))
490 most_exit_error ("Out of memory");
491 }
492
493 (void) most_extract_line (&beg, &end);
494
495 if (reset || (Most_W_Opt == 0))
496 last_color = 0;
497 num_cells_set = most_analyse_line (beg, end, cells, num_cells, &last_color);
498
499 dollar = 0;
500 if (Most_W_Opt)
501 {
502 if (Most_Show_Wrap_Marker
503 && (end < Most_Eob)
504 && (*end != '\n'))
505 dollar = '\\';
506 }
507 else if (num_cells_set > screen_cols)
508 dollar = '$';
509
510 display_cells (cells, num_cells_set, dollar);
511 }
512
513 /* given a position in a line, return apparent distance from bol
514 expanding tabs, etc... up to pos */
most_apparant_distance(unsigned char * pos)515 int most_apparant_distance (unsigned char *pos)
516 {
517 int i, prev_width;
518 unsigned char *save_pos, ch;
519 unsigned int save_offset;
520
521 save_offset = Most_C_Offset;
522 save_pos = pos;
523 Most_C_Offset = (unsigned int) (pos - Most_Beg);
524 pos = most_beg_of_line();
525 Most_C_Offset = save_offset;
526
527 i = 0;
528 prev_width = 1;
529 while (pos < save_pos)
530 {
531 ch = *pos++;
532 if (IS_BYTE_PRINTABLE(ch))
533 {
534 i++;
535 prev_width = 1;
536 continue;
537 }
538
539 if ((ch == '\b') && (Most_V_Opt == 0))
540 {
541 i -= prev_width;
542 if (i < 0) i = 0;
543 continue;
544 }
545 if ((ch == '\r') && (Most_V_Opt == 0))
546 {
547 if (i != 1) i = 0;
548 prev_width = 1;
549 continue;
550 }
551 if ((ch == '\t') && (Most_T_Opt == 0))
552 {
553 prev_width = Most_Tab_Width * (i/Most_Tab_Width + 1) - i; /* Most_Tab_Width column tabs */
554 i += prev_width;
555 continue;
556 }
557
558 if ((ch == 033) && (Most_V_Opt == 0)
559 && (0 == most_parse_color_escape (&pos, save_pos, NULL)))
560 continue;
561
562 if (ch & 0x80)
563 {
564 SLwchar_Type wch;
565 if ((Most_UTF8_Mode)
566 && (NULL != SLutf8_decode (pos-1, save_pos, &wch, NULL)))
567 {
568 prev_width = SLwchar_wcwidth (wch);
569 pos = SLutf8_skip_chars (pos-1, save_pos, 1, NULL, 1);
570 i += prev_width;
571 continue;
572 }
573 prev_width = 4;
574 i += prev_width; /* <XX> */
575 continue;
576 }
577
578 prev_width = 2;
579 i += prev_width; /* ^X */
580 }
581 return i;
582 }
583
584 /*
585 * Returns a pointer to the num_cols'th character after the one
586 * pointed at b. Invisible character runs are not counted toward this
587 * limit, i.e. strings that represent attributes, such as "_\b" for
588 * underlines.
589 *
590 * If multi_column is non-zero, characters spanning more than one
591 * column will add their extra width to the column count.
592 *
593 * If there the end of the buffer is reached, as delimited by argument
594 * e, then e is returned.
595 */
most_forward_columns(unsigned char * b,unsigned char * e,unsigned int num_cols)596 unsigned char *most_forward_columns (unsigned char *b, unsigned char *e, unsigned int num_cols)
597 {
598 unsigned int col = 0;
599 unsigned int prev_width = 1;
600
601 while (b < e)
602 {
603 unsigned char ch = *b++;
604
605 if (col >=num_cols)
606 {
607 if ((ch == 033) && (Most_V_Opt == 0))
608 {
609 while ((ch == 033)
610 && (0 == most_parse_color_escape (&b, e, NULL))
611 && (b < e))
612 ch = *b++;
613 }
614 b--;
615 break;
616 }
617
618 if (IS_BYTE_PRINTABLE(ch))
619 {
620 col++;
621 prev_width = 1;
622 continue;
623 }
624
625 if (ch & 0x80)
626 {
627 SLwchar_Type wch;
628
629 if ((Most_UTF8_Mode)
630 && (NULL != SLutf8_decode (b-1, e, &wch, NULL)))
631 {
632 b = SLutf8_skip_chars (b-1, e, 1, NULL, 1);
633 prev_width = SLwchar_wcwidth (wch);
634 col += prev_width;
635 continue;
636 }
637 prev_width = 4;
638 col += prev_width; /* <XX> */
639 continue;
640 }
641
642 if (ch == '\b')
643 {
644 if (Most_V_Opt == 0)
645 {
646 if (col < prev_width)
647 col = 0;
648 else
649 col -= prev_width;
650 }
651 else col += 2; /* ^H */
652 continue;
653 }
654
655 if (ch == '\r')
656 {
657 if (Most_V_Opt == 0)
658 {
659 prev_width = 1;
660 col = 0;
661 }
662 else col += 2; /* ^M */
663 continue;
664 }
665
666 if (ch == '\t')
667 {
668 if (Most_T_Opt == 0)
669 {
670 prev_width = Most_Tab_Width * (col/Most_Tab_Width + 1) - col;
671 col += prev_width;
672 }
673 else
674 col += 2; /* ^I */
675 continue;
676 }
677
678 if ((ch == 033) && (Most_V_Opt == 0)
679 && (0 == most_parse_color_escape (&b, e, NULL)))
680 continue;
681
682 /* Ctrl-char ^X */
683 prev_width = 2;
684 col += prev_width;
685 }
686 return b;
687 }
688