4b9542bc14fab55aeb6ca16bf8fdb71c3fd9bcc1
[alioth/jupp.git] / uedit.c
1 /* $MirOS: contrib/code/jupp/uedit.c,v 1.15 2017/01/11 22:56:49 tg Exp $ */
2 /*
3  *      Basic user edit functions
4  *      Copyright
5  *              (C) 1992 Joseph H. Allen
6  *
7  *      This file is part of JOE (Joe's Own Editor)
8  */
9 #include "config.h"
10 #include "types.h"
11
12 #include <stdio.h>
13
14 #ifdef HAVE_BSD_STRING_H
15 #include <bsd/string.h>
16 #endif
17
18 #include "b.h"
19 #include "bw.h"
20 #include "macro.h"
21 #include "main.h"
22 #include "pw.h"
23 #include "qw.h"
24 #include "scrn.h"
25 #include "ublock.h"
26 #include "uformat.h"
27 #include "umath.h"
28 #include "utils.h"
29 #include "vs.h"
30 #include "utf8.h"
31 #include "charmap.h"
32 #include "w.h"
33
34 /***************/
35 /* Global options */
36 int pgamnt = -1;                /* No. of PgUp/PgDn lines to keep */
37
38 /******** i don't like global var ******/
39
40 /*
41  * Move cursor to beginning of line
42  */
43 int u_goto_bol(BW *bw)
44 {
45         if (bw->o.hex) {
46                 pbkwd(bw->cursor,bw->cursor->byte%16);
47         } else {
48                 p_goto_bol(bw->cursor);
49         }
50         return 0;
51 }
52
53 /*
54  * Move cursor to first non-whitespace character, unless it is
55  * already there, in which case move it to beginning of line
56  */
57 int uhome(BW *bw)
58 {
59         P *p;
60
61         if (bw->o.hex) {
62                 return u_goto_bol(bw);
63         }
64
65         p = pdup(bw->cursor);
66
67         if (bw->o.indentfirst) {
68                 if ((bw->o.smarthome) && (piscol(p) > pisindent(p))) {
69                         p_goto_bol(p);
70                         while (joe_isblank(p->b->o.charmap,brc(p)))
71                                 pgetc(p);
72                 } else
73                         p_goto_bol(p);
74         } else {
75                 if (bw->o.smarthome && piscol(p)==0 && pisindent(p)) {
76                         while (joe_isblank(p->b->o.charmap,brc(p)))
77                                 pgetc(p);
78                 } else
79                         p_goto_bol(p);
80         }
81
82         pset(bw->cursor, p);
83         prm(p);
84         return 0;
85 }
86
87 /*
88  * Move cursor to end of line
89  */
90 int u_goto_eol(BW *bw)
91 {
92         if (bw->o.hex) {
93                 if (bw->cursor->byte + 15 - bw->cursor->byte%16 > bw->b->eof->byte)
94                         pset(bw->cursor,bw->b->eof);
95                 else
96                         pfwrd(bw->cursor, 15 - bw->cursor->byte%16);
97         } else
98                 p_goto_eol(bw->cursor);
99         return 0;
100 }
101
102 /*
103  * Move cursor to beginning of file
104  */
105 int u_goto_bof(BW *bw)
106 {
107         p_goto_bof(bw->cursor);
108         return 0;
109 }
110
111 /*
112  * Move cursor to end of file
113  */
114 int u_goto_eof(BW *bw)
115 {
116         p_goto_eof(bw->cursor);
117         return 0;
118 }
119
120 /*
121  * Move cursor left
122  */
123 int u_goto_left(BW *bw)
124 {
125         if (bw->o.hex) {
126                 if (prgetb(bw->cursor) != NO_MORE_DATA) {
127                         return 0;
128                 } else {
129                         return -1;
130                 }
131         }
132         if (bw->o.picture) {
133                 if (bw->cursor->xcol) {
134                         --bw->cursor->xcol;
135                         pcol(bw->cursor,bw->cursor->xcol);
136                         return 0;
137                 }
138         } else {
139                 /* Have to do ECHKXCOL here because of picture mode */
140                 if (bw->cursor->xcol != piscol(bw->cursor)) {
141                         bw->cursor->xcol = piscol(bw->cursor);
142                         return 0;
143                 } else if (prgetc(bw->cursor) != NO_MORE_DATA) {
144                         bw->cursor->xcol = piscol(bw->cursor);
145                         return 0;
146                 }
147         }
148         return -1;
149 }
150
151 /*
152  * Move cursor right
153  */
154 int u_goto_right(BW *bw)
155 {
156         if (bw->o.hex) {
157                 if (pgetb(bw->cursor) != NO_MORE_DATA) {
158                         return 0;
159                 } else {
160                         return -1;
161                 }
162         }
163         if (bw->o.picture) {
164                 ++bw->cursor->xcol;
165                 pcol(bw->cursor,bw->cursor->xcol);
166                 return 0;
167         } else {
168                 int rtn;
169                 if (pgetc(bw->cursor) != NO_MORE_DATA) {
170                         bw->cursor->xcol = piscol(bw->cursor);
171                         rtn = 0;
172                 } else {
173                         rtn = -1;
174                 }
175                 /* Have to do EFIXXCOL here because of picture mode */
176                 if (bw->cursor->xcol != piscol(bw->cursor))
177                         bw->cursor->xcol = piscol(bw->cursor);
178                 return rtn;
179         }
180 }
181
182 /*
183  * Move cursor to beginning of previous word or if there isn't
184  * previous word then go to beginning of the file
185  *
186  * WORD is a sequence non-white-space characters
187  */
188 int u_goto_prev(BW *bw)
189 {
190         P *p = pdup(bw->cursor);
191         struct charmap *map=bw->b->o.charmap;
192         int c = prgetc(p);
193
194         if (joe_isalnum_(map,c)) {
195                 while (joe_isalnum_(map,(c=prgetc(p))))
196                         /* Do nothing */;
197                 if (c != NO_MORE_DATA)
198                         pgetc(p);
199         } else if (joe_isspace(map,c) || joe_ispunct(map,c)) {
200                 while ((c=prgetc(p)), (joe_isspace(map,c) || joe_ispunct(map,c)))
201                         /* Do nothing */;
202                 while(joe_isalnum_(map,(c=prgetc(p))))
203                         /* Do nothing */;
204                 if (c != NO_MORE_DATA)
205                         pgetc(p);
206         }
207 /*
208         if (p->byte == bw->cursor->byte) {
209                 prm(p);
210                 return -1;
211         }
212 */
213         pset(bw->cursor, p);
214         prm(p);
215         return 0;
216 }
217
218 /*
219  * Move cursor to end of next word or if there isn't
220  * next word then go to end of the file
221  *
222  * WORD is a sequence non-white-space characters
223  */
224 int u_goto_next(BW *bw)
225 {
226         P *p = pdup(bw->cursor);
227         struct charmap *map=bw->b->o.charmap;
228         int c = brch(p);
229         int rtn = -1;
230
231         if (joe_isalnum_(map,c)) {
232                 rtn = 0;
233                 while (joe_isalnum_(map,(c = brch(p))))
234                         pgetc(p);
235         } else if (joe_isspace(map,c) || joe_ispunct(map,c)) {
236                 while (joe_isspace(map, (c = brch(p))) || joe_ispunct(map,c))
237                         pgetc(p);
238                 while (joe_isalnum_(map,(c = brch(p)))) {
239                         rtn = 0;
240                         pgetc(p);
241                 }
242         } else
243                 pgetc(p);
244         pset(bw->cursor, p);
245         prm(p);
246         return rtn;
247 }
248
249 static P *pboi(P *p)
250 {
251         p_goto_bol(p);
252         while (joe_isblank(p->b->o.charmap,brch(p)))
253                 pgetc(p);
254         return p;
255 }
256
257 static int pisedge(P *p)
258 {
259         P *q;
260         int c;
261
262         if (pisbol(p))
263                 return -1;
264         if (piseol(p))
265                 return 1;
266         q = pdup(p);
267         pboi(q);
268         if (q->byte == p->byte)
269                 goto left;
270         if (joe_isblank(p->b->o.charmap,(c = brch(p)))) {
271                 pset(q, p);
272                 if (joe_isblank(p->b->o.charmap,prgetc(q)))
273                         goto no;
274                 if (c == '\t')
275                         goto right;
276                 pset(q, p);
277                 pgetc(q);
278                 if (pgetc(q) == ' ')
279                         goto right;
280                 goto no;
281         } else {
282                 pset(q, p);
283                 c = prgetc(q);
284                 if (c == '\t')
285                         goto left;
286                 if (c != ' ')
287                         goto no;
288                 if (prgetc(q) == ' ')
289                         goto left;
290                 goto no;
291         }
292
293       right:prm(q);
294         return 1;
295       left:prm(q);
296         return -1;
297       no:prm(q);
298         return 0;
299 }
300
301 int upedge(BW *bw)
302 {
303         if (prgetc(bw->cursor) == NO_MORE_DATA)
304                 return -1;
305         while (pisedge(bw->cursor) != -1)
306                 prgetc(bw->cursor);
307         return 0;
308 }
309
310 int unedge(BW *bw)
311 {
312         if (pgetc(bw->cursor) == NO_MORE_DATA)
313                 return -1;
314         while (pisedge(bw->cursor) != 1)
315                 pgetc(bw->cursor);
316         return 0;
317 }
318
319 /* Move cursor to matching delimiter */
320
321 static int
322 utomatch_i(BW *bw, int dir)
323 {
324         int d;
325         int c;                  /* character under cursor */
326         int f;                  /* character to find */
327         P *p;
328         int cnt = 0;            /* delimiter depth */
329
330         switch (c = brch(bw->cursor)) {
331         case '(':
332                 f = ')';
333                 dir = 1;
334                 break;
335         case '[':
336                 f = ']';
337                 dir = 1;
338                 break;
339         case '{':
340                 f = '}';
341                 dir = 1;
342                 break;
343         case '<':
344                 f = '>';
345                 dir = 1;
346                 break;
347         case ')':
348                 f = '(';
349                 dir = -1;
350                 break;
351         case ']':
352                 f = '[';
353                 dir = -1;
354                 break;
355         case '}':
356                 f = '{';
357                 dir = -1;
358                 break;
359         case '>':
360                 f = '<';
361                 dir = -1;
362                 break;
363         case '"':
364         case '\'':
365         case '`':
366                 f = c;
367                 break;
368         default:
369                 return -1;
370         }
371
372         p = pdup(bw->cursor);
373         if (dir == 1) {
374                 while ((d = pgetc(p)) != NO_MORE_DATA) {
375                         if (d == f && f != c && !--cnt) {
376                                 prgetc(p);
377                                 goto match_found;
378                         } else if (d == c) {
379                                 ++cnt;
380                                 if (f == c)
381                                         c = NO_MORE_DATA;
382                         }
383                 }
384         } else {
385                 while ((d = prgetc(p)) != NO_MORE_DATA) {
386                         if (d == f && !cnt--)
387                                 goto match_found;
388                         else if (d == c)
389                                 ++cnt;
390                 }
391         }
392         if (/* CONSTCOND */ 0) {
393  match_found:
394                 pset(bw->cursor, p);
395         }
396         prm(p);
397         return ((d == NO_MORE_DATA) ? -1 : 0);
398 }
399
400 int utomatch(BW *bw)
401 {
402         return (utomatch_i(bw, 1));
403 }
404
405 int urvmatch(BW *bw)
406 {
407         return (utomatch_i(bw, -1));
408 }
409
410 /* Move cursor up */
411
412 int uuparw(BW *bw)
413 {
414         if (bw->o.hex) {
415                 if (bw->cursor->byte<16)
416                         return -1;
417                 else {
418                         pbkwd(bw->cursor, 16);
419                         return 0;
420                 }
421         }
422         if (bw->cursor->line) {
423                 pprevl(bw->cursor);
424                 pcol(bw->cursor, bw->cursor->xcol);
425                 return 0;
426         } else
427                 return -1;
428 }
429
430 /* Move cursor down */
431
432 int udnarw(BW *bw)
433 {
434         if (bw->o.hex) {
435                 if (bw->cursor->byte+16 <= bw->b->eof->byte) {
436                         pfwrd(bw->cursor, 16);
437                         return 0;
438                 } else if (bw->cursor->byte != bw->b->eof->byte) {
439                         pset(bw->cursor, bw->b->eof);
440                         return 0;
441                 } else {
442                         return -1;
443                 }
444         }
445         if (bw->cursor->line != bw->b->eof->line) {
446                 pnextl(bw->cursor);
447                 pcol(bw->cursor, bw->cursor->xcol);
448                 return 0;
449         } else if(bw->o.picture) {
450                 p_goto_eol(bw->cursor);
451                 binsc(bw->cursor,'\n');
452                 pgetc(bw->cursor);
453                 pcol(bw->cursor, bw->cursor->xcol);
454                 return 0;
455         } else
456                 return -1;
457 }
458
459 /* Move cursor to top of window */
460
461 int utos(BW *bw)
462 {
463         long col = bw->cursor->xcol;
464
465         pset(bw->cursor, bw->top);
466         pcol(bw->cursor, col);
467         bw->cursor->xcol = col;
468         return 0;
469 }
470
471 /* Move cursor to bottom of window */
472
473 int ubos(BW *bw)
474 {
475         long col = bw->cursor->xcol;
476
477         pline(bw->cursor, bw->top->line + bw->h - 1);
478         pcol(bw->cursor, col);
479         bw->cursor->xcol = col;
480         return 0;
481 }
482
483 /* Scroll buffer window up n lines
484  * If beginning of file is close, scrolls as much as it can
485  * If beginning of file is on-screen, cursor jumps to beginning of file
486  *
487  * If flg is set: cursor stays fixed relative to screen edge
488  * If flg is clr: cursor stays fixed on the buffer line
489  */
490
491 void scrup(BW *bw, int n, int flg)
492 {
493         int scrollamnt = 0;
494         int cursoramnt = 0;
495         int x;
496
497         /* Decide number of lines we're really going to scroll */
498
499         if (bw->o.hex) {
500                 if (bw->top->byte/16 >= n)
501                         scrollamnt = cursoramnt = n;
502                 else if (bw->top->byte/16)
503                         scrollamnt = cursoramnt = bw->top->byte/16;
504                 else if (flg)
505                         cursoramnt = bw->cursor->byte/16;
506                 else if (bw->cursor->byte/16 >= n)
507                         cursoramnt = n;
508         } else {
509                 if (bw->top->line >= n)
510                         scrollamnt = cursoramnt = n;
511                 else if (bw->top->line)
512                         scrollamnt = cursoramnt = bw->top->line;
513                 else if (flg)
514                         cursoramnt = bw->cursor->line;
515                 else if (bw->cursor->line >= n)
516                         cursoramnt = n;
517         }
518
519         if (bw->o.hex) {
520                 /* Move top-of-window pointer */
521                 pbkwd(bw->top,scrollamnt*16);
522                 /* Move cursor */
523                 pbkwd(bw->cursor,cursoramnt*16);
524                 /* If window is on the screen, give (buffered) scrolling command */
525                 if (bw->parent->y != -1)
526                         nscrldn(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
527         } else {
528                 /* Move top-of-window pointer */
529                 for (x = 0; x != scrollamnt; ++x)
530                         pprevl(bw->top);
531                 p_goto_bol(bw->top);
532
533                 /* Move cursor */
534                 for (x = 0; x != cursoramnt; ++x)
535                         pprevl(bw->cursor);
536                 p_goto_bol(bw->cursor);
537                 pcol(bw->cursor, bw->cursor->xcol);
538
539                 /* If window is on the screen, give (buffered) scrolling command */
540                 if (bw->parent->y != -1)
541                         nscrldn(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
542         }
543 }
544
545 /* Scroll buffer window down n lines
546  * If end of file is close, scrolls as much as possible
547  * If end of file is on-screen, cursor jumps to end of file
548  *
549  * If flg is set: cursor stays fixed relative to screen edge
550  * If flg is clr: cursor stays fixed on the buffer line
551  */
552
553 void scrdn(BW *bw, int n, int flg)
554 {
555         int scrollamnt = 0;
556         int cursoramnt = 0;
557         int x;
558
559         /* How much we're really going to scroll... */
560         if (bw->o.hex) {
561                 if (bw->top->b->eof->byte/16 < bw->top->byte/16 + bw->h) {
562                         cursoramnt = bw->top->b->eof->byte/16 - bw->cursor->byte/16;
563                         if (!flg && cursoramnt > n)
564                                 cursoramnt = n;
565                 } else if (bw->top->b->eof->byte/16 - (bw->top->byte/16 + bw->h) >= n)
566                         cursoramnt = scrollamnt = n;
567                 else
568                         cursoramnt = scrollamnt = bw->top->b->eof->byte/16 - (bw->top->byte/16 + bw->h) + 1;
569         } else {
570                 if (bw->top->b->eof->line < bw->top->line + bw->h) {
571                         cursoramnt = bw->top->b->eof->line - bw->cursor->line;
572                         if (!flg && cursoramnt > n)
573                                 cursoramnt = n;
574                 } else if (bw->top->b->eof->line - (bw->top->line + bw->h) >= n)
575                         cursoramnt = scrollamnt = n;
576                 else
577                         cursoramnt = scrollamnt = bw->top->b->eof->line - (bw->top->line + bw->h) + 1;
578         }
579
580         if (bw->o.hex) {
581                 /* Move top-of-window pointer */
582                 pfwrd(bw->top,16*scrollamnt);
583                 /* Move cursor */
584                 pfwrd(bw->cursor,16*cursoramnt);
585                 /* If window is on screen, give (buffered) scrolling command to terminal */
586                 if (bw->parent->y != -1)
587                         nscrlup(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
588         } else {
589                 /* Move top-of-window pointer */
590                 for (x = 0; x != scrollamnt; ++x)
591                         pnextl(bw->top);
592
593                 /* Move cursor */
594                 for (x = 0; x != cursoramnt; ++x)
595                         pnextl(bw->cursor);
596                 pcol(bw->cursor, bw->cursor->xcol);
597
598                 /* If window is on screen, give (buffered) scrolling command to terminal */
599                 if (bw->parent->y != -1)
600                         nscrlup(bw->parent->t->t, bw->y, bw->y + bw->h, scrollamnt);
601         }
602 }
603
604 /* Page up */
605
606 int upgup(BW *bw)
607 {
608         bw = (BW *) bw->parent->main->object;
609         if (bw->o.hex ? bw->cursor->byte < 16 : !bw->cursor->line)
610                 return -1;
611         if (pgamnt < 0)
612                 scrup(bw, bw->h / 2 + bw->h % 2, 1);
613         else if (pgamnt < bw->h)
614                 scrup(bw, bw->h - pgamnt, 1);
615         else
616                 scrup(bw, 1, 1);
617         return 0;
618 }
619
620 /* Page down */
621
622 int upgdn(BW *bw)
623 {
624         bw = (BW *) bw->parent->main->object;
625         if (bw->o.hex ? bw->cursor->byte/16 == bw->b->eof->byte/16 : bw->cursor->line == bw->b->eof->line)
626                 return -1;
627         if (pgamnt < 0)
628                 scrdn(bw, bw->h / 2 + bw->h % 2, 1);
629         else if (pgamnt < bw->h)
630                 scrdn(bw, bw->h - pgamnt, 1);
631         else
632                 scrdn(bw, 1, 1);
633         return 0;
634 }
635
636 /* Scroll by a single line.  The cursor moves with the scroll */
637
638 int uupslide(BW *bw)
639 {
640         bw = (BW *) bw->parent->main->object;
641         if (bw->o.hex ? bw->top->byte/16 : bw->top->line) {
642                 if (bw->o.hex ? bw->top->byte/16 + bw->h -1 != bw->cursor->byte/16 : bw->top->line + bw->h - 1 != bw->cursor->line)
643                         udnarw(bw);
644                 scrup(bw, 1, 0);
645                 return 0;
646         } else
647                 return -1;
648 }
649
650 int udnslide(BW *bw)
651 {
652         bw = (BW *) bw->parent->main->object;
653         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) {
654                 if (bw->o.hex ? bw->top->byte/16 != bw->cursor->byte/16 : bw->top->line != bw->cursor->line)
655                         uuparw(bw);
656                 scrdn(bw, 1, 0);
657                 return 0;
658         } else
659                 return -1;
660 }
661
662 /* Move cursor to specified line number */
663
664 static B *linehist = NULL;      /* History of previously entered line numbers */
665
666 static int doline(BW *bw, unsigned char *s, void *object, int *notify)
667 {
668         long num = calc(bw, s);
669
670         if (notify)
671                 *notify = 1;
672         vsrm(s);
673         if (num >= 1 && !merr) {
674                 int tmp = mid;
675
676                 if (num > bw->b->eof->line)
677                         num = bw->b->eof->line + 1;
678                 pline(bw->cursor, num - 1), bw->cursor->xcol = piscol(bw->cursor);
679                 mid = 1;
680                 dofollows();
681                 mid = tmp;
682                 return 0;
683         } else {
684                 if (merr)
685                         msgnw(bw->parent, merr);
686                 else
687                         msgnw(bw->parent, US "Invalid line number");
688                 return -1;
689         }
690 }
691
692 int uline(BW *bw)
693 {
694         if (wmkpw(bw->parent, US "Go to line (^C to abort): ", &linehist, doline, NULL, NULL, NULL, NULL, NULL, locale_map))
695                 return 0;
696         else
697                 return -1;
698 }
699
700 /* Move cursor to specified column number */
701
702 static B *colhist = NULL;       /* History of previously entered column numbers */
703
704 static int docol(BW *bw, unsigned char *s, void *object, int *notify)
705 {
706         long num = calc(bw, s);
707
708         if (notify)
709                 *notify = 1;
710         vsrm(s);
711         if (num >= 1 && !merr) {
712                 int tmp = mid;
713
714                 pcol(bw->cursor, num - 1), bw->cursor->xcol = piscol(bw->cursor);
715                 mid = 1;
716                 dofollows();
717                 mid = tmp;
718                 return 0;
719         } else {
720                 if (merr)
721                         msgnw(bw->parent, merr);
722                 else
723                         msgnw(bw->parent, US "Invalid column number");
724                 return -1;
725         }
726 }
727
728 int ucol(BW *bw)
729 {
730         if (wmkpw(bw->parent, US "Go to column (^C to abort): ", &colhist, docol, NULL, NULL, NULL, NULL, NULL, locale_map))
731                 return 0;
732         else
733                 return -1;
734 }
735
736 /* Move cursor to specified byte number */
737
738 static B *bytehist = NULL;      /* History of previously entered byte numbers */
739
740 static int dobyte(BW *bw, unsigned char *s, void *object, int *notify)
741 {
742         long num = calc(bw, s);
743
744         if (notify)
745                 *notify = 1;
746         vsrm(s);
747         if (num >= 0 && !merr) {
748                 int tmp = mid;
749
750                 pgoto(bw->cursor, num), bw->cursor->xcol = piscol(bw->cursor);
751                 mid = 1;
752                 dofollows();
753                 mid = tmp;
754                 return 0;
755         } else {
756                 if (merr)
757                         msgnw(bw->parent, merr);
758                 else
759                         msgnw(bw->parent, US "Invalid byte number");
760                 return -1;
761         }
762 }
763
764 int ubyte(BW *bw)
765 {
766         if (wmkpw(bw->parent, US "Go to byte (^C to abort): ", &bytehist, dobyte, NULL, NULL, NULL, NULL, NULL, locale_map))
767                 return 0;
768         else
769                 return -1;
770 }
771
772 /* Delete character under cursor
773  * or write ^D to process if we're at end of file in a shell window
774  */
775
776 int udelch(BW *bw)
777 {
778         P *p;
779
780         if (piseof(bw->cursor))
781                 return -1;
782         pgetc(p = pdup(bw->cursor));
783         bdel(bw->cursor, p);
784         prm(p);
785         return 0;
786 }
787
788 /* Backspace */
789
790 int ubacks(BW *bw, int k)
791 {
792         /* Don't backspace when at beginning of line in prompt windows */
793         if (bw->parent->watom->what == TYPETW || !pisbol(bw->cursor)) {
794                 int c;
795                 int indent;
796                 int col;
797                 int indwid;
798                 int wid;
799
800                 if (pisbof(bw->cursor))
801                         return -1;
802
803                 /* Indentation point of this line */
804                 indent = pisindent(bw->cursor);
805
806                 /* Column position of cursor */
807                 col = piscol(bw->cursor);
808
809                 /* Indentation step in columns */
810                 if (bw->o.indentc=='\t')
811                         wid = bw->o.tab;
812                 else
813                         wid = 1;
814
815                 indwid = (bw->o.istep*wid);
816
817                 /* Smart backspace when: cursor is at indentation point, indentation point
818                    is a multiple of indentation width, we're not at beginning of line,
819                    'smarthome' option is enabled, and indentation is purely made out of
820                    indent characters (or purify indents is enabled). */
821
822                 /* Ignore purify for backspace */
823                 if (col == indent && (col%indwid)==0 && col!=0 && bw->o.smartbacks) {
824                         P *p;
825
826                         /* Delete all indentation */
827                         p = pdup(bw->cursor);
828                         p_goto_bol(p);
829                         bdel(p,bw->cursor);
830                         prm(p);
831
832                         /* Indent to new position */
833                         pfill(bw->cursor,col-indwid,bw->o.indentc);
834                 } else if (col<indent && !pisbol(bw->cursor) && bw->o.smartbacks) {
835                         /* We're before indent point: delete indwid worth of space but do not
836                            cross line boundary.  We could probably replace the above with this. */
837                         int cw=0;
838                         P *p = pdup(bw->cursor);
839                         do {
840                                 c = prgetc(bw->cursor);
841                                 if(c=='\t') cw += bw->o.tab;
842                                 else cw += 1;
843                                 bdel(bw->cursor, p);
844                         } while(!pisbol(bw->cursor) && cw<indwid);
845                         prm(p);
846                 } else {
847                         /* Regular backspace */
848                         P *p = pdup(bw->cursor);
849                         if ((c = prgetc(bw->cursor)) != NO_MORE_DATA)
850                                 if (!bw->o.overtype || c == '\t' || pisbol(p) || piseol(p))
851                                         bdel(bw->cursor, p);
852                         prm(p);
853                 }
854                 return 0;
855         } else
856                 return -1;
857 }
858
859 /*
860  * Delete sequence of characters (alphabetic, numeric) or (white-space)
861  *      if cursor is on the white-space it will delete all white-spaces
862  *              until alphanumeric character
863  *      if cursor is on the alphanumeric it will delete all alphanumeric
864  *              characters until character that is not alphanumeric
865  */
866 int u_word_delete(BW *bw)
867 {
868         P *p = pdup(bw->cursor);
869         struct charmap *map=bw->b->o.charmap;
870         int c = brch(p);
871
872         if (joe_isalnum_(map,c))
873                 while (joe_isalnum_(map,(c = brch(p))))
874                         pgetc(p);
875         else if (joe_isspace(map,c))
876                 while (joe_isspace(map,(c = brch(p))))
877                         pgetc(p);
878         else
879                 pgetc(p);
880
881         if (p->byte == bw->cursor->byte) {
882                 prm(p);
883                 return -1;
884         }
885         bdel(bw->cursor, p);
886         prm(p);
887         return 0;
888 }
889
890 /* Delete from cursor to beginning of word it's in or immediately after,
891  * to start of whitespace, or a single character
892  */
893
894 int ubackw(BW *bw)
895 {
896         P *p = pdup(bw->cursor);
897         int c = prgetc(bw->cursor);
898         struct charmap *map=bw->b->o.charmap;
899
900         if (joe_isalnum_(map,c)) {
901                 while (joe_isalnum_(map,(c = prgetc(bw->cursor))))
902                         /* do nothing */;
903                 if (c != NO_MORE_DATA)
904                         pgetc(bw->cursor);
905         } else if (joe_isspace(map,c)) {
906                 while (joe_isspace(map,(c = prgetc(bw->cursor))))
907                         /* do nothing */;
908                 if (c != NO_MORE_DATA)
909                         pgetc(bw->cursor);
910         }
911         if (bw->cursor->byte == p->byte) {
912                 prm(p);
913                 return -1;
914         }
915         bdel(bw->cursor, p);
916         prm(p);
917         return 0;
918 }
919
920 /* Delete from cursor to end of line, or if there's nothing to delete,
921  * delete the line-break
922  */
923
924 int udelel(BW *bw)
925 {
926         P *p = p_goto_eol(pdup(bw->cursor));
927
928         if (bw->cursor->byte == p->byte) {
929                 prm(p);
930                 return udelch(bw);
931         } else
932                 bdel(bw->cursor, p);
933         prm(p);
934         return 0;
935 }
936
937 /* Delete to beginning of line, or if there's nothing to delete,
938  * delete the line-break
939  */
940
941 int udelbl(BW *bw)
942 {
943         P *p = p_goto_bol(pdup(bw->cursor));
944
945         if (p->byte == bw->cursor->byte) {
946                 prm(p);
947                 return ubacks(bw, 8);   /* The 8 goes to the process if we're at EOF of shell window */
948         } else
949                 bdel(p, bw->cursor);
950         prm(p);
951         return 0;
952 }
953
954 /* Delete entire line */
955
956 int udelln(BW *bw)
957 {
958         P *p = pdup(bw->cursor);
959
960         p_goto_bol(bw->cursor);
961         pnextl(p);
962         if (bw->cursor->byte == p->byte) {
963                 prm(p);
964                 return -1;
965         }
966         bdel(bw->cursor, p);
967         prm(p);
968         return 0;
969 }
970
971 /* Insert a space */
972
973 int uinsc(BW *bw)
974 {
975         binsc(bw->cursor, ' ');
976         return 0;
977 }
978
979 /* Move p backwards to first non-blank line and return its indentation */
980
981 int find_indent(P *p)
982 {
983         int x;
984         for (x=0; x != 10; ++x) {
985                 if (!pprevl(p)) return -1;
986                 p_goto_bol(p);
987                 if (!pisblank(p)) break;
988         }
989         if (x==10)
990                 return -1;
991         else
992                 return pisindent(p);
993 }
994
995 /* Type a character into the buffer (deal with left margin, overtype mode and
996  * word-wrap), if cursor is at end of shell window buffer, just send character
997  * to process.
998  */
999
1000 struct utf8_sm utype_utf8_sm;
1001
1002 int utypebw_raw(BW *bw, int k, int no_decode)
1003 {
1004         struct charmap *map=bw->b->o.charmap;
1005
1006         /* Hex mode overtype is real simple */
1007         if (bw->o.hex && bw->o.overtype) {
1008                 P *p;
1009                 unsigned char c = k;
1010                 binsm(bw->cursor, &c, 1);
1011                 pgetb(bw->cursor);
1012                 if (piseof(bw->cursor))
1013                         return 0;
1014                 pgetb(p = pdup(bw->cursor));
1015                 bdel(bw->cursor, p);
1016                 prm(p);
1017                 return 0;
1018         }
1019
1020         if (k == '\t' && bw->o.overtype && !piseol(bw->cursor)) { /* TAB in overtype mode is supposed to be just cursor motion */
1021                 int col = bw->cursor->xcol;             /* Current cursor column */
1022                 col = col + bw->o.tab - (col%bw->o.tab);/* Move to next tab stop */
1023                 pcol(bw->cursor,col);                   /* Try to position cursor there */
1024                 if (!bw->o.picture && piseol(bw->cursor) && piscol(bw->cursor)<col) {   /* We moved past end of line, insert a tab (unless in picture mode) */
1025                         if (bw->o.spaces)
1026                                 pfill(bw->cursor,col,' ');
1027                         else
1028                                 pfill(bw->cursor,col,'\t');
1029                 }
1030                 bw->cursor->xcol = col;                 /* Put cursor there even if we can't really go there */
1031         } else if (k == '\t' && bw->o.smartbacks && bw->o.autoindent && pisindent(bw->cursor)>=piscol(bw->cursor)) {
1032                 P *p = pdup(bw->cursor);
1033                 int n = find_indent(p);
1034                 if (n != -1 && pisindent(bw->cursor)==piscol(bw->cursor) && n > pisindent(bw->cursor)) {
1035                         if (!pisbol(bw->cursor))
1036                                 udelbl(bw);
1037                         while (joe_isspace(map,(k = pgetc(p))) && k != '\n') {
1038                                 binsc(bw->cursor, k);
1039                                 pgetc(bw->cursor);
1040                         }
1041                 } else {
1042                         int x;
1043                         for (x=0;x<bw->o.istep;++x) {
1044                                 binsc(bw->cursor,bw->o.indentc);
1045                                 pgetc(bw->cursor);
1046                         }
1047                 }
1048                 bw->cursor->xcol = piscol(bw->cursor);
1049                 prm (p);
1050         } else if (k == '\t' && bw->o.spaces) {
1051                 long n;
1052
1053                 if (bw->o.picture)
1054                         n = bw->cursor->xcol;
1055                 else
1056                         n = piscol(bw->cursor);
1057
1058                 utype_utf8_sm.state = 0;
1059                 utype_utf8_sm.ptr = 0;
1060
1061                 n = bw->o.tab - n % bw->o.tab;
1062                 while (n--)
1063                         utypebw_raw(bw, ' ', 0);
1064         } else {
1065                 int upd;
1066                 int simple;
1067                 int x;
1068
1069                 /* Picture mode */
1070                 if (bw->o.picture && bw->cursor->xcol!=piscol(bw->cursor))
1071                         pfill(bw->cursor,bw->cursor->xcol,' '); /* Why no tabs? */
1072
1073                 /* UTF8 decoder */
1074                 if(locale_map->type && !no_decode) {
1075                         int utf8_char = utf8_decode(&utype_utf8_sm,k);
1076
1077                         if(utf8_char >= 0)
1078                                 k = utf8_char;
1079                         else
1080                                 return 0;
1081                 }
1082
1083                 upd = bw->parent->t->t->updtab[bw->y + bw->cursor->line - bw->top->line];
1084                 simple = 1;
1085
1086                 if (pisblank(bw->cursor))
1087                         while (piscol(bw->cursor) < bw->o.lmargin) {
1088                                 binsc(bw->cursor, ' ');
1089                                 pgetc(bw->cursor);
1090                         }
1091
1092                 if (no_decode == 2) {
1093                         unsigned char ch = k;
1094
1095                         binsm(bw->cursor, &ch, 1);
1096                         if (!bw->b->o.charmap->type)
1097                                 no_decode = 1;
1098                 } else {
1099                         if (!no_decode) {
1100                                 if(locale_map->type && !bw->b->o.charmap->type) {
1101                                         unsigned char buf[10];
1102                                         utf8_encode(buf,k);
1103                                         k = from_utf8(bw->b->o.charmap,buf);
1104                                 } else if(!locale_map->type && bw->b->o.charmap->type) {
1105                                         unsigned char buf[10];
1106                                         to_utf8(locale_map,buf,k);
1107                                         k = utf8_decode_string(buf);
1108                                 }
1109                         }
1110
1111                         binsc(bw->cursor, k);
1112                 }
1113
1114                 /* We need x position before we move cursor */
1115                 x = piscol(bw->cursor) - bw->offset;
1116                 pgetc(bw->cursor);
1117
1118                 /* Tabs are weird here... */
1119                 if (bw->o.overtype && !piseol(bw->cursor) && k != '\t')
1120                         udelch(bw);
1121
1122                 /* Not sure if we're in right position for wordwrap when we're in overtype mode */
1123                 if (bw->o.wordwrap && piscol(bw->cursor) > bw->o.rmargin && !joe_isblank(map,k)) {
1124                         wrapword(bw->cursor, (long) bw->o.lmargin, bw->o.french, NULL);
1125                         simple = 0;
1126                 }
1127
1128                 bw->cursor->xcol = piscol(bw->cursor);
1129 #ifndef __MSDOS__
1130                 if (x < 0 || x >= bw->w)
1131                         simple = 0;
1132                 if (bw->cursor->line < bw->top->line || bw->cursor->line >= bw->top->line + bw->h)
1133                         simple = 0;
1134                 if (simple && bw->parent->t->t->sary[bw->y + bw->cursor->line - bw->top->line])
1135                         simple = 0;
1136                 else if (simple)
1137                         switch (k) {
1138                         case ' ':
1139                                 if (bw->o.vispace)
1140                                         /* FALLTHROUGH */
1141                         case '\t':
1142                         case '\n':
1143                                   simple = 0;
1144                                 break;
1145                         }
1146                 if (simple && !curmacro) {
1147                         int atr = 0;
1148                         SCRN *t = bw->parent->t->t;
1149                         int y = bw->y + bw->cursor->line - bw->top->line;
1150                         int *screen = t->scrn + y * t->co;
1151                         int *attr = t->attr + y * t->co;
1152                         x += bw->x;
1153
1154                         if (!upd && piseol(bw->cursor) && !bw->o.highlight)
1155                                 t->updtab[y] = 0;
1156                         if (markb &&
1157                             markk &&
1158                             markb->b == bw->b &&
1159                             markk->b == bw->b &&
1160                            ((!square && bw->cursor->byte >= markb->byte && bw->cursor->byte < markk->byte) ||
1161                             ( square && bw->cursor->line >= markb->line && bw->cursor->line <= markk->line && piscol(bw->cursor) >= markb->xcol && piscol(bw->cursor) < markk->xcol)))
1162                                 atr = INVERSE;
1163                         outatr(bw->b->o.charmap, t, screen + x, attr + x, x, y, no_decode == 2 ? 0xFFFD : k, atr);
1164                 }
1165 #endif
1166         }
1167         return 0;
1168 }
1169
1170 int utypebw(BW *bw, int k)
1171 {
1172         return utypebw_raw(bw, k, 0);
1173 }
1174
1175 /* Quoting */
1176
1177 static B *unicodehist = NULL;   /* History of previously entered unicode characters */
1178
1179 static int dounicode(BW *bw, unsigned char *s, void *object, int *notify)
1180 {
1181         int num;
1182
1183         sscanf((char *)s,"%x",&num);
1184         if (notify)
1185                 *notify = 1;
1186         vsrm(s);
1187         if (bw->b->o.charmap->type)
1188                 utypebw_raw(bw, num, 1);
1189         else {
1190                 unsigned char buf[8];
1191                 int x;
1192
1193                 utf8_encode(buf,num);
1194                 for(x=0;buf[x];++x)
1195                         utypebw_raw(bw, buf[x], 1);
1196         }
1197         bw->cursor->xcol = piscol(bw->cursor);
1198         return 0;
1199 }
1200
1201 int quotestate;
1202 int quoteval;
1203
1204 static int doquote(BW *bw, int c, void *object, int *notify)
1205 {
1206         unsigned char buf[40];
1207
1208         if (c < 0 || c >= 256) {
1209                 nungetc(c);
1210                 return -1;
1211         }
1212         switch (quotestate) {
1213         case 0:
1214                 if (c >= '0' && c <= '9') {
1215                         quoteval = c - '0';
1216                         quotestate = 1;
1217                         joe_snprintf_1((char *)buf, sizeof(buf), "ASCII %c--", c);
1218                         if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
1219                                 return -1;
1220                         else
1221                                 return 0;
1222                 } else if (c == 'u' || c == 'U') {
1223                         if (bw->b->o.charmap->type)
1224                                 goto unopoo;
1225  uhex_uni:
1226                         if (!wmkpw(bw->parent, US "Unicode (ISO-10646) character in hex (^C to abort): ", &unicodehist, dounicode,
1227                                    NULL, NULL, NULL, NULL, NULL, locale_map))
1228                                 return 0;
1229                         else
1230                                 return -1;
1231                 } else if (c == 'r' || c == 'R') {
1232                         if (!bw->b->o.charmap->type)
1233                                 goto unopoo;
1234  uhex_raw:
1235                         quotestate = 3;
1236                         if (!mkqwna(bw->parent, sc("ASCII 0x--"), doquote, NULL, NULL, notify))
1237                                 return -1;
1238                         else
1239                                 return 0;
1240                 } else if (c == 'x' || c == 'X') {
1241                         if (bw->b->o.charmap->type)
1242                                 goto uhex_uni;
1243                         else
1244                                 goto uhex_raw;
1245                 } else if (c == 'o' || c == 'O') {
1246                         quotestate = 5;
1247                         if (!mkqwna(bw->parent, sc("ASCII 0---"), doquote, NULL, NULL, notify))
1248                                 return -1;
1249                         else
1250                                 return 0;
1251                 } else {
1252  unopoo:
1253                         if ((c >= 0x40 && c <= 0x5F) || (c >= 'a' && c <= 'z'))
1254                                 c &= 0x1F;
1255                         if (c == '?')
1256                                 c = 127;
1257                         utypebw_raw(bw, c, 1);
1258                         bw->cursor->xcol = piscol(bw->cursor);
1259                 }
1260                 break;
1261         case 1:
1262                 if (c >= '0' && c <= '9') {
1263                         joe_snprintf_2((char *)buf, sizeof(buf), "ASCII %c%c-", quoteval + '0', c);
1264                         quoteval = quoteval * 10 + c - '0';
1265                         quotestate = 2;
1266                         if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
1267                                 return -1;
1268                         else
1269                                 return 0;
1270                 }
1271                 break;
1272         case 2:
1273                 if (c >= '0' && c <= '9') {
1274                         quoteval = quoteval * 10 + c - '0';
1275                         utypebw_raw(bw, quoteval, 1);
1276                         bw->cursor->xcol = piscol(bw->cursor);
1277                 }
1278                 break;
1279         case 3:
1280                 if (c >= '0' && c <= '9') {
1281                         joe_snprintf_1((char *)buf, sizeof(buf), "ASCII 0x%c-", c);
1282                         quoteval = c - '0';
1283                         quotestate = 4;
1284                         if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
1285                                 return -1;
1286                         else
1287                                 return 0;
1288                 } else if (c >= 'a' && c <= 'f') {
1289                         joe_snprintf_1((char *)buf, sizeof(buf), "ASCII 0x%c-", c + 'A' - 'a');
1290                         quoteval = c - 'a' + 10;
1291                         quotestate = 4;
1292                         if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
1293                                 return -1;
1294                         else
1295                                 return 0;
1296                 } else if (c >= 'A' && c <= 'F') {
1297                         joe_snprintf_1((char *)buf, sizeof(buf), "ASCII 0x%c-", c);
1298                         quoteval = c - 'A' + 10;
1299                         quotestate = 4;
1300                         if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
1301                                 return -1;
1302                         else
1303                                 return 0;
1304                 }
1305                 break;
1306         case 4:
1307                 if (c >= '0' && c <= '9') {
1308                         quoteval = quoteval * 16 + c - '0';
1309  u4out:
1310                         utypebw_raw(bw, quoteval, 2);
1311                         bw->cursor->xcol = piscol(bw->cursor);
1312                 } else if (c >= 'a' && c <= 'f') {
1313                         quoteval = quoteval * 16 + c - 'a' + 10;
1314                         goto u4out;
1315                 } else if (c >= 'A' && c <= 'F') {
1316                         quoteval = quoteval * 16 + c - 'A' + 10;
1317                         goto u4out;
1318                 }
1319                 break;
1320         case 5:
1321                 if (c >= '0' && c <= '7') {
1322                         joe_snprintf_1((char *)buf, sizeof(buf), "ASCII 0%c--", c);
1323                         quoteval = c - '0';
1324                         quotestate = 6;
1325                         if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
1326                                 return -1;
1327                         else
1328                                 return 0;
1329                 }
1330                 break;
1331         case 6:
1332                 if (c >= '0' && c <= '7') {
1333                         joe_snprintf_2((char *)buf, sizeof(buf), "ASCII 0%c%c-", quoteval + '0', c);
1334                         quoteval = quoteval * 8 + c - '0';
1335                         quotestate = 7;
1336                         if (!mkqwna(bw->parent, sz(buf), doquote, NULL, NULL, notify))
1337                                 return -1;
1338                         else
1339                                 return 0;
1340                 }
1341                 break;
1342         case 7:
1343                 if (c >= '0' && c <= '7') {
1344                         quoteval = quoteval * 8 + c - '0';
1345                         utypebw_raw(bw, quoteval, 1);
1346                         bw->cursor->xcol = piscol(bw->cursor);
1347                 }
1348                 break;
1349         }
1350         if (notify)
1351                 *notify = 1;
1352         return 0;
1353 }
1354
1355 int uquote(BW *bw)
1356 {
1357         const char *qs;
1358
1359         if (bw->b->o.charmap->type)
1360                 qs = "Ctrl- (or 0-9 for dec. r for hex, o for octal ASCII, x for hex UTF-8)";
1361         else
1362                 qs = "Ctrl- (or 0-9 for dec. x for hex, o for octal ASCII, u for hex UTF-8)";
1363         quotestate = 0;
1364         if (mkqwna(bw->parent, US qs, strlen(qs), doquote, NULL, NULL, NULL))
1365                 return 0;
1366         else
1367                 return -1;
1368 }
1369
1370 static int doquote9(BW *bw, int c, void *object, int *notify)
1371 {
1372         if (notify)
1373                 *notify = 1;
1374         if ((c >= 0x40 && c <= 0x5F) || (c >= 'a' && c <= 'z'))
1375                 c &= 0x1F;
1376         if (c == '?')
1377                 c = 127;
1378         c |= 128;
1379         utypebw_raw(bw, c, 1);
1380         bw->cursor->xcol = piscol(bw->cursor);
1381         return 0;
1382 }
1383
1384 static int doquote8(BW *bw, int c, void *object, int *notify)
1385 {
1386         if (c == '`') {
1387                 if (mkqwna(bw->parent, sc("Meta-Ctrl-"), doquote9, NULL, NULL, notify))
1388                         return 0;
1389                 else
1390                         return -1;
1391         }
1392         if (notify)
1393                 *notify = 1;
1394         c |= 128;
1395         utypebw_raw(bw, c, 1);
1396         bw->cursor->xcol = piscol(bw->cursor);
1397         return 0;
1398 }
1399
1400 int uquote8(BW *bw)
1401 {
1402         if (mkqwna(bw->parent, sc("Meta-"), doquote8, NULL, NULL, NULL))
1403                 return 0;
1404         else
1405                 return -1;
1406 }
1407
1408 extern const unsigned char srchstr[];
1409
1410 static int doctrl(BW *bw, int c, void *object, int *notify)
1411 {
1412         int org = bw->o.overtype;
1413
1414         if (notify)
1415                 *notify = 1;
1416         bw->o.overtype = 0;
1417         if (bw->parent->huh == srchstr && c == '\n') {
1418                 utypebw(bw, '\\');
1419                 utypebw(bw, 'n');
1420         } else
1421                 utypebw_raw(bw, c, 1);
1422         bw->o.overtype = org;
1423         bw->cursor->xcol = piscol(bw->cursor);
1424         return 0;
1425 }
1426
1427 int uctrl(BW *bw)
1428 {
1429         if (mkqwna(bw->parent, sc("Quote"), doctrl, NULL, NULL, NULL))
1430                 return 0;
1431         else
1432                 return -1;
1433 }
1434
1435 /* User hit Return.  Deal with autoindent.
1436  */
1437
1438 int rtntw(BW *bw)
1439 {
1440         if (bw->o.overtype) {
1441                 p_goto_eol(bw->cursor);
1442                 if (piseof(bw->cursor))
1443                         binsc(bw->cursor, '\n');
1444                 pgetc(bw->cursor);
1445                 bw->cursor->xcol = piscol(bw->cursor);
1446         } else {
1447                 P *p = pdup(bw->cursor);
1448                 unsigned char c;
1449
1450                 binsc(bw->cursor, '\n'), pgetc(bw->cursor);
1451                 /* Suppress autoindent if we're on a space or tab... */
1452                 if (bw->o.autoindent && (brch(bw->cursor)!=' ' && brch(bw->cursor)!='\t')) {
1453                         p_goto_bol(p);
1454                         while (joe_isspace(bw->b->o.charmap,(c = pgetc(p))) && c != '\n') {
1455                                 binsc(bw->cursor, c);
1456                                 pgetc(bw->cursor);
1457                         }
1458                 }
1459                 prm(p);
1460                 bw->cursor->xcol = piscol(bw->cursor);
1461         }
1462         return 0;
1463 }
1464
1465 /* Open a line */
1466
1467 int uopen(BW *bw)
1468 {
1469         binsc(bw->cursor,'\n');
1470         return 0;
1471 }
1472
1473 /* Set book-mark */
1474
1475 static int dosetmark(BW *bw, int c, void *object, int *notify)
1476 {
1477         if (notify)
1478                 *notify = 1;
1479         if (c >= '0' && c <= ':') {
1480                 pdupown(bw->cursor, bw->b->marks + c - '0');
1481                 poffline(bw->b->marks[c - '0']);
1482                 if (c!=':') {
1483                         joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "Mark %d set", c - '0');
1484                         msgnw(bw->parent, msgbuf);
1485                 }
1486                 return 0;
1487         } else {
1488                 nungetc(c);
1489                 return -1;
1490         }
1491 }
1492
1493 int usetmark(BW *bw, int c)
1494 {
1495         if (c >= '0' && c <= ':')
1496                 return dosetmark(bw, c, NULL, NULL);
1497         else if (mkqwna(bw->parent, sc("Set mark (0-9):"), dosetmark, NULL, NULL, NULL))
1498                 return 0;
1499         else
1500                 return -1;
1501 }
1502
1503 /* Goto book-mark */
1504
1505 static int dogomark(BW *bw, int c, void *object, int *notify)
1506 {
1507         if (notify)
1508                 *notify = 1;
1509         if (c >= '0' && c <= ':')
1510                 if (bw->b->marks[c - '0']) {
1511                         pset(bw->cursor, bw->b->marks[c - '0']);
1512                         bw->cursor->xcol = piscol(bw->cursor);
1513                         return 0;
1514                 } else {
1515                         joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "Mark %d not set", c - '0');
1516                         msgnw(bw->parent, msgbuf);
1517                         return -1;
1518         } else {
1519                 nungetc(c);
1520                 return -1;
1521         }
1522 }
1523
1524 int ugomark(BW *bw, int c)
1525 {
1526         if (c >= '0' && c <= '9')
1527                 return dogomark(bw, c, NULL, NULL);
1528         else if (mkqwna(bw->parent, sc("Goto bookmark (0-9):"), dogomark, NULL, NULL, NULL))
1529                 return 0;
1530         else
1531                 return -1;
1532 }
1533
1534 /* Goto next instance of character */
1535
1536 static int dobkwdc;
1537
1538 static int dofwrdc(BW *bw, int k, void *object, int *notify)
1539 {
1540         int c;
1541         P *q;
1542
1543         if (notify)
1544                 *notify = 1;
1545         if (k < 0 || k >= 256) {
1546                 nungetc(k);
1547                 return -1;
1548         }
1549         q = pdup(bw->cursor);
1550         if (dobkwdc) {
1551                 while ((c = prgetc(q)) != NO_MORE_DATA)
1552                         if (c == k)
1553                                 break;
1554         } else {
1555                 while ((c = pgetc(q)) != NO_MORE_DATA)
1556                         if (c == k)
1557                                 break;
1558         }
1559         if (c == NO_MORE_DATA) {
1560                 msgnw(bw->parent, US "Not found");
1561                 prm(q);
1562                 return -1;
1563         } else {
1564                 pset(bw->cursor, q);
1565                 bw->cursor->xcol = piscol(bw->cursor);
1566                 prm(q);
1567                 return 0;
1568         }
1569 }
1570
1571 int ufwrdc(BW *bw, int k)
1572 {
1573         dobkwdc = 0;
1574         if (k >= 0 && k < 256)
1575                 return dofwrdc(bw, k, NULL, NULL);
1576         else if (mkqw(bw->parent, sc("Fwrd to char: "), dofwrdc, NULL, NULL, NULL))
1577                 return 0;
1578         else
1579                 return -1;
1580 }
1581
1582 int ubkwdc(BW *bw, int k)
1583 {
1584         dobkwdc = 1;
1585         if (k >= 0 && k < 256)
1586                 return dofwrdc(bw, k, NULL, NULL);
1587         else if (mkqw(bw->parent, sc("Bkwd to char: "), dofwrdc, NULL, NULL, NULL))
1588                 return 0;
1589         else
1590                 return -1;
1591 }
1592
1593 /* Display a message */
1594
1595 static int domsg(BASE *b, unsigned char *s, void *object, int *notify)
1596 {
1597         if (notify)
1598                 *notify = 1;
1599         strlcpy((char *)msgbuf, (char *)s, JOE_MSGBUFSIZE);
1600         vsrm(s);
1601         msgnw(b->parent, *msgbuf ? msgbuf : NULL);
1602         return 0;
1603 }
1604
1605 int umsg(BASE *b)
1606 {
1607         if (wmkpw(b->parent, US "Msg (^C to abort): ", NULL, domsg, NULL, NULL, NULL, NULL, NULL, locale_map))
1608                 return 0;
1609         else
1610                 return -1;
1611 }
1612
1613 /* Insert text */
1614
1615 static int dotxt(BW *bw, unsigned char *s, void *object, int *notify)
1616 {
1617         int x;
1618
1619         if (notify)
1620                 *notify = 1;
1621         for (x = 0; x != sLEN(s); ++x)
1622                 utypebw(bw, s[x]);
1623         vsrm(s);
1624         return 0;
1625 }
1626
1627 int utxt(BW *bw)
1628 {
1629         if (wmkpw(bw->parent, US "Insert (^C to abort): ", NULL, dotxt, NULL, NULL, utypebw, NULL, NULL, bw->b->o.charmap))
1630                 return 0;
1631         else
1632                 return -1;
1633 }