1 /*
2  *	Basic user edit functions
3  *	Copyright
4  * 		(C) 1992 Joseph H. Allen
5  *
6  *	This file is part of JOE (Joe's Own Editor)
7  */
8 #include "types.h"
9 
10 /***************/
11 /* Global options */
12 int pgamnt = -1;		/* No. of PgUp/PgDn lines to keep */
13 
14 /*
15  * Move cursor to beginning of line
16  */
u_goto_bol(W * w,int k)17 int u_goto_bol(W *w, int k)
18 {
19 	BW *bw;
20 	WIND_BW(bw, w);
21 	if (bw->o.hex) {
22 		pbkwd(bw->cursor,bw->cursor->byte%16);
23 	} else {
24 		p_goto_bol(bw->cursor);
25 	}
26 	return 0;
27 }
28 
29 /*
30  * Move cursor to first non-whitespace character, unless it is
31  * already there, in which case move it to beginning of line
32  */
uhome(W * w,int k)33 int uhome(W *w, int k)
34 {
35 	BW *bw;
36 	P *p;
37 	WIND_BW(bw, w);
38 
39 	if (bw->o.hex) {
40 		return u_goto_bol(w, 0);
41 	}
42 
43 	p = pdup(bw->cursor, "uhome");
44 
45 	if (bw->o.indentfirst) {
46 		if ((bw->o.smarthome) && (piscol(p) > pisindent(p))) {
47 			p_goto_bol(p);
48 			while (joe_isblank(p->b->o.charmap,brc(p)))
49 				pgetc(p);
50 		} else
51 			p_goto_bol(p);
52 	} else {
53 		if (bw->o.smarthome && piscol(p)==0 && pisindent(p)) {
54 			while (joe_isblank(p->b->o.charmap,brc(p)))
55 				pgetc(p);
56 		} else
57 			p_goto_bol(p);
58 	}
59 
60 	pset(bw->cursor, p);
61 	prm(p);
62 	return 0;
63 }
64 
65 /*
66  * Move cursor to end of line
67  */
u_goto_eol(W * w,int k)68 int u_goto_eol(W *w, int k)
69 {
70 	BW *bw;
71 	WIND_BW(bw, w);
72 	if (bw->o.hex) {
73 		if (bw->cursor->byte + 15 - bw->cursor->byte%16 > bw->b->eof->byte)
74 			pset(bw->cursor,bw->b->eof);
75 		else
76 			pfwrd(bw->cursor, 15 - bw->cursor->byte%16);
77 	} else
78 		p_goto_eol(bw->cursor);
79 	return 0;
80 }
81 
82 /*
83  * Move cursor to beginning of file
84  */
u_goto_bof(W * w,int k)85 int u_goto_bof(W *w, int k)
86 {
87 	BW *bw;
88 	WIND_BW(bw, w);
89 	p_goto_bof(bw->cursor);
90 	return 0;
91 }
92 
93 /*
94  * Move cursor to end of file
95  */
u_goto_eof(W * w,int k)96 int u_goto_eof(W *w, int k)
97 {
98 	BW *bw;
99 	WIND_BW(bw, w);
100 	if (bw->b->vt && bw->b->pid) {
101 		pset(bw->cursor, bw->b->vt->vtcur);
102 	} else {
103 		p_goto_eof(bw->cursor);
104 	}
105 	return 0;
106 }
107 
108 /*
109  * Move cursor left
110  */
u_goto_left(W * w,int k)111 int u_goto_left(W *w, int k)
112 {
113 	BW *bw;
114 	WIND_BW(bw, w);
115 	if (bw->o.hex) {
116 		if (prgetb(bw->cursor) != NO_MORE_DATA) {
117 			return 0;
118 		} else {
119 			return -1;
120 		}
121 	}
122 	if (bw->o.picture) {
123 		if (bw->cursor->xcol) {
124 			--bw->cursor->xcol;
125 			pcol(bw->cursor,bw->cursor->xcol);
126 			return 0;
127 		} else
128 			return -1;
129 	} else {
130 		/* Have to do ECHKXCOL here because of picture mode */
131 		if (bw->cursor->xcol != piscol(bw->cursor)) {
132 			bw->cursor->xcol = piscol(bw->cursor);
133 			return 0;
134 		} else if (prgetc(bw->cursor) != NO_MORE_DATA) {
135 			bw->cursor->xcol = piscol(bw->cursor);
136 			return 0;
137 		} else {
138 			return -1;
139 		}
140 	}
141 }
142 
143 /*
144  * Move cursor right
145  */
u_goto_right(W * w,int k)146 int u_goto_right(W *w, int k)
147 {
148 	BW *bw;
149 	WIND_BW(bw, w);
150 	if (bw->o.hex) {
151 		if (pgetb(bw->cursor) != NO_MORE_DATA) {
152 			return 0;
153 		} else {
154 			return -1;
155 		}
156 	}
157 	if (bw->o.picture) {
158 		++bw->cursor->xcol;
159 		pcol(bw->cursor,bw->cursor->xcol);
160 		return 0;
161 	} else {
162 		int rtn;
163 		if (pgetc(bw->cursor) != NO_MORE_DATA) {
164 			bw->cursor->xcol = piscol(bw->cursor);
165 			rtn = 0;
166 		} else {
167 			rtn = -1;
168 		}
169 		/* Have to do EFIXXCOL here because of picture mode */
170 		if (bw->cursor->xcol != piscol(bw->cursor))
171 			bw->cursor->xcol = piscol(bw->cursor);
172 		return rtn;
173 	}
174 }
175 
176 /*
177  * Move cursor to beginning of previous word or if there isn't
178  * previous word then go to beginning of the file
179  *
180  * WORD is a sequence non-white-space characters
181  */
p_goto_prev(P * ptr)182 static int p_goto_prev(P *ptr)
183 {
184 	P *p = pdup(ptr, "p_goto_prev");
185 	struct charmap *map=ptr->b->o.charmap;
186 	int c = prgetc(p);
187 
188 	if (joe_isalnum_(map,c)) {
189 		while (joe_isalnum_(map,(c=prgetc(p))))
190 			/* Do nothing */;
191 		if (c != NO_MORE_DATA)
192 			pgetc(p);
193 	} else if (joe_isspace(map,c) || joe_ispunct(map,c)) {
194 		while ((c=prgetc(p)), (joe_isspace(map,c) || joe_ispunct(map,c)))
195 			/* Do nothing */;
196 		while(joe_isalnum_(map,(c=prgetc(p))))
197 			/* Do nothing */;
198 		if (c != NO_MORE_DATA)
199 			pgetc(p);
200 	}
201 	pset(ptr, p);
202 	prm(p);
203 	return 0;
204 }
205 
u_goto_prev(W * w,int k)206 int u_goto_prev(W *w, int k)
207 {
208 	BW *bw;
209 	WIND_BW(bw, w);
210 	return p_goto_prev(bw->cursor);
211 }
212 
213 /*
214  * Move cursor to end of next word or if there isn't
215  * next word then go to end of the file
216  *
217  * WORD is a sequence non-white-space characters
218  */
p_goto_next(P * ptr)219 static int p_goto_next(P *ptr)
220 {
221 	P *p = pdup(ptr, "p_goto_next");
222 	struct charmap *map=ptr->b->o.charmap;
223 	int c = brch(p);
224 	int rtn = -1;
225 
226 	if (joe_isalnum_(map,c)) {
227 		rtn = 0;
228 		while (joe_isalnum_(map,(c = brch(p))))
229 			pgetc(p);
230 	} else if (joe_isspace(map,c) || joe_ispunct(map,c)) {
231 		while (joe_isspace(map, (c = brch(p))) || joe_ispunct(map,c))
232 			pgetc(p);
233 		while (joe_isalnum_(map,(c = brch(p)))) {
234 			rtn = 0;
235 			pgetc(p);
236 		}
237 	} else
238 		pgetc(p);
239 	pset(ptr, p);
240 	prm(p);
241 	return rtn;
242 }
243 
u_goto_next(W * w,int k)244 int u_goto_next(W *w, int k)
245 {
246 	BW *bw;
247 	WIND_BW(bw, w);
248 	return p_goto_next(bw->cursor);
249 }
250 
pboi(P * p)251 static P *pboi(P *p)
252 {
253 	p_goto_bol(p);
254 	while (joe_isblank(p->b->o.charmap,brc(p)))
255 		pgetc(p);
256 	return p;
257 }
258 
pisedge(P * p)259 static int pisedge(P *p)
260 {
261 	P *q;
262 	int c;
263 
264 	if (pisbol(p))
265 		return -1;
266 	if (piseol(p))
267 		return 1;
268 	q = pdup(p, "pisedge");
269 	pboi(q);
270 	if (q->byte == p->byte)
271 		goto left;
272 	if (joe_isblank(p->b->o.charmap,(c = brc(p)))) {
273 		pset(q, p);
274 		if (joe_isblank(p->b->o.charmap,prgetc(q)))
275 			goto no;
276 		if (c == '\t')
277 			goto right;
278 		pset(q, p);
279 		pgetc(q);
280 		if (pgetc(q) == ' ')
281 			goto right;
282 		goto no;
283 	} else {
284 		pset(q, p);
285 		c = prgetc(q);
286 		if (c == '\t')
287 			goto left;
288 		if (c != ' ')
289 			goto no;
290 		if (prgetc(q) == ' ')
291 			goto left;
292 		goto no;
293 	}
294 
295       right:prm(q);
296 	return 1;
297       left:prm(q);
298 	return -1;
299       no:prm(q);
300 	return 0;
301 }
302 
upedge(W * w,int k)303 int upedge(W *w, int k)
304 {
305 	BW *bw;
306 	WIND_BW(bw, w);
307 	if (prgetc(bw->cursor) == NO_MORE_DATA)
308 		return -1;
309 	while (pisedge(bw->cursor) != -1)
310 		prgetc(bw->cursor);
311 	return 0;
312 }
313 
unedge(W * w,int k)314 int unedge(W *w, int k)
315 {
316 	BW *bw;
317 	WIND_BW(bw, w);
318 	if (pgetc(bw->cursor) == NO_MORE_DATA)
319 		return -1;
320 	while (pisedge(bw->cursor) != 1)
321 		pgetc(bw->cursor);
322 	return 0;
323 }
324 
325 /* Move cursor to matching delimiter */
326 /*
327  * begin end
328  *
329  * module endmodule
330  *
331  * function endfunction
332  *
333  * <word </word
334  *
335  * if elif else fi
336  *
337  * do done
338  *
339  * case esac, endcase
340  *
341  * #if #ifdef #ifndef #elseif #else #endif
342  *
343  * `ifdef  `ifndef  `else `endif
344  *
345  */
346 
347 /* A delimiter set list is a : separated list of delimiter sets.
348    A delimiter set is two or more groups of matching delimiters.
349    A group is a list of equivalent delimiters separated with |.
350 
351    For example, here is a delimiter set list, with three sets:
352    	"case|casex|casez=endcase:begin=end:if=elif=else=fi:
353 
354    In the first delimiter set: "case," "casex" and "casez" all match with
355    "endcase." In the third set: "if" matches with "elif," which matches with
356    "else," which finally matches with "fi".
357 
358    The search goes forward if the delimiter matches any words of any group
359    but the last of the set.  If the delimiter matches a word in the last
360    group, the search goes backward to the first delimiter.
361 
362    Delimiter sets lists are now UTF-8.
363 */
364 
365 /* Return pointer to first matching set in delimiter set list.  Returns NULL
366    if no matches were found. */
367 
next_set(const char * set)368 static const char *next_set(const char *set)
369 {
370 	while (*set && *set!=':')
371 		++set;
372 	if (*set==':')
373 		++set;
374 	return set;
375 }
376 
next_group(const char * group)377 static const char *next_group(const char *group)
378 {
379 	while (*group && *group!='=' && *group!=':')
380 		++group;
381 	if (*group=='=')
382 		++group;
383 	return group;
384 }
385 
next_word(const char * word)386 static const char *next_word(const char *word)
387 {
388 	while (*word && *word!='|' && *word!='=' && *word!=':')
389 		++word;
390 	if (*word=='|')
391 		++word;
392 	return word;
393 }
394 
match_word(const char * word,const int * s)395 static int match_word(const char *word,const int *s)
396 {
397 	while (*s && *word && utf8_decode_fwrd(&word, NULL) == *s++);
398 
399 	if (!*s && (!*word || *word=='|' || *word=='=' || *word==':'))
400 		return 1;
401 	else
402 		return 0;
403 }
404 
is_in_group(const char * group,const int * s)405 static int is_in_group(const char *group,const int *s)
406 {
407 	while (*group && *group!='=' && *group!=':') {
408 		if (match_word(group, s))
409 			return 1;
410 		else
411 			group = next_word(group);
412 	}
413 	return 0;
414 }
415 
is_in_any_group(const char * group,const int * s)416 static int is_in_any_group(const char *group,const int *s)
417 {
418 	while (*group && *group!=':') {
419 		if (match_word(group, s))
420 			return 1;
421 		else {
422 			group = next_word(group);
423 			if (*group == '=')
424 				++group;
425 		}
426 	}
427 	return 0;
428 }
429 
find_last_group(const char * group)430 static const char *find_last_group(const char *group)
431 {
432 	const char *s;
433 	for (s = group; *s && *s!=':'; s=next_group(s))
434 		group = s;
435 	return group;
436 }
437 
438 #define MAX_WORD_SIZE 255
439 
440 /* Search for matching delimiter: ignore things in comments or strings */
441 
tomatch_char_or_word(BW * bw,int word_delimiter,int c,int f,const char * set,const char * group,int backward)442 static int tomatch_char_or_word(BW *bw,int word_delimiter,int c,int f,const char *set,const char *group,int backward)
443 {
444 	P *p = pdup(bw->cursor, "tomatch_char_or_word");
445 	P *q = pdup(p, "tomatch_char_or_word");
446 	const char *last_of_set = "";
447 	int buf[MAX_WORD_SIZE+1];
448 	int len;
449 	int query_highlighter = bw->o.highlighter_context && bw->o.syntax && bw->db;
450 	int initial_context = 0;
451 	int col = 0;
452 	int cnt = 0;	/* No. levels of delimiters we're in */
453 	int d;
454 	off_t sod = 0; /* Start of delimiter */
455 
456 	if (word_delimiter) {
457 		if (backward) {
458 			last_of_set = find_last_group(set);
459 			p_goto_next(p);
460 			p_goto_prev(p);
461 		} else {
462 			last_of_set = find_last_group(group);
463 			p_goto_next(p);
464 		}
465 		pset(q, p);
466 	}
467 
468 	if (query_highlighter) {
469 		col = -1;
470 		do {
471 			d = prgetc(q);
472 			++col;
473 		} while (d != NO_MORE_DATA && d != '\n');
474 		if (d != NO_MORE_DATA)
475 			pgetc(q);
476 		parse(bw->o.syntax, q, lattr_get(bw->db, bw->o.syntax, q, q->line),bw->o.charmap);
477 		initial_context = attr_buf[col] & CONTEXT_MASK;
478 	}
479 
480 	if (backward) {
481 		/* Backward search */
482 		while ((d = prgetc(p)) != NO_MORE_DATA) {
483 			int peek;
484 			int peek1;
485 
486 			if (query_highlighter && d == '\n'){
487 				pset(q, p);
488 				col = -1;
489 				do {
490 					d = prgetc(q);
491 					++col;
492 				} while (d != NO_MORE_DATA && d != '\n');
493 				if (d != NO_MORE_DATA)
494 					pgetc(q);
495 				parse(bw->o.syntax, q, lattr_get(bw->db, bw->o.syntax, q, q->line), bw->o.charmap);
496 				continue;
497 			}
498 
499 			peek = prgetc(p);
500 			peek1 = 0;
501 			if(peek != NO_MORE_DATA) {
502 				peek1 = prgetc(p);
503 				if (peek1 != NO_MORE_DATA)
504 					pgetc(p);
505 				pgetc(p);
506 			}
507 			--col;
508 
509 			if (query_highlighter
510 			    && (attr_buf[col] & (CONTEXT_COMMENT | CONTEXT_STRING))
511 			    && (attr_buf[col] & CONTEXT_MASK) != initial_context) {
512 				/* Ignore */
513 			} else if (!query_highlighter
514 			           && (bw->o.cpp_comment || bw->o.pound_comment ||
515 			               bw->o.semi_comment || bw->o.tex_comment || bw->o.vhdl_comment) && d == '\n') {
516 				int cc;
517 				pset(q, p);
518 				p_goto_bol(q);
519 				while((cc = pgetc(q)) != '\n') {
520 					if (cc == '\\') {
521 						if (pgetc(q) == '\n')
522 							break;
523 					} else if (bw->o.pound_comment && cc == '$' && brch(q)=='#') {
524 						pgetc(q);
525 					} else if(!bw->o.no_double_quoted && cc=='"') {
526 						while ((cc = pgetc(q)) != '\n')
527 							if (cc == '"') break;
528 							else if (cc == '\\') if ((cc = pgetc(q)) == '\n') break;
529 						if (cc == '\n')
530 							break;
531 					} else if (bw->o.single_quoted && cc == '\'') {
532 						while((cc = pgetc(q)) != '\n')
533 							if (cc == '\'') break;
534 							else if (cc == '\\') if ((cc = pgetc(q)) == '\n') break;
535 						if (cc == '\n')
536 							break;
537 					} else if (bw->o.cpp_comment && cc == '/') {
538 						if (brch(q)=='/') {
539 							prgetc(q);
540 							pset(p,q);
541 							break;
542 						}
543 					} else if (bw->o.vhdl_comment && cc == '-') {
544 						if (brch(q)=='-') {
545 							prgetc(q);
546 							pset(p,q);
547 							break;
548 						}
549 					} else if (bw->o.pound_comment && cc == '#') {
550 						pset(p,q);
551 						break;
552 					} else if (bw->o.semi_comment && cc == ';') {
553 						pset(p,q);
554 						break;
555 					} else if (bw->o.tex_comment && cc == '%') {
556 						pset(p,q);
557 						break;
558 					}
559 				}
560 			} else if (peek == '\\' && peek1!='\\') {
561 				/* Ignore */
562 			} else if (!query_highlighter && !bw->o.no_double_quoted && d == '"') {
563 				while((d = prgetc(p)) != NO_MORE_DATA) {
564 					if (d == '"') {
565 						d = prgetc(p);
566 						if (d != '\\') {
567 							if (d != NO_MORE_DATA)
568 								pgetc(p);
569 							break;
570 						}
571 					}
572 				}
573 			} else if (!query_highlighter && bw->o.single_quoted && d == '\'' && c != '\'' && c != '`') {
574 				while((d = prgetc(p)) != NO_MORE_DATA)
575 					if (d == '\'') {
576 						d = prgetc(p);
577 						if (d != '\\') {
578 							if (d != NO_MORE_DATA)
579 								pgetc(p);
580 							break;
581 						}
582 					}
583 			} else if (!query_highlighter && bw->o.c_comment && d == '/') {
584 				d = prgetc(p);
585 				if (d == '*') {
586 					d = prgetc(p);
587 					do {
588 						do {
589 							if (d == '*') break;
590 						} while ((d = prgetc(p)) != NO_MORE_DATA);
591 						d = prgetc(p);
592 					} while (d != NO_MORE_DATA && d != '/');
593 				} else if (d != NO_MORE_DATA)
594 					pgetc(p);
595 			} else if (word_delimiter) {
596 				if (joe_isalnum_(p->b->o.charmap, d)) {
597 					int x;
598 					int flg=0;
599 					P *r;
600 					len=0;
601 					while (joe_isalnum_(p->b->o.charmap, d)) {
602 						if(len!=MAX_WORD_SIZE)
603 							buf[len++] = d;
604 						d=prgetc(p);
605 						--col;
606 					}
607 					/* ifdef hack */
608 					r = pdup(p, "tomatch_char_or_word");
609 					while (d ==' ' || d=='\t')
610 						d=prgetc(r);
611 					/* VHDL hack */
612 					if ((d=='d' || d=='D') && bw->o.vhdl_comment) {
613 						d=prgetc(r);
614 						if(d=='n' || d=='N') {
615 							d=prgetc(r);
616 							if(d=='e' || d=='E') {
617 								d=prgetc(r);
618 								if(d==' ' || d=='\t' || d=='\n' || d==NO_MORE_DATA)
619 									flg=1;
620 							}
621 						}
622 					}
623 					prm(r);
624 					if (d == utf8_decode_string(set))
625 						buf[len++] = d;
626 					if(d!=NO_MORE_DATA)
627 						pgetc(p);
628 					++col;
629 					buf[len]=0;
630 					for(x=0;x!=len/2;++x) {
631 						int e = buf[x];
632 						buf[x] = buf[len-x-1];
633 						buf[len-x-1] = e;
634 					}
635 					if (is_in_group(last_of_set,buf)) {
636 						++cnt;
637 					} else if(is_in_group(set,buf) && !flg && !cnt--) {
638 						pset(bw->cursor,p);
639 						prm(q);
640 						prm(p);
641 						return 0;
642 					}
643 				}
644 			} else if (d == c) {
645 				++cnt;
646 			} else if (d == f && !cnt--) {
647 				pset(bw->cursor, p);
648 				prm(q);
649 				prm(p);
650 				return 0;
651 			}
652 		}
653 	} else {
654 		/* Forward search */
655 		while ((sod = p->byte), ((d = pgetc(p)) != NO_MORE_DATA)) {
656 			if (query_highlighter && d == '\n') {
657 				parse(bw->o.syntax, q, lattr_get(bw->db, bw->o.syntax, q, q->line), bw->o.charmap);
658 				col = 0;
659 				continue;
660 			}
661 
662 			if (query_highlighter
663 			    && (attr_buf[col] & (CONTEXT_COMMENT | CONTEXT_STRING))
664 			    && (attr_buf[col] & CONTEXT_MASK) != initial_context) {
665 				/* Ignore */
666 			} else if (d == '\\') {
667 				if (!(query_highlighter && brch(p) == '\n')) {
668 					pgetc(p);
669 					++col;
670 				}
671 			} else if (!query_highlighter && !bw->o.no_double_quoted && d == '"') {
672 				while ((d = pgetc(p)) != NO_MORE_DATA)
673 					if (d == '"') break;
674 					else if (d == '\\') pgetc(p);
675 			} else if (!query_highlighter && bw->o.single_quoted && d == '\'' && c != '\'' && c != '`') {
676 				while((d = pgetc(p)) != NO_MORE_DATA)
677 					if (d == '\'') break;
678 					else if (d == '\\') pgetc(p);
679 			} else if (!query_highlighter && d == '$' && brch(p)=='#' && bw->o.pound_comment) {
680 				pgetc(p);
681 			} else if (!query_highlighter
682 			           && ((bw->o.pound_comment && d == '#') ||
683 				       (bw->o.semi_comment && d == ';') ||
684 				       (bw->o.tex_comment && d == '%') ||
685 				       (bw->o.vhdl_comment && d == '-' && brch(p) == '-') ||
686 				       (bw->o.cpp_comment && d == '/' && brch(p) == '/'))) {
687 				while ((d = pgetc(p)) != NO_MORE_DATA)
688 					if (d == '\n')
689 						break;
690 			} else if (!query_highlighter && bw->o.c_comment && d == '/' && brch(p) == '*') {
691 				pgetc(p);
692 				d = pgetc(p);
693 				do {
694 					do {
695 						if (d == '*') break;
696 					} while ((d = pgetc(p)) != NO_MORE_DATA);
697 					d = pgetc(p);
698 				} while (d != NO_MORE_DATA && d != '/');
699 			} else if (word_delimiter) {
700 				int set0 = utf8_decode_string(set);
701 				if (d == set0) {
702 					/* ifdef hack */
703 					len = 0;
704 					if (!joe_isalnum_(p->b->o.charmap, d)) { /* If it's a # in #ifdef, allow spaces after it */
705 						sod = p->byte;
706 						while ((d = pgetc(p))!=NO_MORE_DATA) {
707 							++col;
708 							if (d!=' ' && d!='\t')
709 								break;
710 							sod = p->byte;
711 						}
712 						buf[0] = set0;
713 						len=1;
714 					}
715 					if (joe_isalnum_(p->b->o.charmap, d))
716 						goto doit;
717 					if (d!=NO_MORE_DATA) {
718 						prgetc(p);
719 						--col;
720 					}
721 				} else if (joe_isalpha_(p->b->o.charmap, d)) {
722 					len=0;
723 					doit:
724 					while (joe_isalnum_(p->b->o.charmap, d)) {
725 						if(len!=MAX_WORD_SIZE)
726 							buf[len++] = d;
727 						d=pgetc(p);
728 						++col;
729 					}
730 					if (d!=NO_MORE_DATA) {
731 						prgetc(p);
732 						--col;
733 					}
734 					buf[len]=0;
735 					if (is_in_group(set,buf)) {
736 						++cnt;
737 					} else if (cnt==0) {
738 						if (is_in_any_group(group,buf)) {
739 							pgoto(p, sod);
740 							pset(bw->cursor,p);
741 							prm(q);
742 							prm(p);
743 							return 0;
744 						}
745 					} else if(is_in_group(last_of_set,buf)) {
746 						/* VHDL hack */
747 						if (bw->o.vhdl_comment && (match_word("end", buf) || !match_word("END", buf)))
748 							while((d=pgetc(p))!=NO_MORE_DATA) {
749 								++col;
750 								if (d==';' || d=='\n') {
751 									prgetc(p);
752 									--col;
753 									break;
754 								}
755 							}
756 						--cnt;
757 					}
758 				}
759 			} else if (d == c) {
760 				++cnt;
761 			} else if (d == f && !--cnt) {
762 				prgetc(p);
763 				pset(bw->cursor, p);
764 				prm(q);
765 				prm(p);
766 				return 0;
767 			}
768 			++col;
769 		}
770 	}
771 	prm(q);
772 	prm(p);
773 	return -1;
774 }
775 
tomatch_char(BW * bw,int c,int f,int dir)776 static int tomatch_char(BW *bw,int c,int f,int dir)
777 {
778 	return tomatch_char_or_word(bw, 0, c, f, 0, 0, dir == -1);
779 }
780 
tomatch_word(BW * bw,const char * set,const char * group)781 static int tomatch_word(BW *bw,const char *set,const char *group)
782 {
783 	return tomatch_char_or_word(bw, 1, 0, 0, set, group, !*group || *group==':');
784 }
785 
786 /* Return true if <foo /> */
787 
xml_startend(P * p)788 static int xml_startend(P *p)
789 {
790 	int c, d=0;
791 	p=pdup(p, "xml_startend");
792 	while((c=pgetc(p)) != NO_MORE_DATA) {
793 		if(d=='/' && c=='>') {
794 			prm(p);
795 			return 1;
796 		} else if(c=='>')
797 			break;
798 		d=c;
799 	}
800 	prm(p);
801 	return 0;
802 }
803 
tomatch_xml(BW * bw,int * word,int dir)804 static int tomatch_xml(BW *bw,int *word,int dir)
805 {
806 	if (dir== -1) {
807 		/* Backward search */
808 		P *p=pdup(bw->cursor, "tomatch_xml");
809 		int c;
810 		int buf[MAX_WORD_SIZE+1];
811 		int len;
812 		int cnt = 1;
813 		p_goto_next(p);
814 		p_goto_prev(p);
815 		while ((c=prgetc(p)) != NO_MORE_DATA) {
816 			if (joe_isalnum_(p->b->o.charmap, c) || c == '.' || c == ':' || c == '-') {
817 				int x;
818 				len=0;
819 				while (joe_isalnum_(p->b->o.charmap, c) || c=='.' || c==':' || c == '-') {
820 					if(len!=MAX_WORD_SIZE)
821 						buf[len++] = c;
822 					c=prgetc(p);
823 				}
824 				if(c!=NO_MORE_DATA)
825 					c = pgetc(p);
826 				buf[len]=0;
827 				for(x=0;x!=len/2;++x) {
828 					int d = buf[x];
829 					buf[x] = buf[len-x-1];
830 					buf[len-x-1] = d;
831 				}
832 				if (!Zcmp(word,buf) && !xml_startend(p)) {
833 					if (c=='<') {
834 						if (!--cnt) {
835 							pset(bw->cursor,p);
836 							prm(p);
837 							return 0;
838 						}
839 					}
840 					else if (c=='/') {
841 						++cnt;
842 					}
843 				}
844 			}
845 		}
846 		prm(p);
847 		return -1;
848 	} else {
849 		/* Forward search */
850 		P *p=pdup(bw->cursor, "tomatch_xml");
851 		int c;
852 		int buf[MAX_WORD_SIZE+1];
853 		int len;
854 		int cnt = 1;
855 		off_t sod = 0;
856 		while ((c=pgetc(p)) != NO_MORE_DATA) {
857 			if (c == '<') {
858 				int e = 1;
859 				sod = p->byte;
860 				c = pgetc(p);
861 				if (c=='/') {
862 					sod = p->byte;
863 					e = 0;
864 					c = pgetc(p);
865 				}
866 				if (joe_isalpha_(p->b->o.charmap, c) || c==':' || c=='-' || c=='.') {
867 					len=0;
868 					while (joe_isalnum_(p->b->o.charmap, c) || c==':' || c=='-' || c=='.') {
869 						if(len!=MAX_WORD_SIZE)
870 							buf[len++]=c;
871 						c=pgetc(p);
872 					}
873 					if (c!=NO_MORE_DATA)
874 						prgetc(p);
875 					buf[len]=0;
876 					if (!Zcmp(word,buf) && !xml_startend(p)) {
877 						if (e) {
878 							++cnt;
879 						}
880 						else if (!--cnt) {
881 							pgoto(p, sod);
882 							pset(bw->cursor,p);
883 							prm(p);
884 							return 0;
885 						}
886 					}
887 				} else if (c!=NO_MORE_DATA) {
888 					prgetc(p);
889 				}
890 			}
891 		}
892 		prm(p);
893 		return -1;
894 	}
895 }
896 
get_xml_name(P * p,int * buf)897 static void get_xml_name(P *p,int *buf)
898 {
899 	int c;
900 	int len=0;
901 	p=pdup(p, "get_xml_name");
902 	c=pgetc(p);
903 	while (joe_isalnum_(p->b->o.charmap, c) || c==':' || c=='-' || c=='.') {
904 		if(len!=MAX_WORD_SIZE)
905 			buf[len++]=c;
906 		c=pgetc(p);
907 	}
908 	buf[len]=0;
909 	prm(p);
910 }
911 
get_delim_name(P * q,int * buf)912 static void get_delim_name(P *q,int *buf)
913 {
914 	int c;
915 	int len=0;
916 	P *p=pdup(q, "get_delim_name");
917 	while ((c=prgetc(p))!=NO_MORE_DATA)
918 		if (c!=' ' && c!='\t')
919 			break;
920 	prm(p);
921 	/* preprocessor directive hack */
922 	if (c=='#' || c=='`')
923 		buf[len++]=c;
924 
925 	p=pdup(q, "get_delim_name");
926 	c=pgetc(p);
927 	while (joe_isalnum_(p->b->o.charmap, c)) {
928 		if(len!=MAX_WORD_SIZE)
929 			buf[len++]=c;
930 		c=pgetc(p);
931 	}
932 	buf[len]=0;
933 	prm(p);
934 }
935 
utomatch(W * w,int k)936 int utomatch(W *w, int k)
937 {
938 	int d;
939 	int c,			/* Character under cursor */
940 	 f,			/* Character to find */
941 	 dir;			/* 1 to search forward, -1 to search backward */
942 	BW *bw;
943 	WIND_BW(bw, w);
944 
945 	c = brch(bw->cursor);
946 
947 	/* Check for word delimiters */
948 	if (joe_isalnum_(bw->cursor->b->o.charmap, c)) {
949 		P *p;
950 		int buf[MAX_WORD_SIZE+1];
951 		char utf8_buf[MAX_WORD_SIZE * 6 + 1]; /* Possibly UTF-8 version of buf */
952 		int buf1[MAX_WORD_SIZE+1];
953 		const char *list = bw->b->o.text_delimiters;
954 		const char *set;
955 		const char *group;
956 		const char *word;
957 		int flg=0;
958 		p=pdup(bw->cursor, "utomatch");
959 		p_goto_next(p);
960 		p_goto_prev(p);
961 		get_delim_name(p,buf);
962 		get_xml_name(p,buf1);
963 		c=prgetc(p);
964 		if (c=='<')
965 			flg = 1;
966 		else if (c=='/') {
967 			c=prgetc(p);
968 			if (c=='<')
969 				flg = -1;
970 		}
971 		prm(p);
972 
973 		if (flg) {
974 			return tomatch_xml(bw, buf1, flg);
975 		}
976 
977 		for (set = list; set && *set; set = next_set(set)) {
978 			for (group = set; *group && *group!='=' && *group!=':'; group=next_group(group)) {
979 				for (word = group; *word && *word!='|' && *word!='=' && *word!=':'; word=next_word(word)) {
980 					if (match_word(word, buf)) {
981 						return tomatch_word(bw, set, next_group(word));
982 					}
983 				}
984 			}
985 
986 		}
987 
988 		/* We don't know the word, so start a search */
989 		if (bw->b->o.charmap->type) {
990 			Ztoutf8(utf8_buf, SIZEOF(utf8_buf), buf);
991 		} else {
992 			Ztoz(utf8_buf, SIZEOF(utf8_buf), buf);
993 		}
994 		return dofirst(bw, 0, 0, utf8_buf);
995 	}
996 
997 	switch (c) {
998 	case '/':
999 		dir = 1;
1000 		pgetc(bw->cursor);
1001 		f = brch(bw->cursor);
1002 		prgetc(bw->cursor);
1003 		if(f=='*') f = '/';
1004 		else {
1005 			dir = -1;
1006 			f = prgetc(bw->cursor);
1007 			if (f!=NO_MORE_DATA)
1008 				pgetc(bw->cursor);
1009 			if(f=='*') f = '/';
1010 			else
1011 				return -1;
1012 		}
1013 		break;
1014 	case '*':
1015 		dir = -1;
1016 		pgetc(bw->cursor);
1017 		f = brch(bw->cursor);
1018 		prgetc(bw->cursor);
1019 		if(f=='/') f = '*';
1020 		else {
1021 			dir = 1;
1022 			f = prgetc(bw->cursor);
1023 			if (f!=NO_MORE_DATA)
1024 				pgetc(bw->cursor);
1025 			if(f=='/') f = '*';
1026 			else
1027 				return -1;
1028 		}
1029 		break;
1030 	case '(':
1031 		f = ')';
1032 		dir = 1;
1033 		break;
1034 	case '[':
1035 		f = ']';
1036 		dir = 1;
1037 		break;
1038 	case '{':
1039 		f = '}';
1040 		dir = 1;
1041 		break;
1042 	case '`':
1043 		f = '\'';
1044 		dir = 1;
1045 		break;
1046 	case '<':
1047 		f = '>';
1048 		dir = 1;
1049 		break;
1050 	case ')':
1051 		f = '(';
1052 		dir = -1;
1053 		break;
1054 	case ']':
1055 		f = '[';
1056 		dir = -1;
1057 		break;
1058 	case '}':
1059 		f = '{';
1060 		dir = -1;
1061 		break;
1062 	case '\'':
1063 		f = '`';
1064 		dir = -1;
1065 		break;
1066 	case '>':
1067 		f = '<';
1068 		dir = -1;
1069 		break;
1070 	default:
1071 		return -1;
1072 	}
1073 
1074 	/* Search for matching C comment */
1075 	if (f == '/' || f == '*') {
1076 		P *p = pdup(bw->cursor, "utomatch");
1077 		if (dir == 1) {
1078 			d = pgetc(p);
1079 			do {
1080 				do {
1081 					if (d == '*') break;
1082 				} while ((d = pgetc(p)) != NO_MORE_DATA);
1083 				d = pgetc(p);
1084 			} while (d != NO_MORE_DATA && d != '/');
1085 			if (d == '/') {
1086 				if (f == '*') {
1087 					prgetc(p);
1088 				}
1089 				pset(bw->cursor,p);
1090 				prgetc(bw->cursor);
1091 			}
1092 		} else {
1093 			d = prgetc(p);
1094 			do {
1095 				do {
1096 					if (d == '*') break;
1097 				} while ((d = prgetc(p)) != NO_MORE_DATA);
1098 				d = prgetc(p);
1099 			} while (d != NO_MORE_DATA && d != '/');
1100 			if (d == '/') {
1101 				if (f == '*') {
1102 					pgetc(p);
1103 				}
1104 				pset(bw->cursor,p);
1105 			}
1106 		}
1107 		prm(p);
1108 		if (d == NO_MORE_DATA)
1109 			return -1;
1110 		else
1111 			return 0;
1112 	}
1113 
1114 	return tomatch_char(bw, c, f, dir);
1115 }
1116 
1117 /* Move cursor up */
1118 
uuparw(W * w,int k)1119 int uuparw(W *w, int k)
1120 {
1121 	BW *bw;
1122 	WIND_BW(bw, w);
1123 	if (bw->o.hex) {
1124 		if (bw->cursor->byte<16)
1125 			return -1;
1126 		else {
1127 			pbkwd(bw->cursor, 16);
1128 			return 0;
1129 		}
1130 	}
1131 	if (bw->cursor->line) {
1132 		pprevl(bw->cursor);
1133 		pcol(bw->cursor, bw->cursor->xcol);
1134 		return 0;
1135 	} else
1136 		return -1;
1137 }
1138 
1139 /* Move cursor down */
1140 
udnarw(W * w,int k)1141 int udnarw(W *w, int k)
1142 {
1143 	BW *bw;
1144 	WIND_BW(bw, w);
1145 	if (bw->o.hex) {
1146 		if (bw->cursor->byte+16 <= bw->b->eof->byte) {
1147 			pfwrd(bw->cursor, 16);
1148 			return 0;
1149 		} else if (bw->cursor->byte != bw->b->eof->byte) {
1150 			pset(bw->cursor, bw->b->eof);
1151 			return 0;
1152 		} else {
1153 			return -1;
1154 		}
1155 	}
1156 	if (bw->cursor->line != bw->b->eof->line) {
1157 		pnextl(bw->cursor);
1158 		pcol(bw->cursor, bw->cursor->xcol);
1159 		return 0;
1160 	} else if(bw->o.picture) {
1161 		p_goto_eol(bw->cursor);
1162 		binsc(bw->cursor,'\n');
1163 		pgetc(bw->cursor);
1164 		pcol(bw->cursor, bw->cursor->xcol);
1165 		return 0;
1166 	} else
1167 		return -1;
1168 }
1169 
1170 /* Move cursor to top of window */
1171 
utos(W * w,int k)1172 int utos(W *w, int k)
1173 {
1174 	off_t col;
1175 	BW *bw;
1176 	WIND_BW(bw, w);
1177 	col = bw->cursor->xcol;
1178 
1179 	pset(bw->cursor, bw->top);
1180 	pcol(bw->cursor, col);
1181 	bw->cursor->xcol = col;
1182 	return 0;
1183 }
1184 
1185 /* Move cursor to bottom of window */
1186 
ubos(W * w,int k)1187 int ubos(W *w, int k)
1188 {
1189 	BW *bw;
1190 	off_t col;
1191 	WIND_BW(bw, w);
1192 	col = bw->cursor->xcol;
1193 
1194 	pline(bw->cursor, bw->top->line + bw->h - 1);
1195 	pcol(bw->cursor, col);
1196 	bw->cursor->xcol = col;
1197 	return 0;
1198 }
1199 
1200 /* Scroll buffer window up n lines
1201  * If beginning of file is close, scrolls as much as it can
1202  * If beginning of file is on-screen, cursor jumps to beginning of file
1203  *
1204  * If flg is set: cursor stays fixed relative to screen edge
1205  * If flg is clr: cursor stays fixed on the buffer line
1206  */
1207 
scrup(BW * bw,ptrdiff_t n,int flg)1208 void scrup(BW *bw, ptrdiff_t n, int flg)
1209 {
1210 	ptrdiff_t scrollamnt = 0;
1211 	ptrdiff_t cursoramnt = 0;
1212 	ptrdiff_t x;
1213 
1214 	/* Decide number of lines we're really going to scroll */
1215 
1216 	if (bw->o.hex) {
1217 		if (bw->top->byte/16 >= n)
1218 			scrollamnt = cursoramnt = n;
1219 		else if (bw->top->byte/16)
1220 			scrollamnt = cursoramnt = (ptrdiff_t)(bw->top->byte/16);
1221 		else if (flg)
1222 			cursoramnt = (ptrdiff_t)(bw->cursor->byte/16);
1223 		else if (bw->cursor->byte/16 >= n)
1224 			cursoramnt = n;
1225 	} else {
1226 		if (bw->top->line >= n)
1227 			scrollamnt = cursoramnt = n;
1228 		else if (bw->top->line)
1229 			scrollamnt = cursoramnt = (ptrdiff_t)bw->top->line;
1230 		else if (flg)
1231 			cursoramnt = (ptrdiff_t)bw->cursor->line;
1232 		else if (bw->cursor->line >= n)
1233 			cursoramnt = n;
1234 	}
1235 
1236 	if (bw->o.hex) {
1237 		/* Move top-of-window pointer */
1238 		pbkwd(bw->top,scrollamnt*16);
1239 		/* Move cursor */
1240 		pbkwd(bw->cursor,cursoramnt*16);
1241 		/* If window is on the screen, give (buffered) scrolling command */
1242 		if (bw->parent->y != -1)
1243 			nscrldn(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
1244 	} else {
1245 		/* Move top-of-window pointer */
1246 		for (x = 0; x != scrollamnt; ++x)
1247 			pprevl(bw->top);
1248 		p_goto_bol(bw->top);
1249 
1250 		/* Move cursor */
1251 		for (x = 0; x != cursoramnt; ++x)
1252 			pprevl(bw->cursor);
1253 		p_goto_bol(bw->cursor);
1254 		pcol(bw->cursor, bw->cursor->xcol);
1255 
1256 		/* If window is on the screen, give (buffered) scrolling command */
1257 		if (bw->parent->y != -1)
1258 			nscrldn(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
1259 	}
1260 }
1261 
1262 /* Scroll buffer window down n lines
1263  * If end of file is close, scrolls as much as possible
1264  * If end of file is on-screen, cursor jumps to end of file
1265  *
1266  * If flg is set: cursor stays fixed relative to screen edge
1267  * If flg is clr: cursor stays fixed on the buffer line
1268  */
1269 
scrdn(BW * bw,ptrdiff_t n,int flg)1270 void scrdn(BW *bw, ptrdiff_t n, int flg)
1271 {
1272 	ptrdiff_t scrollamnt = 0;
1273 	ptrdiff_t cursoramnt = 0;
1274 	ptrdiff_t x;
1275 
1276 	/* How much we're really going to scroll... */
1277 	if (bw->o.hex) {
1278 		if (bw->top->b->eof->byte/16 < bw->top->byte/16 + bw->h) {
1279 			cursoramnt = (ptrdiff_t)(bw->top->b->eof->byte/16 - bw->cursor->byte/16);
1280 			if (!flg && cursoramnt > n)
1281 				cursoramnt = n;
1282 		} else if (bw->top->b->eof->byte/16 - (bw->top->byte/16 + bw->h) >= n)
1283 			cursoramnt = scrollamnt = n;
1284 		else
1285 			cursoramnt = scrollamnt = (ptrdiff_t)(bw->top->b->eof->byte/16 - (bw->top->byte/16 + bw->h) + 1);
1286 	} else {
1287 		if (bw->top->b->eof->line < bw->top->line + bw->h) {
1288 			cursoramnt = (ptrdiff_t)(bw->top->b->eof->line - bw->cursor->line);
1289 			if (!flg && cursoramnt > n)
1290 				cursoramnt = n;
1291 		} else if (bw->top->b->eof->line - (bw->top->line + bw->h) >= n)
1292 			cursoramnt = scrollamnt = n;
1293 		else
1294 			cursoramnt = scrollamnt = (ptrdiff_t)(bw->top->b->eof->line - (bw->top->line + bw->h) + 1);
1295 	}
1296 
1297 	if (bw->o.hex) {
1298 		/* Move top-of-window pointer */
1299 		pfwrd(bw->top,16*scrollamnt);
1300 		/* Move cursor */
1301 		pfwrd(bw->cursor,16*cursoramnt);
1302 		/* If window is on screen, give (buffered) scrolling command to terminal */
1303 		if (bw->parent->y != -1)
1304 			nscrlup(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
1305 	} else {
1306 		/* Move top-of-window pointer */
1307 		for (x = 0; x != scrollamnt; ++x)
1308 			pnextl(bw->top);
1309 
1310 		/* Move cursor */
1311 		for (x = 0; x != cursoramnt; ++x)
1312 			pnextl(bw->cursor);
1313 		pcol(bw->cursor, bw->cursor->xcol);
1314 
1315 		/* If window is on screen, give (buffered) scrolling command to terminal */
1316 		if (bw->parent->y != -1)
1317 			nscrlup(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
1318 	}
1319 }
1320 
1321 /* Page up */
1322 
upgup(W * w,int k)1323 int upgup(W *w, int k)
1324 {
1325 	BW *bw;
1326 	WIND_BW(bw, w);
1327 	if (menu_above) {
1328 		if (w->link.prev->watom == &watommenu && w->link.prev->win == w) {
1329 			return umpgup(w->link.prev, 0);
1330 		}
1331 	} else {
1332 		if (w->link.next->watom == &watommenu && w->link.next->win == w) {
1333 			return umpgup(w->link.next, 0);
1334 		}
1335 	}
1336 	bw = (BW *) bw->parent->main->object;
1337 
1338 	if (bw->o.hex ? bw->cursor->byte < 16 : !bw->cursor->line)
1339 		return -1;
1340 	if (pgamnt < 0)
1341 		scrup(bw, bw->h / 2 + bw->h % 2, 1);
1342 	else if (pgamnt < bw->h)
1343 		scrup(bw, bw->h - pgamnt, 1);
1344 	else
1345 		scrup(bw, 1, 1);
1346 	return 0;
1347 }
1348 
1349 /* Page down */
1350 
upgdn(W * w,int k)1351 int upgdn(W *w, int k)
1352 {
1353 	BW *bw;
1354 	WIND_BW(bw, w);
1355 	if (menu_above) {
1356 		if (w->link.prev->watom == &watommenu && w->link.prev->win == w) {
1357 			return umpgdn(w->link.prev, 0);
1358 		}
1359 	} else {
1360 		if (w->link.next->watom == &watommenu && w->link.next->win == w) {
1361 			return umpgdn(w->link.next, 0);
1362 		}
1363 	}
1364 	bw = (BW *)bw->parent->main->object;
1365 	if (bw->o.hex ? bw->cursor->byte/16 == bw->b->eof->byte/16 : bw->cursor->line == bw->b->eof->line)
1366 		return -1;
1367 	if (pgamnt < 0)
1368 		scrdn(bw, bw->h / 2 + bw->h % 2, 1);
1369 	else if (pgamnt < bw->h)
1370 		scrdn(bw, bw->h - pgamnt, 1);
1371 	else
1372 		scrdn(bw, 1, 1);
1373 	return 0;
1374 }
1375 
1376 /* Scroll by a single line.  The cursor moves with the scroll */
1377 
uupslide(W * w,int k)1378 int uupslide(W *w, int k)
1379 {
1380 	BW *bw;
1381 	WIND_BW(bw, w);
1382 	if (bw->o.hex ? bw->top->byte/16 : bw->top->line) {
1383 		if (bw->o.hex ? bw->top->byte/16 + bw->h -1 != bw->cursor->byte/16 : bw->top->line + bw->h - 1 != bw->cursor->line)
1384 			udnarw(w, 0);
1385 		scrup(bw, 1, 0);
1386 		return 0;
1387 	} else
1388 		/* was return -1; */
1389 		return uuparw(w, 0);
1390 }
1391 
udnslide(W * w,int k)1392 int udnslide(W *w, int k)
1393 {
1394 	BW *bw;
1395 	WIND_BW(bw, w);
1396 	if (bw->o.hex ? bw->top->line/16 + bw->h <= bw->top->b->eof->byte/16 : bw->top->line + bw->h <= bw->top->b->eof->line) {
1397 		if (bw->o.hex ? bw->top->byte/16 != bw->cursor->byte/16 : bw->top->line != bw->cursor->line)
1398 			uuparw(w, 0);
1399 		scrdn(bw, 1, 0);
1400 		return 0;
1401 	} else
1402 		/* was return -1; */
1403 		return udnarw(w, 0);
1404 }
1405 
1406 /* Move cursor to specified line number */
1407 
1408 static B *linehist = NULL;	/* History of previously entered line numbers */
1409 
doline(W * w,char * s,void * object,int * notify)1410 static int doline(W *w, char *s, void *object, int *notify)
1411 {
1412 	BW *bw;
1413 	off_t num;
1414 	WIND_BW(bw, w);
1415 	num = (off_t)calc(bw, s, 1);
1416 
1417 	if (notify)
1418 		*notify = 1;
1419 	vsrm(s);
1420 	if (num >= 1 && !merr) {
1421 		int tmp = opt_mid;
1422 
1423 		if (num > bw->b->eof->line)
1424 			num = bw->b->eof->line + 1;
1425 		pline(bw->cursor, num - 1), bw->cursor->xcol = piscol(bw->cursor);
1426 		opt_mid = 1;
1427 		dofollows();
1428 		opt_mid = tmp;
1429 		return 0;
1430 	} else {
1431 		if (merr)
1432 			msgnw(bw->parent, merr);
1433 		else
1434 			msgnw(bw->parent, joe_gettext(_("Invalid line number")));
1435 		return -1;
1436 	}
1437 }
1438 
uline(W * w,int k)1439 int uline(W *w, int k)
1440 {
1441 	BW *bw;
1442 	WIND_BW(bw, w);
1443 	if (wmkpw(bw->parent, joe_gettext(_("Go to line (%{abort} to abort): ")), &linehist, doline, NULL, NULL, math_cmplt, NULL, NULL, utf8_map, 0))
1444 		return 0;
1445 	else
1446 		return -1;
1447 }
1448 
1449 /* Move cursor to specified column number */
1450 
1451 static B *colhist = NULL;	/* History of previously entered column numbers */
1452 
docol(W * w,char * s,void * object,int * notify)1453 static int docol(W *w, char *s, void *object, int *notify)
1454 {
1455 	BW *bw;
1456 	off_t num;
1457 	WIND_BW(bw, w);
1458 	num = (off_t)calc(bw, s, 1);
1459 
1460 	if (notify)
1461 		*notify = 1;
1462 	vsrm(s);
1463 	if (num >= 1 && !merr) {
1464 		int tmp = opt_mid;
1465 
1466 		pcol(bw->cursor, num - 1), bw->cursor->xcol = piscol(bw->cursor);
1467 		opt_mid = 1;
1468 		dofollows();
1469 		opt_mid = tmp;
1470 		return 0;
1471 	} else {
1472 		if (merr)
1473 			msgnw(bw->parent, merr);
1474 		else
1475 			msgnw(bw->parent, joe_gettext(_("Invalid column number")));
1476 		return -1;
1477 	}
1478 }
1479 
ucol(W * w,int k)1480 int ucol(W *w, int k)
1481 {
1482 	BW *bw;
1483 	WIND_BW(bw, w);
1484 	if (wmkpw(bw->parent, joe_gettext(_("Go to column (%{abort} to abort): ")), &colhist, docol, NULL, NULL, math_cmplt, NULL, NULL, utf8_map, 0))
1485 		return 0;
1486 	else
1487 		return -1;
1488 }
1489 
1490 /* Move cursor to specified byte number */
1491 
1492 static B *bytehist = NULL;	/* History of previously entered byte numbers */
1493 
dobyte(W * w,char * s,void * object,int * notify)1494 static int dobyte(W *w, char *s, void *object, int *notify)
1495 {
1496 	BW *bw;
1497 	off_t num;
1498 	WIND_BW(bw, w);
1499 	num = (off_t)calc(bw, s, 1);
1500 
1501 	if (notify)
1502 		*notify = 1;
1503 	vsrm(s);
1504 	if (num >= 0 && !merr) {
1505 		int tmp = opt_mid;
1506 
1507 		pgoto(bw->cursor, num), bw->cursor->xcol = piscol(bw->cursor);
1508 		opt_mid = 1;
1509 		dofollows();
1510 		opt_mid = tmp;
1511 		return 0;
1512 	} else {
1513 		if (merr)
1514 			msgnw(bw->parent, merr);
1515 		else
1516 			msgnw(bw->parent, joe_gettext(_("Invalid byte number")));
1517 		return -1;
1518 	}
1519 }
1520 
ubyte(W * w,int k)1521 int ubyte(W *w, int k)
1522 {
1523 	BW *bw;
1524 	WIND_BW(bw, w);
1525 	if (wmkpw(bw->parent, joe_gettext(_("Go to byte (%{abort} to abort): ")), &bytehist, dobyte, NULL, NULL, math_cmplt, NULL, NULL, utf8_map, 0))
1526 		return 0;
1527 	else
1528 		return -1;
1529 }
1530 
1531 /* Delete character under cursor
1532  * or write ^D to process if we're at end of file in a shell window
1533  */
1534 
udelch(W * w,int k)1535 int udelch(W *w, int k)
1536 {
1537 	BW *bw;
1538 	P *p;
1539 	WIND_BW(bw, w);
1540 
1541 	if (piseof(bw->cursor))
1542 		return -1;
1543 	pgetc(p = pdup(bw->cursor, "udelch"));
1544 	bdel(bw->cursor, p);
1545 	prm(p);
1546 	return 0;
1547 }
1548 
1549 /* Backspace */
1550 
ubacks(W * w,int k)1551 int ubacks(W *w, int k)
1552 {
1553 	BW *bw;
1554 	WIND_BW(bw, w);
1555 	/* Don't backspace when at beginning of line in prompt windows */
1556 	if (bw->parent->watom->what == TYPETW || !pisbol(bw->cursor)) {
1557 		int c;
1558 		off_t indent;
1559 		off_t col;
1560 		off_t indwid;
1561 		off_t wid;
1562 
1563 		/* Degenerate into ltarw for overtype mode */
1564 		if (bw->o.overtype) {
1565 			return u_goto_left(bw->parent, 0);
1566 		}
1567 
1568 		if (pisbof(bw->cursor))
1569 			return -1;
1570 
1571 		/* Indentation point of this line */
1572 		indent = pisindent(bw->cursor);
1573 
1574 		/* Column position of cursor */
1575 		col = piscol(bw->cursor);
1576 
1577 		/* Indentation step in columns */
1578 		if (bw->o.indentc=='\t')
1579 			wid = bw->o.tab;
1580 		else
1581 			wid = 1;
1582 
1583 		indwid = (bw->o.istep*wid);
1584 
1585 		/* Smart backspace when: cursor is at indentation point, indentation point
1586 		   is a multiple of indentation width, we're not at beginning of line,
1587 		   'smarthome' option is enabled, and indentation is purely made out of
1588 		   indent characters (or purify indents is enabled). */
1589 
1590 		/* Ignore purify for backspace */
1591 		if (col == indent && (col%indwid)==0 && col!=0 && bw->o.smartbacks && bw->o.autoindent) {
1592 			P *p;
1593 
1594 			/* Delete all indentation */
1595 			p = pdup(bw->cursor, "ubacks");
1596 			p_goto_bol(p);
1597 			bdel(p,bw->cursor);
1598 			prm(p);
1599 
1600 			/* Indent to new position */
1601 			pfill(bw->cursor,col-indwid,bw->o.indentc);
1602 		} else if (col<indent && bw->o.smartbacks && !pisbol(bw->cursor)) {
1603 			/* We're before indent point: delete indwid worth of space but do not
1604 			   cross line boundary.  We could probably replace the above with this. */
1605 			off_t cw=0;
1606 			P *p = pdup(bw->cursor, "ubacks");
1607 			do {
1608 				c = prgetc(bw->cursor);
1609 				if(c=='\t') cw += bw->o.tab;
1610 				else cw += 1;
1611 				bdel(bw->cursor, p);
1612 			} while(!pisbol(bw->cursor) && cw<indwid);
1613 			prm(p);
1614 		} else {
1615 			/* Regular backspace */
1616 			P *p = pdup(bw->cursor, "ubacks");
1617 			if ((c = prgetc(bw->cursor)) != NO_MORE_DATA)
1618 				if (!bw->o.overtype || c == '\t' || pisbol(p) || piseol(p))
1619 					bdel(bw->cursor, p);
1620 			prm(p);
1621 		}
1622 		return 0;
1623 	} else
1624 		return -1;
1625 }
1626 
1627 /*
1628  * Delete sequence of characters (alphabetic, numeric) or (white-space)
1629  *	if cursor is on the white-space it will delete all white-spaces
1630  *		until alphanumeric character
1631  *      if cursor is on the alphanumeric it will delete all alphanumeric
1632  *		characters until character that is not alphanumeric
1633  */
u_word_delete(W * w,int k)1634 int u_word_delete(W *w, int k)
1635 {
1636 	BW *bw;
1637 	P *p;
1638 	struct charmap *map;
1639 	int c;
1640 	WIND_BW(bw, w);
1641 
1642 	p = pdup(bw->cursor, "u_word_delete");
1643 	map=bw->b->o.charmap;
1644 	c = brch(p);
1645 
1646 	if (joe_isalnum_(map,c))
1647 		while (joe_isalnum_(map,(c = brch(p))))
1648 			pgetc(p);
1649 	else if (joe_isspace(map,c))
1650 		while (joe_isspace(map,(c = brch(p))))
1651 			pgetc(p);
1652 	else
1653 		pgetc(p);
1654 
1655 	if (p->byte == bw->cursor->byte) {
1656 		prm(p);
1657 		return -1;
1658 	}
1659 	bdel(bw->cursor, p);
1660 	prm(p);
1661 	return 0;
1662 }
1663 
1664 /* Delete from cursor to beginning of word it's in or immediately after,
1665  * to start of whitespace, or a single character
1666  */
1667 
ubackw(W * w,int k)1668 int ubackw(W *w, int k)
1669 {
1670 	BW *bw;
1671 	P *p;
1672 	int c;
1673 	struct charmap *map;
1674 	WIND_BW(bw, w);
1675 
1676 	p = pdup(bw->cursor, "ubackw");
1677 	c = prgetc(bw->cursor);
1678 	map=bw->b->o.charmap;
1679 
1680 	if (joe_isalnum_(map,c)) {
1681 		while (joe_isalnum_(map,(c = prgetc(bw->cursor))))
1682 			/* do nothing */;
1683 		if (c != NO_MORE_DATA)
1684 			pgetc(bw->cursor);
1685 	} else if (joe_isspace(map,c)) {
1686 		while (joe_isspace(map,(c = prgetc(bw->cursor))))
1687 			/* do nothing */;
1688 		if (c != NO_MORE_DATA)
1689 			pgetc(bw->cursor);
1690 	}
1691 	if (bw->cursor->byte == p->byte) {
1692 		prm(p);
1693 		return -1;
1694 	}
1695 	bdel(bw->cursor, p);
1696 	prm(p);
1697 	return 0;
1698 }
1699 
1700 /* Delete from cursor to end of line, or if there's nothing to delete,
1701  * delete the line-break
1702  */
1703 
udelel(W * w,int k)1704 int udelel(W *w,int k)
1705 {
1706 	BW *bw;
1707 	P *p;
1708 	WIND_BW(bw, w);
1709 	p = p_goto_eol(pdup(bw->cursor, "udelel"));
1710 
1711 	if (bw->cursor->byte == p->byte) {
1712 		prm(p);
1713 		return udelch(w, 0);
1714 	} else
1715 		bdel(bw->cursor, p);
1716 	prm(p);
1717 	return 0;
1718 }
1719 
1720 /* Delete to beginning of line, or if there's nothing to delete,
1721  * delete the line-break
1722  */
1723 
udelbl(W * w,int k)1724 int udelbl(W *w, int k)
1725 {
1726 	BW *bw;
1727 	P *p;
1728 	WIND_BW(bw, w);
1729 	p = p_goto_bol(pdup(bw->cursor, "udelbl"));
1730 
1731 	if (p->byte == bw->cursor->byte) {
1732 		prm(p);
1733 		return ubacks(w, 8);	/* The 8 goes to the process if we're at EOF of shell window */
1734 	} else
1735 		bdel(p, bw->cursor);
1736 	prm(p);
1737 	return 0;
1738 }
1739 
1740 /* Delete entire line */
1741 
udelln(W * w,int k)1742 int udelln(W *w, int k)
1743 {
1744 	BW *bw;
1745 	P *p;
1746 	WIND_BW(bw, w);
1747 	p = pdup(bw->cursor, "udelln");
1748 
1749 	p_goto_bol(bw->cursor);
1750 	pnextl(p);
1751 	if (bw->cursor->byte == p->byte) {
1752 		prm(p);
1753 		return -1;
1754 	}
1755 	bdel(bw->cursor, p);
1756 	prm(p);
1757 	return 0;
1758 }
1759 
1760 /* Insert a space */
1761 
uinsc(W * w,int k)1762 int uinsc(W *w, int k)
1763 {
1764 	BW *bw;
1765 	WIND_BW(bw, w);
1766 	binsc(bw->cursor, ' ');
1767 	return 0;
1768 }
1769 
1770 /* Move p backwards to first non-blank line and return its indentation */
1771 
find_indent(P * p)1772 static off_t find_indent(P *p)
1773 {
1774 	int x;
1775 	for (x=0; x != 10; ++x) {
1776 		if (!pprevl(p)) return -1;
1777 		p_goto_bol(p);
1778 		if (!pisblank(p)) break;
1779 	}
1780 	if (x==10)
1781 		return -1;
1782 	else
1783 		return pisindent(p);
1784 }
1785 
1786 /* Type a character into the buffer (deal with left margin, overtype mode and
1787  * word-wrap), if cursor is at end of shell window buffer, just send character
1788  * to process.
1789  */
1790 
utypebw_raw(BW * bw,int k,int no_decode)1791 static int utypebw_raw(BW *bw, int k, int no_decode)
1792 {
1793 	/* Character map of buffer */
1794 	struct charmap *map = bw->b->o.charmap;
1795 
1796 
1797 	/* Send data to shell window */
1798 	if ((bw->b->pid && !bw->b->vt && piseof(bw->cursor)) ||
1799 	   ( bw->b->pid && bw->b->vt && bw->cursor->byte == bw->b->vt->vtcur->byte)) {
1800 	   	if (locale_map->type) {
1801 	   		char buf[8];
1802 	   		ptrdiff_t len = utf8_encode(buf, k);
1803 	   		joe_write(bw->b->out, buf, len);
1804 	   	} else {
1805 	   		if (!no_decode) {
1806 		   		k = from_uni(locale_map, k);
1807 			}
1808 	   		if (k != -1) {
1809 	   			char c = TO_CHAR_OK(k);
1810 	   			joe_write(bw->b->out, &c, 1);
1811 	   		}
1812 		}
1813 		return 0;
1814 	}
1815 
1816 	/* Hex mode overtype needs to preserve file size */
1817 	if (bw->o.hex && bw->o.overtype) {
1818 		char buf[8];
1819 		ptrdiff_t x;
1820 		ptrdiff_t len;
1821 		if (map->type) {
1822 			len = utf8_encode(buf, k);
1823 		} else {
1824 			if (!no_decode)
1825 				k = from_uni(map, k);
1826 			if (k == -1)
1827 				return 1;
1828 			buf[0] = TO_CHAR_OK(k);
1829 			len = 1;
1830 		}
1831 		binsm(bw->cursor, buf, len);
1832 		for (x = 0; x != len; ++x)
1833 			pgetb(bw->cursor);
1834 		while (len--) {
1835 			P *p;
1836 			if (piseof(bw->cursor))
1837 				return 0;
1838 			p = pdup(bw->cursor, "utypebw_raw");
1839 			pgetb(p);
1840 			bdel(bw->cursor, p);
1841 			prm(p);
1842 		}
1843 		return 0;
1844 	}
1845 
1846 	if (k == '\t' && bw->o.overtype && !piseol(bw->cursor) && !no_decode) { /* TAB in overtype mode is supposed to be just cursor motion */
1847 		off_t col = bw->cursor->xcol;		/* Current cursor column */
1848 		col = col + bw->o.tab - (col%bw->o.tab);/* Move to next tab stop */
1849 		pcol(bw->cursor,col);			/* Try to position cursor there */
1850 		if (!bw->o.picture && piseol(bw->cursor) && piscol(bw->cursor)<col) {	/* We moved past end of line, insert a tab (unless in picture mode) */
1851 			if (bw->o.spaces)
1852 				pfill(bw->cursor,col,' ');
1853 			else
1854 				pfill(bw->cursor,col,'\t');
1855 		}
1856 		bw->cursor->xcol = col;			/* Put cursor there even if we can't really go there */
1857 	} else if (k == '\t' && bw->o.smartbacks && bw->o.autoindent && pisindent(bw->cursor)>=piscol(bw->cursor) && !no_decode) {
1858 		P *p = pdup(bw->cursor, "utypebw_raw");
1859 		off_t n = find_indent(p);
1860 		if (n != -1 && pisindent(bw->cursor)==piscol(bw->cursor) && n > pisindent(bw->cursor)) {
1861 			if (!pisbol(bw->cursor))
1862 				udelbl(bw->parent, 0);
1863 			while (joe_isspace(map,(k = pgetc(p))) && k != '\n') {
1864 				binsc(bw->cursor, k);
1865 				pgetc(bw->cursor);
1866 			}
1867 		} else {
1868 			int x;
1869 			for (x=0;x<bw->o.istep;++x) {
1870 				binsc(bw->cursor,bw->o.indentc);
1871 				pgetc(bw->cursor);
1872 			}
1873 		}
1874 		bw->cursor->xcol = piscol(bw->cursor);
1875 		prm (p);
1876 	} else if (k == '\t' && bw->o.spaces && !no_decode) {
1877 		off_t n;
1878 
1879 		if (bw->o.picture)
1880 			n = bw->cursor->xcol;
1881 		else
1882 			n = piscol(bw->cursor);
1883 
1884 		n = bw->o.tab - n % bw->o.tab;
1885 		while (n--)
1886 			utypebw(bw, ' ');
1887 	} else {
1888 		int upd;
1889 		int simple;
1890 		ptrdiff_t x;
1891 
1892 		/* Picture mode */
1893 		if (bw->o.picture && bw->cursor->xcol!=piscol(bw->cursor))
1894 			pfill(bw->cursor,bw->cursor->xcol,' '); /* Why no tabs? */
1895 
1896 		upd = bw->parent->t->t->updtab[bw->y + bw->cursor->line - bw->top->line];
1897 		simple = 1;
1898 
1899 		if (pisblank(bw->cursor))
1900 			while (piscol(bw->cursor) < bw->o.lmargin) {
1901 				binsc(bw->cursor, ' ');
1902 				pgetc(bw->cursor);
1903 			}
1904 
1905 		if (!no_decode) {
1906 			if(!map->type) {
1907 				/* Convert to byte code */
1908 				k = from_uni(map, k);
1909 			}
1910 		}
1911 
1912 		binsc(bw->cursor, k);
1913 
1914 		/* We need x position before we move cursor */
1915 		x = piscol(bw->cursor) - bw->offset;
1916 		pgetc(bw->cursor);
1917 
1918 		/* Tabs are weird here... */
1919 		if (bw->o.overtype && !piseol(bw->cursor) && k != '\t')
1920 			udelch(bw->parent, 0);
1921 
1922 		/* Not sure if we're in right position for wordwrap when we're in overtype mode */
1923 		if (bw->o.wordwrap && piscol(bw->cursor) > bw->o.rmargin && !joe_isblank(map,k)) {
1924 			wrapword(bw, bw->cursor, bw->o.lmargin, bw->o.french, 0, NULL);
1925 			simple = 0;
1926 		}
1927 
1928 		bw->cursor->xcol = piscol(bw->cursor);
1929 #ifndef __MSDOS__
1930 		if (x < 0 || x >= bw->w)
1931 			simple = 0;
1932 		if (bw->cursor->line < bw->top->line || bw->cursor->line >= bw->top->line + bw->h)
1933 			simple = 0;
1934 		if (simple && bw->parent->t->t->sary[bw->y + bw->cursor->line - bw->top->line])
1935 			simple = 0;
1936 		if (cclass_lookup(cclass_combining, k))
1937 			simple = 0;
1938 		if (simple && k != '\t' && k != '\n' && !curmacro) {
1939 			int atr;
1940 			SCRN *t = bw->parent->t->t;
1941 			ptrdiff_t y = bw->y + TO_DIFF_OK(bw->cursor->line - bw->top->line);
1942 			int (*screen)[COMPOSE] = t->scrn + y * t->co;
1943 			int *attr = t->attr + y * t->co;
1944 			x += bw->x;
1945 
1946 			atr = BG_COLOR(bg_text);
1947 
1948 			if (!upd && piseol(bw->cursor) && !bw->o.highlight)
1949 				t->updtab[y] = 0;
1950 			if (markb &&
1951 			    markk &&
1952 			    markb->b == bw->b &&
1953 			    markk->b == bw->b &&
1954 			   ((!square && bw->cursor->byte >= markb->byte && bw->cursor->byte < markk->byte) ||
1955 			    ( square && bw->cursor->line >= markb->line && bw->cursor->line <= markk->line && piscol(bw->cursor) >= markb->xcol && piscol(bw->cursor) < markk->xcol)))
1956 				atr |= INVERSE;
1957 			outatr(bw->b->o.charmap, t, screen + x, attr + x, x, y, k, atr);
1958 		}
1959 #endif
1960 	}
1961 	return 0;
1962 }
1963 
utypebw(BW * bw,int k)1964 int utypebw(BW *bw, int k)
1965 {
1966 	return utypebw_raw(bw, k, 0);
1967 }
1968 
utypew(W * w,int k)1969 int utypew(W *w, int k)
1970 {
1971 	BW *bw;
1972 	WIND_BW(bw, w);
1973 	return utypebw(bw, k);
1974 }
1975 
1976 /* Quoting */
1977 
1978 static B *unicodehist = NULL;	/* History of previously entered unicode characters */
1979 
dounicode(W * w,char * s,void * object,int * notify)1980 static int dounicode(W *w, char *s, void *object, int *notify)
1981 {
1982 	BW *bw;
1983 	int num;
1984 	WIND_BW(bw, w);
1985 	num = zhtoi(s);
1986 	if (notify)
1987 		*notify = 1;
1988 	vsrm(s);
1989 	utypebw_raw(bw, num, 1);
1990 	bw->cursor->xcol = piscol(bw->cursor);
1991 	return 0;
1992 }
1993 
1994 int quotestate;
1995 int quoteval;
1996 
doquote(W * w,int c,void * object,int * notify)1997 static int doquote(W *w, int c, void *object, int *notify)
1998 {
1999 	BW *bw;
2000 	char buf[40];
2001 	WIND_BW(bw, w);
2002 
2003 /*
2004 	if (c < 0 || c >= 256) {
2005 		nungetc(c);
2006 		return -1;
2007 	}
2008 */
2009 	switch (quotestate) {
2010 	case 0:
2011 		if (c >= '0' && c <= '9') {
2012 			quoteval = c - '0';
2013 			quotestate = 1;
2014 			joe_snprintf_1(buf, SIZEOF(buf), "ASCII %c--", c);
2015 			if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
2016 				return -1;
2017 			else
2018 				return 0;
2019 		} else if (c == 'x' || c == 'X') {
2020 			if (bw->b->o.charmap->type) {
2021 				if (!wmkpw(bw->parent, joe_gettext(_("Unicode (ISO-10646) character in hex (%{abort} to abort): ")), &unicodehist, dounicode,
2022 				           NULL, NULL, NULL, NULL, NULL, locale_map, 0))
2023 					return 0;
2024 				else
2025 					return -1;
2026 			} else {
2027 				quotestate = 3;
2028 				if (!mkqwna(bw->parent, sc("ASCII 0x--"), doquote, NULL, NULL, notify))
2029 					return -1;
2030 				else
2031 					return 0;
2032 			}
2033 		} else if (c == 'o' || c == 'O') {
2034 			quotestate = 5;
2035 			if (!mkqwna(bw->parent, sc("ASCII 0---"), doquote, NULL, NULL, notify))
2036 				return -1;
2037 			else
2038 				return 0;
2039 		} else {
2040 			if ((c >= 0x40 && c <= 0x5F) || (c >= 'a' && c <= 'z'))
2041 				c &= 0x1F;
2042 			if (c == '?')
2043 				c = 127;
2044 			utypebw_raw(bw, c, 1);
2045 			bw->cursor->xcol = piscol(bw->cursor);
2046 		}
2047 		break;
2048 	case 1:
2049 		if (c >= '0' && c <= '9') {
2050 			joe_snprintf_2(buf, SIZEOF(buf), "ASCII %c%c-", quoteval + '0', c);
2051 			quoteval = quoteval * 10 + c - '0';
2052 			quotestate = 2;
2053 			if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
2054 				return -1;
2055 			else
2056 				return 0;
2057 		}
2058 		break;
2059 	case 2:
2060 		if (c >= '0' && c <= '9') {
2061 			quoteval = quoteval * 10 + c - '0';
2062 			utypebw_raw(bw, quoteval, 1);
2063 			bw->cursor->xcol = piscol(bw->cursor);
2064 		}
2065 		break;
2066 	case 3:
2067 		if (c >= '0' && c <= '9') {
2068 			joe_snprintf_1(buf, SIZEOF(buf), "ASCII 0x%c-", c);
2069 			quoteval = c - '0';
2070 			quotestate = 4;
2071 			if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
2072 				return -1;
2073 			else
2074 				return 0;
2075 		} else if (c >= 'a' && c <= 'f') {
2076 			joe_snprintf_1(buf, SIZEOF(buf), "ASCII 0x%c-", c + 'A' - 'a');
2077 			quoteval = c - 'a' + 10;
2078 			quotestate = 4;
2079 			if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
2080 				return -1;
2081 			else
2082 				return 0;
2083 		} else if (c >= 'A' && c <= 'F') {
2084 			joe_snprintf_1(buf, SIZEOF(buf), "ASCII 0x%c-", c);
2085 			quoteval = c - 'A' + 10;
2086 			quotestate = 4;
2087 			if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
2088 				return -1;
2089 			else
2090 				return 0;
2091 		}
2092 		break;
2093 	case 4:
2094 		if (c >= '0' && c <= '9') {
2095 			quoteval = quoteval * 16 + c - '0';
2096 			utypebw_raw(bw, quoteval, 1);
2097 			bw->cursor->xcol = piscol(bw->cursor);
2098 		} else if (c >= 'a' && c <= 'f') {
2099 			quoteval = quoteval * 16 + c - 'a' + 10;
2100 			utypebw_raw(bw, quoteval, 1);
2101 			bw->cursor->xcol = piscol(bw->cursor);
2102 		} else if (c >= 'A' && c <= 'F') {
2103 			quoteval = quoteval * 16 + c - 'A' + 10;
2104 			utypebw_raw(bw, quoteval, 1);
2105 			bw->cursor->xcol = piscol(bw->cursor);
2106 		}
2107 		break;
2108 	case 5:
2109 		if (c >= '0' && c <= '7') {
2110 			joe_snprintf_1(buf, SIZEOF(buf), "ASCII 0%c--", c);
2111 			quoteval = c - '0';
2112 			quotestate = 6;
2113 			if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
2114 				return -1;
2115 			else
2116 				return 0;
2117 		}
2118 		break;
2119 	case 6:
2120 		if (c >= '0' && c <= '7') {
2121 			joe_snprintf_2(buf, SIZEOF(buf), "ASCII 0%c%c-", quoteval + '0', c);
2122 			quoteval = quoteval * 8 + c - '0';
2123 			quotestate = 7;
2124 			if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
2125 				return -1;
2126 			else
2127 				return 0;
2128 		}
2129 		break;
2130 	case 7:
2131 		if (c >= '0' && c <= '7') {
2132 			quoteval = quoteval * 8 + c - '0';
2133 			utypebw_raw(bw, quoteval, 1);
2134 			bw->cursor->xcol = piscol(bw->cursor);
2135 		}
2136 		break;
2137 	}
2138 	if (notify)
2139 		*notify = 1;
2140 	return 0;
2141 }
2142 
uquote(W * w,int k)2143 int uquote(W *w, int k)
2144 {
2145 	BW *bw;
2146 	WIND_BW(bw, w);
2147 	quotestate = 0;
2148 	if (mkqwna(bw->parent, sz(joe_gettext(_("Ctrl- (or 0-9 for dec. ascii, x for hex, or o for octal)"))), doquote, NULL, NULL, NULL))
2149 		return 0;
2150 	else
2151 		return -1;
2152 }
2153 
doquote9(W * w,int c,void * object,int * notify)2154 static int doquote9(W *w, int c, void *object, int *notify)
2155 {
2156 	BW *bw;
2157 	WIND_BW(bw, w);
2158 	if (notify)
2159 		*notify = 1;
2160 	if ((c >= 0x40 && c <= 0x5F) || (c >= 'a' && c <= 'z'))
2161 		c &= 0x1F;
2162 	if (c == '?')
2163 		c = 127;
2164 	if (c >= 0 && c <= 127)
2165 		c |= 128;
2166 	utypebw_raw(bw, c, 1);
2167 	bw->cursor->xcol = piscol(bw->cursor);
2168 	return 0;
2169 }
2170 
doquote8(W * w,int c,void * object,int * notify)2171 static int doquote8(W *w, int c, void *object, int *notify)
2172 {
2173 	BW *bw;
2174 	WIND_BW(bw, w);
2175 	if (c == '`') {
2176 		if (mkqwna(bw->parent, sc("Meta-Ctrl-"), doquote9, NULL, NULL, notify))
2177 			return 0;
2178 		else
2179 			return -1;
2180 	}
2181 	if (notify)
2182 		*notify = 1;
2183 	if (c >= 0 && c <= 127)
2184 		c |= 128;
2185 	utypebw_raw(bw, c, 1);
2186 	bw->cursor->xcol = piscol(bw->cursor);
2187 	return 0;
2188 }
2189 
uquote8(W * w,int k)2190 int uquote8(W *w, int k)
2191 {
2192 	if (mkqwna(w, sc("Meta-"), doquote8, NULL, NULL, NULL))
2193 		return 0;
2194 	else
2195 		return -1;
2196 }
2197 
doctrl(W * w,int c,void * object,int * notify)2198 static int doctrl(W *w, int c, void *object, int *notify)
2199 {
2200 	BW *bw;
2201 	int org;
2202 	WIND_BW(bw, w);
2203 	org = bw->o.overtype;
2204 
2205 	if (notify)
2206 		*notify = 1;
2207 	bw->o.overtype = 0;
2208 	if ((bw->parent->huh == srchstr || bw->parent->huh == replstr) && c == '\n') {
2209 		utypebw(bw, '\\');
2210 		utypebw(bw, 'n');
2211 	} else
2212 		utypebw_raw(bw, c, 1);
2213 	bw->o.overtype = org;
2214 	bw->cursor->xcol = piscol(bw->cursor);
2215 	return 0;
2216 }
2217 
uctrl(W * w,int k)2218 int uctrl(W *w, int k)
2219 {
2220 	BW *bw;
2221 	WIND_BW(bw, w);
2222 	if (mkqwna(bw->parent, sz(joe_gettext(_("Quote"))), doctrl, NULL, NULL, NULL))
2223 		return 0;
2224 	else
2225 		return -1;
2226 }
2227 
2228 /* User hit Return.  Deal with autoindent.
2229  */
2230 
rtntw(W * w)2231 int rtntw(W *w)
2232 {
2233 	BW *bw;
2234 	WIND_BW(bw, w);
2235 	if (bw->o.overtype) {
2236 		p_goto_eol(bw->cursor);
2237 		if (piseof(bw->cursor))
2238 			binsc(bw->cursor, '\n');
2239 		pgetc(bw->cursor);
2240 		bw->cursor->xcol = piscol(bw->cursor);
2241 	} else {
2242 		P *p = pdup(bw->cursor, "rtntw");
2243 		int c;
2244 
2245 		binsc(bw->cursor, '\n'), pgetc(bw->cursor);
2246 		/* Suppress autoindent if we're on a space or tab... */
2247 		if (bw->o.autoindent /* && (brch(bw->cursor)!=' ' && brch(bw->cursor)!='\t')*/) {
2248 			p_goto_bol(p);
2249 			while (joe_isspace(bw->b->o.charmap,(c = pgetc(p))) && c != '\n') {
2250 				binsc(bw->cursor, c);
2251 				pgetc(bw->cursor);
2252 			}
2253 		}
2254 		prm(p);
2255 		bw->cursor->xcol = piscol(bw->cursor);
2256 	}
2257 	return 0;
2258 }
2259 
2260 /* Open a line */
2261 
uopen(W * w,int k)2262 int uopen(W *w, int k)
2263 {
2264 	BW *bw;
2265 	WIND_BW(bw, w);
2266 	binsc(bw->cursor,'\n');
2267 	if (bw->o.autoindent && (brch(bw->cursor)!=' ' && brch(bw->cursor)!='\t')) {
2268 		P *p = pdup(bw->cursor, "uopen");
2269 		P *q = pdup(p, "uopen");
2270 		int c;
2271 		pgetc(q);
2272 		p_goto_bol(p);
2273 		while (joe_isspace(bw->b->o.charmap,(c = pgetc(p))) && c != '\n') {
2274 			binsc(q, c);
2275 			pgetc(q);
2276 		}
2277 		prm(p); prm(q);
2278 	}
2279 
2280 	return 0;
2281 }
2282 
2283 /* Set book-mark */
2284 
dosetmark(W * w,int c,void * object,int * notify)2285 static int dosetmark(W *w, int c, void *object, int *notify)
2286 {
2287 	BW *bw;
2288 	WIND_BW(bw, w);
2289 	if (notify)
2290 		*notify = 1;
2291 	if (c >= '0' && c <= ':') {
2292 		pdupown(bw->cursor, bw->b->marks + c - '0', "dosetmark");
2293 		poffline(bw->b->marks[c - '0']);
2294 		if (c!=':') {
2295 			joe_snprintf_1(msgbuf, JOE_MSGBUFSIZE, joe_gettext(_("Mark %d set")), c - '0');
2296 			msgnw(bw->parent, msgbuf);
2297 		}
2298 		return 0;
2299 	} else {
2300 		nungetc(c);
2301 		return -1;
2302 	}
2303 }
2304 
usetmark(W * w,int c)2305 int usetmark(W *w, int c)
2306 {
2307 	if (c >= '0' && c <= ':')
2308 		return dosetmark(w, c, NULL, NULL);
2309 	else if (mkqwna(w, sz(joe_gettext(_("Set mark (0-9):"))), dosetmark, NULL, NULL, NULL))
2310 		return 0;
2311 	else
2312 		return -1;
2313 }
2314 
2315 /* Goto book-mark */
2316 
dogomark(W * w,int c,void * object,int * notify)2317 static int dogomark(W *w, int c, void *object, int *notify)
2318 {
2319 	BW *bw;
2320 	WIND_BW(bw, w);
2321 	if (notify)
2322 		*notify = 1;
2323 	if (c >= '0' && c <= ':')
2324 		if (bw->b->marks[c - '0']) {
2325 			pset(bw->cursor, bw->b->marks[c - '0']);
2326 			bw->cursor->xcol = piscol(bw->cursor);
2327 			return 0;
2328 		} else {
2329 			joe_snprintf_1(msgbuf, JOE_MSGBUFSIZE, joe_gettext(_("Mark %d not set")), c - '0');
2330 			msgnw(bw->parent, msgbuf);
2331 			return -1;
2332 	} else {
2333 		nungetc(c);
2334 		return -1;
2335 	}
2336 }
2337 
ugomark(W * w,int c)2338 int ugomark(W *w, int c)
2339 {
2340 	if (c >= '0' && c <= '9')
2341 		return dogomark(w, c, NULL, NULL);
2342 	else if (mkqwna(w, sz(joe_gettext(_("Goto bookmark (0-9):"))), dogomark, NULL, NULL, NULL))
2343 		return 0;
2344 	else
2345 		return -1;
2346 }
2347 
2348 /* Goto next instance of character */
2349 
2350 static int dobkwdc;
2351 
dofwrdc(W * w,int k,void * object,int * notify)2352 static int dofwrdc(W *w, int k, void *object, int *notify)
2353 {
2354 	BW *bw;
2355 	int c;
2356 	P *q;
2357 	WIND_BW(bw, w);
2358 
2359 	if (notify)
2360 		*notify = 1;
2361 	if (k < 0 || k >= 256) {
2362 		nungetc(k);
2363 		return -1;
2364 	}
2365 	q = pdup(bw->cursor, "dofwrdc");
2366 	if (dobkwdc) {
2367 		while ((c = prgetc(q)) != NO_MORE_DATA)
2368 			if (c == k)
2369 				break;
2370 	} else {
2371 		while ((c = pgetc(q)) != NO_MORE_DATA)
2372 			if (c == k)
2373 				break;
2374 	}
2375 	if (c == NO_MORE_DATA) {
2376 		msgnw(bw->parent, joe_gettext(_("Not found")));
2377 		prm(q);
2378 		return -1;
2379 	} else {
2380 		pset(bw->cursor, q);
2381 		bw->cursor->xcol = piscol(bw->cursor);
2382 		prm(q);
2383 		return 0;
2384 	}
2385 }
2386 
ufwrdc(W * w,int k)2387 int ufwrdc(W *w, int k)
2388 {
2389 	dobkwdc = 0;
2390 	if (k >= 0 && k < 256)
2391 		return dofwrdc(w, k, NULL, NULL);
2392 	else if (mkqw(w, sz(joe_gettext(_("Forward to char: "))), dofwrdc, NULL, NULL, NULL))
2393 		return 0;
2394 	else
2395 		return -1;
2396 }
2397 
ubkwdc(W * w,int k)2398 int ubkwdc(W *w, int k)
2399 {
2400 	dobkwdc = 1;
2401 	if (k >= 0 && k < 256)
2402 		return dofwrdc(w, k, NULL, NULL);
2403 	else if (mkqw(w, sz(joe_gettext(_("Backward to char: "))), dofwrdc, NULL, NULL, NULL))
2404 		return 0;
2405 	else
2406 		return -1;
2407 }
2408 
2409 /* Display a message */
2410 
domsg(W * w,char * s,void * object,int * notify)2411 static int domsg(W *w, char *s, void *object, int *notify)
2412 {
2413 	if (notify)
2414 		*notify = 1;
2415 	zlcpy(msgbuf, SIZEOF(msgbuf), s);
2416 	vsrm(s);
2417 	msgnw(w, msgbuf);
2418 	return 0;
2419 }
2420 
umsg(W * w,int k)2421 int umsg(W *w, int k)
2422 {
2423 	if (wmkpw(w, joe_gettext(_("Message (%{abort} to abort): ")), NULL, domsg, NULL, NULL, NULL, NULL, NULL, locale_map, 0))
2424 		return 0;
2425 	else
2426 		return -1;
2427 }
2428 
2429 /* Insert text */
2430 
dotxt(W * w,char * s,void * object,int * notify)2431 static int dotxt(W *w, char *s, void *object, int *notify)
2432 {
2433 	char fill;
2434 	char *str;
2435 	BW *bw;
2436 	WIND_BW(bw, w);
2437 
2438 	if (notify)
2439 		*notify = 1;
2440 	if (s[0] == '`') {
2441 		str = vsmk(1024);
2442 		fill = ' ';
2443 		str = stagen(str, bw, &s[1], fill);
2444 		vsrm(s);
2445 		s = str;
2446 	}
2447 	if (s) {
2448 	 	const char *t = s;
2449 		ptrdiff_t len = sLEN(s);
2450 		while (len) {
2451 			int c;
2452 			if (bw->b->o.charmap->type)
2453 				c = utf8_decode_fwrd(&t, &len);
2454 			else {
2455 				c = *(const unsigned char *)t++;
2456 				--len;
2457 			}
2458 			if (c >= 0)
2459 				utypebw_raw(bw, c, 1);
2460 		}
2461 		vsrm(s);
2462 	}
2463 	return 0;
2464 }
2465 
utxt(W * w,int k)2466 int utxt(W *w, int k)
2467 {
2468 	BW *bw;
2469 	WIND_BW(bw, w);
2470 	if (wmkpw(w, joe_gettext(_("Insert (%{abort} to abort): ")), NULL, dotxt, NULL, NULL, utypebw, NULL, NULL, bw->b->o.charmap, 0))
2471 		return 0;
2472 	else
2473 		return -1;
2474 }
2475 
2476 /* Insert current file name */
2477 
uname_joe(W * w,int k)2478 int uname_joe(W *w, int k)
2479 {
2480 	const char *s;
2481 	s=((BW *)w->main->object)->b->name;
2482 	if (!s || !*s)
2483 		return -1;
2484 	while (*s)
2485 		if (utypew(w,*(const unsigned char *)s++))
2486 			return -1;
2487 	return 0;
2488 }
2489 
2490 /* Insert until non-base64 character received */
2491 
upaste(W * w,int k)2492 int upaste(W *w, int k)
2493 {
2494 	int c;
2495 	int accu = 0;
2496 	int count;
2497 	int tmp_ww;
2498 	int tmp_ai;
2499 	BW *bw;
2500 	WIND_BW(bw, w);
2501 	tmp_ww = bw->o.wordwrap;
2502 	tmp_ai = bw->o.autoindent;
2503 
2504 	bw->o.wordwrap = 0;
2505 	bw->o.autoindent = 0;
2506 	count = 0;
2507 
2508 	/* We have to wait for the second ';' */
2509 	while ((c = ttgetch()) != -1)
2510 		if (c == ';')
2511 			break;
2512 	if (c == -1)
2513 		goto bye;
2514 
2515 	while ((c = ttgetch()) != -1) {
2516 		if (c >= 'A' && c <= 'Z')
2517 			c = c - 'A';
2518 		else if (c >= 'a' && c <= 'z')
2519 			c = c - 'a' + 26;
2520 		else if (c >= '0' && c <= '9')
2521 			c = c - '0' + 52;
2522 		else if (c == '+')
2523 			c = 62;
2524 		else if (c == '/')
2525 			c = 63;
2526 		else if (c == '=')
2527 			continue;
2528 		else
2529 			break;
2530 
2531 		switch (count) {
2532 			case 0:
2533 				accu = c;
2534 				count = 6;
2535 				break;
2536 			case 2:
2537 				accu = (accu << 6) + c;
2538 				if (accu == 13)
2539 					rtntw(bw->parent);
2540 				else
2541 					utypebw(bw, accu);
2542 				count = 0;
2543 				break;
2544 			case 4:
2545 				accu = (accu << 4) + (c >> 2);
2546 				if (accu == 13)
2547 					rtntw(bw->parent);
2548 				else
2549 					utypebw(bw, accu);
2550 				accu = (c & 0x3);
2551 				count = 2;
2552 				break;
2553 			case 6:
2554 				accu = (accu << 2) + (c >> 4);
2555 				if (accu == 13)
2556 					rtntw(bw->parent);
2557 				else
2558 					utypebw(bw, accu);
2559 				accu = (c & 0xF);
2560 				count = 4;
2561 				break;
2562 		}
2563 	}
2564 	/* Terminator is ESC \ */
2565 	if (c == 033) {
2566 		ttgetch();
2567 	}
2568 
2569 	bye:
2570 
2571 	bw->o.wordwrap = tmp_ww;
2572 	bw->o.autoindent = tmp_ai;
2573 
2574 	return 0;
2575 }
2576 
2577 /* Bracketed paste */
2578 
2579 int saved_ww;
2580 int saved_ai;
2581 int saved_sp;
2582 
ubrpaste(W * w,int k)2583 int ubrpaste(W *w, int k)
2584 {
2585 	BW *bw;
2586 
2587 	WIND_BW(bw, w);
2588 
2589 	saved_ww = bw->o.wordwrap;
2590 	saved_ai = bw->o.autoindent;
2591 	saved_sp = bw->o.spaces;
2592 
2593 	bw->o.wordwrap = bw->o.autoindent = bw->o.spaces = 0;
2594 
2595 	return 0;
2596 }
2597 
ubrpaste_done(W * w,int k)2598 int ubrpaste_done(W *w, int k)
2599 {
2600 	BW *bw;
2601 
2602 	WIND_BW(bw, w);
2603 
2604 	bw->o.wordwrap = saved_ww;
2605 	bw->o.autoindent = saved_ai;
2606 	bw->o.spaces = saved_sp;
2607 
2608 	return 0;
2609 }
2610