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