1 /*======================================================================*\
2 |* Editor mined *|
3 |* Text editing functions *|
4 \*======================================================================*/
5
6 #include "mined.h"
7 #include "charprop.h"
8 #include "textfile.h" /* default_lineend, code_NL, code_LF */
9 #include "io.h"
10 #include "termprop.h" /* utf8_input */
11
12
13 /*======================================================================*\
14 |* Language detection *|
15 \*======================================================================*/
16
17 #define Turkish (language_tag == 't') /* locale begins with tr, az */
18 #define Lithuanian (language_tag == 'l') /* locale begins with lt */
19 #define Dutch (language_tag == 'n') /* locale begins with nl */
20
21
22 /*======================================================================*\
23 |* Local function forward declarations *|
24 \*======================================================================*/
25
26 static void S _((character newchar));
27
28
29 /*======================================================================*\
30 |* Global variables *|
31 \*======================================================================*/
32
33 FLAG redraw_pending = False; /* was a redraw suppressed in find_y ? */
34 FLAG suppress_pasting_double_LF = False; /* filter multiple LF (from Exceed) */
35
36
37 /*======================================================================*\
38 |* Local variables *|
39 \*======================================================================*/
40
41 static character lastchar = '\0';
42 static FLAG lastspace_smart = False;
43
44
45 /*======================================================================*\
46 |* Aux *|
47 \*======================================================================*/
48
49 int
ishex(c)50 ishex (c)
51 character c;
52 {
53 if (ebcdic_text) {
54 return (c >= 0xF0 && c <= 0xF9)
55 || (c >= 0x81 && c <= 0x86)
56 || (c >= 0xC1 && c <= 0xC6);
57 }
58 return (c >= '0' && c <= '9')
59 || (c >= 'A' && c <= 'F')
60 || (c >= 'a' && c <= 'f');
61 }
62
63 unsigned int
hexval(c)64 hexval (c)
65 character c;
66 {
67 if (ebcdic_text) {
68 if (c >= 0xF0 && c <= 0xF9) {
69 return c - 0xF0;
70 } else if (c >= 0x81 && c <= 0x86) {
71 return c - 0x81 + 10;
72 } else if (c >= 0xC1 && c <= 0xC6) {
73 return c - 0xC1 + 10;
74 } else {
75 return 0;
76 }
77 }
78 if (c >= '0' && c <= '9') {
79 return c - '0';
80 } else if (c >= 'A' && c <= 'F') {
81 return c - 'A' + 10;
82 } else if (c >= 'a' && c <= 'f') {
83 return c - 'a' + 10;
84 } else {
85 return 0;
86 }
87 }
88
89 static
90 FLAG
idfchar(cpoi)91 idfchar (cpoi)
92 character * cpoi;
93 {
94 unsigned long unichar = unicodevalue (cpoi);
95
96 if ('0' <= unichar && unichar <= '9') {
97 return True;
98 } else if (unichar == '_' || unichar == '$') {
99 return True;
100 } else {
101 char * cat = category (unichar);
102 return streq (cat, "Letter") || streq (cat, "Mark");
103 }
104 }
105
106
107 /**
108 charcode () returns the encoded character value of a Unicode character
109 */
110 static
111 unsigned long
charcode(code)112 charcode (code)
113 unsigned long code;
114 {
115 if (cjk_text || mapped_text) {
116 return encodedchar (code);
117 } else if (utf8_text || code < 0x100) {
118 return code;
119 } else {
120 return CHAR_INVALID;
121 }
122 }
123
124
125 /**
126 Check whether character is a item bullet.
127 */
128 static
129 FLAG
isitemchar(unich)130 isitemchar (unich)
131 unsigned long unich;
132 {
133 return is_bullet_or_dash (unich);
134 }
135
136
137 /**
138 Check whether strings starts with a numbering 1.2.3.
139 */
140
141 static char * lastsubnumberpoi;
142 static int lastsubnumber;
143 static char * last2subnumberpoi;
144 static int last2subnumber;
145
146 static
147 int
numbering(cpoi)148 numbering (cpoi)
149 char * cpoi;
150 {
151 char * cp = cpoi;
152 /*int found = 0;*/
153 FLAG ok = False;
154 /*char post = '\0';*/
155 lastsubnumberpoi = NIL_PTR;
156 do {
157 if (* cp >= '0' && * cp <= '9') {
158 int number;
159 char * lastnum = cp;
160 cp = scan_int (cp, & number);
161 /*post = * cp;*/
162 ok = False;
163 if (* cp == '.') {
164 cp ++;
165 /*found ++;*/
166 ok = True;
167
168 last2subnumberpoi = lastsubnumberpoi;
169 last2subnumber = lastsubnumber;
170 lastsubnumberpoi = lastnum;
171 lastsubnumber = number;
172 #ifdef with_parenthesized_numbering
173 #warning auto-increment / undent not implemented for parenthesized numbering
174 } else if (* cp == ')') {
175 cp ++;
176 ok = True;
177 break;
178 #endif
179 } else {
180 break;
181 }
182 } else {
183 break;
184 }
185 } while (1);
186
187 if (/*found*/ ok) {
188 return cp - cpoi;
189 } else {
190 return 0;
191 }
192 }
193
194
195 static
196 FLAG
onlywhitespacespacebefore(l,s)197 onlywhitespacespacebefore (l, s)
198 LINE * l;
199 char * s;
200 {
201 char * cpoi = l->text;
202 while (cpoi < s && iswhitespace (unicodevalue (cpoi))) {
203 advance_char (& cpoi);
204 }
205 if (cpoi >= s) {
206 return True;
207 } else {
208 return False;
209 }
210 }
211
212 static
213 FLAG
paragraphending(l,le)214 paragraphending (l, le)
215 LINE * l;
216 lineend_type le;
217 {
218 if (l == header) {
219 return True;
220 } else if (le == lineend_PS) {
221 return True;
222 } else if (le == lineend_LS) {
223 return False;
224 } else if (JUSmode == 1) { /* paragraphs end at empty line */
225 char * cpoi = l->text;
226 while (iswhitespace (unicodevalue (cpoi))) {
227 advance_char (& cpoi);
228 }
229 if (* cpoi == '\n' || * cpoi == '\0') {
230 return True;
231 } else {
232 return False;
233 }
234 } else {
235 if (strstr (l->text, " \n")) {
236 return False;
237 } else {
238 return True;
239 }
240 }
241 }
242
243
244 /*======================================================================*\
245 |* Scrolling *|
246 \*======================================================================*/
247
248 /*
249 * Perform a forward scroll. It returns ERRORS if we're at the last line of
250 * the file.
251 */
252 static
253 int
forward_scroll(update)254 forward_scroll (update)
255 FLAG update;
256 {
257 if (bot_line->next == tail) { /* Last line of file. No dice */
258 return ERRORS;
259 }
260 top_line = top_line->next;
261 bot_line = bot_line->next;
262 cur_line = cur_line->next;
263 line_number ++;
264
265 /* Perform the scroll on screen */
266 if (update) {
267 clean_menus ();
268
269 if (MENU) {
270 if (can_delete_line) {
271 delete_line (0);
272 } else {
273 scroll_forward ();
274 displaymenuline (True);
275 }
276 } else {
277 scroll_forward ();
278 }
279
280 scrollbar_scroll_up (0);
281
282 print_line (SCREENMAX, bot_line);
283 }
284
285 return FINE;
286 }
287
288 /*
289 * Perform a backwards scroll. It returns ERRORS if we're at the first line
290 * of the file. It updates the display completely if update is True.
291 * Otherwise it leaves that to the caller (page up function).
292 */
293 static
294 int
reverse_scroll(update)295 reverse_scroll (update)
296 FLAG update;
297 {
298 if (top_line->prev == header) {
299 /* Top of file. Can't scroll */
300 return ERRORS;
301 }
302
303 if (last_y != SCREENMAX) { /* Reset last_y if necessary */
304 last_y ++;
305 } else {
306 bot_line = bot_line->prev; /* Else adjust bot_line */
307 }
308 top_line = top_line->prev;
309 cur_line = cur_line->prev;
310 line_number --;
311
312 /* Perform the scroll on screen */
313 if (update) {
314 if (can_add_line || can_scroll_reverse) {
315 clean_menus ();
316 if (MENU && can_add_line) {
317 add_line (0);
318 } else {
319 set_cursor (0, - MENU);
320 scroll_reverse ();
321 }
322 scrollbar_scroll_down (0);
323 set_cursor (0, YMAX); /* Erase very bottom line */
324 clear_lastline ();
325 if (MENU && ! can_add_line) {
326 displaymenuline (True);
327 }
328 print_line (0, top_line);
329 } else {
330 display (0, top_line, last_y, y);
331 }
332 }
333
334 return FINE;
335 }
336
337
338 /*======================================================================*\
339 |* Text/line insertion and deletion *|
340 \*======================================================================*/
341
342 static
343 int
chars_in_line(line)344 chars_in_line (line)
345 LINE * line;
346 {
347 return char_count (line->text) - (line->return_type == lineend_NONE);
348 }
349
350
351 /*
352 * text_bytes_of () returns the number of bytes in the string 'string'
353 * up to and excluding the first '\n'.
354 */
355 static
356 int
text_bytes_of(string)357 text_bytes_of (string)
358 register char * string;
359 {
360 register int count = 0;
361
362 if (string != NIL_PTR) {
363 while (* string != '\0' && * string != '\n') {
364 string ++;
365 count ++;
366 }
367 }
368 return count;
369 }
370
371
372 #define dont_debug_syntax_state
373
374 #ifdef debug_syntax_state
375 #define trace_state(tag) if (next_state_on || strcmp (#tag, "cancel")) \
376 printf (#tag" {%d: %02X} %02X%c->%02X @%s", \
377 state_delay, next_state, prev, \
378 value_term ? value_term : '.', \
379 ret, * s ? s : "NUL\n")
380 #else
381 #define trace_state(tag)
382 #endif
383
384 #define future_state(st, n) next_state = st; state_delay = n; next_state_at = s + n; next_state_on = beg
385 static char next_state = 0;
386 static short state_delay = 0;
387 static char * next_state_at = NIL_PTR;
388 static char * next_state_on = NIL_PTR;
389 static char * next_state_from = NIL_PTR;
390
391 /*
392 * Determine new syntax status, given previous status and current
393 * text pointer.
394 */
395 char
syntax_state(prev,old,s,beg)396 syntax_state (prev, old, s, beg)
397 char prev;
398 char old;
399 char * s;
400 char * beg;
401 {
402 static short prev_space = 1;
403 static char value_term = ' ';
404 char was_space = prev_space;
405 char is_space = white_space (* s);
406 char ret = prev;
407
408 /* cancel future state if either of
409 – terminating line display ("")
410 – displaying different line than before
411 – displaying line again, starting before previous position
412 */
413 if (! * s
414 || beg != next_state_on
415 || s < next_state_from
416 ) {
417 prev_space = 1;
418 trace_state (cancel);
419 state_delay = 0;
420 next_state_on = NIL_PTR;
421 value_term = ' ';
422 }
423 next_state_from = s; /* detect repeated display of same line */
424
425 if (state_delay > 0) {
426 if (state_delay == 1) {
427 state_delay = 0;
428 ret = next_state;
429 prev = ret;
430 trace_state (delayed:);
431 } else {
432 state_delay --;
433 trace_state (delayed--);
434 return prev;
435 }
436 }
437
438 #ifdef state_machine
439 none html comment jsp attrib value
440 <%= jsp+val +j+v +j+v % +j+v +j+v
441 <% jsp +jsp +jsp % +jsp +jsp
442 <!-- comment % % % % %
443 < html % % % % %
444 %> % (%) % —jsp % (jsp)!—val
445 --> % (%) —comm % % %
446 > % —h—v—a % % —h!—at q?%:–h—v—a
447 q % % % % +val q?—v—a
448 = % +value % % +val—a? %
449 % !—at % % !—at q?%:!–v—a
450 ^ % +at % % % %
451 #endif
452
453 prev_space = is_space;
454
455 if (is_space) {
456 if ((prev & (syntax_scripting | syntax_value)) == syntax_value) {
457 if (value_term <= ' ') {
458 ret = prev & ~ (syntax_attrib | syntax_value);
459 }
460 } else if (prev & syntax_HTML) {
461 ret = prev & ~ syntax_attrib;
462 }
463 } else if (* s == '<') {
464 if (strncmp (s, "<!--", 4) == 0) {
465 if (! prev) {
466 ret = prev | syntax_comment;
467 }
468 } else if (strncmp (s, "<%=", 3) == 0 || strncmp (s, "<%:", 3) == 0) {
469 if (! (prev & syntax_scripting)) {
470 future_state (prev | syntax_JSP | syntax_value, 3);
471 ret = prev | syntax_JSP;
472 }
473 } else if (strncmp (s, "<%", 2) == 0) {
474 if (! (prev & syntax_scripting)) {
475 ret = prev | syntax_JSP;
476 }
477 } else if (strncmp (s, "<?", 2) == 0) {
478 if (! (prev & syntax_scripting)) {
479 ret = prev | syntax_PHP;
480 }
481 } else {
482 if (! prev && (* (s + 1) > '@' || * (s + 1) == '/')) {
483 ret = syntax_HTML;
484 }
485 }
486 #ifdef state_machine
487 none html comment jsp attrib value
488 %> % (%) % —jsp % (jsp)!—val
489 --> % (%) —comm % % %
490 > % —h—v—a % % —h!—at q?%:–h—v—a
491 q % % % % +v? q?—v—a
492 = % +value % % +v!—a? %
493 #endif
494 } else if (strncmp (s, "-->", 3) == 0) {
495 if (prev & syntax_comment) {
496 future_state (prev & ~ syntax_comment, 3);
497 }
498 } else if (strncmp (s, "%>", 2) == 0) {
499 if ((prev & (syntax_JSP | syntax_value)) == (syntax_JSP | syntax_value)) {
500 future_state (prev & ~ (syntax_JSP | syntax_value), 2);
501 ret = prev & ~ syntax_value;
502 } else if (prev & syntax_JSP) {
503 future_state (prev & ~ syntax_JSP, 2);
504 }
505 } else if (strncmp (s, "?>", 2) == 0) {
506 if (prev & syntax_PHP) {
507 future_state (prev & ~ syntax_PHP, 2);
508 }
509 } else if (* s == '>') {
510 if (prev & syntax_scripting) {
511 /* skip */
512 } else if (prev & syntax_value) {
513 if (value_term <= ' ') {
514 future_state (prev & ~ (syntax_HTML | syntax_attrib | syntax_value), 1);
515 ret = prev & ~ (syntax_attrib | syntax_value);
516 }
517 } else if (prev & syntax_attrib) {
518 future_state (prev & ~ (syntax_HTML | syntax_attrib), 1);
519 ret = prev & ~ syntax_attrib;
520 } else if (prev & syntax_HTML) {
521 future_state (prev & ~ syntax_HTML, 1);
522 }
523 } else if (* s == '"' || * s == '\'') {
524 if (prev & syntax_value) {
525 if (! value_term) {
526 value_term = * s;
527 } else if (* s == value_term) {
528 future_state (prev & ~ (syntax_attrib | syntax_value), 1);
529 }
530 }
531 } else if ((prev & syntax_attrib) && * s == '=') {
532 if (! (prev & syntax_value)) {
533 future_state ((prev & ~ syntax_attrib) | syntax_value, 1);
534 value_term = '\0';
535 ret = prev & ~ syntax_attrib;
536 }
537 } else if (was_space) {
538 if (prev & syntax_scripting) {
539 /* skip */
540 } else if ((prev & syntax_HTML) && ! (prev & syntax_value)) {
541 ret = prev | syntax_attrib;
542 }
543 } else if ((prev & syntax_value) && ! value_term) {
544 value_term = ' ';
545 }
546
547 trace_state (state);
548 return ret;
549 }
550
551 /*
552 * Determine status of HTML on this line:
553 * line->syntax_mask is a bitmask indicating in which kinds of
554 * syntax constructs the line ends
555 * If changing, continue with subsequent lines.
556 */
557 void
update_syntax_state(line)558 update_syntax_state (line)
559 LINE * line;
560 {
561 char * lpoi = line->text;
562 character syntax_mask = line->prev->syntax_mask; /* state at line begin */
563 character prev_syntax_mask = line->syntax_mask; /* previous state of line */
564 character syntax_mask_old = syntax_none;
565 character syntax_mask_new = syntax_mask;
566
567 if (mark_HTML == False &&
568 prev_syntax_mask == syntax_unknown && syntax_mask == syntax_unknown) {
569 return;
570 }
571
572 while (* lpoi != '\0') {
573 syntax_mask_new = syntax_state (syntax_mask_new, syntax_mask_old, lpoi, line->text);
574 syntax_mask_old = syntax_mask;
575 syntax_mask = syntax_mask_new;
576 advance_char (& lpoi);
577 }
578 line->syntax_mask = syntax_mask;
579 #ifdef debug_syntax_state
580 printf ("update_syntax_state %02X..%02X (was %02X) @%s", line->prev->syntax_mask, syntax_mask, prev_syntax_mask, line->text);
581 #endif
582 line->dirty = True;
583 if (syntax_mask != prev_syntax_mask && line->next != tail) {
584 #ifdef debug_syntax_state
585 printf ("...update_syntax_state @%s", line->next->text);
586 #endif
587 update_syntax_state (line->next);
588 }
589 }
590
591 #define dont_debug_out_of_memory 5
592
593 /*
594 * make_line installs the buffer into a LINE structure.
595 * It returns a pointer to the allocated structure.
596 If length < 0, it returns a static emergency line (to be used after
597 out of memory).
598 */
599 static
600 LINE *
make_line(buffer,length,return_type)601 make_line (buffer, length, return_type)
602 char * buffer;
603 int length;
604 lineend_type return_type;
605 {
606 LINE * new_line;
607 static LINE emergency_line;
608 static char emergency_text [2];
609
610 #ifdef debug_out_of_memory
611 static int count_make_line = 0;
612 if (length >= 0 && ++ count_make_line > debug_out_of_memory) {
613 return NIL_LINE;
614 }
615 #endif
616
617 if (length >= 0) {
618 new_line = alloc_header ();
619 } else {
620 new_line = & emergency_line;
621 }
622
623 if (new_line == NIL_LINE) {
624 ring_bell ();
625 error ("Cannot allocate more memory for new line header");
626 return NIL_LINE;
627 } else {
628 if (length >= 0) {
629 new_line->text = alloc (length + 1);
630 } else {
631 new_line->text = emergency_text;
632 }
633 if (new_line->text == NIL_PTR) {
634 ring_bell ();
635 error ("Cannot allocate more memory for new line");
636 return NIL_LINE;
637 } else {
638 new_line->shift_count = 0;
639 new_line->return_type = return_type;
640 if (length >= 0) {
641 new_line->allocated = True;
642 strncpy (new_line->text, buffer, length);
643 new_line->text [length] = '\0';
644 } else {
645 new_line->allocated = False;
646 strncpy (new_line->text, "\n", 2);
647 }
648 new_line->syntax_mask = syntax_unknown;
649 new_line->sel_begin = NIL_PTR;
650 new_line->sel_end = NIL_PTR;
651 new_line->dirty = False;
652 return new_line;
653 }
654 }
655 }
656
657 /*
658 * Line_insert_after () inserts a new line with text pointed to by 'string',
659 after the given line.
660 It returns the address of the new line which is appended to the previous.
661 */
662 LINE *
line_insert_after(line,string,len,return_type)663 line_insert_after (line, string, len, return_type)
664 register LINE * line;
665 char * string;
666 int len;
667 lineend_type return_type;
668 {
669 register LINE * new_line;
670
671 /* Allocate space for LINE structure and text */
672 new_line = make_line (string, len, return_type);
673
674 if (new_line != NIL_LINE) {
675 /* Install the line into the double linked list */
676 new_line->prev = line;
677 new_line->next = line->next;
678 line->next = new_line;
679 new_line->next->prev = new_line;
680
681 /* Adjust information about text attribute state (HTML marker) */
682 update_syntax_state (new_line);
683
684 /* Increment total_lines */
685 total_lines ++;
686 if (total_chars >= 0) {
687 total_chars += chars_in_line (new_line);
688 }
689 }
690 return new_line;
691 }
692
693 /*
694 * Insert_text () inserts the string 'string' at the given line and location.
695 Do not pass a string with an embedded (non-terminating) newline!
696 Make sure cur_text is properly reset afterwards! (may be left undefined)
697 */
698 int
insert_text(line,location,string)699 insert_text (line, location, string)
700 register LINE * line;
701 char * location;
702 char * string;
703 {
704 register char * bufp = text_buffer; /* Buffer for building line */
705 register char * textp = line->text;
706 char * newbuf;
707 int old_line_chars = chars_in_line (line);
708
709 if (dont_modify ()) {
710 return ERRORS;
711 }
712
713 if (length_of (textp) + text_bytes_of (string) >= maxLINElen) {
714 error ("Cannot insert properly: Line too long");
715 return ERRORS;
716 }
717
718 /* Copy part of line until 'location' has been reached */
719 while (textp != location) {
720 * bufp ++ = * textp ++;
721 }
722
723 /* Insert string at this location */
724 while (* string != '\0') {
725 * bufp ++ = * string ++;
726 }
727 * bufp = '\0';
728
729 /* First, allocate memory for next line contents to make sure the */
730 /* operation succeeds or fails as a whole */
731 newbuf = alloc (length_of (text_buffer) + length_of (location) + 1);
732 if (newbuf == NIL_PTR) {
733 ring_bell ();
734 status_fmt2 ("Out of memory - ", "Insertion failed");
735 return ERRORS;
736 } else { /* Install the new text in this line */
737 if (* (string - 1) == '\n') { /* Insert a new line */
738 lineend_type new_return_type;
739 lineend_type old_return_type = line->return_type;
740 if ((keyshift & altctrl_mask) == altctrl_mask) {
741 if (keyshift & shift_mask) {
742 line->return_type = lineend_CR;
743 } else if (default_lineend == lineend_CRLF) {
744 line->return_type = lineend_LF;
745 } else {
746 line->return_type = lineend_CRLF;
747 }
748 new_return_type = old_return_type;
749 } else if (utf8_lineends
750 && ((keyshift & ctrlshift_mask) || (hop_flag > 0)))
751 {
752 if (ebcdic_text || ebcdic_file) {
753 if (keyshift & ctrl_mask) {
754 line->return_type = lineend_LF;
755 } else {
756 line->return_type = lineend_NL1;
757 }
758 } else if (utf8_text
759 #ifdef support_gb18030_all_line_ends
760 || (cjk_text && text_encoding_tag == 'G')
761 #endif
762 ) {
763 if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
764 /* ISO 8859 NEXT LINE */
765 line->return_type = lineend_NL2;
766 } else if (keyshift & ctrl_mask) {
767 /* Unicode line separator */
768 line->return_type = lineend_LS;
769 } else {
770 /* Unicode paragraph separator */
771 line->return_type = lineend_PS;
772 }
773 } else if (! cjk_text && ! no_char (encodedchar (0x0085))) {
774 if (keyshift & ctrl_mask) {
775 /* ISO 8859 NEXT LINE */
776 line->return_type = lineend_NL1;
777 }
778 }
779 new_return_type = old_return_type;
780 } else if (old_return_type == lineend_NUL ||
781 old_return_type == lineend_NONE)
782 {
783 /* line->return_type = top_line->return_type; */
784 line->return_type = default_lineend;
785 new_return_type = old_return_type;
786 } else if (old_return_type == lineend_LS ||
787 old_return_type == lineend_PS)
788 {
789 if (hop_flag > 0) {
790 line->return_type = lineend_PS;
791 } else {
792 line->return_type = lineend_LS;
793 }
794 new_return_type = old_return_type;
795 } else {
796 new_return_type = old_return_type;
797 }
798
799 clear_highlight_selection (); /* before insertion! */
800 /* this will initially cause update_syntax_state to
801 proceed based on old contents;
802 we could try to avoid that here but it will be
803 fixed soon after anyway
804 */
805 if (line_insert_after (line, location, length_of (location),
806 new_return_type) == NIL_LINE)
807 {
808 /* restore return type of line */
809 line->return_type = old_return_type;
810
811 ring_bell ();
812 status_fmt2 ("Out of memory for new line - ", "Insertion failed");
813 return ERRORS;
814 }
815 set_modified (); /* after successful insertion! */
816 } else {
817 /* Append last part of line to text_buffer */
818 copy_string (bufp, location);
819 }
820
821 free_space (line->text);
822 set_modified ();
823 line->text = newbuf;
824 copy_string (line->text, text_buffer);
825 update_syntax_state (line);
826 if (total_chars >= 0) {
827 total_chars = total_chars + chars_in_line (line) - old_line_chars;
828 }
829 return FINE;
830 }
831 }
832
833 /*
834 * Line_delete () deletes the argument line out of the line list.
835 * The pointer to the next line is returned.
836 */
837 static
838 LINE *
line_delete(line)839 line_delete (line)
840 register LINE * line;
841 {
842 register LINE * next_line = line->next;
843
844 /* Decrement total_lines */
845 total_lines --;
846 if (total_chars >= 0) {
847 total_chars -= chars_in_line (line);
848 }
849
850 line->prev->return_type = line->return_type;
851
852 /* Delete the line */
853 line->prev->next = line->next;
854 line->next->prev = line->prev;
855
856 /* Free allocated space */
857 free_space (line->text);
858 free_header (line);
859
860 return next_line;
861 }
862
863
864 /*
865 * delete_text () deletes all the characters (including newlines) between
866 startposition and endposition and fixes the screen accordingly;
867 it displays the number of lines deleted.
868 * delete_text_only () deletes but does not update the screen.
869 Parameters start_line/start_textp and end_line/end_textp
870 must be in the correct order!
871 */
872 static
873 int
do_delete_text(start_line,start_textp,end_line,end_textp,update_screen)874 do_delete_text (start_line, start_textp, end_line, end_textp, update_screen)
875 LINE * start_line;
876 char * start_textp;
877 LINE * end_line;
878 char * end_textp;
879 FLAG update_screen;
880 {
881 register char * textp = start_line->text;
882 register char * bufp = text_buffer; /* Storage for new line->text */
883 LINE * line;
884 LINE * after_end = end_line->next;
885 int line_cnt = 0; /* Nr of lines deleted */
886 int count = 0;
887 int old_last_y;
888 int nx = x;
889 int ret = FINE;
890 char * newbuf;
891 int newpos_offset = start_textp - textp;
892 FLAG isdeleting_lastcombining = False;
893 int redraw_cols = 0;
894 int old_line_chars = chars_in_line (start_line);
895
896 if (dont_modify ()) {
897 return ret;
898 }
899
900 set_modified (); /* File will have been modified */
901
902 if (combining_mode && encoding_has_combining ()) {
903 unsigned long unichar = unicodevalue (start_textp);
904 if (iscombined (unichar, start_textp, start_line->text)) {
905 char * cp = start_textp;
906 advance_char (& cp);
907 unichar = unicodevalue (cp);
908 if (! iscombining_unichar (unichar)) {
909 isdeleting_lastcombining = True;
910 cp = start_textp;
911 do {
912 precede_char (& cp, start_line->text);
913 unichar = unicodevalue (cp);
914 } while (cp != start_line->text && iscombining_unichar (unichar));
915 if (unichar == '\t') {
916 redraw_cols = 0;
917 } else if (iswide (unichar)) {
918 redraw_cols = 2;
919 } else {
920 redraw_cols = 1;
921 }
922 }
923 }
924 }
925
926 /* Set up new line. Copy first part of start line until start_position. */
927 while (textp < start_textp) {
928 * bufp ++ = * textp ++;
929 count ++;
930 }
931
932 /* Check if line doesn't exceed maxLINElen */
933 if (count + length_of (end_textp) >= maxLINElen) {
934 error ("Cannot delete properly: Remaining line too long");
935 return ret;
936 }
937
938 /* Copy last part of end_line if end_line is not tail */
939 copy_string (bufp, (end_textp != NIL_PTR) ? end_textp : "\n");
940
941 /* Delete all lines between start and end_position (including end_line) */
942 line = start_line->next;
943 while (line != after_end && line != tail) {
944 /* Here, the original mined compared with end_line->next which has
945 already been discarded when the comparison should become true.
946 This severe error remained undetected until I ported to MSDOS */
947 line = line_delete (line);
948 line_cnt ++;
949 }
950
951 /* Check if last line of file should be deleted */
952 if (end_textp == NIL_PTR && length_of (start_line->text) == 1 && total_lines > 1) {
953 start_line = start_line->prev;
954 (void) line_delete (start_line->next);
955 line_cnt ++;
956 } else { /* Install new text */
957 newbuf = alloc (length_of (text_buffer) + 1);
958 if (newbuf == NIL_PTR) {
959 ring_bell ();
960 error ("No more memory after deletion");
961 ret = ERRORS;
962 } else {
963 free_space (start_line->text);
964 start_line->text = newbuf;
965 copy_string (start_line->text, text_buffer);
966 update_syntax_state (start_line);
967 }
968 }
969
970 if (total_chars >= 0) {
971 total_chars = total_chars + chars_in_line (start_line) - old_line_chars;
972 }
973
974 if (! update_screen) {
975 return ret;
976 }
977
978 /* Update screen */
979 if (line_cnt == 0) { /* Check if only one line changed */
980 /* fix position */
981 move_address (cur_line->text + newpos_offset, y);
982 /* display last part of line */
983 if (isdeleting_lastcombining) {
984 if (redraw_cols == 0 || proportional) {
985 print_line (y, start_line);
986 } else {
987 set_cursor (x - redraw_cols, y);
988 put_line (y, start_line, x - redraw_cols, True, False);
989 }
990 } else {
991 put_line (y, start_line, x, True, False);
992 }
993
994 set_cursor_xy ();
995 return ret;
996 }
997
998 old_last_y = last_y;
999 reset (top_line, y);
1000 if ((line_cnt <= SCREENMAX - y) && can_delete_line) {
1001 clear_status ();
1002 display (y, start_line, 0, y);
1003 line = proceed (start_line, SCREENMAX - y - line_cnt + 1);
1004 while (line_cnt -- > 0) {
1005 delete_line (y + 1);
1006 scrollbar_scroll_up (y + 1);
1007 if (line != tail) {
1008 print_line (SCREENMAX, line);
1009 line = line->next;
1010 }
1011 }
1012 } else {
1013 display (y, start_line, old_last_y - y, y);
1014 }
1015 move_to (nx, y);
1016 return ret;
1017 }
1018
1019 int
delete_text(start_line,start_textp,end_line,end_textp)1020 delete_text (start_line, start_textp, end_line, end_textp)
1021 LINE * start_line;
1022 char * start_textp;
1023 LINE * end_line;
1024 char * end_textp;
1025 {
1026 return do_delete_text (start_line, start_textp, end_line, end_textp, True);
1027 }
1028
1029 int
delete_text_only(start_line,start_textp,end_line,end_textp)1030 delete_text_only (start_line, start_textp, end_line, end_textp)
1031 LINE * start_line;
1032 char * start_textp;
1033 LINE * end_line;
1034 char * end_textp;
1035 {
1036 return do_delete_text (start_line, start_textp, end_line, end_textp, False);
1037 }
1038
1039
1040 /*======================================================================*\
1041 |* Move commands *|
1042 \*======================================================================*/
1043
1044 /*
1045 * Move left one Unicode character (may enter into combined character).
1046 */
1047 static
1048 void
ctrl_MLF()1049 ctrl_MLF ()
1050 {
1051 char * curpoi;
1052
1053 if (hop_flag > 0) {
1054 MOVLBEG ();
1055 } else if (cur_text == cur_line->text) {/* Begin of line */
1056 if (cur_line->prev != header) {
1057 MOVUP (); /* Move one line up */
1058 move_to (LINE_END, y);
1059 }
1060 } else {
1061 curpoi = cur_text;
1062 precede_char (& curpoi, cur_line->text);
1063 move_address (curpoi, y);
1064 }
1065 }
1066
1067 /*
1068 * Move left one position.
1069 */
1070 void
MOVLF()1071 MOVLF ()
1072 {
1073 if (x == 0 && cur_line->shift_count == 0) {
1074 /* Begin of line */
1075 if (cur_line->prev != header) {
1076 MOVUP (); /* Move one line up */
1077 move_to (LINE_END, y);
1078 }
1079 } else {
1080 move_to (x - 1, y);
1081 }
1082 }
1083
1084 void
LFkey()1085 LFkey ()
1086 {
1087 if (apply_shift_selection) {
1088 trigger_highlight_selection ();
1089
1090 if (keyshift & ctrl_mask) {
1091 keyshift = 0;
1092 MPW ();
1093 return;
1094 }
1095 }
1096
1097 if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
1098 keyshift = 0;
1099 BLINEORUP ();
1100 return;
1101 }
1102 if (keyshift & ctrl_mask) {
1103 keyshift = 0;
1104 ctrl_MLF ();
1105 return;
1106 }
1107 if (keyshift & shift_mask) {
1108 keyshift = 0;
1109 MPW ();
1110 return;
1111 }
1112
1113 if (hop_flag > 0) {
1114 MOVLBEG ();
1115 } else {
1116 MOVLF ();
1117 }
1118 }
1119
1120 /*
1121 * Move right one Unicode character (may enter into combined character).
1122 */
1123 static
1124 void
ctrl_MRT()1125 ctrl_MRT ()
1126 {
1127 char * curpoi;
1128
1129 if (hop_flag > 0) {
1130 MOVLEND ();
1131 } else if (* cur_text == '\n') {
1132 if (cur_line->next != tail) { /* Last char of file */
1133 MOVDN (); /* Move one line down */
1134 move_to (LINE_START, y);
1135 }
1136 } else {
1137 curpoi = cur_text;
1138 advance_char (& curpoi);
1139 move_address (curpoi, y);
1140 }
1141 }
1142
1143 /*
1144 * Move right one position.
1145 */
1146 void
MOVRT()1147 MOVRT ()
1148 {
1149 if (* cur_text == '\n') {
1150 if (cur_line->next != tail) { /* Last char of file */
1151 MOVDN (); /* Move one line down */
1152 move_to (LINE_START, y);
1153 }
1154 } else {
1155 move_to (x + 1, y);
1156 }
1157 }
1158
1159 void
RTkey()1160 RTkey ()
1161 {
1162 if (apply_shift_selection) {
1163 trigger_highlight_selection ();
1164
1165 if (keyshift & ctrl_mask) {
1166 keyshift = 0;
1167 MNW ();
1168 return;
1169 }
1170 }
1171
1172 if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
1173 keyshift = 0;
1174 ELINEORDN ();
1175 return;
1176 }
1177 if (keyshift & ctrl_mask) {
1178 keyshift = 0;
1179 ctrl_MRT ();
1180 return;
1181 }
1182 if (keyshift & shift_mask) {
1183 keyshift = 0;
1184 MNW ();
1185 return;
1186 }
1187
1188 if (hop_flag > 0) {
1189 MOVLEND ();
1190 } else {
1191 MOVRT ();
1192 }
1193 }
1194
1195 /*
1196 * Move one line up.
1197 */
1198 void
MOVUP()1199 MOVUP ()
1200 {
1201 if (y == 0) { /* Top line of screen. Scroll one line */
1202 if (reverse_scroll (True) != ERRORS) {
1203 move_y (y);
1204 }
1205 } else { /* Move to previous line */
1206 move_y (y - 1);
1207 }
1208 }
1209
1210 void
UPkey()1211 UPkey ()
1212 {
1213 if (apply_shift_selection) {
1214 trigger_highlight_selection ();
1215
1216 if (keyshift & ctrl_mask) {
1217 keyshift = 0 | shift_mask;
1218 MPPARA (); /* was BLINEORUP (); */
1219 return;
1220 }
1221 }
1222
1223 if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
1224 keyshift = 0;
1225 MPPARA ();
1226 return;
1227 } else if (keyshift & shift_mask) {
1228 keyshift = 0;
1229 HIGH ();
1230 return;
1231 }
1232
1233 if (hop_flag > 0) {
1234 HIGH ();
1235 } else {
1236 MOVUP ();
1237 }
1238 }
1239
1240 /*
1241 * Move one line down.
1242 */
1243 void
MOVDN()1244 MOVDN ()
1245 {
1246 if (y == last_y) { /* Last line of screen. Scroll one line */
1247 if (bot_line->next == tail && bot_line->text [0] != '\n') {
1248 return;
1249 } else {
1250 (void) forward_scroll (True);
1251 move_y (y);
1252 }
1253 } else { /* Move to next line */
1254 move_y (y + 1);
1255 }
1256 }
1257
1258 void
DNkey()1259 DNkey ()
1260 {
1261 if (apply_shift_selection) {
1262 trigger_highlight_selection ();
1263
1264 if (keyshift & ctrl_mask) {
1265 keyshift = 0 | shift_mask;
1266 MNPARA (); /* was ELINEORDN (); MS Word: MOVDN and MOVLBEG */
1267 return;
1268 }
1269 }
1270
1271 if ((keyshift & ctrlshift_mask) == ctrlshift_mask) {
1272 keyshift = 0;
1273 MNPARA ();
1274 return;
1275 } else if (keyshift & shift_mask) {
1276 keyshift = 0;
1277 LOW ();
1278 return;
1279 }
1280
1281 if (hop_flag > 0) {
1282 LOW ();
1283 } else {
1284 MOVDN ();
1285 }
1286 }
1287
1288 /*
1289 * Move to top of screen
1290 */
1291 void
HIGH()1292 HIGH ()
1293 {
1294 move_y (0);
1295 }
1296
1297 /*
1298 * Move to bottom of screen
1299 */
1300 void
LOW()1301 LOW ()
1302 {
1303 move_y (last_y);
1304 }
1305
1306 /*
1307 * Move to begin of current line.
1308 */
1309 void
MOVLBEG()1310 MOVLBEG ()
1311 {
1312 move_to (LINE_START, y);
1313 }
1314
1315 /*
1316 * Move to begin of (current or previous) line.
1317 */
1318 void
BLINEORUP()1319 BLINEORUP ()
1320 {
1321 if (cur_text == cur_line->text) {
1322 /* already at line beginning, move to previous line */
1323 MOVUP ();
1324 }
1325 MOVLBEG ();
1326 }
1327
1328 /*
1329 * Move to end of current line.
1330 */
1331 void
MOVLEND()1332 MOVLEND ()
1333 {
1334 move_to (LINE_END, y);
1335 }
1336
1337 /*
1338 * Move to end of (current or next) line.
1339 */
1340 void
ELINEORDN()1341 ELINEORDN ()
1342 {
1343 if (* cur_text == '\n') {
1344 /* already at line end, move to end of next line */
1345 MOVDN ();
1346 }
1347 MOVLEND ();
1348 }
1349
1350 /*
1351 * GOTO () prompts for a linenumber and moves to that line.
1352 * GOHOP () prompts (for number or command) with a delay.
1353 */
1354 void
goline(number)1355 goline (number)
1356 int number;
1357 {
1358 LINE * line;
1359 if (number <= 0 || (line = proceed (header->next, number - 1)) == tail) {
1360 error2 ("Invalid line number: ", dec_out ((long) number));
1361 } else {
1362 Pushmark ();
1363 clear_status ();
1364 move_y (find_y (line));
1365 }
1366 }
1367
1368 void
goproz(number)1369 goproz (number)
1370 int number;
1371 {
1372 goline ((long) (total_lines - 1) * number / 100 + 1);
1373 }
1374
1375 static
1376 void
go_or_hop(always_prompt)1377 go_or_hop (always_prompt)
1378 FLAG always_prompt;
1379 {
1380 unsigned long c;
1381 int number;
1382
1383 if (MENU) {
1384 hop_flag = 1;
1385 displayflags ();
1386 set_cursor_xy ();
1387 flush ();
1388 hop_flag = 0;
1389 }
1390 if (always_prompt || ! char_ready_within (500, NIL_PTR)) {
1391 status_uni ("HOP/Go: type command (to amplify/expand) or number (to go to ...) ...");
1392 }
1393 if (quit) {
1394 return;
1395 }
1396 c = readcharacter_unicode ();
1397 if (quit) { /* don't quit on ESC, allow ^G ESC ... */
1398 clear_status ();
1399 return;
1400 }
1401
1402 if ('0' <= c && c <= '9') {
1403 int end;
1404 if (lines_per_page > 0) {
1405 end = get_number ("...number [% | p(age | m(ark | g(o marker | f(ile #]", c, & number);
1406 } else {
1407 end = get_number ("...number [% | m(ark | g(o marker | f(ile #]", c, & number);
1408 }
1409
1410 if (! visselect_keeponsearch) {
1411 clear_highlight_selection ();
1412 }
1413
1414 if (end == '%') {
1415 goproz (number);
1416 } else if (end == 'm' || end == 'M' || end == ',') {
1417 MARKn (number);
1418 } else if (end == '\'' || end == '.' || end == 'g' || end == 'G') {
1419 GOMAn (number);
1420 } else if (end == 'f' || end == 'F' || end == '#') {
1421 edit_nth_file (number);
1422 } else if (lines_per_page > 0 && (end == 'p' || end == 'P') && number > 0) {
1423 goline (number * lines_per_page - lines_per_page + 1);
1424 } else if (end != ERRORS) {
1425 goline (number);
1426 }
1427 return;
1428 } else {
1429 clear_status ();
1430 hop_flag = 1;
1431 invoke_key_function (c);
1432 return;
1433 }
1434 }
1435
1436 void
GOTO()1437 GOTO ()
1438 {
1439 go_or_hop (True);
1440 }
1441
1442 void
GOHOP()1443 GOHOP ()
1444 {
1445 go_or_hop (False);
1446 }
1447
1448
1449 /*
1450 * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes
1451 * top_line of display.) Try to leave the cursor on the same line. If this is
1452 * not possible, leave cursor on the line halfway the page.
1453 */
1454 void
MOVPD()1455 MOVPD ()
1456 {
1457 int i;
1458 int new_y;
1459
1460 for (i = 0; i < SCREENMAX; i ++) {
1461 if (forward_scroll (page_scroll) == ERRORS) {
1462 break; /* EOF reached */
1463 }
1464 }
1465
1466 if (y - i < 0) { /* Line no longer on screen */
1467 new_y = page_stay ? 0 : SCREENMAX >> 1;
1468 } else {
1469 new_y = y - i;
1470 }
1471
1472 if (page_scroll == False) {
1473 display (0, top_line, last_y, new_y);
1474 } else {
1475 (void) display_scrollbar (False);
1476 if (MENU && ! can_delete_line) {
1477 displaymenuline (True);
1478 }
1479 }
1480
1481 move_y (new_y);
1482 }
1483
1484 void
PGDNkey()1485 PGDNkey ()
1486 {
1487 if (apply_shift_selection) {
1488 trigger_highlight_selection ();
1489 }
1490
1491 if (keyshift & ctrl_mask) {
1492 keyshift = 0;
1493 SD ();
1494 return;
1495 }
1496
1497 if (hop_flag > 0) {
1498 hop_flag = 0;
1499 EFILE ();
1500 } else {
1501 MOVPD ();
1502 }
1503 }
1504
1505 /*
1506 * Scroll backwards one page or to top of file, whatever comes first.
1507 * (Top_line becomes bot_line of display).
1508 * The very bottom line (YMAX) is always blank.
1509 * Try to leave the cursor on the same line.
1510 * If this is not possible, leave cursor on the line halfway the page.
1511 */
1512 void
MOVPU()1513 MOVPU ()
1514 {
1515 int i;
1516 int new_y;
1517
1518 for (i = 0; i < SCREENMAX; i ++) {
1519 if (reverse_scroll (page_scroll) == ERRORS) {
1520 /* should also flag reverse_scroll that clearing of
1521 bottom line is not desired */
1522 break; /* Top of file reached */
1523 }
1524 }
1525
1526 if (y + i > SCREENMAX) { /* line no longer on screen */
1527 new_y = page_stay ? last_y : SCREENMAX >> 1;
1528 } else {
1529 new_y = y + i;
1530 }
1531
1532 if (can_scroll_reverse && page_scroll) {
1533 set_cursor (0, YMAX); /* Erase very bottom line */
1534 clear_lastline ();
1535 (void) display_scrollbar (False);
1536 } else {
1537 display (0, top_line, last_y, new_y);
1538 }
1539
1540 move_y (new_y);
1541 }
1542
1543 void
PGUPkey()1544 PGUPkey ()
1545 {
1546 if (apply_shift_selection) {
1547 trigger_highlight_selection ();
1548 }
1549
1550 if (keyshift & ctrl_mask) {
1551 keyshift = 0;
1552 SU ();
1553 return;
1554 }
1555
1556 if (hop_flag > 0) {
1557 hop_flag = 0;
1558 BFILE ();
1559 } else {
1560 MOVPU ();
1561 }
1562 }
1563
1564 /*
1565 * Go to top of file, scrolling if possible, else redrawing screen.
1566 */
1567 void
BFILE()1568 BFILE ()
1569 {
1570 Pushmark ();
1571
1572 if (proceed (top_line, - SCREENMAX) == header) {
1573 MOVPU (); /* It fits; let MOVPU do it */
1574 } else {
1575 reset (header->next, 0); /* Reset top_line, etc. */
1576 RD_y (0); /* Display full page */
1577 }
1578 move_to (LINE_START, 0);
1579 }
1580
1581 /*
1582 * Go to last position of text, scrolling if possible, else redrawing screen
1583 */
1584 void
EFILE()1585 EFILE ()
1586 {
1587 Pushmark ();
1588
1589 if (proceed (bot_line, SCREENMAX) == tail) {
1590 MOVPD (); /* It fits; let MOVPD do it */
1591 } else {
1592 reset (proceed (tail->prev, - SCREENMAX), SCREENMAX);
1593 RD_y (last_y); /* Display full page */
1594 }
1595 move_to (LINE_END, last_y);
1596 }
1597
1598 /*
1599 * Scroll one line up. Leave the cursor on the same line (if possible).
1600 */
1601 void
SU()1602 SU ()
1603 {
1604 register int i;
1605
1606 if (hop_flag > 0) {
1607 hop_flag = 0;
1608 for (i = 0; i < (SCREENMAX >> 1); i ++) {
1609 if (i > 0 && disp_scrollbar) {
1610 (void) display_scrollbar (True);
1611 }
1612 SU ();
1613 }
1614 return;
1615 }
1616
1617 if (reverse_scroll (True) != ERRORS) { /* else we are at top of file */
1618 move_y ((y == SCREENMAX) ? SCREENMAX : y + 1);
1619 }
1620 }
1621
1622 /*
1623 * Scroll one line down. Leave the cursor on the same line (if possible).
1624 */
1625 void
SD()1626 SD ()
1627 {
1628 register int i;
1629
1630 if (hop_flag > 0) {
1631 hop_flag = 0;
1632 for (i = 0; i < (SCREENMAX >> 1); i ++) {
1633 if (i > 0 && disp_scrollbar) {
1634 (void) display_scrollbar (True);
1635 }
1636 SD ();
1637 }
1638 return;
1639 }
1640
1641 if (forward_scroll (True) != ERRORS) {
1642 move_y ((y == 0) ? 0 : y - 1);
1643 }
1644 }
1645
1646
1647 /*----------------------------------------------------------------------*\
1648 Contents-dependent moves
1649 \*----------------------------------------------------------------------*/
1650
1651 /*
1652 * A word was previously defined as a number of non-blank characters
1653 * separated by tabs, spaces or linefeeds.
1654 * By consulting idfchar (), sequences of real letters only or digits
1655 * or underlines are recognized as words.
1656 */
1657
1658 /*
1659 * BSEN () and ESEN () look for the beginning or end of the current sentence.
1660 */
1661 void
BSEN()1662 BSEN ()
1663 {
1664 search_for ("[;.]", REVERSE, False);
1665 }
1666
1667 void
ESEN()1668 ESEN ()
1669 {
1670 search_for ("[;.]", FORWARD, False);
1671 }
1672
1673 /*
1674 * MNPARA () and MPPARA () look for end or beginning of current paragraph.
1675 */
1676 void
MNPARA()1677 MNPARA ()
1678 {
1679 do {
1680 if (cur_line->next == tail) {
1681 MOVLEND ();
1682 break;
1683 }
1684 if (JUSmode == 0 && cur_line->text [strlen (cur_line->text) - 2] != ' ') {
1685 MOVDN ();
1686 MOVLBEG ();
1687 break;
1688 }
1689 MOVDN ();
1690 if (JUSmode == 1 && * (cur_line->text) == '\n') {
1691 break;
1692 }
1693 } while (True);
1694 }
1695
1696 void
MPPARA()1697 MPPARA ()
1698 {
1699 if (JUSmode == 0 && cur_text == cur_line->text) {
1700 /* prevent sticking if already at paragraph beginning */
1701 MOVUP ();
1702 }
1703 do {
1704 if (cur_line->prev == header) {
1705 MOVLBEG ();
1706 break;
1707 }
1708 if (JUSmode == 0 && cur_line->prev->text [strlen (cur_line->prev->text) - 2] != ' ') {
1709 MOVLBEG ();
1710 break;
1711 }
1712 MOVUP ();
1713 if (JUSmode == 1 && * (cur_line->text) == '\n') {
1714 break;
1715 }
1716 } while (True);
1717 }
1718
1719 static
1720 void
search_tag(poi)1721 search_tag (poi)
1722 char * poi;
1723 {
1724 char pat [maxPROMPTlen];
1725 FLAG direction = FORWARD;
1726 char * patpoi = & pat [3];
1727
1728 strcpy (pat, "</*");
1729 poi ++;
1730 if (* poi == '/') {
1731 direction = REVERSE;
1732 poi ++;
1733 }
1734 while (! white_space (* poi) && * poi != '\n' && * poi != '>' && * poi != '\0') {
1735 * patpoi = * poi;
1736 patpoi ++;
1737 poi ++;
1738 }
1739 * patpoi = '\0';
1740
1741 search_corresponding (pat, direction, "/");
1742 }
1743
1744 /**
1745 SCORR () looks for a corresponding bracket, HTML tag,
1746 or looks for the next/previous MIME separator or mail header
1747 */
1748 void
SCORR(pref_direction)1749 SCORR (pref_direction)
1750 FLAG pref_direction;
1751 {
1752 char * poi;
1753 unsigned int cv = charvalue (cur_text);
1754 char * errmsg;
1755
1756 if (hop_flag > 0) {
1757 hop_flag = 0;
1758 search_wrong_enc ();
1759 return;
1760 }
1761
1762 switch (cv) {
1763 case '(': search_corresponding ("[()]", FORWARD, ")");
1764 return;
1765 case ')': search_corresponding ("[()]", REVERSE, "(");
1766 return;
1767 case '[': search_corresponding ("[\\[\\]]", FORWARD, "]");
1768 return;
1769 case ']': search_corresponding ("[\\[\\]]", REVERSE, "[");
1770 return;
1771 case '{': search_corresponding ("[{}]", FORWARD, "}");
1772 return;
1773 case '}': search_corresponding ("[{}]", REVERSE, "{");
1774 return;
1775 case (character) '�': /* « LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
1776 if (utf8_text) {
1777 search_corresponding ("[«»]", FORWARD, "»");
1778 } else {
1779 search_corresponding ("[��]", FORWARD, "�");
1780 }
1781 return;
1782 case (character) '�': /* » RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
1783 if (utf8_text) {
1784 search_corresponding ("[«»]", REVERSE, "«");
1785 } else {
1786 search_corresponding ("[��]", REVERSE, "�");
1787 }
1788 return;
1789 case 0x2039: /* ‹ SINGLE LEFT-POINTING ANGLE QUOTATION MARK */
1790 search_corresponding ("[‹›]", FORWARD, "›");
1791 return;
1792 case 0x203A: /* › SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */
1793 search_corresponding ("[‹›]", REVERSE, "‹");
1794 return;
1795 case 0x2045: /* ⁅ LEFT SQUARE BRACKET WITH QUILL */
1796 search_corresponding ("[⁅⁆]", FORWARD, "⁆");
1797 return;
1798 case 0x2046: /* ⁆ RIGHT SQUARE BRACKET WITH QUILL */
1799 search_corresponding ("[⁅⁆]", REVERSE, "⁅");
1800 return;
1801 case 0x207D: /* ⁽ SUPERSCRIPT LEFT PARENTHESIS */
1802 search_corresponding ("[⁽⁾]", FORWARD, "⁾");
1803 return;
1804 case 0x207E: /* ⁾ SUPERSCRIPT RIGHT PARENTHESIS */
1805 search_corresponding ("[⁽⁾]", REVERSE, "⁽");
1806 return;
1807 case 0x208D: /* ₍ SUBSCRIPT LEFT PARENTHESIS */
1808 search_corresponding ("[₍₎]", FORWARD, "₎");
1809 return;
1810 case 0x208E: /* ₎ SUBSCRIPT RIGHT PARENTHESIS */
1811 search_corresponding ("[₍₎]", REVERSE, "₍");
1812 return;
1813 default: if (mark_HTML) {
1814 poi = cur_text;
1815 if (* poi == '>') {
1816 MOVLF ();
1817 }
1818 while (poi != cur_line->text && * poi != '<') {
1819 precede_char (& poi, cur_line->text);
1820 }
1821 if (* poi == '<') {
1822 search_tag (poi);
1823 return;
1824 } else {
1825 errmsg = "No bracket or tag to match";
1826 }
1827 } else {
1828 if (* cur_text == '<') {
1829 search_corresponding ("[<>]", FORWARD, ">");
1830 return;
1831 } else if (* cur_text == '>') {
1832 search_corresponding ("[<>]", REVERSE, "<");
1833 return;
1834 } else {
1835 errmsg = "No bracket to match";
1836 }
1837 }
1838 }
1839
1840 if (pref_direction != REVERSE) {
1841 pref_direction = FORWARD;
1842 }
1843
1844 if ((cv == '/' && * (cur_text + 1) == '*')
1845 ||
1846 (cv == '*' && cur_text != cur_line->text && * (cur_text - 1) == '/')
1847 ) {
1848 search_for ("\\*/", FORWARD, False);
1849 } else if ((cv == '*' && * (cur_text + 1) == '/')
1850 ||
1851 (cv == '/' && cur_text != cur_line->text && * (cur_text - 1) == '*')
1852 ) {
1853 search_for ("/\\*", REVERSE, False);
1854 } else if (* cur_line->text == '#') {
1855 /* #if #else #endif */
1856 char * cp = cur_line->text;
1857 cp ++;
1858 while (white_space (* cp)) {
1859 cp ++;
1860 }
1861 /* #if/#elif/#else/#endif matching */
1862 if (strisprefix ("if", cp)) {
1863 search_corresponding ("^#[ ]*[ie][fln]", FORWARD, "#1");
1864 } else if (strisprefix ("end", cp)) {
1865 search_corresponding ("^#[ ]*[ie][fln]", REVERSE, "#3");
1866 } else if (strisprefix ("el", cp)) {
1867 if (pref_direction == FORWARD) {
1868 search_corresponding ("^#[ ]*[ie][fln]", FORWARD, "#2");
1869 } else {
1870 search_corresponding ("^#[ ]*[ie][fln]", REVERSE, "#2");
1871 }
1872 } else {
1873 /* nothing to match */
1874 error (errmsg);
1875 }
1876 } else if (strisprefix ("--", cur_line->text)) {
1877 /* search for next/previous MIME separator */
1878 char pattern [maxPROMPTlen + 1];
1879 int len = strlen (cur_line->text);
1880 MOVLBEG ();
1881 pattern [0] = '^';
1882 strcpy (& pattern [1], cur_line->text);
1883 if (pattern [len] == '\n') {
1884 pattern [len] = '\0';
1885 len --;
1886 }
1887 if (streq ("--", & pattern [len - 1])) {
1888 pattern [len - 1] = '\0';
1889 }
1890 search_for (pattern, pref_direction, False);
1891 } else {
1892 /* try to find mail header and search for next/previous mail */
1893 LINE * line = cur_line;
1894 char * text;
1895 while (white_space (* (line->text)) && line->prev != header) {
1896 line = line->prev;
1897 }
1898 text = line->text;
1899 while (* text != ':' && * text != '\0' && ! white_space (* text)) {
1900 advance_char (& text);
1901 }
1902 if ((* text == ':' && text != line->text) || strisprefix ("From ", line->text)) {
1903 /* mail header found */
1904 if (pref_direction == REVERSE) {
1905 /* go to beginning of current mail message */
1906 MOVDN ();
1907 search_for ("^From ", REVERSE, True);
1908 }
1909 search_for ("^From ", pref_direction, True);
1910 } else {
1911 /* nothing to match */
1912 error (errmsg);
1913 }
1914 }
1915 }
1916
1917 /*
1918 get_idf extracts the identifier at the current position (text)
1919 into idf_buf.
1920 start points to the beginning of the current line.
1921 */
1922 int
get_idf(idf_buf,text,start)1923 get_idf (idf_buf, text, start)
1924 char * idf_buf;
1925 char * text;
1926 char * start;
1927 {
1928 char * idf_buf_poi = idf_buf;
1929 char * idf_poi;
1930 char * copy_poi;
1931
1932 if (! idfchar (text)) {
1933 error ("No identifier");
1934 return ERRORS;
1935 } else {
1936 idf_poi = text;
1937 while (idfchar (idf_poi) && idf_poi != start) {
1938 precede_char (& idf_poi, start);
1939 }
1940 if (! idfchar (idf_poi)) {
1941 advance_char (& idf_poi);
1942 }
1943 while (idfchar (idf_poi)) {
1944 copy_poi = idf_poi;
1945 advance_char (& idf_poi);
1946 while (copy_poi != idf_poi) {
1947 * idf_buf_poi ++ = * copy_poi ++;
1948 }
1949 }
1950 * idf_buf_poi = '\0';
1951 return FINE;
1952 }
1953 }
1954
1955 /*
1956 * SIDF () searches for the identifier at the current position
1957 */
1958 void
SIDF(method)1959 SIDF (method)
1960 FLAG method;
1961 {
1962 char idf_buf [maxLINElen]; /* identifier to search for */
1963
1964 int ret = get_idf (idf_buf, cur_text, cur_line->text);
1965 if (ret == ERRORS) {
1966 return;
1967 }
1968
1969 search_expr (idf_buf, method, False);
1970 }
1971
1972 /*
1973 * MPW () moves to the start of the previous word. A word is defined as a
1974 * number of non-blank characters separated by tabs spaces or linefeeds.
1975 */
1976 static
1977 void
move_previous_word(remove)1978 move_previous_word (remove)
1979 FLAG remove;
1980 {
1981 register char * begin_line;
1982 char * textp;
1983 char start_char = * cur_text;
1984 char * start_pos = cur_text;
1985 FLAG idfsearch = False; /* avoid -Wmaybe-uninitialized */
1986
1987 if (remove == DELETE && dont_modify ()) {
1988 return;
1989 }
1990
1991 /* First check if we're at the beginning of line. */
1992 if (cur_text == cur_line->text) {
1993 if (cur_line->prev == header) {
1994 return;
1995 }
1996 start_char = '\0';
1997 }
1998
1999 MOVLF ();
2000
2001 begin_line = cur_line->text;
2002 textp = cur_text;
2003
2004 if (* textp == '\n' && textp != begin_line && ! white_space (* (textp - 1))) {
2005 /* stop at line-end (unless it ends with a space) */
2006 } else {
2007 /* Check if we're in the middle of a word. */
2008 if (! alpha (* textp) || ! alpha (start_char)) {
2009 while (textp != begin_line
2010 && (white_space (* textp) || * textp == '\n'))
2011 {
2012 precede_char (& textp, begin_line);
2013 }
2014 }
2015
2016 /* Now we're at the end of previous word. Skip non-blanks until a blank comes */
2017 if (wordnonblank) {
2018 while (textp != begin_line && alpha (* textp)) {
2019 precede_char (& textp, begin_line);
2020 }
2021 } else {
2022 if (idfchar (textp)) {
2023 idfsearch = True;
2024 while (textp != begin_line && idfchar (textp)) {
2025 precede_char (& textp, begin_line);
2026 }
2027 } else {
2028 idfsearch = False;
2029 while (textp != begin_line
2030 && alpha (* textp) && ! idfchar (textp))
2031 {
2032 precede_char (& textp, begin_line);
2033 }
2034 }
2035 }
2036
2037 /* Go to the next char if we're not at the beginning of the line */
2038 /* At the beginning of the line, check whether to stay or to go to the word */
2039 if (textp != begin_line && * textp != '\n') {
2040 advance_char (& textp);
2041 } else if (textp == begin_line && * textp != '\n' &&
2042 (wordnonblank
2043 ? * textp == ' '
2044 : (idfsearch
2045 ? ! idfchar (textp)
2046 : (! alpha (* textp) || idfchar (textp)))))
2047 {
2048 advance_char (& textp);
2049 if (white_space (* textp) || textp == start_pos) {
2050 /* no word there or not moved, so go back */
2051 precede_char (& textp, begin_line);
2052 }
2053 }
2054 }
2055
2056 /* Find the x-coordinate of this address, and move to it */
2057 move_address (textp, y);
2058 if (remove == DELETE) {
2059 (void) delete_text (cur_line, textp, cur_line, start_pos);
2060 }
2061 }
2062
2063 void
MPW()2064 MPW ()
2065 {
2066 if (hop_flag > 0) {
2067 BSEN ();
2068 } else {
2069 move_previous_word (NO_DELETE);
2070 }
2071 }
2072
2073 /*
2074 * MNW () moves to the start of the next word. A word is defined as a number of
2075 * non-blank characters separated by tabs spaces or linefeeds. Always keep in
2076 * mind that the pointer shouldn't pass the '\n'.
2077 */
2078 static
2079 void
move_next_word(remove)2080 move_next_word (remove)
2081 FLAG remove;
2082 {
2083 char * textp = cur_text;
2084
2085 if (remove == DELETE && dont_modify ()) {
2086 return;
2087 }
2088
2089 /* Move to the end of the current word. */
2090 if (wordnonblank) {
2091 if (* textp != '\n') {
2092 advance_char (& textp);
2093 }
2094 while (alpha (* textp)) {
2095 advance_char (& textp);
2096 }
2097 } else {
2098 if (idfchar (textp)) {
2099 while (* textp != '\n' && idfchar (textp)) {
2100 advance_char (& textp);
2101 }
2102 } else {
2103 while (alpha (* textp) && ! idfchar (textp)) {
2104 advance_char (& textp);
2105 }
2106 }
2107 }
2108
2109 /* Skip all white spaces */
2110 while (* textp != '\n' && white_space (* textp)) {
2111 textp ++;
2112 }
2113 /* If we're deleting, delete the text in between */
2114 if (remove == DELETE) {
2115 delete_text_buf (cur_line, cur_text, cur_line, textp);
2116 return;
2117 }
2118
2119 /* If we're at end of line, move to the beginning of (first word on) the next line */
2120 if (* textp == '\n' && cur_line->next != tail
2121 /* stop at line-end (unless it ends with a space) */
2122 && (textp == cur_text || white_space (* (textp - 1)))
2123 ) {
2124 MOVDN ();
2125 move_to (LINE_START, y);
2126 textp = cur_text;
2127 /*
2128 while (* textp != '\n' && white_space (* textp)) {
2129 textp ++;
2130 }
2131 */
2132 }
2133 move_address (textp, y);
2134 }
2135
2136 void
MNW()2137 MNW ()
2138 {
2139 if (hop_flag > 0) {
2140 ESEN ();
2141 } else {
2142 move_next_word (NO_DELETE);
2143 }
2144 }
2145
2146
2147 /*======================================================================*\
2148 |* Modify commands: delete *|
2149 \*======================================================================*/
2150
2151 /*
2152 * DCC deletes the character under the cursor. If this character is a '\n' the
2153 * current line is joined with the next one.
2154 * If this character is the only character of the line, the current line will
2155 * be deleted.
2156 * DCC0 deletes without justification.
2157 */
2158 static
2159 void
delete_char(with_combinings)2160 delete_char (with_combinings)
2161 FLAG with_combinings;
2162 {
2163 if (* cur_text == '\n') {
2164 if (cur_line->next == tail) {
2165 if (cur_line->return_type != lineend_NONE) {
2166 if (dont_modify ()) {
2167 return;
2168 }
2169 set_modified ();
2170 cur_line->return_type = lineend_NONE;
2171 if (total_chars >= 0) {
2172 total_chars --;
2173 }
2174 set_cursor_xy ();
2175 put_line (y, cur_line, x, True, False);
2176 status_msg ("Trailing line-end deleted");
2177 }
2178 } else {
2179 (void) delete_text (cur_line, cur_text, cur_line->next, cur_line->next->text);
2180 }
2181 } else {
2182 char * after_char = cur_text;
2183 advance_char (& after_char);
2184
2185 if (with_combinings && combining_mode && encoding_has_combining ()) {
2186 /* check subsequent characters whether they are
2187 actually combined (or joined);
2188 mind: doesn't work with poor man's bidi
2189 */
2190 unsigned long unichar = unicodevalue (cur_text);
2191
2192 /* skip this if already positioned within a combined char */
2193 if (! iscombined_unichar (unichar, cur_text, cur_line->text)) {
2194 /* delete combining accents together with base char */
2195 unichar = unicodevalue (after_char);
2196 while (iscombined_unichar (unichar, after_char, cur_line->text)) {
2197 advance_char (& after_char);
2198 unichar = unicodevalue (after_char);
2199 }
2200 }
2201 }
2202
2203 (void) delete_text (cur_line, cur_text, cur_line, after_char);
2204 }
2205 }
2206
2207 void
DCC()2208 DCC ()
2209 {
2210 if (hop_flag > 0 && (keyshift & ctrl_mask)) {
2211 /* Delete line right, to end of line */
2212 hop_flag = 0;
2213 DLN ();
2214 } else if ((shift_selection == True) && (keyshift & ctrl_mask)) {
2215 keyshift = 0;
2216 /* Delete word right */
2217 DNW ();
2218 } else if (keyshift & ctrl_mask) {
2219 keyshift = 0;
2220 delete_char (False);
2221 } else {
2222 delete_char (True);
2223 }
2224 }
2225
2226 void
DCC0()2227 DCC0 ()
2228 {
2229 DCC ();
2230 }
2231
2232 /*
2233 "Plain BS":
2234 DPC0 deletes the character on the left side of the cursor.
2235 If the cursor is at the beginning of the line, the line end
2236 is deleted, merging the two lines.
2237 With hop flag, delete left part of line from current point.
2238 */
2239 void
DPC0()2240 DPC0 ()
2241 {
2242 char * delete_pos;
2243
2244 if (x == 0 && cur_line->prev == header) {
2245 /* Top of file; if line is shifted, pos is not 0 (shift symbol!) */
2246 return;
2247 }
2248
2249 if (dont_modify ()) {
2250 return;
2251 }
2252
2253 if (hop_flag > 0) {
2254 hop_flag = 0;
2255 if (emulation == 'e') { /* emacs mode */
2256 DPW ();
2257 } else if (cur_text != cur_line->text) {
2258 delete_pos = cur_text;
2259 MOVLBEG ();
2260 (void) delete_text (cur_line, cur_line->text, cur_line, delete_pos);
2261 }
2262 } else {
2263 /*FLAG was_on_comb = iscombining (unicodevalue (cur_text));*/
2264 FLAG was_on_comb = iscombined_unichar (unicodevalue (cur_text), cur_text, cur_line->text);
2265 if (keyshift & ctrl_mask) {
2266 ctrl_MLF ();
2267 } else {
2268 unsigned long unichar;
2269 MOVLF ();
2270 /* handle spacing combining characters */
2271 unichar = unicodevalue (cur_text);
2272 if (isspacingcombining_unichar (unichar)
2273 || /* handle ARABIC TAIL FRAGMENT */
2274 (unichar == 0xFE73
2275 && iscombined_unichar (unichar, cur_text, cur_line->text))
2276 ) {
2277 MOVLF ();
2278 }
2279 }
2280 if (was_on_comb) {
2281 delete_char (False);
2282 } else {
2283 delete_char (True);
2284 }
2285 }
2286 }
2287
2288 /*
2289 DPC normally deletes the character left just as DPC0 does.
2290 However, unless the hop flag is set, it first checks if there is
2291 anything but white space on the current line left of the current position.
2292 If there is only white space, it tries to perform a "backtab" function,
2293 reverting the indentation to the previous amount above in the text
2294 (unless if it's in the line immediately above the current line).
2295 */
2296 void
DPC()2297 DPC ()
2298 {
2299 if (keyshift & alt_mask) {
2300 DCC ();
2301 } else if (hop_flag > 0 && (keyshift & ctrl_mask)) {
2302 /* Delete line left, to beginning of line */
2303 if (dont_modify ()) {
2304 return;
2305 }
2306 if (cur_text != cur_line->text) {
2307 char * delete_pos = cur_text;
2308 MOVLBEG ();
2309 (void) delete_text (cur_line, cur_line->text, cur_line, delete_pos);
2310 }
2311 } else if ((shift_selection == True) && (keyshift & ctrlshift_mask)) {
2312 keyshift = 0;
2313 /* Delete word left */
2314 DPW ();
2315 } else if ((keyshift & ctrlshift_mask) == ctrl_mask) {
2316 /* with Control and without Shift -> Plain BS */
2317 DPC0 ();
2318 } else if (hop_flag > 0) { /* deprecated? */
2319 DPC0 (); /* delete line left; emacs mode: delete word left */
2320 } else if (has_active_selection () && ! (keyshift & ctrl_mask)) {
2321 CUT ();
2322 } else if (plain_BS && ! (keyshift & shift_mask)) {
2323 /* unless Shifted: Plain BS preference configured -> Plain BS */
2324 keyshift |= ctrl_mask;
2325 DPC0 ();
2326 } else {
2327 /* "Smart BS": with auto-undent and combined character handling */
2328 char * cp = cur_line->text;
2329 int column = 0;
2330 int numberlen = 0;
2331 int stop_col = 0;
2332
2333 if (autoindent) {
2334 FLAG afterpara = paragraphending (cur_line->prev, cur_line->prev->return_type);
2335 FLAG is_itemchar = False;
2336 /* check white space left of cursor, bullet or list numbering */
2337 while (* cp != '\0' && cp != cur_text &&
2338 (white_space (* cp) ||
2339 (afterpara &&
2340 ( (is_itemchar = isitemchar (unicodevalue (cp)))
2341 || (autonumber && backnumber && numberlen == 0 && (numberlen = numbering (cp)) > 0)
2342 )
2343 )
2344 )
2345 ) {
2346 if (numberlen > 0) {
2347 cp += numberlen;
2348 column += numberlen;
2349 } else {
2350 if (is_itemchar) {
2351 stop_col = column;
2352 is_itemchar = False;
2353 }
2354 advance_char_scr (& cp, & column, cur_line->text);
2355 }
2356 }
2357 }
2358
2359 if (cp == cur_text) {
2360 /* only white space/bullet/numbering left of position */
2361 int previous_col = column;
2362 LINE * lp = cur_line->prev;
2363
2364 if (autonumber && backnumber && numberlen > 0) {
2365 if (last2subnumberpoi != NIL_PTR) {
2366 char numbuf [22];
2367 char * numpoi = numbuf;
2368 if (backincrem) {
2369 (void) delete_text (cur_line, last2subnumberpoi, cur_line, cur_text);
2370 /* insert incremented subnumber, e.g. 4. */
2371 sprintf (numbuf, "%d", last2subnumber + 1);
2372 while (* numpoi) {
2373 insert_unichar (* numpoi ++);
2374 }
2375 insert_unichar ('.');
2376 } else {
2377 (void) delete_text (cur_line, lastsubnumberpoi, cur_line, cur_text);
2378 }
2379 } else {
2380 (void) delete_text (cur_line, lastsubnumberpoi, cur_line, cur_text);
2381 }
2382 return;
2383 }
2384
2385 while (previous_col >= column && lp != header) {
2386 /* count white space on line lp */
2387 cp = lp->text;
2388 previous_col = 0;
2389 while (* cp != '\0' && previous_col < column && white_space (* cp)) {
2390 advance_char_scr (& cp, & previous_col, lp->text);
2391 }
2392 if (* cp == '\n' || * cp == '\0') {
2393 /* don't count space lines */
2394 previous_col = column;
2395 }
2396
2397 lp = lp->prev;
2398 }
2399
2400 /* Undent: if less indented previous line was found,
2401 (* and this was not on the immediately preceeding line *)
2402 perform the back TAB function */
2403 if (previous_col < column /* && cur_line->prev != lp->next */) {
2404 /* important check, or we may hang */
2405 if (dont_modify ()) {
2406 return;
2407 }
2408
2409 while (column > previous_col && column > stop_col) {
2410 DPC0 ();
2411 column = 0;
2412 cp = cur_line->text;
2413 while (* cp != '\0' && cp != cur_text) {
2414 advance_char_scr (& cp, & column, cur_line->text);
2415 }
2416 }
2417 while (column < previous_col) {
2418 S (' ');
2419 column ++;
2420 }
2421 } else {
2422 DPC0 ();
2423 }
2424 } else {
2425 /* check whether in combined character
2426 b/B is current position, b, c are combining:
2427 AB delete A (whole char)
2428 AcB delete Ac (whole char)
2429 Ab delete A (partial char)
2430 Acb delete c (partial char)
2431 */
2432 unsigned long unichar = unicodevalue (cur_text);
2433 if (iscombined_unichar (unichar, cur_text, cur_line->text)) {
2434 /* if in combined char, delete at most base char, not 1 more left */
2435 keyshift |= ctrl_mask;
2436 }
2437
2438 DPC0 ();
2439 }
2440 }
2441 }
2442
2443 /*
2444 * DLINE delete the whole current line.
2445 */
2446 void
DLINE()2447 DLINE ()
2448 {
2449 if (dont_modify ()) {
2450 return;
2451 }
2452
2453 if (hop_flag > 0) {
2454 hop_flag = 0;
2455 if (* cur_text != '\n') {
2456 delete_text_buf (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
2457 }
2458 } else {
2459 MOVLBEG ();
2460 if (* cur_text != '\n') {
2461 DLN ();
2462 }
2463 DCC ();
2464 }
2465 }
2466
2467 /*
2468 * DLN deletes all characters until the end of the line. If the current
2469 * character is a '\n', then delete that char.
2470 */
2471 void
DLN()2472 DLN ()
2473 {
2474 if (hop_flag > 0) {
2475 hop_flag = 0;
2476 DLINE ();
2477 } else if (* cur_text == '\n') {
2478 /* DCC (); */
2479 if (cur_line->next != tail) {
2480 delete_text_buf (cur_line, cur_text, cur_line->next, cur_line->next->text);
2481 }
2482 } else {
2483 delete_text_buf (cur_line, cur_text, cur_line, cur_text + length_of (cur_text) - 1);
2484 }
2485 }
2486
2487 /*
2488 * DNW () deletes the next word (as defined in MNW ())
2489 */
2490 void
DNW()2491 DNW ()
2492 {
2493 if (* cur_text == '\n') {
2494 DCC ();
2495 } else {
2496 move_next_word (DELETE);
2497 }
2498 }
2499
2500 /*
2501 * DPW () deletes the previous word (as defined in MPW ())
2502 */
2503 void
DPW()2504 DPW ()
2505 {
2506 if (cur_text == cur_line->text) {
2507 DPC0 ();
2508 } else {
2509 move_previous_word (DELETE);
2510 }
2511 }
2512
2513
2514 /*======================================================================*\
2515 |* Modify commands: insert *|
2516 \*======================================================================*/
2517
2518 static
2519 void
enterNUL()2520 enterNUL ()
2521 {
2522 if (dont_modify ()) {
2523 return;
2524 }
2525
2526 S ('\n'); /* Insert a new line */
2527 MOVUP (); /* Move one line up */
2528 move_to (LINE_END, y); /* Move to end of this line */
2529 cur_line->return_type = lineend_NUL;
2530 put_line (y, cur_line, x, True, False);
2531 MOVRT (); /* move behind inserted NUL */
2532 }
2533
2534
2535 /*
2536 * Functions to insert character at current location.
2537 * S0 inserts without justification.
2538 */
2539
2540 static unsigned long previous_unichar = 0;
2541
2542 /*
2543 * S1byte: enter a byte of a character; collect bytes for multi-byte characters
2544 */
2545 static
2546 int
S1byte(newchar,JUSlvl,utf8_transform)2547 S1byte (newchar, JUSlvl, utf8_transform)
2548 register character newchar;
2549 int JUSlvl;
2550 FLAG utf8_transform;
2551 {
2552 static character buffer [7];
2553 static character * utfpoi = buffer;
2554 static int utfcount = 1;
2555 static int cjkremaining = 0;
2556 static character firstbyte = '\0';
2557 static unsigned long unichar;
2558 int offset;
2559
2560 int width = 1;
2561
2562 if (newchar == '\0') {
2563 if (firstbyte != '\0') {
2564 firstbyte = '\0';
2565 ring_bell ();
2566 } else {
2567 enterNUL ();
2568 }
2569 return FINE;
2570 }
2571
2572 if (utf8_text) {
2573 if (utf8_transform) {
2574 /* UTF-8 input for UTF-8 text */
2575 if (newchar < 0x80) {
2576 unichar = newchar;
2577 * utfpoi = newchar;
2578 utfpoi ++;
2579 * utfpoi = '\0';
2580 utfpoi = buffer;
2581 } else if ((newchar & 0xC0) == 0x80) {
2582 /* UTF-8 sequence byte */
2583 unichar = (unichar << 6) | (newchar & 0x3F);
2584 * utfpoi = newchar;
2585 utfpoi ++;
2586 utfcount --;
2587 if (utfcount == 0) {
2588 * utfpoi = '\0';
2589 utfpoi = buffer;
2590 width = uniscrwidth (unichar, cur_text, cur_line->text);
2591 } else {
2592 return FINE;
2593 }
2594 } else { /* first UTF-8 byte */
2595 utfpoi = buffer;
2596 * utfpoi = newchar;
2597
2598 if ((newchar & 0xE0) == 0xC0) {
2599 utfcount = 2;
2600 unichar = newchar & 0x1F;
2601 } else if ((newchar & 0xF0) == 0xE0) {
2602 utfcount = 3;
2603 unichar = newchar & 0x0F;
2604 } else if ((newchar & 0xF8) == 0xF0) {
2605 utfcount = 4;
2606 unichar = newchar & 0x07;
2607 } else if ((newchar & 0xFC) == 0xF8) {
2608 utfcount = 5;
2609 unichar = newchar & 0x03;
2610 } else if ((newchar & 0xFE) == 0xFC) {
2611 utfcount = 6;
2612 unichar = newchar & 0x01;
2613 } else /* ignore illegal UTF-8 code */
2614 return FINE;
2615
2616 utfpoi ++;
2617 utfcount --;
2618 return FINE;
2619 }
2620 } else {
2621 /* 8-bit input for UTF-8 text */
2622 unichar = newchar;
2623 if (newchar < 0x80) {
2624 buffer [0] = newchar;
2625 buffer [1] = '\0';
2626 } else {
2627 buffer [0] = (newchar >> 6) | 0xC0;
2628 buffer [1] = (newchar & 0x3F) | 0x80;
2629 buffer [2] = '\0';
2630 }
2631 }
2632 } else if (cjk_text) {
2633 /* 8/16-bit (CJK) input for CJK text */
2634 if (cjkremaining > 0) {
2635 * utfpoi ++ = newchar;
2636 * utfpoi = '\0';
2637 cjkremaining --;
2638 if (cjkremaining > 0) {
2639 return FINE;
2640 }
2641 utfpoi = buffer;
2642 } else if (firstbyte != '\0') {
2643 buffer [0] = firstbyte;
2644 buffer [1] = newchar;
2645 buffer [2] = '\0';
2646 cjkremaining = CJK_len (buffer) - 2;
2647 if (cjkremaining > 0) {
2648 firstbyte = '\0';
2649 utfpoi = & buffer [2];
2650 return FINE;
2651 }
2652 } else if (multichar (newchar)) {
2653 firstbyte = newchar;
2654 return FINE;
2655 } else {
2656 buffer [0] = newchar;
2657 buffer [1] = '\0';
2658 }
2659 if (* buffer == '\t') {
2660 width = 1;
2661 } else {
2662 width = col_count (buffer);
2663 }
2664 firstbyte = '\0';
2665 } else if (utf8_transform) {
2666 /* UTF-8 input for 8-bit text */
2667 buffer [1] = '\0';
2668 if (newchar < 0x80) {
2669 buffer [0] = newchar;
2670 } else if ((newchar & 0xC0) == 0x80) {
2671 /* UTF-8 sequence byte; not handled here anymore */
2672 unichar = (unichar << 6) | (newchar & 0x3F);
2673 utfcount --;
2674 if (utfcount == 0) {
2675 if ((unichar & 0xFF) == unichar) {
2676 buffer [0] = unichar & 0xFF;
2677 } else {
2678 buffer [0] = '';
2679 }
2680 } else {
2681 return FINE;
2682 }
2683 } else {
2684 /* first UTF-8 byte; not handled here anymore */
2685 if ((newchar & 0xE0) == 0xC0) {
2686 utfcount = 2;
2687 unichar = newchar & 0x1F;
2688 } else if ((newchar & 0xF0) == 0xE0) {
2689 utfcount = 3;
2690 unichar = newchar & 0x0F;
2691 } else if ((newchar & 0xF8) == 0xF0) {
2692 utfcount = 4;
2693 unichar = newchar & 0x07;
2694 } else if ((newchar & 0xFC) == 0xF8) {
2695 utfcount = 5;
2696 unichar = newchar & 0x03;
2697 } else if ((newchar & 0xFE) == 0xFC) {
2698 utfcount = 6;
2699 unichar = newchar & 0x01;
2700 } else { /* ignore illegal UTF-8 code */
2701 return FINE;
2702 }
2703 utfcount --;
2704 return FINE;
2705 }
2706 } else {
2707 /* 8-bit input for 8-bit text */
2708 buffer [0] = newchar;
2709 buffer [1] = '\0';
2710 }
2711
2712 /* right-to-left support */
2713 if (poormansbidi) {
2714 if (! utf8_text) {
2715 unichar = unicodevalue (buffer);
2716 }
2717 if (is_right_to_left (previous_unichar)) {
2718 if (newchar == '\n') {
2719 MOVLEND ();
2720 } else if (iscombining (unichar) && * cur_text != '\n') {
2721 MOVRT ();
2722 } else if (unichar != ' ' && unichar != '\t' && ! is_right_to_left (unichar)) {
2723 unsigned long rc = unicodevalue (cur_text);
2724 while (rc == ' ' || rc == '\t' || is_right_to_left (rc) || iscombining (rc)) {
2725 MOVRT ();
2726 rc = unicodevalue (cur_text);
2727 }
2728 }
2729 }
2730 }
2731
2732
2733 /* Insert the new character */
2734 offset = cur_text - cur_line->text + length_of (buffer);
2735 if (insert_text (cur_line, cur_text, buffer) == ERRORS) {
2736 return ERRORS;
2737 }
2738
2739 /* Fix screen */
2740 if (newchar == '\n') {
2741 if (y == SCREENMAX) { /* Can't use display () */
2742 print_line (y, cur_line);
2743 (void) forward_scroll (True);
2744 move_to (0, y);
2745 } else {
2746 reset (top_line, y); /* Reset pointers */
2747 if (can_add_line) {
2748 clean_menus ();
2749 add_line (y + 1);
2750 scrollbar_scroll_down (y + 1);
2751 clear_status ();
2752 display (y, cur_line, 1, y + 1);
2753 } else {
2754 display (y, cur_line, last_y - y, y + 1);
2755 }
2756 move_to (0, y + 1);
2757 }
2758 } else if (x + width == XBREAK) { /* If line must be shifted, just call move_to */
2759 move_to (x + width, y);
2760 /* adjust in case of combining character position */
2761 if (cur_line->text + offset != cur_text) {
2762 move_address (cur_line->text + offset, y);
2763 }
2764 } else { /* else display rest of line */
2765 /* also redraw previous char if it was incomplete ...
2766 just always redraw it!
2767 if (width != 0) {
2768 put_line (y, cur_line, x, False, False);
2769 move_to (x + width, y);
2770 } else
2771 */
2772 {
2773 /* take care of combining char added to left char */
2774 int newx = x + width;
2775 move_to (newx, y);
2776 if (iswide (unicodevalue (buffer))) {
2777 move_to (x - 3, y);
2778 } else {
2779 move_to (x - 1, y);
2780 }
2781 put_line (y, cur_line, x, False, False);
2782 /* adjust to new position */
2783 move_address (cur_line->text + offset, y);
2784 }
2785 }
2786
2787 /* right-to-left support */
2788 if (poormansbidi) {
2789 if (unichar == ' ' || unichar == '\t') {
2790 if (is_right_to_left (previous_unichar)) {
2791 move_to (x - 1, y);
2792 }
2793 } else if (unichar != '\n') {
2794 if (iscombining (unichar)) {
2795 if (is_right_to_left (previous_unichar)) {
2796 move_to (x - 1, y);
2797 }
2798 } else {
2799 if (is_right_to_left (unichar)) {
2800 move_to (x - 1, y);
2801 }
2802 previous_unichar = unichar;
2803 }
2804 }
2805 }
2806
2807 if (JUSlvl > 0) {
2808 JUSandreturn ();
2809 }
2810 return FINE;
2811 }
2812
2813 void
S0(newchar)2814 S0 (newchar)
2815 register character newchar;
2816 {
2817 S1byte (newchar, 0, utf8_input);
2818 }
2819
2820 static
2821 int
Sbyte(newchar)2822 Sbyte (newchar)
2823 register character newchar;
2824 {
2825 return S1byte (newchar, JUSlevel, False);
2826 }
2827
2828
2829 static
2830 FLAG
Sutf8char(newchar)2831 Sutf8char (newchar)
2832 unsigned long newchar;
2833 {
2834 if (newchar < 0x80) {
2835 S1byte ((character) newchar, JUSlevel, True);
2836 } else if (newchar < 0x800) {
2837 S1byte ((character) (0xC0 | (newchar >> 6)), JUSlevel, True);
2838 S1byte ((character) (0x80 | (newchar & 0x3F)), JUSlevel, True);
2839 } else if (newchar < 0x10000) {
2840 S1byte ((character) (0xE0 | (newchar >> 12)), JUSlevel, True);
2841 S1byte ((character) (0x80 | ((newchar >> 6) & 0x3F)), JUSlevel, True);
2842 S1byte ((character) (0x80 | (newchar & 0x3F)), JUSlevel, True);
2843 } else if (newchar < 0x200000) {
2844 S1byte ((character) (0xF0 | (newchar >> 18)), JUSlevel, True);
2845 S1byte ((character) (0x80 | ((newchar >> 12) & 0x3F)), JUSlevel, True);
2846 S1byte ((character) (0x80 | ((newchar >> 6) & 0x3F)), JUSlevel, True);
2847 S1byte ((character) (0x80 | (newchar & 0x3F)), JUSlevel, True);
2848 } else if (newchar < 0x4000000) {
2849 S1byte ((character) (0xF8 | (newchar >> 24)), JUSlevel, True);
2850 S1byte ((character) (0x80 | ((newchar >> 18) & 0x3F)), JUSlevel, True);
2851 S1byte ((character) (0x80 | ((newchar >> 12) & 0x3F)), JUSlevel, True);
2852 S1byte ((character) (0x80 | ((newchar >> 6) & 0x3F)), JUSlevel, True);
2853 S1byte ((character) (0x80 | (newchar & 0x3F)), JUSlevel, True);
2854 } else if (newchar < 0x80000000) {
2855 S1byte ((character) (0xFC | (newchar >> 30)), JUSlevel, True);
2856 S1byte ((character) (0x80 | ((newchar >> 24) & 0x3F)), JUSlevel, True);
2857 S1byte ((character) (0x80 | ((newchar >> 18) & 0x3F)), JUSlevel, True);
2858 S1byte ((character) (0x80 | ((newchar >> 12) & 0x3F)), JUSlevel, True);
2859 S1byte ((character) (0x80 | ((newchar >> 6) & 0x3F)), JUSlevel, True);
2860 S1byte ((character) (0x80 | (newchar & 0x3F)), JUSlevel, True);
2861 } else {
2862 error ("Invalid Unicode value");
2863 return False;
2864 }
2865 return True;
2866 }
2867
2868 static
2869 void
Sutf8(newchar)2870 Sutf8 (newchar)
2871 unsigned long newchar;
2872 {
2873 (void) Sutf8char (newchar);
2874 }
2875
2876 static
2877 FLAG
Scjk(code)2878 Scjk (code)
2879 unsigned long code;
2880 {
2881 character cjkbytes [5];
2882 character * cp;
2883
2884 if (no_char (code)) {
2885 ring_bell ();
2886 error ("Invalid character");
2887 return False;
2888 } else {
2889 (void) cjkencode (code, cjkbytes);
2890 if (* cjkbytes != '\0') {
2891 cp = cjkbytes;
2892 while (* cp != '\0') {
2893 S1byte (* cp ++, JUSlevel, False);
2894 }
2895 } else {
2896 ring_bell ();
2897 error ("Invalid CJK character code");
2898 return False;
2899 }
2900 }
2901 return True;
2902 }
2903
2904 static FLAG deliver_dont_insert = False;
2905 static unsigned long delivered_character;
2906
2907 /*
2908 * insert_character inserts character literally
2909 */
2910 static
2911 FLAG
insert_character(code)2912 insert_character (code)
2913 unsigned long code;
2914 {
2915 if (deliver_dont_insert) {
2916 delivered_character = code;
2917 return True;
2918 }
2919
2920 if (code == CHAR_UNKNOWN) {
2921 ring_bell ();
2922 error ("Unknown character mnemonic");
2923 return False;
2924 } else if (code == CHAR_INVALID) {
2925 ring_bell ();
2926 error ("Invalid character");
2927 return False;
2928 } else if (utf8_text) {
2929 return Sutf8char (code);
2930 } else if (cjk_text) {
2931 return Scjk (code);
2932 } else if (code < 0x100) {
2933 Sbyte (code);
2934 return True;
2935 } else {
2936 ring_bell ();
2937 error ("Invalid character");
2938 return False;
2939 }
2940 }
2941
2942 FLAG
insert_unichar(uc)2943 insert_unichar (uc)
2944 unsigned long uc;
2945 {
2946 return insert_character (charcode (uc));
2947 }
2948
2949
2950 /*
2951 * Insert newline with auto indentation.
2952 */
2953 static
2954 void
SNLindent(do_autonumber)2955 SNLindent (do_autonumber)
2956 FLAG do_autonumber;
2957 {
2958 char * coptext;
2959 unsigned long ch;
2960 unsigned long unich;
2961 FLAG afterpara;
2962 lineend_type prev_ret_type = cur_line->return_type;
2963 int numberlen = 0;
2964
2965 if (Sbyte ('\n') == ERRORS) {
2966 return;
2967 }
2968 coptext = cur_line->prev->text;
2969 afterpara = paragraphending (cur_line->prev, prev_ret_type);
2970
2971 /* find beginning of ended paragraph */
2972 if (afterpara) {
2973 LINE * paraline = cur_line->prev;
2974 char * s;
2975 /* should the following be a preference option?
2976 meaning an item starts with a bullet or numbering
2977 even "within a paragraph"
2978 */
2979 FLAG stop_at_item = True; /* regardless of paragraphending? */
2980
2981 while (paraline->prev != header) {
2982 LINE * prevline = paraline->prev;
2983 if (paragraphending (prevline, prevline->return_type)) {
2984 stop_at_item = False; /* trigger final item check */
2985 break;
2986 }
2987
2988 if (stop_at_item) {
2989 s = paraline->text;
2990 while (iswhitespace (unicodevalue (s))) {
2991 advance_char (& s);
2992 }
2993 numberlen = numbering (s);
2994 if (isitemchar (unicodevalue (s)) || numberlen > 0) {
2995 coptext = paraline->text;
2996 break;
2997 }
2998 }
2999
3000 paraline = prevline;
3001 }
3002
3003 if (! stop_at_item || paraline->prev == header) {
3004 s = paraline->text;
3005 while (iswhitespace (unicodevalue (s))) {
3006 advance_char (& s);
3007 }
3008 numberlen = numbering (s);
3009 if (isitemchar (unicodevalue (s)) || numberlen > 0) {
3010 coptext = paraline->text;
3011 }
3012 }
3013 }
3014
3015 ch = charvalue (coptext);
3016 unich = unicode (ch);
3017 if (unich == 0xFEFF) {
3018 /* skip initial ZERO WIDTH NO-BREAK SPACE; BYTE ORDER MARK */
3019 advance_char (& coptext);
3020 ch = charvalue (coptext);
3021 unich = unicode (ch);
3022 }
3023 /* clone white space indentation */
3024 while (iswhitespace (unich)) {
3025 insert_character (ch);
3026 advance_char (& coptext);
3027 ch = charvalue (coptext);
3028 unich = unicode (ch);
3029 }
3030 numberlen = numbering (coptext);
3031
3032 /* list and auto-numbering support */
3033
3034 /* bullet list: check whether there is a bullet or dash */
3035 if (isitemchar (unich)) {
3036 /* cancel right-to-left memory for proper positioning */
3037 previous_unichar = 0;
3038
3039 if (afterpara) {
3040 /* clone bullet from previous line */
3041 insert_character (ch);
3042 } else {
3043 /* indent over bullet (within list item) */
3044 if (is_wideunichar (unich)) {
3045 insert_unichar (0x3000); /* wide space */
3046 } else {
3047 insert_character (' ');
3048 }
3049 }
3050
3051 advance_char (& coptext);
3052 /* clone white space separator */
3053 ch = charvalue (coptext);
3054 unich = unicode (ch);
3055 while (iswhitespace (unich)) {
3056 insert_character (ch);
3057 advance_char (& coptext);
3058 ch = charvalue (coptext);
3059 unich = unicode (ch);
3060 }
3061
3062 if (prev_ret_type == lineend_PS) {
3063 cur_line->prev->return_type = lineend_PS;
3064 }
3065 return;
3066 }
3067
3068 /* auto-numbering: check whether there is numbering (1. 2. ...) */
3069 if (do_autonumber && numberlen > 0) {
3070 char numbuf [22];
3071 char * numpoi = numbuf;
3072 char * afternumber = coptext + numberlen;
3073
3074 /* cancel right-to-left memory for proper positioning */
3075 previous_unichar = 0;
3076
3077 if (afterpara) {
3078 /* copy numbering prefix, e.g. 1.2. of 1.2.3. */
3079 while (coptext < lastsubnumberpoi && * coptext != '\n') {
3080 ch = charvalue (coptext);
3081 insert_character (ch);
3082 advance_char (& coptext);
3083 }
3084 /* insert incremented subnumber, e.g. 4. */
3085 sprintf (numbuf, "%d", lastsubnumber + 1);
3086 while (* numpoi) {
3087 insert_character (* numpoi ++);
3088 /* align source and insertion for remaining whitespace */
3089 if (* coptext >= '.' && * coptext <= '9') {
3090 coptext ++;
3091 }
3092 }
3093 insert_character ('.');
3094 } else {
3095 while (numberlen -- > 0) {
3096 insert_character (' ');
3097 }
3098 }
3099 coptext = afternumber;
3100
3101 /* clone white space separator */
3102 ch = charvalue (coptext);
3103 unich = unicode (ch);
3104 while (iswhitespace (unich)) {
3105 insert_character (ch);
3106 advance_char (& coptext);
3107 ch = charvalue (coptext);
3108 unich = unicode (ch);
3109 }
3110
3111 if (prev_ret_type == lineend_PS) {
3112 cur_line->prev->return_type = lineend_PS;
3113 }
3114 return;
3115 }
3116 }
3117
3118 /*
3119 * Insert new line at current location. Triggered by Enter key / CR.
3120 */
3121 void
SNL()3122 SNL ()
3123 {
3124 trace_keytrack ("SNL", 0);
3125 if ((keyshift & altctrlshift_mask) == alt_mask) {
3126 keyshift = 0;
3127 Popmark ();
3128 return;
3129 }
3130
3131 if (dont_modify ()) {
3132 return;
3133 }
3134
3135 if (utf8_lineends == False && (keyshift & ctrl_mask)) {
3136 keyshift = 0;
3137 /* new line within same paragraph */
3138 if (JUSmode == 0) {
3139 if (* (cur_text -1) != ' ') {
3140 S (' ');
3141 }
3142 }
3143 }
3144
3145 if (autoindent == False
3146 || last_delta_readchar < 10 || average_delta_readchar < 10) {
3147 if (suppress_pasting_double_LF) {
3148 /* try to compensate an Exceed bug pasting LF twice */
3149 if (lastchar != '\r') {
3150 S ('\n');
3151 } else {
3152 /* skipping LF, should prevent skipping multiple */
3153 lastchar = ' '; /* does not work */
3154 }
3155 } else {
3156 S ('\n');
3157 }
3158 } else {
3159 SNLindent (autonumber);
3160 }
3161 lastchar = '\r';
3162 }
3163
3164 static
3165 void
Spair(l,r)3166 Spair (l, r)
3167 character l;
3168 character r;
3169 {
3170 S1byte (l, JUSlevel, utf8_input);
3171 SNLindent (False);
3172 S1byte (r, JUSlevel, utf8_input);
3173
3174 MOVUP ();
3175 MOVLEND ();
3176 }
3177
3178 static
3179 void
S(newchar)3180 S (newchar)
3181 register character newchar;
3182 {
3183 FLAG prev_lastspace_smart = lastspace_smart;
3184 lastspace_smart = False;
3185
3186 if (hop_flag > 0) {
3187 lastchar = newchar;
3188 if (newchar == '\n') {
3189 S1byte (newchar, JUSlevel, utf8_input);
3190 return;
3191 }
3192 hop_flag = 0;
3193 flags_changed = True;
3194 if (newchar == '(') {
3195 Spair ('(', ')');
3196 return;
3197 } else if (newchar == '[') {
3198 Spair ('[', ']');
3199 return;
3200 } else if (newchar == '{') {
3201 Spair ('{', '}');
3202 return;
3203 } else if (newchar == '<') {
3204 Spair ('<', '>');
3205 return;
3206 } else if (newchar == '/') {
3207 if (* cur_text != '\n') {
3208 SNLindent (False);
3209 MOVUP ();
3210 }
3211 S1byte ('/', JUSlevel, utf8_input);
3212 S1byte ('*', JUSlevel, utf8_input);
3213 S1byte ('*', JUSlevel, utf8_input);
3214 SNLindent (False);
3215 S1byte (' ', JUSlevel, utf8_input);
3216 SNLindent (False);
3217 S1byte ('*', JUSlevel, utf8_input);
3218 S1byte ('/', JUSlevel, utf8_input);
3219 MOVUP ();
3220 S1byte (' ', JUSlevel, utf8_input);
3221 S1byte (' ', JUSlevel, utf8_input);
3222 return;
3223 }
3224 } else if (expand_tabs && newchar == '\t') {
3225 do {
3226 S1byte (' ', JUSlevel, utf8_input);
3227 } while (x % tabsize != 0);
3228 return;
3229 } else if (newchar == ' ' && (keyshift & ctrlshift_mask) == ctrlshift_mask) {
3230 (void) insert_unichar (0xA0); /* NO-BREAK SPACE */
3231 return;
3232 } else if (quote_type != 0 && newchar == '-'
3233 && lastchar == '-'
3234 && cur_text != cur_line->text && * (cur_text - 1) == '-'
3235 && (utf8_text || ! no_char (charcode (0x2013)))
3236 )
3237 { /* handle smart dashes */
3238 lastchar = ' ';
3239 DPC0 ();
3240 if (cur_text != cur_line->text && * (cur_text - 1) == ' ') {
3241 Scharacter (charcode (0x2013)); /* – EN DASH */
3242
3243 S1byte (' ', JUSlevel, utf8_input);
3244 lastspace_smart = True;
3245 } else {
3246 Scharacter (charcode (0x2014)); /* – EM DASH */
3247 }
3248 return;
3249 } else if (quote_type != 0 && lastchar == '-'
3250 && (newchar == ' ' || newchar == '\t')
3251 && cur_text != cur_line->text && * (cur_text - 1) == '-'
3252 && onlywhitespacespacebefore (cur_line, cur_text - 1)
3253 && (utf8_text || ! no_char (charcode (0x2013)))
3254 )
3255 { /* handle leading smart dash */
3256 lastchar = ' ';
3257 DPC0 ();
3258 Scharacter (charcode (0x2013)); /* – EN DASH */
3259 S1byte (newchar, JUSlevel, utf8_input);
3260 return;
3261 } else if (quote_type != 0 && newchar == '-'
3262 && (* cur_text == '\n' || iswhitespace (unicodevalue (cur_text)))
3263 && onlywhitespacespacebefore (cur_line, cur_text)
3264 && (utf8_text || ! no_char (charcode (0x2013)))
3265 )
3266 { /* insert leading smart dash */
3267 lastchar = '�'; /* use as marker */
3268 Scharacter (charcode (0x2013)); /* – EN DASH */
3269 return;
3270 } else if (quote_type != 0 && newchar == '-' && lastchar == (character) '�'
3271 && (utf8_text || ! no_char (charcode (0x2013)))
3272 )
3273 { /* swallow second '-' for already inserted leading smart dash */
3274 lastchar = ' ';
3275 return;
3276 } else if (quote_type != 0 && newchar == ' '
3277 && lastchar == '-'
3278 && cur_text - cur_line->text >= 2
3279 && * (cur_text - 1) == '-'
3280 && * (cur_text - 2) == ' '
3281 && (utf8_text || ! no_char (charcode (0x2212)))
3282 )
3283 { /* handle smart minus */
3284 lastchar = ' ';
3285 DPC0 ();
3286 Scharacter (charcode (0x2212)); /* − MINUS SIGN */
3287 S1byte (' ', JUSlevel, utf8_input);
3288 return;
3289 } else if (utf8_text && quote_type != 0 && newchar == '-'
3290 && lastchar == '<'
3291 && cur_text != cur_line->text && * (cur_text - 1) == '<')
3292 { /* handle smart arrows */
3293 lastchar = ' ';
3294 DPC0 ();
3295 Sutf8 (0x2190); /* ← LEFTWARDS ARROW */
3296 return;
3297 } else if (utf8_text && quote_type != 0 && newchar == '>'
3298 && cur_text != cur_line->text
3299 && ((lastchar == '-' && * (cur_text - 1) == '-')
3300 || (lastchar == (character) '�' /*&& prev U+2013*/)
3301 )
3302 )
3303 { /* handle smart arrows */
3304 lastchar = ' ';
3305 DPC0 ();
3306 Sutf8 (0x2192); /* → RIGHTWARDS ARROW */
3307 return;
3308 } else if (utf8_text && quote_type != 0 && newchar == '>'
3309 && lastchar == '<'
3310 && cur_text != cur_line->text && * (cur_text - 1) == '<')
3311 { /* handle smart arrows */
3312 lastchar = ' ';
3313 DPC0 ();
3314 Sutf8 (0x2194); /* ↔ LEFT RIGHT ARROW */
3315 return;
3316 } else if (quote_type != 0 && newchar == '-'
3317 && (streq (script (charvalue (cur_text)), "Hebrew")
3318 || streq (script (precedingchar (cur_text, cur_line->text)), "Hebrew")
3319 )
3320 )
3321 { /* handle Maqaf */
3322 lastchar = ' ';
3323 (void) insert_unichar (0x05BE); /* ־ MAQAF */
3324 return;
3325 } else if (newchar == ' ' && prev_lastspace_smart) {
3326 return;
3327 }
3328
3329 lastchar = newchar;
3330 S1byte (newchar, JUSlevel, utf8_input);
3331 }
3332
3333 /**
3334 Insert character by function
3335 */
3336 void
STAB()3337 STAB ()
3338 {
3339 S ('\t');
3340 }
3341
3342 void
SSPACE()3343 SSPACE ()
3344 {
3345 S (' ');
3346 }
3347
3348 /**
3349 Underline preceding header
3350 */
3351 void
Underline()3352 Underline ()
3353 {
3354 FLAG at_end = * cur_text == '\n';
3355 int cols;
3356
3357 hop_flag = 0;
3358
3359 if (cur_text == cur_line->text) {
3360 MOVLF ();
3361 }
3362 MOVLEND ();
3363 cols = col_count (cur_line->text);
3364
3365 SNLindent (False);
3366 cols -= col_count (cur_line->text);
3367 while (cols > 0) {
3368 S0 ('-');
3369 cols --;
3370 }
3371
3372 if (! at_end) {
3373 MOVRT ();
3374 if (white_space (* cur_text)) {
3375 MNW ();
3376 }
3377 }
3378 }
3379
3380 void
reset_smart_replacement()3381 reset_smart_replacement ()
3382 {
3383 lastchar = '\0';
3384 lastspace_smart = False;
3385 }
3386
3387
3388 static
3389 unsigned long
stropped(ch,cpoi,begin_line)3390 stropped (ch, cpoi, begin_line)
3391 unsigned long ch;
3392 char * cpoi;
3393 char * begin_line;
3394 {
3395 unsigned long unichar = unicode (ch);
3396 unsigned long chup = case_convert (unichar, 1);
3397 if (chup == unichar) {
3398 /* wasn't a small letter */
3399 return ch;
3400 } else {
3401 /* small letter to be inserted, check whether to capitalize */
3402 FLAG strop = False;
3403 while (cpoi != begin_line) {
3404 unsigned long pre;
3405 precede_char (& cpoi, begin_line);
3406 pre = unicodevalue (cpoi);
3407 if (isLetter (pre)) {
3408 unsigned long small = case_convert (pre, -1);
3409 if (small == pre) {
3410 /* preceding small letter */
3411 return ch;
3412 } else {
3413 /* minimal condition found */
3414 strop = True;
3415 }
3416 } else {
3417 /* arrived before identifier */
3418 break;
3419 }
3420 }
3421 if (strop) {
3422 /* check escaping context (string) */
3423 int qn = 0;
3424 char prevc = '\0';
3425 while (begin_line <= cpoi) {
3426 unsigned long qc = charvalue (begin_line);
3427 if ((qc == '"' || qc == '\'')
3428 && (qn % 2 == 0 || prevc != '\\')) {
3429 qn ++;
3430 }
3431 prevc = qc;
3432 advance_char (& begin_line);
3433 }
3434 if (qn % 2) { /* within string */
3435 return ch;
3436 } else { /* return capital letter to insert */
3437 return encodedchar (chup);
3438 }
3439 } else {
3440 return ch;
3441 }
3442 }
3443 }
3444
3445 void
Scharacter(code)3446 Scharacter (code)
3447 unsigned long code;
3448 {
3449 if (no_char (code)) {
3450 ring_bell ();
3451 error ("Invalid character");
3452 return;
3453 }
3454
3455 if (lowcapstrop) {
3456 code = stropped (code, cur_text, cur_line->text);
3457 }
3458
3459 if (code < 0x80) {
3460 #ifdef suppress_pasting_CRLF
3461 if (code == '\n' && lastchar == '\r'
3462 && (last_delta_readchar < 1000 || average_delta_readchar < 1000)) {
3463 /* skip on pasting CRLF */
3464 lastchar = '\n';
3465 } else {
3466 S (code); /* go through HOP handling */
3467 }
3468 #else
3469 S (code); /* go through HOP handling */
3470 #endif
3471 } else if (utf8_text) {
3472 Sutf8 (code);
3473 } else if (cjk_text) {
3474 (void) Scjk (code);
3475 } else if (code < 0x100) {
3476 Sbyte (code & 0xFF);
3477 } else {
3478 ring_bell ();
3479 error ("Invalid character");
3480 }
3481 }
3482
3483
3484 /*
3485 APPNL inserts a newline at the current position;
3486 stays in front of it (on current line);
3487 keeps the line end type of the current line.
3488 */
3489 void
APPNL()3490 APPNL ()
3491 {
3492 lineend_type return_type;
3493
3494 if (dont_modify ()) {
3495 return;
3496 }
3497
3498 if (hop_flag > 0) {
3499 return_type = lineend_NONE;
3500 hop_flag = 0;
3501 } else if (cur_line->return_type == lineend_NONE
3502 || cur_line->return_type == lineend_NUL) {
3503 return_type = default_lineend;
3504 } else if (! utf8_text &&
3505 (cur_line->return_type == lineend_PS
3506 || cur_line->return_type == lineend_LS)) {
3507 return_type = default_lineend;
3508 } else if (cur_line->return_type == lineend_PS) {
3509 return_type = lineend_LS;
3510 } else {
3511 return_type = cur_line->return_type;
3512 }
3513
3514 if (Sbyte ('\n') == ERRORS) {
3515 return;
3516 }
3517 MOVUP (); /* Move one line up */
3518 move_to (LINE_END, y); /* Move to end of this line */
3519 if (cur_line->return_type != return_type) {
3520 cur_line->return_type = return_type;
3521 if (return_type == lineend_NONE) {
3522 if (total_chars >= 0) {
3523 total_chars --;
3524 }
3525 }
3526 put_line (y, cur_line, x, True, False);
3527 }
3528 }
3529
3530
3531 /*======================================================================*\
3532 |* Convert lineend type *|
3533 \*======================================================================*/
3534
3535 static
3536 void
convlineend_cur(ret)3537 convlineend_cur (ret)
3538 lineend_type ret;
3539 {
3540 if (dont_modify ()) {
3541 return;
3542 }
3543
3544 if (cur_line->return_type != ret) {
3545 if (cur_line->return_type == lineend_NONE) {
3546 if (total_chars >= 0) {
3547 total_chars ++;
3548 }
3549 }
3550 cur_line->return_type = ret;
3551 set_modified ();
3552 }
3553 }
3554
3555 static
3556 void
convlineend_all(ret)3557 convlineend_all (ret)
3558 lineend_type ret;
3559 {
3560 LINE * line;
3561 FLAG modified = False;
3562
3563 if (dont_modify ()) {
3564 return;
3565 }
3566
3567 for (line = header->next; line != tail; line = line->next) {
3568 if (line->return_type == lineend_LF
3569 || line->return_type == lineend_CRLF
3570 || line->return_type == lineend_CR) {
3571 if (line->return_type != ret) {
3572 modified = True;
3573 line->return_type = ret;
3574 }
3575 }
3576 }
3577 if (modified) {
3578 set_modified ();
3579 }
3580 }
3581
3582
3583 void
convlineend_cur_LF()3584 convlineend_cur_LF ()
3585 {
3586 convlineend_cur (lineend_LF);
3587 }
3588
3589 void
convlineend_cur_CRLF()3590 convlineend_cur_CRLF ()
3591 {
3592 convlineend_cur (lineend_CRLF);
3593 }
3594
3595 void
convlineend_all_LF()3596 convlineend_all_LF ()
3597 {
3598 convlineend_all (lineend_LF);
3599 }
3600
3601 void
convlineend_all_CRLF()3602 convlineend_all_CRLF ()
3603 {
3604 convlineend_all (lineend_CRLF);
3605 }
3606
3607
3608 /*======================================================================*\
3609 |* Smart quotes *|
3610 \*======================================================================*/
3611
3612 static
3613 unsigned long
quote_mark_value(qt,pos)3614 quote_mark_value (qt, pos)
3615 int qt;
3616 quoteposition_type pos;
3617 {
3618 unsigned long unichar;
3619 int utflen;
3620 char * q = quote_mark (qt, pos);
3621 utf8_info (q, & utflen, & unichar);
3622 return unichar;
3623 }
3624
3625 int quote_type = 0;
3626 int prev_quote_type = 0;
3627 int default_quote_type = 0;
3628 static FLAG quote_open [2] = {False, False};
3629
3630 static
3631 void
reset_quote_state()3632 reset_quote_state ()
3633 {
3634 quote_open [False] = False;
3635 quote_open [True] = False;
3636 }
3637
3638 void
set_quote_type(qt)3639 set_quote_type (qt)
3640 int qt;
3641 {
3642 if (qt >= 0 && qt < count_quote_types ()) {
3643 quote_type = qt;
3644 /*spacing_quotes = spacing_quote_type (qt);*/
3645 }
3646 reset_quote_state ();
3647 set_quote_menu ();
3648 }
3649
3650 void
set_quote_style(q)3651 set_quote_style (q)
3652 char * q;
3653 {
3654 set_quote_type (lookup_quotes (q));
3655 }
3656
3657
3658 static
3659 void
Sapostrophe()3660 Sapostrophe ()
3661 {
3662 unsigned long apostrophe = 0x2019;
3663 /* check whether apostrophe is available in text encoding */
3664 if (! utf8_text) {
3665 apostrophe = encodedchar (apostrophe);
3666 if (no_char (apostrophe)) {
3667 error ("Apostrophe not available in current encoding");
3668 return;
3669 }
3670 }
3671 /* insert apostrophe */
3672 Scharacter (apostrophe);
3673 }
3674
3675 static
3676 void
Squote(doublequote)3677 Squote (doublequote)
3678 FLAG doublequote;
3679 {
3680 int alternate = keyshift & alt_mask;
3681 int qt = alternate ? prev_quote_type : quote_type;
3682 FLAG spacing_quotes = spacing_quote_type (qt);
3683
3684 if (qt == 0) {
3685 if (doublequote) {
3686 S ('"');
3687 } else {
3688 S ('\'');
3689 }
3690 } else {
3691 unsigned long leftquote;
3692 unsigned long rightquote;
3693 FLAG insert_left;
3694 unsigned long prevchar = unicode (precedingchar (cur_text, cur_line->text));
3695 char * cp = cur_line->text;
3696
3697 /* check white space left of cursor */
3698 while (* cp != '\0' && cp != cur_text && white_space (* cp)) {
3699 advance_char (& cp);
3700 }
3701
3702 /* determine mode (left/right) and state */
3703 if (cp == cur_text) {
3704 /* only white space left of current position */
3705 insert_left = True;
3706 quote_open [doublequote] = True;
3707 } else if (quote_open [doublequote]) {
3708 insert_left = False;
3709 quote_open [doublequote] = False;
3710 } else if (prevchar == 0x0A) {
3711 insert_left = True;
3712 quote_open [doublequote] = UNSURE;
3713 } else if (! doublequote && isLetter (prevchar)) {
3714 Sapostrophe ();
3715 return;
3716 } else if (is_wideunichar (prevchar)) {
3717 if (quote_open [doublequote] == UNSURE
3718 || quote_open [doublequote] == OPEN) {
3719 insert_left = False;
3720 quote_open [doublequote] = False;
3721 } else {
3722 insert_left = True;
3723 quote_open [doublequote] = True;
3724 }
3725 } else {
3726 /* ! quote_open [doublequote] */
3727 insert_left = opensquote (prevchar)
3728 || prevchar == quote_mark_value (qt, LEFTDOUBLE)
3729 || prevchar == quote_mark_value (qt, LEFTSINGLE)
3730 ;
3731 if (insert_left) {
3732 quote_open [doublequote] = OPEN;
3733 } else {
3734 quote_open [doublequote] = False;
3735 }
3736 }
3737
3738 /* determine quotemarks of selected quote style */
3739 if (doublequote) {
3740 leftquote = quote_mark_value (qt, LEFTDOUBLE);
3741 rightquote = quote_mark_value (qt, RIGHTDOUBLE);
3742 } else {
3743 leftquote = quote_mark_value (qt, LEFTSINGLE);
3744 rightquote = quote_mark_value (qt, RIGHTSINGLE);
3745 }
3746
3747 /* check whether quote marks are available in text encoding */
3748 if (! utf8_text) {
3749 leftquote = encodedchar (leftquote);
3750 rightquote = encodedchar (rightquote);
3751 if (no_char (leftquote) || no_char (rightquote)) {
3752 error ("Quote marks style not available in current encoding");
3753 return;
3754 }
3755 }
3756
3757 /* insert quote mark */
3758 if (insert_left) {
3759 Scharacter (leftquote);
3760 if (spacing_quotes) {
3761 if (no_char (encodedchar (0x00A0))) {
3762 Scharacter (0x0020);
3763 } else {
3764 Scharacter (0x00A0);
3765 }
3766 lastspace_smart = True;
3767 }
3768 } else {
3769 if (spacing_quotes) {
3770 if (lastchar == ' ' && cur_text != cur_line->text
3771 && * (cur_text - 1) == ' ')
3772 {
3773 DPC0 ();
3774 }
3775 if (no_char (encodedchar (0x00A0))) {
3776 Scharacter (0x0020);
3777 } else {
3778 Scharacter (0x00A0);
3779 }
3780 }
3781 Scharacter (rightquote);
3782 }
3783 }
3784 }
3785
3786 void
Sdoublequote()3787 Sdoublequote ()
3788 {
3789 if ((keyshift & altshift_mask) == altshift_mask) {
3790 S ('"');
3791 } else {
3792 Squote (True);
3793 }
3794 }
3795
3796 void
Ssinglequote()3797 Ssinglequote ()
3798 {
3799 if ((keyshift & altshift_mask) == altshift_mask) {
3800 S ('\'');
3801 } else if (hop_flag > 0 && ! (keyshift & alt_mask)) {
3802 /* HOP (not Alt): enter apostrophe */
3803 Sapostrophe ();
3804 } else {
3805 Squote (False);
3806 }
3807 }
3808
3809 void
Sacute()3810 Sacute ()
3811 {
3812 if (quote_type == 0 && hop_flag == 0) {
3813 insert_unichar ((character) '�');
3814 } else {
3815 Sapostrophe ();
3816 }
3817 }
3818
3819 void
Sgrave()3820 Sgrave ()
3821 {
3822 if (quote_type == 0 && hop_flag == 0) {
3823 S ('`');
3824 } else {
3825 unsigned long okina = 0x02BB; /* ʻ MODIFIER LETTER TURNED COMMA */
3826 /* check whether okina is available in text encoding */
3827 if (! utf8_text) {
3828 okina = encodedchar (okina);
3829 if (no_char (okina)) {
3830 status_uni ("Glottal stop/ʻokina not available in current encoding");
3831 return;
3832 }
3833 }
3834 /* insert okina */
3835 Scharacter (okina);
3836 }
3837 }
3838
3839 void
Sdash()3840 Sdash ()
3841 {
3842 if (hop_flag > 0) {
3843 Underline ();
3844 } else {
3845 S ('-');
3846 }
3847 }
3848
3849
3850 /*======================================================================*\
3851 |* Web marker insertion *|
3852 \*======================================================================*/
3853
3854 /*
3855 Strip string from first blank.
3856 */
3857 static
3858 void
strip(s)3859 strip (s)
3860 char * s;
3861 {
3862 while (* s != '\0' && (ebcdic_text ? * s != code_SPACE : * s != ' ')) {
3863 s ++;
3864 }
3865 * s = '\0';
3866 }
3867
3868 /*
3869 Turn string single-line (replace line-ends with space).
3870 */
3871 static
3872 void
unwrap(s)3873 unwrap (s)
3874 char * s;
3875 {
3876 while (* s != '\0') {
3877 if (* s == '\n') {
3878 * s = ' ';
3879 }
3880 s ++;
3881 }
3882 }
3883
3884 /*
3885 Insert string buffer.
3886 */
3887 static
3888 void
Sbuf(s)3889 Sbuf (s)
3890 char * s;
3891 {
3892 while (* s) {
3893 Scharacter (charvalue (s));
3894 advance_char (& s);
3895 }
3896 }
3897
3898 static
3899 void
Sunibuf(s)3900 Sunibuf (s)
3901 char * s;
3902 {
3903 while (* s) {
3904 Scharacter (encodedchar (utf8value (s)));
3905 advance_utf8 (& s);
3906 }
3907 }
3908
3909 static
3910 void
embed_HTML()3911 embed_HTML ()
3912 {
3913 char marker [maxPROMPTlen];
3914 char tag [maxPROMPTlen];
3915
3916 if (dont_modify ()) {
3917 return;
3918 }
3919 if (get_string_nokeymap ("Embed text in HTML marker:", marker, True, "") != FINE) {
3920 return;
3921 }
3922 unwrap (marker);
3923
3924 yank_HTML (DELETE);
3925
3926 Sunibuf ("<");
3927 if ((marker [0] == 'A' || marker [0] == 'a') && marker [1] == '\0') {
3928 Sbuf (marker);
3929 Sunibuf (" href=");
3930 } else {
3931 Sbuf (marker);
3932 }
3933 Sunibuf (">");
3934
3935 paste_HTML ();
3936
3937 Sunibuf ("</");
3938 strcpy (tag, marker);
3939 strip (tag);
3940 Sbuf (tag);
3941 Sunibuf (">");
3942 }
3943
3944 static char HTMLmarker [maxPROMPTlen];
3945 static FLAG HTMLmarking = False;
3946
3947 void
HTML()3948 HTML ()
3949 {
3950 if (dont_modify ()) {
3951 return;
3952 }
3953
3954 if (hop_flag > 0) {
3955 hop_flag = 0;
3956 embed_HTML ();
3957 } else {
3958 keyshift = 0;
3959 if (HTMLmarking == False) {
3960 if (FINE != get_string_nokeymap ("Begin HTML marker:", HTMLmarker, True, "")) {
3961 return;
3962 }
3963 unwrap (HTMLmarker);
3964 clear_status ();
3965 Sunibuf ("<");
3966 Sbuf (HTMLmarker);
3967 Sunibuf (">");
3968 HTMLmarking = True;
3969 } else {
3970 Sunibuf ("</");
3971 strip (HTMLmarker);
3972 Sbuf (HTMLmarker);
3973 Sunibuf (">");
3974 HTMLmarking = False;
3975 }
3976 }
3977 }
3978
3979
3980 /*======================================================================*\
3981 |* Character code handling *|
3982 \*======================================================================*/
3983
3984 /*
3985 * Replace current character with its hex/octal/decimal representation.
3986 */
3987 static
3988 character
hexdiguni(c)3989 hexdiguni (c)
3990 unsigned int c;
3991 {
3992 if (c < 10) {
3993 return c + '0';
3994 } else {
3995 return c - 10 + 'A';
3996 }
3997 }
3998
3999 character
hexdig(c)4000 hexdig (c)
4001 unsigned int c;
4002 {
4003 if (ebcdic_text) {
4004 if (c < 10) {
4005 return c + 0xF0;
4006 } else {
4007 return c - 10 + 0xC1;
4008 }
4009 }
4010 return hexdiguni (c);
4011 }
4012
4013 #ifdef gcc_O_bug_fixed
4014 static
4015 #else
4016 /* workaround for gcc -O bug */
4017 extern void insertcode _((character c, int radix));
4018 #endif
4019 void
insertcode(c,radix)4020 insertcode (c, radix)
4021 character c;
4022 int radix;
4023 {
4024 if (radix == 8) {
4025 S (hexdig ((c >> 6) & 007));
4026 S (hexdig ((c >> 3) & 007));
4027 S (hexdig ((c) & 007));
4028 } else if (radix == 16) {
4029 S (hexdig ((c >> 4) & 017));
4030 S (hexdig ((c) & 017));
4031 } else { /* assume radix = 10 or, at least, three digits suffice */
4032 unsigned int radix2 = radix * radix;
4033 S (hexdig (c / radix2));
4034 S (hexdig ((c % radix2) / radix));
4035 S (hexdig (c % radix));
4036 }
4037 }
4038
4039 static
4040 void
insertunicode(unichar)4041 insertunicode (unichar)
4042 unsigned long unichar;
4043 {
4044 if (no_unichar (unichar)) {
4045 error ("No Unicode value");
4046 } else {
4047 if (unichar > 0xFFFF) {
4048 if (unichar > 0x10FFFF) {
4049 insertcode (unichar >> 24, 16);
4050 }
4051 insertcode (unichar >> 16, 16);
4052 }
4053 insertcode (unichar >> 8, 16);
4054 insertcode (unichar, 16);
4055 }
4056 }
4057
4058 static
4059 void
insertvalue(v,radix)4060 insertvalue (v, radix)
4061 unsigned long v;
4062 int radix;
4063 {
4064 char buffer [12];
4065 char * bufpoi = & buffer [11];
4066
4067 if (radix == 16) {
4068 insertunicode (v);
4069 } else {
4070 * bufpoi = '\0';
4071 while (v > 0) {
4072 bufpoi --;
4073 * bufpoi = hexdig (v % radix);
4074 v = v / radix;
4075 }
4076 while (* bufpoi != '\0') {
4077 S (* bufpoi ++);
4078 }
4079 }
4080 }
4081
4082 static
4083 void
changetocode(radix,univalue)4084 changetocode (radix, univalue)
4085 int radix;
4086 FLAG univalue;
4087 {
4088 character c = * cur_text;
4089 int utfcount = 1;
4090 unsigned long unichar;
4091 #ifdef endlessloop
4092 int utflen;
4093 #endif
4094 char buffer [7];
4095 char * textpoi;
4096 char * utfpoi;
4097
4098 if (c == '\n') {
4099 switch (cur_line->return_type) {
4100 case lineend_NONE: unichar = CHAR_INVALID;
4101 break;
4102 case lineend_NUL: unichar = 0x00;
4103 break;
4104 case lineend_LF: unichar = 0x0A;
4105 break;
4106 case lineend_CRLF: unichar = 0x0D0A; /* transforms like 2 bytes */
4107 break;
4108 case lineend_CR: unichar = 0x0D;
4109 break;
4110 case lineend_NL1:
4111 case lineend_NL2:
4112 unichar = 0x85;
4113 break;
4114 case lineend_LS: unichar = 0x2028;
4115 break;
4116 case lineend_PS: unichar = 0x2029;
4117 break;
4118 default: unichar = CHAR_INVALID;
4119 break;
4120 }
4121 if (unichar < 0x100) {
4122 insertcode (unichar, 16);
4123 } else if (! no_unichar (unichar)) {
4124 insertunicode (unichar);
4125 }
4126 } else if (utf8_text) {
4127 if ((c & 0x80) == 0x00) {
4128 utfcount = 1;
4129 unichar = c;
4130 } else if ((c & 0xE0) == 0xC0) {
4131 utfcount = 2;
4132 unichar = c & 0x1F;
4133 } else if ((c & 0xF0) == 0xE0) {
4134 utfcount = 3;
4135 unichar = c & 0x0F;
4136 } else if ((c & 0xF8) == 0xF0) {
4137 utfcount = 4;
4138 unichar = c & 0x07;
4139 } else if ((c & 0xFC) == 0xF8) {
4140 utfcount = 5;
4141 unichar = c & 0x03;
4142 } else if ((c & 0xFE) == 0xFC) {
4143 utfcount = 6;
4144 unichar = c & 0x01;
4145 } else /* illegal UTF-8 code */ {
4146 if (! univalue) {
4147 /* DCC (); */
4148 insertcode (c, radix);
4149 }
4150 error ("Invalid UTF-8 sequence");
4151 return;
4152 }
4153 utfcount --;
4154 utfpoi = buffer;
4155 * utfpoi ++ = c;
4156 textpoi = cur_text;
4157 textpoi ++;
4158 while (utfcount > 0 && (* textpoi & 0xC0) == 0x80) {
4159 c = * textpoi ++;
4160 * utfpoi ++ = c;
4161 unichar = (unichar << 6) | (c & 0x3F);
4162 utfcount --;
4163 }
4164 * utfpoi = '\0';
4165 /* delete_char (False); */
4166 if (univalue) {
4167 insertvalue (unichar, radix);
4168 } else {
4169 utfpoi = buffer;
4170 while (* utfpoi != '\0') {
4171 insertcode (* utfpoi ++, radix);
4172 }
4173 }
4174
4175 #ifdef endlessloop
4176 utf8_info (cur_text, & utflen, & unichar);
4177 if (iscombining_unichar (unichar)) {
4178 changetocode (radix, univalue);
4179 }
4180 #endif
4181
4182 if (utfcount > 0) {
4183 error ("Invalid UTF-8 sequence");
4184 }
4185 } else if (cjk_text) {
4186 if (univalue) {
4187 insertvalue (lookup_encodedchar (charvalue (cur_text)), radix);
4188 } else {
4189 (void) cjkencode (charvalue (cur_text), buffer);
4190 /* DCC (); */
4191 textpoi = buffer;
4192 while (* textpoi != '\0') {
4193 insertcode (* textpoi ++, radix);
4194 }
4195 }
4196 } else if (mapped_text && univalue) {
4197 /* DCC (); */
4198 insertvalue (lookup_encodedchar (c), radix);
4199 } else {
4200 /* DCC (); */
4201 insertcode (c, radix);
4202 }
4203 }
4204
4205 static
4206 void
changefromcode(format,univalue)4207 changefromcode (format, univalue)
4208 char * format;
4209 FLAG univalue;
4210 {
4211 long scancode;
4212 unsigned long code;
4213
4214 if (sscanf (cur_text, format, & scancode) > 0) {
4215 if (scancode == -1) {
4216 ring_bell ();
4217 error ("Character code too long to scan");
4218 return;
4219 }
4220 code = scancode;
4221 if (univalue && (cjk_text || mapped_text)) {
4222 code = encodedchar (code);
4223 if (no_char (code)) {
4224 ring_bell ();
4225 error ("Invalid character");
4226 return;
4227 }
4228 }
4229 if (utf8_text && ! univalue) {
4230 unsigned char buffer [9];
4231 int i = 9;
4232 int utfcount;
4233 buffer [-- i] = '\0';
4234 while (code) {
4235 buffer [-- i] = code & 0xFF;
4236 code = code >> 8;
4237 }
4238 utf8_info (& buffer [i], & utfcount, & code);
4239 if (utfcount != sizeof (buffer) - 1 - i
4240 || utfcount != UTF8_len (buffer [i])
4241 || (buffer [i] & 0xC0) == 0x80) {
4242 ring_bell ();
4243 error ("Illegal UTF-8 sequence");
4244 return;
4245 }
4246 }
4247 (void) insert_character (code);
4248 } else {
4249 ring_bell ();
4250 error ("No character code at text position");
4251 hop_flag = 0;
4252 MOVRT ();
4253 }
4254 }
4255
4256 void
changehex()4257 changehex ()
4258 {
4259 if (hop_flag > 0) {
4260 hop_flag = 0;
4261 if (utf8_text || cjk_text) {
4262 changefromcode ("%lx", False);
4263 } else {
4264 changefromcode ("%2lx", False);
4265 }
4266 } else {
4267 changetocode (16, False);
4268 }
4269 }
4270
4271 void
changeuni()4272 changeuni ()
4273 {
4274 if (hop_flag > 0) {
4275 hop_flag = 0;
4276 changefromcode ("%lx", True);
4277 } else {
4278 changetocode (16, True);
4279 }
4280 }
4281
4282 void
changeoct()4283 changeoct ()
4284 {
4285 if (hop_flag > 0) {
4286 hop_flag = 0;
4287 changefromcode ("%lo", True);
4288 } else {
4289 changetocode (8, True);
4290 }
4291 }
4292
4293 void
changedec()4294 changedec ()
4295 {
4296 if (hop_flag > 0) {
4297 hop_flag = 0;
4298 changefromcode ("%lu", True);
4299 } else {
4300 changetocode (10, True);
4301 }
4302 }
4303
4304
4305 /*======================================================================*\
4306 |* Character information display *|
4307 \*======================================================================*/
4308
4309 static char title [maxXMAX]; /* buffer for description menu title */
4310
4311 /**
4312 Han character information display:
4313 Han info popup menu structure;
4314 must be static because it may be refreshed later.
4315 */
4316 static menuitemtype descr_menu [27];
4317
4318 /**
4319 display_Han shows information about Han characters
4320 (pronunciations and definition from Unihan database)
4321 */
4322 void
display_Han(cpoi,force_utf8)4323 display_Han (cpoi, force_utf8)
4324 char * cpoi;
4325 FLAG force_utf8;
4326 {
4327 int utfcount;
4328 unsigned long hanchar = 0; /* avoid -Wmaybe-uninitialized */
4329 unsigned long unichar;
4330
4331 struct hanentry * entry;
4332 char * flag_Mandarin = "";
4333 char * value_Mandarin = "";
4334 char * flag_Cantonese = "";
4335 char * value_Cantonese = "";
4336 char * flag_Japanese = "";
4337 char * value_Japanese = "";
4338 char * flag_Sino_Japanese = "";
4339 char * value_Sino_Japanese = "";
4340 char * flag_Hangul = "";
4341 char * value_Hangul = "";
4342 char * flag_Korean = "";
4343 char * value_Korean = "";
4344 char * flag_Vietnamese = "";
4345 char * value_Vietnamese = "";
4346 char * flag_HanyuPinlu = "";
4347 char * value_HanyuPinlu = "";
4348 char * flag_HanyuPinyin = "";
4349 char * value_HanyuPinyin = "";
4350 char * flag_XHCHanyuPinyin = "";
4351 char * value_XHCHanyuPinyin = "";
4352 char * flag_Tang = "";
4353 char * value_Tang = "";
4354 char * flag_description = "";
4355 char * value_description = "";
4356
4357 char s [27] [maxXMAX]; /* buffer for description menu */
4358
4359 if (force_utf8 || utf8_text) {
4360 utf8_info (cpoi, & utfcount, & unichar);
4361 if (cjk_text) {
4362 hanchar = encodedchar (unichar);
4363 }
4364 } else if (cjk_text) {
4365 hanchar = charvalue (cpoi);
4366 unichar = lookup_encodedchar (hanchar);
4367 } else {
4368 return;
4369 }
4370
4371 entry = lookup_handescr (unichar);
4372 if (! entry) {
4373 /* ignore missing entry if valid Unicode character is
4374 not assigned to Han character range */
4375 if (! no_char (unichar) && ! streq (script (unichar), "Han")) {
4376 return;
4377 }
4378 }
4379
4380 if (disp_Han_full && ! force_utf8) {
4381 int i = 0;
4382 if (cjk_text && ! no_unichar (unichar)) {
4383 build_string (title, "%04lX U+%04lX", hanchar, unichar);
4384 } else if (cjk_text) {
4385 if (valid_cjk (hanchar, NIL_PTR)) {
4386 build_string (title, "%04lX U? unknown", hanchar);
4387 } else if (no_char (hanchar)) {
4388 build_string (title, "Invalid");
4389 } else {
4390 build_string (title, "%04lX invalid", hanchar);
4391 }
4392 } else {
4393 build_string (title, "U+%04lX", unichar);
4394 }
4395 if (entry && disp_Han_Mandarin && * entry->Mandarin) {
4396 strcpy (s [i], "Mandarin: ");
4397 strcat (s [i], entry->Mandarin);
4398 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4399 i ++;
4400 }
4401 if (entry && disp_Han_Cantonese && * entry->Cantonese) {
4402 strcpy (s [i], "Cantonese: ");
4403 strcat (s [i], entry->Cantonese);
4404 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4405 i ++;
4406 }
4407 if (entry && disp_Han_Japanese && * entry->Japanese) {
4408 strcpy (s [i], "Japanese: ");
4409 strcat (s [i], entry->Japanese);
4410 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4411 i ++;
4412 }
4413 if (entry && disp_Han_Sino_Japanese && * entry->Sino_Japanese) {
4414 strcpy (s [i], "Sino-Japanese: ");
4415 strcat (s [i], entry->Sino_Japanese);
4416 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4417 i ++;
4418 }
4419 if (entry && disp_Han_Hangul && * entry->Hangul) {
4420 strcpy (s [i], "Hangul: ");
4421 strcat (s [i], entry->Hangul);
4422 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4423 i ++;
4424 }
4425 if (entry && disp_Han_Korean && * entry->Korean) {
4426 strcpy (s [i], "Korean: ");
4427 strcat (s [i], entry->Korean);
4428 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4429 i ++;
4430 }
4431 if (entry && disp_Han_Vietnamese && * entry->Vietnamese) {
4432 strcpy (s [i], "Vietnamese: ");
4433 strcat (s [i], entry->Vietnamese);
4434 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4435 i ++;
4436 }
4437 if (entry && disp_Han_HanyuPinlu && * entry->HanyuPinlu) {
4438 strcpy (s [i], "HanyuPinlu: ");
4439 strcat (s [i], entry->HanyuPinlu);
4440 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4441 i ++;
4442 }
4443 if (entry && disp_Han_HanyuPinyin && * entry->HanyuPinyin) {
4444 strcpy (s [i], "HanyuPinyin: ");
4445 strcat (s [i], entry->HanyuPinyin);
4446 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4447 i ++;
4448 }
4449 if (entry && disp_Han_XHCHanyuPinyin && * entry->XHCHanyuPinyin) {
4450 strcpy (s [i], "XHC Hànyǔ pīnyīn: ");
4451 strcat (s [i], entry->XHCHanyuPinyin);
4452 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4453 i ++;
4454 }
4455 if (entry && disp_Han_Tang && * entry->Tang) {
4456 strcpy (s [i], "Tang: ");
4457 strcat (s [i], entry->Tang);
4458 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4459 i ++;
4460 }
4461
4462 /* append separator */
4463 fill_menuitem (& descr_menu [i], NIL_PTR, NIL_PTR);
4464 i ++;
4465
4466 if (entry && disp_Han_description && * entry->Definition) {
4467 #define wrap_description
4468 #ifdef wrap_description
4469 char * descrpoi = entry->Definition;
4470 char * descrline;
4471 char * lastcomma;
4472 char * lastsemicolon;
4473 char * lastblank;
4474 char * lastcharacter;
4475 char * cut;
4476 char * bufpoi;
4477 int maxwidth = XMAX - 2;
4478 int col;
4479
4480 while (* descrpoi != '\0') {
4481 descrline = descrpoi;
4482 col = 0;
4483 lastcomma = NIL_PTR;
4484 lastsemicolon = NIL_PTR;
4485 lastblank = NIL_PTR;
4486 lastcharacter = NIL_PTR;
4487
4488 /* find max string length to fit in line */
4489 while (col < maxwidth && * descrpoi != '\0') {
4490 if (* descrpoi == ' ') {
4491 lastblank = descrpoi;
4492 } else if (* descrpoi == ',') {
4493 lastcomma = descrpoi;
4494 } else if (* descrpoi == ';') {
4495 lastsemicolon = descrpoi;
4496 } else {
4497 lastcharacter = descrpoi;
4498 }
4499 advance_char_scr (& descrpoi, & col, descrline);
4500 }
4501
4502 /* determine cut at last separator */
4503 if (* descrpoi == '\0') {
4504 cut = descrpoi;
4505 } else if (lastsemicolon != NIL_PTR) {
4506 cut = lastsemicolon + 1;
4507 } else if (lastcomma != NIL_PTR) {
4508 cut = lastcomma + 1;
4509 } else if (lastblank != NIL_PTR) {
4510 cut = lastblank;
4511 } else if (lastcharacter != NIL_PTR) {
4512 cut = lastcharacter;
4513 } else {
4514 cut = descrpoi;
4515 }
4516
4517 /* add line to menu, adjust poi to cut */
4518 descrpoi = descrline;
4519 bufpoi = s [i];
4520 while (descrpoi != cut) {
4521 * bufpoi ++ = * descrpoi ++;
4522 }
4523 * bufpoi = '\0';
4524
4525 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4526 i ++;
4527
4528 /* skip white space */
4529 while (* descrpoi == ' ') {
4530 descrpoi ++;
4531 }
4532 }
4533 #else
4534 strcpy (s [i], entry->Definition);
4535 fill_menuitem (& descr_menu [i], s [i], NIL_PTR);
4536 i ++;
4537 #endif
4538 }
4539
4540 { /* determine menu position; adjust if too far down */
4541 int descr_col = x;
4542 int descr_line = y + 1;
4543 if (descr_line + i + 2 > YMAX) {
4544 descr_line = y - i - 2;
4545 if (descr_line < 0) {
4546 descr_line = 0;
4547 descr_col ++;
4548 }
4549 }
4550
4551 (void) popup_menu (descr_menu, i, descr_col, descr_line, title, False, True, NIL_PTR);
4552 }
4553 } else {
4554 if (entry && disp_Han_Mandarin && * entry->Mandarin) {
4555 flag_Mandarin = " M: ";
4556 value_Mandarin = entry->Mandarin;
4557 }
4558 if (entry && disp_Han_Cantonese && * entry->Cantonese) {
4559 flag_Cantonese = " C: ";
4560 value_Cantonese = entry->Cantonese;
4561 }
4562 if (entry && disp_Han_Japanese && * entry->Japanese) {
4563 flag_Japanese = " J: ";
4564 value_Japanese = entry->Japanese;
4565 }
4566 if (entry && disp_Han_Sino_Japanese && * entry->Sino_Japanese) {
4567 flag_Sino_Japanese = " S: ";
4568 value_Sino_Japanese = entry->Sino_Japanese;
4569 }
4570 if (entry && disp_Han_Hangul && * entry->Hangul) {
4571 flag_Hangul = " H: ";
4572 value_Hangul = entry->Hangul;
4573 }
4574 if (entry && disp_Han_Korean && * entry->Korean) {
4575 flag_Korean = " K: ";
4576 value_Korean = entry->Korean;
4577 }
4578 if (entry && disp_Han_Vietnamese && * entry->Vietnamese) {
4579 flag_Vietnamese = " V: ";
4580 value_Vietnamese = entry->Vietnamese;
4581 }
4582 if (entry && disp_Han_HanyuPinlu && * entry->HanyuPinlu) {
4583 flag_HanyuPinlu = " P: ";
4584 value_HanyuPinlu = entry->HanyuPinlu;
4585 }
4586 if (entry && disp_Han_HanyuPinyin && * entry->HanyuPinyin) {
4587 flag_HanyuPinyin = " X: ";
4588 value_HanyuPinyin = entry->HanyuPinyin;
4589 }
4590 if (entry && disp_Han_XHCHanyuPinyin && * entry->XHCHanyuPinyin) {
4591 flag_XHCHanyuPinyin = " X: ";
4592 value_XHCHanyuPinyin = entry->XHCHanyuPinyin;
4593 }
4594 if (entry && disp_Han_Tang && * entry->Tang) {
4595 flag_Tang = " T: ";
4596 value_Tang = entry->Tang;
4597 }
4598 if (entry && disp_Han_description && * entry->Definition) {
4599 flag_description = " D: ";
4600 value_description = entry->Definition;
4601 }
4602
4603 /* MIND! When adding new pronunciation tags (Unihan update),
4604 be sure to add %s%s tags each in all three formats!
4605 */
4606 if (cjk_text && ! no_unichar (unichar)) {
4607 if (no_char (hanchar)) {
4608 build_string (text_buffer,
4609 "Unmapped Han (U+%04lX)%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
4610 unichar,
4611 flag_Mandarin, value_Mandarin,
4612 flag_Cantonese, value_Cantonese,
4613 flag_Japanese, value_Japanese,
4614 flag_Sino_Japanese, value_Sino_Japanese,
4615 flag_Hangul, value_Hangul,
4616 flag_Korean, value_Korean,
4617 flag_Vietnamese, value_Vietnamese,
4618 flag_HanyuPinlu, value_HanyuPinlu,
4619 flag_HanyuPinyin, value_HanyuPinyin,
4620 flag_XHCHanyuPinyin, value_XHCHanyuPinyin,
4621 flag_Tang, value_Tang,
4622 flag_description, value_description
4623 );
4624 } else {
4625 build_string (text_buffer,
4626 "%04lX (U+%04lX)%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
4627 hanchar,
4628 unichar,
4629 flag_Mandarin, value_Mandarin,
4630 flag_Cantonese, value_Cantonese,
4631 flag_Japanese, value_Japanese,
4632 flag_Sino_Japanese, value_Sino_Japanese,
4633 flag_Hangul, value_Hangul,
4634 flag_Korean, value_Korean,
4635 flag_Vietnamese, value_Vietnamese,
4636 flag_HanyuPinlu, value_HanyuPinlu,
4637 flag_HanyuPinyin, value_HanyuPinyin,
4638 flag_XHCHanyuPinyin, value_XHCHanyuPinyin,
4639 flag_Tang, value_Tang,
4640 flag_description, value_description
4641 );
4642 }
4643 } else if (cjk_text) {
4644 if (valid_cjk (hanchar, NIL_PTR)) {
4645 build_string (text_buffer, "%04lX (U? unknown)", hanchar);
4646 } else if (no_char (hanchar)) {
4647 build_string (text_buffer, "Invalid");
4648 } else {
4649 build_string (text_buffer, "%04lX invalid", hanchar);
4650 }
4651 } else {
4652 build_string (text_buffer,
4653 "U+%04lX%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
4654 unichar,
4655 flag_Mandarin, value_Mandarin,
4656 flag_Cantonese, value_Cantonese,
4657 flag_Japanese, value_Japanese,
4658 flag_Sino_Japanese, value_Sino_Japanese,
4659 flag_Hangul, value_Hangul,
4660 flag_Korean, value_Korean,
4661 flag_Vietnamese, value_Vietnamese,
4662 flag_HanyuPinlu, value_HanyuPinlu,
4663 flag_HanyuPinyin, value_HanyuPinyin,
4664 flag_XHCHanyuPinyin, value_XHCHanyuPinyin,
4665 flag_Tang, value_Tang,
4666 flag_description, value_description
4667 );
4668 }
4669
4670 status_uni (text_buffer);
4671 }
4672 }
4673
4674
4675 /*
4676 * display_code shows UTF-8 code sequence and Unicode value on the status line
4677 */
4678 char hexbuf [20];
4679 char * hexbufpoi = hexbuf;
4680
4681 static
4682 void
appendbyte(c)4683 appendbyte (c)
4684 unsigned int c; /* 'char' leads to spurious bugs when shifting */
4685 {
4686 * hexbufpoi ++ = hexdiguni ((c >> 4) & 0x0F);
4687 * hexbufpoi ++ = hexdiguni (c & 0x0F);
4688 }
4689
4690 static
4691 void
appendword(c)4692 appendword (c)
4693 unsigned long c;
4694 {
4695 if (utf16_little_endian) {
4696 appendbyte (c & 0xFF);
4697 appendbyte (c >> 8);
4698 } else {
4699 appendbyte (c >> 8);
4700 appendbyte (c & 0xFF);
4701 }
4702 }
4703
4704 static char * scriptmsg;
4705 static char * categorymsg;
4706 static char * scriptsep;
4707 static char * chardescr;
4708 static char * charsep;
4709 static int namedseqlen;
4710 static unsigned long * namedseq;
4711
4712 static
4713 void
setup_charinfo(unichar,follow)4714 setup_charinfo (unichar, follow)
4715 unsigned long unichar;
4716 char * follow;
4717 {
4718 if (disp_charseqname && combining_mode) {
4719 chardescr = charseqname (unichar, follow, & namedseqlen, & namedseq);
4720 if (chardescr != NIL_PTR) {
4721 charsep = " ";
4722 scriptmsg = "";
4723 categorymsg = "";
4724 scriptsep = "";
4725 return;
4726 }
4727 }
4728 namedseqlen = 0;
4729
4730 /* prepare script info */
4731 if (disp_scriptname) {
4732 struct scriptentry * script_info = scriptinfo (unichar);
4733 if (script_info) {
4734 scriptmsg = category_names [script_info->scriptname];
4735 categorymsg = category_names [script_info->categoryname];
4736 scriptsep = " ";
4737 } else {
4738 scriptmsg = "Not Assigned ";
4739 categorymsg = "";
4740 scriptsep = "";
4741 }
4742 } else {
4743 scriptmsg = "";
4744 categorymsg = "";
4745 scriptsep = "";
4746 }
4747
4748 /* prepare character description (name, decomposition, mnemos) */
4749 if (disp_charname) {
4750 chardescr = charname (unichar);
4751 charsep = " ";
4752 } else if (disp_mnemos) {
4753 chardescr = mnemos (unichar);
4754 charsep = " mnemos:";
4755 } else if (disp_decomposition) {
4756 chardescr = decomposition_string (unichar);
4757 charsep = " decompose:";
4758 } else {
4759 chardescr = 0;
4760 }
4761 if (! chardescr || ! * chardescr) {
4762 chardescr = "";
4763 charsep = "";
4764 }
4765 }
4766
4767 static
4768 void
build_namedseq(text_buffer)4769 build_namedseq (text_buffer)
4770 char * text_buffer;
4771 {
4772 if (namedseqlen == 2) {
4773 build_string (text_buffer, "Named Seq U+%04lX U+%04lX %s",
4774 namedseq [0], namedseq [1],
4775 chardescr);
4776 } else if (namedseqlen == 3) {
4777 build_string (text_buffer, "Named Seq U+%04lX U+%04lX U+%04lX %s",
4778 namedseq [0], namedseq [1], namedseq [2],
4779 chardescr);
4780 } else if (namedseqlen == 4) {
4781 build_string (text_buffer, "Named Seq U+%04lX U+%04lX U+%04lX U+%04lX %s",
4782 namedseq [0], namedseq [1], namedseq [2], namedseq [3],
4783 chardescr);
4784 }
4785 }
4786
4787 void
display_the_code()4788 display_the_code ()
4789 {
4790 character c = * cur_text;
4791 int utfcount = 1;
4792 unsigned long unichar;
4793 int utfcount2;
4794 unsigned long unichar2;
4795 char * textpoi;
4796 FLAG invalid = False;
4797 char * lengthmsg;
4798 char * widemsg;
4799 char * combinedmsg;
4800
4801 hexbufpoi = hexbuf;
4802
4803 if (c == '\n') {
4804 switch (cur_line->return_type) {
4805 case lineend_NONE: textpoi = "no line end / split line";
4806 break;
4807 case lineend_NUL: appendbyte ('\0');
4808 textpoi = "U+00 NUL";
4809 break;
4810 case lineend_LF: appendbyte (code_LF);
4811 textpoi = "U+0A (Unix) LINE FEED";
4812 break;
4813 case lineend_CRLF: appendbyte ('\r');
4814 appendbyte (code_LF);
4815 textpoi = "U+0D U+0A (DOS) CRLF";
4816 break;
4817 case lineend_CR: appendbyte ('\r');
4818 textpoi = "U+0D (Mac) CARRIAGE RETURN";
4819 break;
4820 case lineend_NL1: appendbyte (code_NL);
4821 textpoi = "U+85 (ISO 8859) NEXT LINE";
4822 break;
4823 case lineend_NL2: appendbyte ('\302');
4824 appendbyte ('\205');
4825 textpoi = "U+85 NEXT LINE";
4826 break;
4827 case lineend_LS: appendbyte ('\342');
4828 appendbyte ('\200');
4829 appendbyte ('\250');
4830 textpoi = "U+2028 LINE SEPARATOR";
4831 break;
4832 case lineend_PS: appendbyte ('\342');
4833 appendbyte ('\200');
4834 appendbyte ('\251');
4835 textpoi = "U+2029 PARAGRAPH SEPARATOR";
4836 break;
4837 default: appendbyte (code_LF);
4838 textpoi = "unknown line end";
4839 break;
4840 }
4841 * hexbufpoi = '\0';
4842 build_string (text_buffer, "Line end: %s - %s", hexbuf, textpoi);
4843 status_uni (text_buffer);
4844 } else if (utf8_text) {
4845 int utf_utf_len;
4846 int uni_utf_len;
4847
4848 if ((c & 0x80) == 0x00) {
4849 utfcount = 1;
4850 unichar = c;
4851 } else if ((c & 0xE0) == 0xC0) {
4852 utfcount = 2;
4853 unichar = c & 0x1F;
4854 } else if ((c & 0xF0) == 0xE0) {
4855 utfcount = 3;
4856 unichar = c & 0x0F;
4857 } else if ((c & 0xF8) == 0xF0) {
4858 utfcount = 4;
4859 unichar = c & 0x07;
4860 } else if ((c & 0xFC) == 0xF8) {
4861 utfcount = 5;
4862 unichar = c & 0x03;
4863 } else if ((c & 0xFE) == 0xFC) {
4864 utfcount = 6;
4865 unichar = c & 0x01;
4866 } else /* illegal UTF-8 code */ {
4867 appendbyte (c);
4868 invalid = True;
4869 unichar = 0;
4870 }
4871
4872 utf_utf_len = utfcount;
4873
4874 textpoi = cur_text;
4875 if (invalid == False) {
4876 utfcount --;
4877 appendbyte (c);
4878 textpoi ++;
4879 while (utfcount > 0 && (* textpoi & 0xC0) == 0x80) {
4880 c = * textpoi ++;
4881 appendbyte (c);
4882 unichar = (unichar << 6) | (c & 0x3F);
4883 utfcount --;
4884 }
4885 if (utfcount > 0) {
4886 invalid = True;
4887 }
4888 } else {
4889 textpoi ++;
4890 }
4891
4892 if (unichar < 0x80) {
4893 uni_utf_len = 1;
4894 } else if (unichar < 0x800) {
4895 uni_utf_len = 2;
4896 } else if (unichar < 0x10000) {
4897 uni_utf_len = 3;
4898 } else if (unichar < 0x200000) {
4899 uni_utf_len = 4;
4900 } else if (unichar < 0x4000000) {
4901 uni_utf_len = 5;
4902 } else if (unichar < 0x80000000) {
4903 uni_utf_len = 6;
4904 } else {
4905 uni_utf_len = 0;
4906 }
4907 if (utf_utf_len == uni_utf_len) {
4908 lengthmsg = "";
4909 } else {
4910 lengthmsg = " (too long)";
4911 }
4912
4913 utf8_info (textpoi, & utfcount2, & unichar2);
4914 if (isspacingcombining_unichar (unichar2)) {
4915 combinedmsg = " - with ...";
4916 } else if (iscombining (unichar2)) {
4917 combinedmsg = " - combined ...";
4918 } else if (isjoined (unichar2, textpoi, cur_line->text)) {
4919 combinedmsg = " - joined ...";
4920 } else {
4921 combinedmsg = "";
4922 }
4923
4924 * hexbufpoi = '\0';
4925 if (invalid == False) {
4926 char * combmsg;
4927
4928 /* prepare character information */
4929 setup_charinfo (unichar, textpoi);
4930
4931 if (iscombining_unichar (unichar)) {
4932 if (isspacingcombining_unichar (unichar)) {
4933 combmsg = "spacing combining ";
4934 } else {
4935 combmsg = "combining ";
4936 }
4937 } else if (iscombined (unichar, cur_text, cur_line->text)) {
4938 if (iscombining (unichar)) {
4939 /* catch cases missed above; to be fixed? */
4940 combmsg = "combining ";
4941 } else {
4942 combmsg = "joining ";
4943 }
4944 } else {
4945 combmsg = "";
4946 }
4947
4948 if (is_wideunichar (unichar)) {
4949 widemsg = "wide ";
4950 } else if ((unichar & 0x7FFFFC00) == 0xD800) {
4951 widemsg = "single high surrogate ";
4952 } else if ((unichar & 0x7FFFFC00) == 0xDC00) {
4953 widemsg = "single low surrogate ";
4954 } else if ((unichar & 0x7FFFFFFE) == 0xFFFE) {
4955 widemsg = "reserved ";
4956 } else {
4957 widemsg = "";
4958 }
4959
4960 if (namedseqlen) {
4961 build_namedseq (text_buffer);
4962 } else if (utf16_file) {
4963 hexbufpoi = hexbuf;
4964 if (unichar > 0x10FFFF) {
4965 build_string (text_buffer,
4966 "Illegal UTF-16 %s - %s%s%s%s%s%sU+%08lX%s%s%s",
4967 lengthmsg, widemsg, combmsg, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
4968 } else if (unichar > 0xFFFF) {
4969 appendword (0xD800 | ((unichar - 0x10000) >> 10));
4970 appendword (0xDC00 | ((unichar - 0x10000) & 0x03FF));
4971 * hexbufpoi = '\0';
4972 build_string (text_buffer,
4973 "UTF-16: %s%s - %s%s%s%s%s%sU+%06lX%s%s%s",
4974 hexbuf, lengthmsg, widemsg, combmsg, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
4975 } else {
4976 appendword (unichar);
4977 * hexbufpoi = '\0';
4978 build_string (text_buffer,
4979 "UTF-16: %s%s - %s%s%s%s%s%sU+%04lX%s%s%s",
4980 hexbuf, lengthmsg, widemsg, combmsg, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
4981 }
4982 } else {
4983 if (unichar > 0x10FFFF) {
4984 build_string (text_buffer,
4985 "Non-Unicode UTF-8: %s%s - %s%s%s%s%s%sU+%08lX%s%s%s",
4986 hexbuf, lengthmsg, widemsg, combmsg, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
4987 } else if (unichar > 0xFFFF) {
4988 build_string (text_buffer,
4989 "UTF-8: %s%s - %s%s%s%s%s%sU+%06lX%s%s%s",
4990 hexbuf, lengthmsg, widemsg, combmsg, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
4991 } else {
4992 build_string (text_buffer,
4993 "UTF-8: %s%s - %s%s%s%s%s%sU+%04lX%s%s%s",
4994 hexbuf, lengthmsg, widemsg, combmsg, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
4995 }
4996 }
4997 } else {
4998 build_string (text_buffer, "Invalid UTF-8 sequence: %s%s", hexbuf, combinedmsg);
4999 }
5000 status_uni (text_buffer);
5001 } else if (cjk_text) {
5002 int len = CJK_len (cur_text);
5003 unsigned long cjkchar = charvalue (cur_text);
5004 character cjkbytes [5];
5005 int charlen = cjkencode (cjkchar, cjkbytes);
5006
5007 textpoi = cur_text;
5008 while (len > 0 && * textpoi != '\0' && * textpoi != '\n') {
5009 appendbyte (* textpoi);
5010 textpoi ++;
5011 len --;
5012 charlen --;
5013 }
5014 * hexbufpoi = '\0';
5015
5016 combinedmsg = "";
5017 if (text_encoding_tag == 'G' || text_encoding_tag == 'X' || text_encoding_tag == 'x') {
5018 unichar2 = lookup_encodedchar (charvalue (textpoi));
5019 if (isspacingcombining_unichar (unichar2)) {
5020 combinedmsg = " - with ...";
5021 } else if (iscombining (unichar2)) {
5022 combinedmsg = " - combined ...";
5023 } else if (text_encoding_tag == 'G' && isjoined (unichar2, textpoi, cur_line->text)) {
5024 combinedmsg = " - joined ...";
5025 }
5026 }
5027
5028 if (len != 0 || charlen != 0) {
5029 build_string (text_buffer, "Incomplete CJK character code: %s%s", hexbuf, combinedmsg);
5030 } else {
5031 unichar = lookup_encodedchar (cjkchar);
5032 if (c > 0x7F || unichar != (character) c || * combinedmsg) {
5033 if (valid_cjk (cjkchar, NIL_PTR)) {
5034 if (no_unichar (unichar)) {
5035 build_string (text_buffer, "CJK character code: %s (U? unknown)%s", hexbuf, combinedmsg);
5036 } else {
5037 char * format;
5038
5039 unichar2 = 0;
5040 if (unichar >= 0x80000000) {
5041 unichar2 = (unichar >> 16) & 0x7FFF;
5042 unichar = unichar & 0xFFFF;
5043 format = "CJK %scharacter: %s - %s%s%s%sU+%04lX (with U+%04lX)%s%s%s";
5044 } else if (unichar > 0x10FFFF) {
5045 format = "CJK %scharacter: %s - %s%s%s%sU+%08lX%s%s%s";
5046 } else if (unichar > 0xFFFF) {
5047 format = "CJK %scharacter: %s - %s%s%s%sU+%06lX%s%s%s";
5048 } else {
5049 format = "CJK %scharacter: %s - %s%s%s%sU+%04lX%s%s%s";
5050 }
5051
5052 /* prepare character information */
5053 setup_charinfo (unichar, textpoi);
5054
5055 /* determine combining status */
5056 widemsg = "";
5057 if (text_encoding_tag == 'G' || text_encoding_tag == 'X' || text_encoding_tag == 'x') {
5058 if (iscombining_unichar (unichar)) {
5059 if (isspacingcombining_unichar (unichar)) {
5060 widemsg = "spacing combining ";
5061 } else {
5062 widemsg = "combining ";
5063 }
5064 }
5065 }
5066
5067 if (namedseqlen) {
5068 build_namedseq (text_buffer);
5069 } else if (unichar2 == 0) {
5070 build_string (text_buffer, format, widemsg, hexbuf, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
5071 } else {
5072 build_string (text_buffer, format, widemsg, hexbuf, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, unichar2, charsep, chardescr, combinedmsg);
5073 }
5074 }
5075 } else {
5076 build_string (text_buffer, "Invalid CJK character code: %s%s", hexbuf, combinedmsg);
5077 }
5078 } else {
5079 /* prepare character information */
5080 setup_charinfo (unichar, textpoi);
5081
5082 if (namedseqlen) {
5083 build_namedseq (text_buffer);
5084 } else {
5085 build_string (text_buffer, "Character: %s%s%s%sU+%02X%s%s", scriptmsg, scriptsep, categorymsg, scriptsep, c, charsep, chardescr);
5086 }
5087 }
5088 }
5089 status_uni (text_buffer);
5090 } else { /* 8 bit text */
5091 appendbyte (c);
5092 * hexbufpoi = '\0';
5093
5094 if (mapped_text) {
5095 unichar2 = lookup_encodedchar ((character) * (cur_text + 1));
5096 combinedmsg = "";
5097 if (isspacingcombining_unichar (unichar2)) {
5098 combinedmsg = " - with ...";
5099 } else if (iscombining (unichar2)) {
5100 combinedmsg = " - combined ...";
5101 } else if (isjoined (unichar2, cur_text + 1, cur_line->text)) {
5102 combinedmsg = " - joined ...";
5103 }
5104
5105 unichar = lookup_encodedchar (c);
5106 if (no_unichar (unichar)) {
5107 build_string (text_buffer, "Character code: %s (U? unknown)%s", hexbuf, combinedmsg);
5108 } else {
5109 widemsg = "C";
5110 if (iscombining_unichar (unichar)) {
5111 if (isspacingcombining_unichar (unichar)) {
5112 widemsg = "Spacing combining c";
5113 } else {
5114 widemsg = "Combining c";
5115 }
5116 }
5117
5118 /* prepare character information */
5119 setup_charinfo (unichar, cur_text + 1);
5120
5121 if (namedseqlen) {
5122 build_namedseq (text_buffer);
5123 } else {
5124 build_string (text_buffer, "%sharacter: %s - %s%s%s%sU+%04lX%s%s%s", widemsg, hexbuf, scriptmsg, scriptsep, categorymsg, scriptsep, unichar, charsep, chardescr, combinedmsg);
5125 }
5126 }
5127 } else {
5128 /* prepare character information */
5129 setup_charinfo (c, cur_text + 1);
5130
5131 if (ebcdic_file) {
5132 character e;
5133 mapped_text = True;
5134 e = encodedchar (c);
5135 mapped_text = False;
5136 build_string (text_buffer, "Character: %02X - %s%s%s%sU+%02X%s%s", e, scriptmsg, scriptsep, categorymsg, scriptsep, c, charsep, chardescr);
5137 } else {
5138 build_string (text_buffer, "Character: %s%s%s%sU+%02X%s%s", scriptmsg, scriptsep, categorymsg, scriptsep, c, charsep, chardescr);
5139 }
5140 }
5141 status_uni (text_buffer);
5142 }
5143 }
5144
5145 void
display_code()5146 display_code ()
5147 {
5148 if (hop_flag > 0) {
5149 hop_flag = 0;
5150 if (always_disp_code) {
5151 always_disp_code = False;
5152 } else {
5153 always_disp_code = True;
5154 always_disp_fstat = False;
5155 if (! disp_Han_full) {
5156 always_disp_Han = False;
5157 }
5158 }
5159 } else {
5160 display_the_code ();
5161 }
5162 }
5163
5164
5165 /*======================================================================*\
5166 |* Character composition *|
5167 \*======================================================================*/
5168
5169 /**
5170 do_insert_accented combines and inserts accented character,
5171 handles multiple accent keys
5172 */
5173 static
5174 void
do_insert_accented(accentnames,ps,ps2,ps3)5175 do_insert_accented (accentnames, ps, ps2, ps3)
5176 char * accentnames;
5177 struct prefixspec * ps;
5178 struct prefixspec * ps2;
5179 struct prefixspec * ps3;
5180 {
5181 unsigned long base;
5182 struct prefixspec * newps = 0;
5183 unsigned char prefix_keyshift = keyshift;
5184
5185 if (* accentnames == '\0') {
5186 return;
5187 }
5188
5189 build_string (text_buffer, "Compose %s with:", accentnames);
5190 status_uni (text_buffer);
5191 base = readcharacter_unicode_mapped ();
5192
5193 if (command (base) == DPC) {
5194 clear_status ();
5195 keyshift = prefix_keyshift | ctrl_mask;
5196 if (keyshift & shift_mask) {
5197 DPC ();
5198 } else {
5199 DPC0 ();
5200 }
5201 return;
5202 }
5203
5204 if (command (base) == CTRLINS) {
5205 unsigned long ctrl;
5206 status_uni ("Enter compose char / <blank> mnemonic ...");
5207 ctrl = readcharacter_unicode ();
5208
5209 newps = lookup_prefix_char (ctrl);
5210 if (newps) {
5211 /* continue below */
5212 } else if (ctrl == FUNcmd) {
5213 keyshift |= ctrl_mask;
5214 newps = lookup_prefix (keyproc, keyshift);
5215 if (newps) {
5216 /* continue below */
5217 } else {
5218 error ("Mnemonic input or accent prefix expected");
5219 return;
5220 }
5221 } else if (ctrl == ' ') {
5222 char mnemonic [maxPROMPTlen];
5223 build_string (text_buffer, "Compose %s with character mnemonic:", accentnames);
5224 if (get_string_uni (text_buffer, mnemonic, False, " ") == ERRORS) {
5225 return;
5226 }
5227 base = compose_mnemonic (mnemonic);
5228 /* final compose and insert below */
5229 } else if (ctrl > ' ' && ctrl != '#' && ctrl != 0x7F) {
5230 static character buf [7];
5231 unsigned long ch;
5232 (void) utfencode (ctrl, buf);
5233 build_string (text_buffer, "Compose %s with %s..", accentnames, buf);
5234 status_uni (text_buffer);
5235 ch = readcharacter_unicode ();
5236 if (ch == '\033' || ch == FUNcmd) {
5237 clear_status ();
5238 return;
5239 }
5240 base = compose_chars (ctrl, ch);
5241 /* final compose and insert below */
5242 } else {
5243 error ("Mnemonic input expected");
5244 return;
5245 }
5246 } else if (base == FUNcmd) {
5247 newps = lookup_prefix (keyproc, keyshift);
5248 if (newps) {
5249 /* continue below */
5250 } else {
5251 clear_status ();
5252 return;
5253 }
5254 } else if (no_char (base) || base == '\033') {
5255 clear_status ();
5256 return;
5257 }
5258
5259 if (newps) {
5260 /* handle multiple accent prefixes */
5261 if (ps3) {
5262 error ("Max. 3 accent prefix keys anticipated");
5263 } else {
5264 char newaccentnames [maxPROMPTlen];
5265 if (ps2) {
5266 strcpy (newaccentnames, accentnames);
5267 } else {
5268 strcpy (newaccentnames, ps->accentsymbol);
5269 }
5270 strcat (newaccentnames, " and ");
5271 strcat (newaccentnames, newps->accentsymbol);
5272 if (ps2) {
5273 do_insert_accented (newaccentnames, ps, ps2, newps);
5274 } else {
5275 do_insert_accented (newaccentnames, ps, newps, 0);
5276 }
5277 }
5278 } else {
5279 clear_status ();
5280 (void) insert_character (compose_patterns (base, ps, ps2, ps3));
5281 }
5282 }
5283
5284 /**
5285 insert_accented combines and inserts accented character
5286 */
5287 static
5288 void
insert_accented(ps)5289 insert_accented (ps)
5290 struct prefixspec * ps;
5291 {
5292 do_insert_accented (ps->accentname, ps, 0, 0);
5293 }
5294
5295 /*
5296 * CTRLGET reads a control-char or encoded or mnemonic character
5297 used by mousemen
5298 */
5299 unsigned long
CTRLGET(check_prefix)5300 CTRLGET (check_prefix)
5301 FLAG check_prefix;
5302 {
5303 FLAG save_utf8_text = utf8_text;
5304 utf8_text = True;
5305 /* temporarily switch insert_character to not enter into the text */
5306 deliver_dont_insert = True;
5307 delivered_character = CHAR_UNKNOWN;
5308
5309 if (check_prefix) {
5310 struct prefixspec * ps = lookup_prefix (keyproc, keyshift);
5311 if (ps) {
5312 insert_accented (ps);
5313 }
5314 } else {
5315 CTRLINS ();
5316 }
5317
5318 deliver_dont_insert = False;
5319 utf8_text = save_utf8_text;
5320 return delivered_character;
5321 }
5322
5323 /*
5324 * CTRLINS inserts a control-char or encoded or mnemonic character
5325 */
5326 void
CTRLINS()5327 CTRLINS ()
5328 {
5329 unsigned long ctrl;
5330 unsigned long ch;
5331
5332 status_uni ("Enter control char / # hex/octal/decimal / compose char / <blank> mnemonic ...");
5333 ctrl = readcharacter_unicode ();
5334 trace_keytrack ("CTRLINS", ctrl);
5335
5336 if (command (ctrl) == COMPOSE && keyshift >= '@' && keyshift <= '_') {
5337 /* allow input of control char, esp ^^ */
5338 clear_status ();
5339 (void) insert_character (keyshift & 0x1F);
5340 return;
5341 } else {
5342 struct prefixspec * ps = lookup_prefix_char (ctrl);
5343 if (ps) {
5344 insert_accented (ps);
5345 return;
5346 }
5347 }
5348
5349 if (ctrl == FUNcmd) {
5350 struct prefixspec * ps;
5351 keyshift |= ctrl_mask;
5352 ps = lookup_prefix (keyproc, keyshift);
5353 if (ps) {
5354 insert_accented (ps);
5355 } else {
5356 clear_status ();
5357 (* keyproc) ('\0');
5358 }
5359 } else if (ctrl < ' ' || ctrl == 0x7F) {
5360 clear_status ();
5361 if (ctrl != '\177') {
5362 ctrl = ctrl & '\237';
5363 }
5364 (void) insert_character (ctrl);
5365 } else if (ctrl == '#') {
5366 int finich;
5367 do {
5368 if (utf8_text) {
5369 finich = prompt_num_char (& ch, 0x7FFFFFFF);
5370 if (finich != ERRORS) {
5371 (void) insert_character (ch);
5372 }
5373 } else if (cjk_text || mapped_text) {
5374 finich = prompt_num_char (& ch, max_char_value ());
5375 if (finich != ERRORS) {
5376 if ((cjk_text && valid_cjk (ch, NIL_PTR))
5377 || ch < 0x100) {
5378 (void) insert_character (ch);
5379 } else {
5380 ring_bell ();
5381 error ("Invalid character value");
5382 }
5383 }
5384 } else {
5385 finich = prompt_num_char (& ch, 0xFF);
5386 if (finich != ERRORS) {
5387 if (ch < 0x100) {
5388 (void) insert_character (ch);
5389 } else {
5390 ring_bell ();
5391 error ("Invalid character value");
5392 }
5393 }
5394 }
5395 } while (finich == ' ' && ! deliver_dont_insert);
5396 } else if (ctrl == ' ') {
5397 char mnemonic [maxPROMPTlen];
5398 if (get_string_uni ("Enter character mnemonic:", mnemonic, False, " ")
5399 == ERRORS) {
5400 return;
5401 }
5402 ch = compose_mnemonic (mnemonic);
5403 clear_status ();
5404 (void) insert_character (ch);
5405 } else {
5406 static character buf [7];
5407 (void) utfencode (ctrl, buf);
5408 build_string (text_buffer, "Enter second composing character: %s..", buf);
5409 status_uni (text_buffer);
5410 ch = readcharacter_unicode ();
5411 if (ch == '\033' || ch == FUNcmd) {
5412 clear_status ();
5413 return;
5414 }
5415 ch = compose_chars (ctrl, ch);
5416 clear_status ();
5417 (void) insert_character (ch);
5418 }
5419 }
5420
5421
5422 /*
5423 Insert next char with defined accent composition pattern
5424 (invoked by function key).
5425 */
5426 static
5427 void
insert_prefix(prefunc)5428 insert_prefix (prefunc)
5429 voidfunc prefunc;
5430 {
5431 struct prefixspec * ps = lookup_prefix (prefunc, keyshift);
5432 if (ps) {
5433 insert_accented (ps);
5434 } else {
5435 status_msg ("Accent prefix with this shift state not assigned");
5436 }
5437 }
5438
5439 void
COMPOSE()5440 COMPOSE ()
5441 {
5442 insert_prefix (COMPOSE);
5443 }
5444
5445 void
F5()5446 F5 ()
5447 {
5448 insert_prefix (F5);
5449 }
5450
5451 void
F6()5452 F6 ()
5453 {
5454 insert_prefix (F6);
5455 }
5456
5457 void
key_0()5458 key_0 ()
5459 {
5460 insert_prefix (key_0);
5461 }
5462
5463 void
key_1()5464 key_1 ()
5465 {
5466 insert_prefix (key_1);
5467 }
5468
5469 void
key_2()5470 key_2 ()
5471 {
5472 insert_prefix (key_2);
5473 }
5474
5475 void
key_3()5476 key_3 ()
5477 {
5478 insert_prefix (key_3);
5479 }
5480
5481 void
key_4()5482 key_4 ()
5483 {
5484 insert_prefix (key_4);
5485 }
5486
5487 void
key_5()5488 key_5 ()
5489 {
5490 insert_prefix (key_5);
5491 }
5492
5493 void
key_6()5494 key_6 ()
5495 {
5496 insert_prefix (key_6);
5497 }
5498
5499 void
key_7()5500 key_7 ()
5501 {
5502 insert_prefix (key_7);
5503 }
5504
5505 void
key_8()5506 key_8 ()
5507 {
5508 insert_prefix (key_8);
5509 }
5510
5511 void
key_9()5512 key_9 ()
5513 {
5514 insert_prefix (key_9);
5515 }
5516
5517
5518 /*======================================================================*\
5519 |* Character transformation *|
5520 \*======================================================================*/
5521
5522 /*
5523 Search for wrongly encoded character.
5524 Searches for UTF-8 character in Latin-1 text or vice versa.
5525 */
5526 void
search_wrong_enc()5527 search_wrong_enc ()
5528 {
5529 LINE * prev_line;
5530 char * prev_text;
5531 LINE * lpoi;
5532 char * cpoi;
5533 int utfcount;
5534 unsigned long unichar;
5535
5536 if (hop_flag > 0) {
5537 int text_offset = cur_text - cur_line->text;
5538 (void) CONV ();
5539 move_address (cur_line->text + text_offset, y);
5540 }
5541
5542 prev_line = cur_line;
5543 prev_text = cur_text;
5544 lpoi = cur_line;
5545 cpoi = cur_text;
5546 while (True) {
5547 /* advance character pointer */
5548 if (* cpoi == '\n') {
5549 lpoi = lpoi->next;
5550 if (lpoi == tail) {
5551 lpoi = header->next;
5552 status_uni (">>>>>>>> Search wrapped around end of file >>>>>>>>");
5553 }
5554 cpoi = lpoi->text;
5555 } else {
5556 advance_char (& cpoi);
5557 }
5558
5559 /* check for wrongly encoded character */
5560 if ((* cpoi & 0x80) != 0) {
5561 FLAG isutf;
5562 if ((* cpoi & 0xC0) == 0xC0) {
5563 utf8_info (cpoi, & utfcount, & unichar);
5564 isutf = UTF8_len (* cpoi) == utfcount
5565 && (* cpoi & 0xFE) != 0xFE;
5566 } else {
5567 isutf = False;
5568 }
5569 if (isutf != utf8_text) {
5570 break;
5571 }
5572 }
5573
5574 /* check search wrap-around */
5575 if (lpoi == prev_line && cpoi == prev_text) {
5576 status_msg ("No more wrong encoding found");
5577 return;
5578 }
5579 }
5580 move_address (cpoi, find_y (lpoi));
5581 }
5582
5583 /*
5584 Replace character mnemonic with character.
5585 Prefer national characters according to parameter:
5586 g (German): ae -> ä/�, oe -> ö/�, ue -> ü/�
5587 d (Danish) ae -> æ/�, oe -> ø/�
5588 f (French) oe -> œ/oe ligature
5589 */
5590 void
UML(lang)5591 UML (lang)
5592 char lang;
5593 {
5594 unsigned long uc;
5595 unsigned long c1;
5596 unsigned long c2;
5597 char * cpoi = cur_text;
5598
5599 unsigned long unichar = 0;
5600
5601 keyshift = 0;
5602 hop_flag = 0;
5603
5604 c1 = unicodevalue (cpoi);
5605
5606 if (c1 == '&') {
5607 char mnemo [maxMSGlen];
5608 char * mp = mnemo + 1;
5609 char * text = cur_text + 1;
5610 int del_offset;
5611
5612 if (* text == '#') {
5613 int count = 0;
5614 uc = 0;
5615
5616 text ++;
5617 if (* text == 'x' || * text == 'X') {
5618 /* € -> € */
5619 text ++;
5620 /*format = "%lx";*/
5621 while (ishex (* text)) {
5622 uc = uc * 16 + hexval (* text ++);
5623 count ++;
5624 }
5625 } else {
5626 /* € -> € */
5627 /*format = "%ld";*/
5628 while (* text >= '0' && * text <= '9') {
5629 uc = uc * 10 + hexval (* text ++);
5630 count ++;
5631 }
5632 }
5633 /*if (sscanf (text, format, & uc) <= 0) {...*/
5634 if (! count) {
5635 ring_bell ();
5636 error ("Invalid character numeric");
5637 return;
5638 }
5639
5640 if (cjk_text || mapped_text) {
5641 uc = encodedchar (uc);
5642 }
5643 if (* text == ';') {
5644 text ++;
5645 }
5646 } else {
5647 /* " -> " */
5648 mnemo [0] = '&';
5649 while ((* text >= 'a' && * text <= 'z') || (* text >= 'A' && * text <= 'Z')) {
5650 * mp ++ = * text ++;
5651 }
5652 * mp = '\0';
5653 if (* text == ';') {
5654 text ++;
5655 }
5656 uc = compose_mnemonic (mnemo);
5657 }
5658
5659 del_offset = text - cur_text;
5660 if (insert_character (uc)) {
5661 (void) delete_text (cur_line, cur_text, cur_line, cur_text + del_offset);
5662 }
5663 /*
5664 if (uc == CHAR_UNKNOWN) {
5665 ring_bell ();
5666 error ("Unknown character mnemonic");
5667 return;
5668 } else if (uc == CHAR_INVALID) {
5669 ring_bell ();
5670 error ("Invalid character");
5671 return;
5672 } else if (delete_text (cur_line, cur_text, cur_line, text) != ERRORS) {
5673 insert_character (uc);
5674 }
5675 */
5676
5677 return;
5678 }
5679
5680 if (c1 == '%') {
5681 /* HTTP replacement back-conversion, e.g. %40 -> @ */
5682 char * chpoi = cpoi;
5683 int codelen = 2;
5684 int bytecount = 0;
5685 unsigned int bytecode;
5686 char allbytes [7];
5687 char * bytepoi = allbytes;
5688 unsigned long code = 0;
5689 while (bytecount < codelen && sscanf (chpoi, "%%%02X", & bytecode) > 0) {
5690 code = (code << 8) + bytecode;
5691 * bytepoi ++ = bytecode;
5692 * bytepoi = 0;
5693 bytecount ++;
5694 chpoi += 3;
5695 if (utf8_text) {
5696 if (bytecount == 1) {
5697 codelen = UTF8_len (bytecode);
5698 }
5699 } else if (cjk_text) {
5700 if (bytecount == 2) {
5701 codelen = CJK_len (allbytes);
5702 }
5703 } else {
5704 codelen = 1;
5705 }
5706 }
5707
5708 if (bytecount == codelen) {
5709 int offset = cur_text - cur_line->text;
5710 (void) delete_text (cur_line, cpoi, cur_line, chpoi);
5711 (void) insert_text (cur_line, cur_text, allbytes);
5712 print_line (y, cur_line);
5713 move_address (cur_line->text + offset, y);
5714 MOVRT ();
5715 } else {
5716 error ("Invalid code sequence");
5717 }
5718 return;
5719 }
5720
5721 if (* cpoi != '\n') {
5722 advance_char (& cpoi);
5723 }
5724 c2 = unicodevalue (cpoi);
5725
5726 /* language-specific accent preferences */
5727 if (lang == 'i') {
5728 if (c1 == '\'' || c1 == (character) '�') {
5729 c1 = '`';
5730 } else if (c2 == '\'' || c2 == (character) '�') {
5731 c2 = '`';
5732 }
5733 }
5734 if (lang == 'e') {
5735 if (c2 == ',' && strchr ("aeiuAEIU", c1)) {
5736 c2 = ';';
5737 } else if (c1 == ',' && strchr ("aeiuAEIU", c2)) {
5738 c1 = ';';
5739 } else if (c2 == '-' && (c1 == 'd' || c1 == 'D')) {
5740 c2 = '/';
5741 } else if (c1 == '-' && (c2 == 'd' || c2 == 'D')) {
5742 c1 = '/';
5743 }
5744 }
5745
5746 /* first, try mnemonic / accented conversion */
5747 uc = compose_chars (c1, c2);
5748 /* result is already converted to encoding */
5749
5750 /* override with language-specific conversion preferences */
5751 if (lang == 'd') {
5752 if (c1 == 'a' && c2 == 'e') {
5753 unichar = (character) '�'; /* æ */
5754 } else if (c1 == 'A' && (c2 & ~0x20) == 'E') {
5755 unichar = (character) '�'; /* Æ */
5756 } else if (c1 == 'o' && c2 == 'e') {
5757 unichar = (character) '�'; /* ø */
5758 } else if (c1 == 'O' && (c2 & ~0x20) == 'E') {
5759 unichar = (character) '�'; /* Ø */
5760 }
5761 } else if (lang == 'f') {
5762 if (c1 == 'o' && c2 == 'e') {
5763 unichar = 0x0153; /* œ */
5764 } else if (c1 == 'O' && (c2 & ~0x20) == 'E') {
5765 unichar = 0x0152; /* Œ */
5766 }
5767 } else if (lang == 'g') {
5768 if (c1 == 'a' && c2 == 'e') {
5769 unichar = (character) '�'; /* ä */
5770 } else if (c1 == 'o' && c2 == 'e') {
5771 unichar = (character) '�'; /* ö */
5772 } else if (c1 == 'u' && c2 == 'e') {
5773 unichar = (character) '�'; /* ü */
5774 } else if (c1 == 'A' && (c2 & ~0x20) == 'E') {
5775 unichar = (character) '�'; /* Ä */
5776 } else if (c1 == 'O' && (c2 & ~0x20) == 'E') {
5777 unichar = (character) '�'; /* Ö */
5778 } else if (c1 == 'U' && (c2 & ~0x20) == 'E') {
5779 unichar = (character) '�'; /* Ü */
5780 } else if (c1 == 's' && c2 == 's') {
5781 unichar = (character) '�'; /* ß */
5782 }
5783 }
5784 if (unichar != 0) { /* was explicitly set above */
5785 uc = charcode (unichar);
5786 }
5787
5788 if (uc == CHAR_INVALID) {
5789 ring_bell ();
5790 error ("Invalid character");
5791
5792 } else if (! no_char (uc)) {
5793 /* apply mnemonic conversion */
5794 if (utf8_text) {
5795 DCC (); DCC ();
5796 Sutf8 (uc);
5797 } else if (cjk_text && ! mapped_text) {
5798 DCC (); DCC ();
5799 (void) Scjk (uc);
5800 } else {
5801 if (uc < 0x100) {
5802 DCC (); DCC ();
5803 Sbyte (uc);
5804 } else {
5805 ring_bell ();
5806 error ("Invalid character");
5807 }
5808 }
5809
5810 } else if (! CONV ()) {
5811 ring_bell ();
5812 error ("Unknown character mnemonic");
5813 }
5814 }
5815
5816 /*
5817 Convert between UTF-8 and other encoding (from Latin-1 or to current).
5818 */
5819 FLAG
CONV()5820 CONV ()
5821 {
5822 character c1 = * cur_text;
5823 int utfcount;
5824 unsigned long unichar = 0;
5825
5826 if (utf8_text && ((character) * cur_text) >= 0x80) {
5827 /* convert Latin-1 -> UTF-8 */
5828 if (c1 >= 0xC0) {
5829 utf8_info (cur_text, & utfcount, & unichar);
5830 if (UTF8_len (c1) == utfcount && (c1 & 0xFE) != 0xFE) {
5831 ring_bell ();
5832 error ("Already a UTF-8 character");
5833 return True;
5834 }
5835 }
5836 if (delete_text (cur_line, cur_text, cur_line, cur_text + 1) == FINE) {
5837 Sutf8 (c1);
5838 }
5839
5840 return True;
5841
5842 } else if (! utf8_text && (c1 & 0xC0) == 0xC0) {
5843 /* convert UTF-8 -> Latin-1 / mapped / CJK */
5844 utf8_info (cur_text, & utfcount, & unichar);
5845 if (UTF8_len (c1) != utfcount || (c1 & 0xFE) == 0xFE) {
5846 ring_bell ();
5847 error ("Not a UTF-8 character");
5848 return True;
5849 }
5850 if (mapped_text || cjk_text) {
5851 unsigned long nc = encodedchar (unichar);
5852 if (no_char (nc)) {
5853 ring_bell ();
5854 error ("Cannot convert Unicode character");
5855 return True;
5856 } else {
5857 if (delete_text (cur_line, cur_text, cur_line, cur_text + utfcount) == FINE) {
5858 (void) insert_character (nc);
5859 }
5860 return False;
5861 }
5862 }
5863 if (unichar < 0x100) {
5864 if (delete_text (cur_line, cur_text, cur_line, cur_text + utfcount) == FINE) {
5865 (void) insert_character (unichar);
5866 }
5867 } else {
5868 ring_bell ();
5869 error ("Cannot encode Unicode character");
5870 }
5871
5872 return True;
5873
5874 } else {
5875 return False;
5876 }
5877 }
5878
5879
5880 /*======================================================================*\
5881 |* Case conversion *|
5882 \*======================================================================*/
5883
5884 /**
5885 Delete base character only of combined character, leave
5886 combining accents. Called by case conversion functions.
5887 */
5888 static
5889 void
delete_basechar()5890 delete_basechar ()
5891 {
5892 char * after_char = cur_text;
5893 int text_offset = cur_text - cur_line->text;
5894
5895 advance_char (& after_char);
5896 (void) delete_text (cur_line, cur_text, cur_line, after_char);
5897 /* enforce proper placement of cursor on combining characters */
5898 move_address (cur_line->text + text_offset, y);
5899 }
5900
5901
5902 FLAG
iscombined_unichar(unichar,charpos,linebegin)5903 iscombined_unichar (unichar, charpos, linebegin)
5904 unsigned long unichar;
5905 char * charpos;
5906 char * linebegin;
5907 {
5908 if (isjoined (unichar, charpos, linebegin)) {
5909 return True;
5910 }
5911 if (iscombining_unichar (unichar)) {
5912 return True;
5913 }
5914 /* handle ARABIC TAIL FRAGMENT */
5915 if (unichar == 0xFE73 && charpos != linebegin) {
5916 unsigned long prev_unichar;
5917 char * sp;
5918 precede_char (& charpos, linebegin);
5919 prev_unichar = unicodevalue (charpos);
5920 sp = script (prev_unichar);
5921 if (streq (sp, "Arabic")) {
5922 return True;
5923 }
5924 }
5925 return False;
5926 }
5927
5928
5929 static
5930 void
check_After(unichar)5931 check_After (unichar)
5932 unsigned long unichar;
5933 {
5934 if ((Turkish && unichar == 'I') /* tr / az */
5935 || (Lithuanian && soft_dotted (unichar)) /* lt */
5936 ) {
5937 /* Handle U+0307 COMBINING DOT ABOVE
5938 After_Soft_Dotted / After_I
5939 while handling the I or the soft dotted letter
5940 */
5941 char * comb_char = cur_text;
5942 unsigned long unichar2;
5943 int utfcount;
5944
5945 utf8_info (comb_char, & utfcount, & unichar2);
5946 while (iscombining_unichar (unichar2) && unichar2 != 0x0307) {
5947 advance_char (& comb_char);
5948 utf8_info (comb_char, & utfcount, & unichar2);
5949 }
5950 if (unichar2 == 0x0307) {
5951 char * after_char = comb_char;
5952 advance_char (& after_char);
5953 (void) delete_text (cur_line, comb_char, cur_line, after_char);
5954 }
5955 }
5956 }
5957
5958
5959 /**
5960 Convert lower and upper case letters
5961 dir == 0: toggle
5962 dir == 2: convert to title case
5963 dir == 1: convert to upper case
5964 dir == -1: convert to lower case
5965 */
5966 static
5967 void
lowcap(dir)5968 lowcap (dir)
5969 int dir;
5970 {
5971 short condition = 0;
5972 unsigned long unichar;
5973
5974 if (* cur_text == '\n') {
5975 MOVRT ();
5976 return;
5977 }
5978
5979 do {
5980 int tabix = 0;
5981 int prev_x;
5982 unsigned long convchar;
5983 unichar = unicodevalue (cur_text);
5984
5985 /* initialize condition; do it inside the loop to avoid
5986 inconsistencies with Shift-F11, Shift-F3, dropping handling of
5987 language-specific cases after special cases (e.g. ßi)
5988 */
5989 if (Turkish) {
5990 condition = U_cond_tr | U_cond_az;
5991 }
5992 if (Lithuanian) {
5993 condition = U_cond_lt;
5994 }
5995
5996 if (dir >= 0 &&
5997 ( (unichar >= 0x3041 && unichar <= 0x3096)
5998 || (unichar >= 0x309D && unichar <= 0x309F)
5999 || unichar == 0x1B001 || unichar == 0x1F200
6000 )
6001 ) { /* Hiragana -> Katakana */
6002 if (unichar == 0x1B001) {
6003 convchar = 0x1B000;
6004 } else if (unichar == 0x1F200) {
6005 convchar = 0x1F201;
6006 } else {
6007 convchar = charcode (unichar + 0x60);
6008 }
6009 if (no_char (convchar)) {
6010 /* does not occur */
6011 ring_bell ();
6012 error ("Unencoded Katakana character");
6013 break;
6014 } else {
6015 prev_x = x;
6016 delete_basechar ();
6017 (void) insert_character (convchar);
6018 if (x == prev_x) { /* may occur with combining chars */
6019 move_to (prev_x + 1, y);
6020 }
6021 }
6022 }
6023 else if (dir <= 0 &&
6024 ( (unichar >= 0x30A1 && unichar <= 0x30F6)
6025 || (unichar >= 0x30FD && unichar <= 0x30FF)
6026 || unichar == 0x1B000 || unichar == 0x1F201
6027 )
6028 ) { /* Katakana -> Hiragana */
6029 if (unichar == 0x1B000) {
6030 convchar = 0x1B001;
6031 } else if (unichar == 0x1F201) {
6032 convchar = 0x1F200;
6033 } else {
6034 convchar = charcode (unichar - 0x60);
6035 }
6036 if (no_char (convchar)) {
6037 /* can occur in Big5, Johab, UHC encodings
6038 missing U+3094, U+3095, U+3096
6039 */
6040 ring_bell ();
6041 error ("Unencoded Hiragana character");
6042 break;
6043 } else {
6044 prev_x = x;
6045 delete_basechar ();
6046 (void) insert_character (convchar);
6047 if (x == prev_x) { /* may occur with combining chars */
6048 move_to (prev_x + 1, y);
6049 }
6050 }
6051 }
6052 else if ((tabix = lookup_caseconv_special (unichar, condition)) >= 0)
6053 {
6054 char * after_char;
6055 unsigned long unichar2;
6056 condition = caseconv_special [tabix].condition &= ~ U_conds_lang;
6057 if (condition & U_cond_Final_Sigma) {
6058 /** Final_Cased:
6059 Within the closest word boundaries containing
6060 C, there is a cased letter before C, and there
6061 is no cased letter after C.
6062 Before C [{cased=true}] [{word-Boundary≠true}]*
6063 After C !([{wordBoundary≠true}]* [{cased}]))
6064 */
6065 after_char = cur_text;
6066 advance_char (& after_char);
6067 unichar2 = unicodevalue (after_char);
6068 while (iscombining_unichar (unichar2)) {
6069 advance_char (& after_char);
6070 unichar2 = unicodevalue (after_char);
6071 }
6072 if (unichar2 < (unsigned long) 'A'
6073 || (unichar2 > (unsigned long) 'Z' && unichar2 < (unsigned long) 'a')
6074 || (unichar2 > (unsigned long) 'z' && unichar2 < (unsigned long) (character) '�')
6075 ) { /* final position detected */
6076 condition &= ~ U_cond_Final_Sigma;
6077 }
6078 }
6079 if (condition & U_cond_Not_Before_Dot) { /* tr / az */
6080 /** Before_Dot:
6081 C is followed by U+0307 COMBINING DOT ABOVE.
6082 Any sequence of characters with a combining
6083 class that is neither 0 nor 230 may intervene
6084 between the current character and the
6085 combining dot above.
6086 After C ([{cc≠230} & {cc≠0}])* [\u0307]
6087 */
6088 after_char = cur_text;
6089 advance_char (& after_char);
6090 unichar2 = unicodevalue (after_char);
6091 while (iscombining_notabove (unichar2) && unichar2 != 0x0307) {
6092 advance_char (& after_char);
6093 unichar2 = unicodevalue (after_char);
6094 }
6095 if (unichar2 != 0x0307) {
6096 condition &= ~ U_cond_Not_Before_Dot;
6097 }
6098 }
6099 if (condition & U_cond_After_I) { /* tr / az */
6100 /** After_I:
6101 The last preceding base character was an
6102 uppercase I, and there is no intervening
6103 Canonical_Combining_Class 230 (Above).
6104 Before C [I] ([{cc≠230} & {cc≠0}])*
6105 */
6106 /* This case only works in separated display mode;
6107 for combined mode see explicit handling below.
6108 */
6109 after_char = cur_text;
6110 precede_char (& after_char, cur_line->text);
6111 unichar2 = unicodevalue (after_char);
6112 while (iscombining_notabove (unichar2) && after_char != cur_line->text) {
6113 precede_char (& after_char, cur_line->text);
6114 unichar2 = unicodevalue (after_char);
6115 }
6116 if (unichar2 == 'I') {
6117 condition &= ~ U_cond_After_I;
6118 }
6119 }
6120 if (condition & U_cond_After_Soft_Dotted) { /* lt */
6121 /** After_Soft_Dotted:
6122 The last preceding character with a combining class
6123 of zero before C was Soft_Dotted, and there is
6124 no intervening Canonical_Combining_Class 230 (Above).
6125 Before C [{Soft_Dotted=true}] ([{cc≠230} & {cc≠0}])*
6126 */
6127 /* This case only works in separated display mode;
6128 for combined mode see explicit handling below.
6129 */
6130 after_char = cur_text;
6131 precede_char (& after_char, cur_line->text);
6132 unichar2 = unicodevalue (after_char);
6133 while (iscombining_notabove (unichar2) && after_char != cur_line->text) {
6134 precede_char (& after_char, cur_line->text);
6135 unichar2 = unicodevalue (after_char);
6136 }
6137 if (soft_dotted (unichar2)) {
6138 condition &= ~ U_cond_After_Soft_Dotted;
6139 }
6140 }
6141 if (condition & U_cond_More_Above) { /* lt */
6142 /** More_Above:
6143 C is followed by one or more characters of
6144 Canonical_Combining_Class 230 (Above)
6145 in the combining character sequence.
6146 After C [{cc≠0}]* [{cc=230}]
6147 */
6148 after_char = cur_text;
6149 advance_char (& after_char);
6150 unichar2 = unicodevalue (after_char);
6151 while (iscombining_notabove (unichar2)) {
6152 advance_char (& after_char);
6153 unichar2 = unicodevalue (after_char);
6154 }
6155 if (iscombining_above (unichar2)) {
6156 condition &= ~ U_cond_More_Above;
6157 }
6158 }
6159
6160 if (condition == 0) { /* no condition or condition resolved */
6161 FLAG do_convert = False;
6162 unsigned long convchar2;
6163 unsigned long convchar3;
6164
6165 if (caseconv_special [tabix].base == caseconv_special [tabix].lower.u1)
6166 { /* is lower, toggle or convert to upper */
6167 if (dir == 2) {
6168 convchar = caseconv_special [tabix].title.u1;
6169 convchar2 = caseconv_special [tabix].title.u2;
6170 convchar3 = caseconv_special [tabix].title.u3;
6171 do_convert = True;
6172 } else if (dir >= 0) {
6173 convchar = caseconv_special [tabix].upper.u1;
6174 convchar2 = caseconv_special [tabix].upper.u2;
6175 convchar3 = caseconv_special [tabix].upper.u3;
6176 do_convert = True;
6177 }
6178 } else { /* is upper, toggle or convert to lower */
6179 if (dir <= 0) {
6180 convchar = caseconv_special [tabix].lower.u1;
6181 convchar2 = caseconv_special [tabix].lower.u2;
6182 convchar3 = caseconv_special [tabix].lower.u3;
6183 do_convert = True;
6184 }
6185 }
6186
6187 if (do_convert) {
6188 if (convchar != 0) {
6189 convchar = charcode (convchar);
6190 if (convchar2 != 0) {
6191 convchar2 = charcode (convchar2);
6192 if (convchar3 != 0) {
6193 convchar3 = charcode (convchar3);
6194 }
6195 }
6196 }
6197
6198 if (no_char (convchar) || no_char (convchar2) || no_char (convchar3)) {
6199 ring_bell ();
6200 error ("Unencoded case converted character(s)");
6201 break;
6202 } else {
6203 FLAG inserted_something = False;
6204
6205 prev_x = x;
6206 delete_basechar ();
6207 if (convchar != 0) {
6208 inserted_something = True;
6209 (void) insert_character (convchar);
6210 if (convchar2 != 0) {
6211 (void) insert_character (convchar2);
6212 if (convchar3 != 0) {
6213 (void) insert_character (convchar3);
6214 }
6215 }
6216 }
6217
6218 if (inserted_something) {
6219 check_After (unichar);
6220
6221 if (x == prev_x) {
6222 /* may occur with combining chars */
6223 move_to (prev_x + 1, y);
6224 }
6225 }
6226 }
6227 } else {
6228 move_to (x + 1, y);
6229 }
6230 } else {
6231 /* notify to try further */
6232 tabix = -1;
6233 }
6234 }
6235 if (tabix == -1 && (tabix = lookup_caseconv (unichar)) >= 0)
6236 {
6237 convchar = unichar;
6238 if (dir == 2 && caseconv_table [tabix].title != 0) {
6239 convchar = caseconv_table [tabix].title;
6240 } else if (caseconv_table [tabix].toupper != 0) {
6241 if (dir >= 0) {
6242 convchar = unichar + caseconv_table [tabix].toupper;
6243 }
6244 } else {
6245 if (dir <= 0) {
6246 convchar = unichar + caseconv_table [tabix].tolower;
6247 }
6248 }
6249
6250 convchar = charcode (convchar);
6251 if (no_char (convchar)) {
6252 ring_bell ();
6253 error ("Unencoded case converted character");
6254 break;
6255 } else {
6256 prev_x = x;
6257 delete_basechar ();
6258 (void) insert_character (convchar);
6259
6260 if (Turkish || Lithuanian) {
6261 char * comb_char;
6262 move_to (prev_x, y);
6263 comb_char = cur_text;
6264 advance_char (& comb_char);
6265 move_address (comb_char, y);
6266 }
6267 check_After (unichar);
6268
6269 if (x == prev_x) { /* may occur with combining chars */
6270 move_to (prev_x + 1, y);
6271 }
6272 }
6273 }
6274 else if (tabix == -1)
6275 {
6276 move_to (x + 1, y);
6277 }
6278 } while ((hop_flag > 0 && idfchar (cur_text))
6279 || (Dutch && (unichar == 'I' || unichar == 'i')
6280 && (* cur_text == 'J' || * cur_text == 'j')
6281 )
6282 );
6283 }
6284
6285 /**
6286 Toggle lower and upper case letters
6287 */
6288 void
LOWCAP()6289 LOWCAP ()
6290 {
6291 lowcap (0);
6292 }
6293
6294 /**
6295 Convert to lower case letters
6296 */
6297 void
LOWER()6298 LOWER ()
6299 {
6300 lowcap (-1);
6301 }
6302
6303 /**
6304 Convert to upper case letters
6305 */
6306 void
UPPER()6307 UPPER ()
6308 {
6309 lowcap (1);
6310 }
6311
6312 /**
6313 Convert single character to upper case letter, then skip word (emacs)
6314 */
6315 void
CAPWORD()6316 CAPWORD ()
6317 {
6318 hop_flag = 0;
6319
6320 lowcap (1);
6321 MOVLF ();
6322 MNW ();
6323 }
6324
6325 /**
6326 Toggle low/cap/all cap (Windows)
6327 */
6328 void
LOWCAPWORD()6329 LOWCAPWORD ()
6330 {
6331 char * cp = cur_line->text;
6332 char * first_alpha = NIL_PTR;
6333 FLAG found = False;
6334 FLAG first_cap = False;
6335 FLAG first_title = False;
6336 FLAG subseq_cap = False;
6337 FLAG subseq_small = False;
6338 int letters = 0;
6339 #ifdef hop_title_case
6340 int upper_type = hop_flag > 0 ? 2 : 1; /* HOP -> title case */
6341 #endif
6342 unsigned long prev_uc = 0;
6343
6344 while (* cp != '\0' && * cp != '\n') {
6345 unsigned long uc = unicodevalue (cp);
6346
6347 if (Dutch && prev_uc == 'I' && uc == 'J') {
6348 advance_char (& cp);
6349 uc = unicodevalue (cp);
6350 }
6351
6352 if (cp == cur_text) {
6353 found = True;
6354 }
6355 if (idfchar (cp) /* && * cp != '_' && * cp != '$' */) {
6356 /* idfchar includes categories "Letter" and "Mark"
6357 and thus all combining characters */
6358 /* check:
6359 any subsequent letter capital → make all letters small
6360 first letter capital → make all letters capital
6361 (all letters small) → make first letter capital
6362 consider the following Unicode categories as upper:
6363 Letter, uppercase
6364 Letter, titlecase
6365 (based on caseconv_table [...].tolower != 0)
6366 and these as lower or insignificant:
6367 Letter, other
6368 Letter, lowercase
6369 Letter, modifier
6370 all others
6371 */
6372 FLAG iscapital = False;
6373 FLAG issmall = False;
6374 int tabix = lookup_caseconv (uc);
6375 if (tabix >= 0) {
6376 iscapital = caseconv_table [tabix].tolower != 0;
6377 issmall = caseconv_table [tabix].toupper != 0;
6378 }
6379
6380 if (first_alpha == NIL_PTR) {
6381 first_alpha = cp;
6382 if (iscapital) {
6383 first_cap = True;
6384 }
6385 first_title = caseconv_table [tabix].title == uc;
6386 } else {
6387 if (iscapital) {
6388 subseq_cap = True;
6389 }
6390 if (issmall) {
6391 subseq_small = True;
6392 }
6393 }
6394
6395 letters ++;
6396 } else if (found) {
6397 /* word has been scanned */
6398 break;
6399 } else {
6400 /* word has not yet been passed; reset info */
6401 first_alpha = NIL_PTR;
6402 first_cap = False;
6403 subseq_cap = False;
6404 letters = 0;
6405 }
6406
6407 prev_uc = uc;
6408 advance_char (& cp);
6409 }
6410
6411 if (found && first_alpha != NIL_PTR) {
6412 int offset = cur_line->shift_count * SHIFT_SIZE + x;
6413 unsigned long uc;
6414 char * sn;
6415
6416 move_address (first_alpha, y);
6417 uc = unicodevalue (cur_text);
6418 sn = script (uc);
6419 if (streq (sn, "Hiragana")) {
6420 hop_flag = 1;
6421 lowcap (1);
6422 } else if (streq (sn, "Katakana")) {
6423 hop_flag = 1;
6424 lowcap (-1);
6425 } else if (subseq_cap || (first_cap && ! first_title && ! subseq_small)) {
6426 hop_flag = 1;
6427 lowcap (-1);
6428 } else if (first_cap) {
6429 hop_flag = 1;
6430 lowcap (1);
6431 } else {
6432 hop_flag = 0;
6433 lowcap (2);
6434 }
6435 move_to (offset - cur_line->shift_count * SHIFT_SIZE, y);
6436 }
6437 }
6438
6439
6440 /*======================================================================*\
6441 |* Character/Code conversion *|
6442 \*======================================================================*/
6443
6444 /**
6445 AltX toggles text left of cursor between character and Unicode value
6446 * sequence of 2 <= n <= 6 hex digits, value <= 10FFFF -> Unicode character
6447 * no-digit character -> hex Unicode value, represented with >= 4 digits
6448 */
6449 void
AltX()6450 AltX ()
6451 {
6452 char * cp = cur_text;
6453 char * pp = cp;
6454 char * pp1;
6455 int n = 0;
6456 unsigned long c = 0;
6457
6458 if (cur_text == cur_line->text) {
6459 return;
6460 }
6461
6462 precede_char (& cp, cur_line->text);
6463 pp1 = cp;
6464
6465 while (n < 6 && cp != pp && ishex (* cp)) {
6466 n ++;
6467 pp = cp;
6468 precede_char (& cp, cur_line->text);
6469 }
6470
6471 if (pp != cur_text && n >= 2) {
6472 /* hex value found */
6473 char * hp = pp;
6474 while (hp != cur_text) {
6475 c = ((c << 4) + hexval (* hp));
6476 hp ++;
6477 }
6478 if (c > (unsigned long) 0x10FFFF) {
6479 n = 1;
6480 }
6481 }
6482 if (n >= 2) {
6483 /* hex -> uni */
6484
6485 (void) delete_text (cur_line, pp, cur_line, cur_text);
6486
6487 /* sequence of 2 <= n <= 6 hex digits, value <= 10FFFF -> Unicode character */
6488 if (insert_unichar (c)) {
6489 }
6490 } else {
6491 /* uni -> hex */
6492 unsigned long cv = unicodevalue (pp1);
6493
6494 /* no-digit character -> hex Unicode value, represented with >= 4 digits */
6495 if (! no_char (cv)) {
6496 move_address (pp1, y);
6497 delete_char (False);
6498
6499 insertunicode (cv);
6500 }
6501 }
6502 }
6503
6504
6505 /*======================================================================*\
6506 |* End *|
6507 \*======================================================================*/
6508