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 >	%	—hva	%	%	—h!—at	q?%:–hva
447 q	%	%	%	%	+val	q?—va
448 =	%	+value	%	%	+vala?	%
449  	%	!—at	%	%	!—at	q?%:!–va
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 >	%	—hva	%	%	—h!—at	q?%:–hva
491 q	%	%	%	%	+v?	q?—va
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 			/* &#x20AC; -> € */
5619 			text ++;
5620 			/*format = "%lx";*/
5621 			while (ishex (* text)) {
5622 				uc = uc * 16 + hexval (* text ++);
5623 				count ++;
5624 			}
5625 		} else {
5626 			/* &#8364; -> € */
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 		/* &quot; -> " */
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