another update from CVS HEAD, for QA
[alioth/jupp.git] / cmd.c
1 /*
2  *      Command execution
3  *      Copyright
4  *              (C) 1992 Joseph H. Allen
5  *
6  *      This file is part of JOE (Joe's Own Editor)
7  */
8 #define EXTERN_CMD_C
9 #include "config.h"
10 #include "types.h"
11
12 __RCSID("$MirOS: contrib/code/jupp/cmd.c,v 1.26 2017/12/08 02:00:38 tg Exp $");
13
14 #include <stdlib.h>
15 #include <string.h>
16
17 #include "b.h"
18 #include "bw.h"
19 #include "cmd.h"
20 #include "hash.h"
21 #include "help.h"
22 #include "kbd.h"
23 #include "macro.h"
24 #include "main.h"
25 #include "menu.h"
26 #include "path.h"
27 #include "poshist.h"
28 #include "pw.h"
29 #include "rc.h"
30 #include "tty.h"
31 #include "tw.h"
32 #include "ublock.h"
33 #include "uedit.h"
34 #include "uerror.h"
35 #include "ufile.h"
36 #include "uformat.h"
37 #include "uisrch.h"
38 #include "umath.h"
39 #include "undo.h"
40 #include "usearch.h"
41 #include "ushell.h"
42 #include "utag.h"
43 #include "utils.h"
44 #include "va.h"
45 #include "vs.h"
46 #include "utf8.h"
47 #include "w.h"
48
49 extern int marking;
50 extern int smode;
51 int dobeep = 0;
52 int uexecmd(BW *bw);
53
54 /* Command table */
55
56 static int
57 ubeep(void)
58 {
59         ttputc(7);
60         return 0;
61 }
62
63 extern char main_context[];
64 static int do_keymap(BW *bw, unsigned char *s, void *object, int *notify)
65 {
66         KMAP *new_kmap;
67
68         if (notify)
69                 *notify = 1;
70         if (!*s || !(new_kmap = kmap_getcontext(s, 0))) {
71                 vsrm(s);
72                 return (-1);
73         }
74         if (bw->o.context != (unsigned char *)main_context)
75                 free(bw->o.context);
76         bw->o.context = strcmp((char *)s, main_context) ?
77             (unsigned char *)strdup((char *)s) : (unsigned char *)main_context;
78         rmkbd(bw->parent->kbd);
79         bw->parent->kbd = mkkbd(new_kmap);
80         joe_snprintf_1((char *)msgbuf, JOE_MSGBUFSIZE, "New keymap: %s", s);
81         vsrm(s);
82         msgnw(bw->parent, msgbuf);
83         return (0);
84 }
85 static int ukeymap(BW *bw)
86 {
87         if (wmkpw(bw->parent, UC "Name of keymap to switch to: ", NULL,
88             do_keymap, NULL, NULL, utypebw, NULL, NULL, locale_map)) {
89                 return (0);
90         }
91         return (-1);
92 }
93
94 static int unop(void)
95 {
96         return (0);
97 }
98
99 #if !WANT_FORK
100 static int
101 unommu(BW *bw) {
102         msgnw(bw->parent, UC "Sorry, not supported without MMU");
103         return (-1);
104 }
105 #define ubknd   unommu
106 #define ubuild  unommu
107 #define urun    unommu
108 #endif
109
110 #define C(name,flag,func,m,arg,negarg) { UC name, UC negarg, func, m, flag, arg }
111 CMD cmds[] = {
112 C("abendjoe", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uabendjoe, NULL, 0, NULL),
113 C("abort", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uabort, NULL, 0, NULL),
114 C("abortbuf", TYPETW, uabortbuf, NULL, 0, NULL),
115 C("arg", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uarg, NULL, 0, NULL),
116 C("ask", TYPETW + TYPEPW, uask, NULL, 0, NULL),
117 C("backs", TYPETW + TYPEPW + ECHKXCOL + EFIXXCOL + EMINOR + EKILL + EMOD, ubacks, NULL, 1, "delch"),
118 C("backsmenu", TYPEMENU, umbacks, NULL, 1, NULL),
119 C("backw", TYPETW + TYPEPW + ECHKXCOL + EFIXXCOL + EKILL + EMOD, ubackw, NULL, 1, "delw"),
120 C("beep", TYPETW + TYPEPW + TYPEMENU + TYPEQW, ubeep, NULL, 0, NULL),
121 C("begin_marking", TYPETW + TYPEPW, ubegin_marking, NULL, 0, NULL),
122 C("bknd", TYPETW + TYPEPW, ubknd, NULL, 0, NULL),
123 C("bkwdc", TYPETW + TYPEPW, ubkwdc, NULL, 1, "fwrdc"),
124 C("blkcpy", TYPETW + TYPEPW + EFIXXCOL + EMOD + EBLOCK, ublkcpy, NULL, 1, NULL),
125 C("blkdel", TYPETW + TYPEPW + EFIXXCOL + EKILL + EMOD + EBLOCK, ublkdel, NULL, 0, NULL),
126 C("blkmove", TYPETW + TYPEPW + EFIXXCOL + EMOD + EBLOCK, ublkmove, NULL, 0, NULL),
127 C("blksave", TYPETW + TYPEPW + EBLOCK, ublksave, NULL, 0, NULL),
128 C("bof", TYPETW + TYPEPW + EMOVE + EFIXXCOL, u_goto_bof, NULL, 0, NULL),
129 C("bofmenu", TYPEMENU, umbof, NULL, 0, NULL),
130 C("bol", TYPETW + TYPEPW + EFIXXCOL, u_goto_bol, NULL, 0, NULL),
131 C("bolmenu", TYPEMENU, umbol, NULL, 0, NULL),
132 C("bop", TYPETW + TYPEPW + EFIXXCOL, ubop, NULL, 1, "eop"),
133 C("bos", TYPETW + TYPEPW + EMOVE, ubos, NULL, 0, NULL),
134 C("bufed", TYPETW, ubufed, NULL, 0, NULL),
135 C("build", TYPETW + TYPEPW, ubuild, NULL, 0, NULL),
136 C("byte", TYPETW + TYPEPW, ubyte, NULL, 0, NULL),
137 C("cancel", TYPETW + TYPEPW + TYPEMENU + TYPEQW, ucancel, NULL, 0, NULL),
138 C("center", TYPETW + TYPEPW + EFIXXCOL + EMOD, ucenter, NULL, 1, NULL),
139 C("col", TYPETW + TYPEPW, ucol, NULL, 0, NULL),
140 C("complete", TYPEPW + EMINOR + EMOD, ucmplt, NULL, 0, NULL),
141 C("copy", TYPETW + TYPEPW, ucopy, NULL, 0, NULL),
142 C("crawll", TYPETW + TYPEPW, ucrawll, NULL, 1, "crawlr"),
143 C("crawlr", TYPETW + TYPEPW, ucrawlr, NULL, 1, "crawll"),
144 C("ctrl", TYPETW + TYPEPW + EMOD, uctrl, NULL, 0, NULL),
145 C("delbol", TYPETW + TYPEPW + EFIXXCOL + EKILL + EMOD, udelbl, NULL, 1, "deleol"),
146 C("delch", TYPETW + TYPEPW + ECHKXCOL + EFIXXCOL + EMINOR + EKILL + EMOD, udelch, NULL, 1, "backs"),
147 C("deleol", TYPETW + TYPEPW + EKILL + EMOD, udelel, NULL, 1, "delbol"),
148 C("dellin", TYPETW + TYPEPW + EFIXXCOL + EKILL + EMOD, udelln, NULL, 1, NULL),
149 C("delw", TYPETW + TYPEPW + EFIXXCOL + ECHKXCOL + EKILL + EMOD, u_word_delete, NULL, 1, "backw"),
150 C("dnarw", TYPETW + TYPEPW + EMOVE, udnarw, NULL, 1, "uparw"),
151 C("dnarwmenu", TYPEMENU, umdnarw, NULL, 1, "uparwmenu"),
152 C("dnslide", TYPETW + TYPEPW + TYPEMENU + TYPEQW + EMOVE, udnslide, NULL, 1, "upslide"),
153 C("drop", TYPETW + TYPEPW, udrop, NULL, 0, NULL),
154 C("dupw", TYPETW, uduptw, NULL, 0, NULL),
155 C("edit", TYPETW, uedit, NULL, 0, NULL),
156 C("eof", TYPETW + TYPEPW + EFIXXCOL + EMOVE, u_goto_eof, NULL, 0, NULL),
157 C("eofmenu", TYPEMENU, umeof, NULL, 0, NULL),
158 C("eol", TYPETW + TYPEPW + EFIXXCOL, u_goto_eol, NULL, 0, NULL),
159 C("eolmenu", TYPEMENU, umeol, NULL, 0, NULL),
160 C("eop", TYPETW + TYPEPW + EFIXXCOL, ueop, NULL, 1, "bop"),
161 C("execmd", TYPETW + TYPEPW, uexecmd, NULL, 0, NULL),
162 C("explode", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uexpld, NULL, 0, NULL),
163 C("exsave", TYPETW + TYPEPW, uexsve, NULL, 0, NULL),
164 C("ffirst", TYPETW + TYPEPW, pffirst, NULL, 0, NULL),
165 C("filt", TYPETW + TYPEPW + EMOD + EBLOCK, ufilt, NULL, 0, NULL),
166 C("finish", TYPETW + TYPEPW + EMOD, ufinish, NULL, 1, NULL),
167 C("fmtblk", TYPETW + EMOD + EFIXXCOL + EBLOCK, ufmtblk, NULL, 1, NULL),
168 C("fnext", TYPETW + TYPEPW, pfnext, NULL, 1, NULL),
169 C("format", TYPETW + TYPEPW + EFIXXCOL + EMOD, uformat, NULL, 1, NULL),
170 C("fwrdc", TYPETW + TYPEPW, ufwrdc, NULL, 1, "bkwdc"),
171 C("gomark", TYPETW + TYPEPW + EMOVE, ugomark, NULL, 0, NULL),
172 C("groww", TYPETW, ugroww, NULL, 1, "shrinkw"),
173 C("help", TYPETW + TYPEPW + TYPEQW, u_help, NULL, 0, NULL),
174 C("helpcard", TYPETW + TYPEPW + TYPEQW, u_helpcard, NULL, 0, NULL),
175 C("hnext", TYPETW + TYPEPW + TYPEQW, u_help_next, NULL, 0, NULL),
176 C("home", TYPETW + TYPEPW + EFIXXCOL, uhome, NULL, 0, NULL),
177 C("hprev", TYPETW + TYPEPW + TYPEQW, u_help_prev, NULL, 0, NULL),
178 C("insc", TYPETW + TYPEPW + EFIXXCOL + EMOD, uinsc, NULL, 1, "delch"),
179 C("insf", TYPETW + TYPEPW + EMOD, uinsf, NULL, 0, NULL),
180 C("isrch", TYPETW + TYPEPW, uisrch, NULL, 0, NULL),
181 C("keymap", TYPETW + TYPEPW, ukeymap, NULL, 0, NULL),
182 C("killjoe", TYPETW + TYPEPW + TYPEMENU + TYPEQW, ukilljoe, NULL, 0, NULL),
183 C("killproc", TYPETW + TYPEPW, ukillpid, NULL, 0, NULL),
184 C("lindent", TYPETW + TYPEPW + EFIXXCOL + EMOD + EBLOCK, ulindent, NULL, 1, "rindent"),
185 C("line", TYPETW + TYPEPW, uline, NULL, 0, NULL),
186 C("lose", TYPETW + TYPEPW, ulose, NULL, 0, NULL),
187 C("lower", TYPETW + TYPEPW + EMOD + EBLOCK, ulower, NULL, 0, NULL),
188 C("ltarw", TYPETW + TYPEPW /* + EFIXXCOL + ECHKXCOL */, u_goto_left, NULL, 1, "rtarw"),
189 C("ltarwmenu", TYPEMENU, umltarw, NULL, 1, "rtarwmenu"),
190 C("macros", TYPETW + EFIXXCOL, umacros, NULL, 0, NULL),
191 C("markb", TYPETW + TYPEPW, umarkb, NULL, 0, NULL),
192 C("markk", TYPETW + TYPEPW, umarkk, NULL, 0, NULL),
193 C("markl", TYPETW + TYPEPW, umarkl, NULL, 0, NULL),
194 C("math", TYPETW + TYPEPW, umath, NULL, 0, NULL),
195 C("mathins", TYPETW + TYPEPW, umathins, NULL, 0, NULL),
196 C("mathres", TYPETW + TYPEPW, umathres, NULL, 0, NULL),
197 C("mode", TYPETW + TYPEPW + TYPEQW, umode, NULL, 0, NULL),
198 C("msg", TYPETW + TYPEPW + TYPEQW + TYPEMENU, umsg, NULL, 0, NULL),
199 C("nbuf", TYPETW, unbuf, NULL, 1, "pbuf"),
200 C("nedge", TYPETW + TYPEPW + EFIXXCOL, unedge, NULL, 1, "pedge"),
201 C("nextpos", TYPETW + TYPEPW + EFIXXCOL + EMID + EPOS, unextpos, NULL, 1, "prevpos"),
202 C("nextw", TYPETW + TYPEPW + TYPEMENU + TYPEQW, unextw, NULL, 1, "prevw"),
203 C("nextword", TYPETW + TYPEPW + EFIXXCOL, u_goto_next, NULL, 1, "prevword"),
204 C("nmark", TYPETW + TYPEPW, unmark, NULL, 0, NULL),
205 C("nop", TYPETW + TYPEPW + TYPEMENU + TYPEQW, unop, NULL, 0, NULL),
206 C("notmod", TYPETW, unotmod, NULL, 0, NULL),
207 C("nxterr", TYPETW, unxterr, NULL, 1, "prverr"),
208 C("open", TYPETW + TYPEPW + EFIXXCOL + EMOD, uopen, NULL, 1, "deleol"),
209 C("parserr", TYPETW, uparserr, NULL, 0, NULL),
210 C("pbuf", TYPETW, upbuf, NULL, 1, "nbuf"),
211 C("pedge", TYPETW + TYPEPW + EFIXXCOL, upedge, NULL, 1, "nedge"),
212 C("pgdn", TYPETW + TYPEPW + TYPEMENU + TYPEQW + EMOVE, upgdn, NULL, 1, "pgup"),
213 C("pgdnmenu", TYPEMENU, umpgdn, NULL, 1, "pgupmenu"),
214 C("pgup", TYPETW + TYPEPW + TYPEMENU + TYPEQW + EMOVE, upgup, NULL, 1, "pgdn"),
215 C("pgupmenu", TYPEMENU, umpgup, NULL, 1, "pgdnmenu"),
216 C("picokill", TYPETW + TYPEPW + EFIXXCOL + EKILL + EMOD, upicokill, NULL, 1, NULL),
217 C("play", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uplay, NULL, 1, NULL),   /* EFIXX? */
218 C("pop", TYPETW + TYPEPW + TYPEMENU + TYPEQW, upop, NULL, 0, NULL),
219 C("prevpos", TYPETW + TYPEPW + EPOS + EMID + EFIXXCOL, uprevpos, NULL, 1, "nextpos"),
220 C("prevw", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uprevw, NULL, 1, "nextw"),
221 C("prevword", TYPETW + TYPEPW + EFIXXCOL + ECHKXCOL, u_goto_prev, NULL, 1, "nextword"),
222 C("prverr", TYPETW, uprverr, NULL, 1, "nxterr"),
223 C("psh", TYPETW + TYPEPW + TYPEMENU + TYPEQW, upsh, NULL, 0, NULL),
224 C("qrepl", TYPETW + TYPEPW + EMOD, pqrepl, NULL, 0, NULL),
225 C("query", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uquery, NULL, 0, NULL),
226 C("querysave", TYPETW, uquerysave, NULL, 0, NULL),
227 C("quote", TYPETW + TYPEPW + EMOD, uquote, NULL, 0, NULL),
228 C("quote8", TYPETW + TYPEPW + EMOD, uquote8, NULL, 0, NULL),
229 C("record", TYPETW + TYPEPW + TYPEMENU + TYPEQW, urecord, NULL, 0, NULL),
230 C("redo", TYPETW + TYPEPW + EFIXXCOL, uredo, NULL, 1, "undo"),
231 C("retype", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uretyp, NULL, 0, NULL),
232 C("rfirst", TYPETW + TYPEPW, prfirst, NULL, 0, NULL),
233 C("rindent", TYPETW + TYPEPW + EFIXXCOL + EMOD + EBLOCK, urindent, NULL, 1, "lindent"),
234 C("rsrch", TYPETW + TYPEPW, ursrch, NULL, 0, NULL),
235 C("rtarw", TYPETW + TYPEPW /* + EFIXXCOL */, u_goto_right, NULL, 1, "ltarw"), /* EFIX removed for picture mode */
236 C("rtarwmenu", TYPEMENU, umrtarw, NULL, 1, "ltarwmenu"),
237 C("rtn", TYPETW + TYPEPW + TYPEMENU + TYPEQW + EMOD, urtn, NULL, 1, NULL),
238 C("run", TYPETW + TYPEPW, urun, NULL, 0, NULL),
239 C("rvmatch", TYPETW + TYPEPW + EFIXXCOL, urvmatch, NULL, 0, NULL),
240 C("save", TYPETW, usave, NULL, 0, NULL),
241 C("scratch", TYPETW + TYPEPW, uscratch, NULL, 0, NULL),
242 C("select", TYPETW + TYPEPW, uselect, NULL, 0, NULL),
243 C("setmark", TYPETW + TYPEPW, usetmark, NULL, 0, NULL),
244 C("shell", TYPETW + TYPEPW + TYPEMENU + TYPEQW, ushell, NULL, 0, NULL),
245 C("shrinkw", TYPETW, ushrnk, NULL, 1, "groww"),
246 C("splitw", TYPETW, usplitw, NULL, 0, NULL),
247 C("stat", TYPETW + TYPEPW, ustat_j, NULL, 0, NULL),
248 C("stop", TYPETW + TYPEPW + TYPEMENU + TYPEQW, ustop, NULL, 0, NULL),
249 C("swap", TYPETW + TYPEPW + EFIXXCOL, uswap, NULL, 0, NULL),
250 C("switch", TYPETW + TYPEPW, uswitch, NULL, 0, NULL),
251 C("tabmenu", TYPEMENU, umtab, NULL, 1, "ltarwmenu"),
252 C("tag", TYPETW + TYPEPW, utag, NULL, 0, NULL),
253 C("toggle_marking", TYPETW + TYPEPW, utoggle_marking, NULL, 0, NULL),
254 C("tomarkb", TYPETW + TYPEPW + EFIXXCOL + EBLOCK, utomarkb, NULL, 0, NULL),
255 C("tomarkbk", TYPETW + TYPEPW + EFIXXCOL + EBLOCK, utomarkbk, NULL, 0, NULL),
256 C("tomarkk", TYPETW + TYPEPW + EFIXXCOL + EBLOCK, utomarkk, NULL, 0, NULL),
257 C("tomatch", TYPETW + TYPEPW + EFIXXCOL, utomatch, NULL, 0, NULL),
258 C("tos", TYPETW + TYPEPW + EMOVE, utos, NULL, 0, NULL),
259 C("tw0", TYPETW + TYPEPW + TYPEQW + TYPEMENU, utw0, NULL, 0, NULL),
260 C("tw1", TYPETW + TYPEPW + TYPEQW + TYPEMENU, utw1, NULL, 0, NULL),
261 C("txt", TYPETW + TYPEPW, utxt, NULL, 0, NULL),
262 C("type", TYPETW + TYPEPW + TYPEQW + TYPEMENU + EMINOR + EMOD, utype, NULL, 1, "backs"),
263 C("uarg", TYPETW + TYPEPW + TYPEMENU + TYPEQW, uuarg, NULL, 0, NULL),
264 C("undo", TYPETW + TYPEPW + EFIXXCOL, uundo, NULL, 1, "redo"),
265 C("uparw", TYPETW + TYPEPW + EMOVE, uuparw, NULL, 1, "dnarw"),
266 C("uparwmenu", TYPEMENU, umuparw, NULL, 1, "dnarwmenu"),
267 C("upper", TYPETW + TYPEPW + EMOD + EBLOCK, uupper, NULL, 0, NULL),
268 C("upslide", TYPETW + TYPEPW + TYPEMENU + TYPEQW + EMOVE, uupslide, NULL, 1, "dnslide"),
269 C("yank", TYPETW + TYPEPW + EFIXXCOL + EMOD, uyank, NULL, 1, NULL),
270 C("yankpop", TYPETW + TYPEPW + EFIXXCOL + EMOD, uyankpop, NULL, 1, NULL),
271 C("yapp", TYPETW + TYPEPW + EKILL, uyapp, NULL, 0, NULL)
272 };
273
274 /* Execute a command n with key k */
275
276 int execmd(CMD *cmd, int k)
277 {
278         BW *bw = maint->curwin->object.bw;
279         int ret = -1;
280
281         /* Send data to shell window: this is broken ^K ^H (help) sends its ^H to shell */
282         if ((maint->curwin->watom->what & TYPETW) && bw->b->pid && piseof(bw->cursor) &&
283         (k==3 || k==13 || k==8 || k==127 || k==4 || ((cmd->func==utype) && (k>=32) && (k<256)))) {
284                 unsigned char c = k;
285                 joe_write(bw->b->out, &c, 1);
286                 return 0;
287         }
288
289         if (cmd->m)
290                 return exmacro(cmd->m, 0);
291
292         /* We don't execute if we have to fix the column position first
293          * (i.e., left arrow when cursor is in middle of nowhere) */
294         if (cmd->flag & ECHKXCOL) {
295                 if (bw->o.hex)
296                         bw->cursor->xcol = piscol(bw->cursor);
297                 else if (bw->cursor->xcol != piscol(bw->cursor))
298                         goto skip;
299         }
300
301         /* Don't execute command if we're in wrong type of window */
302         if (!(cmd->flag & maint->curwin->watom->what))
303                 goto skip;
304
305         /* Complete selection for block commands */
306         if ((cmd->flag & EBLOCK) && marking)
307                 utoggle_marking(maint->curwin->object.bw);
308
309         if ((maint->curwin->watom->what & TYPETW) && bw->b->rdonly && (cmd->flag & EMOD)) {
310                 msgnw(bw->parent, UC "Read only");
311                 if (dobeep)
312                         ttputc(7);
313                 goto skip;
314         }
315
316         /* Execute command */
317         ret = cmd->func(maint->curwin->object, k);
318
319         if (smode)
320                 --smode;
321
322         /* Don't update anything if we're going to leave */
323         if (leave)
324                 return 0;
325
326         /* cmd->func could have changed bw on us */
327         bw = maint->curwin->object.bw;
328
329         /* Maintain position history */
330         /* If command was not a positioning command */
331         if (!(cmd->flag & EPOS)
332             && (maint->curwin->watom->what & (TYPETW | TYPEPW)))
333                 afterpos();
334
335         /* If command was not a movement */
336         if (!(cmd->flag & (EMOVE | EPOS)) && (maint->curwin->watom->what & (TYPETW | TYPEPW)))
337                 aftermove(maint->curwin, bw->cursor);
338
339         if (cmd->flag & EKILL)
340                 justkilled = 1;
341         else
342                 justkilled = 0;
343
344  skip:
345
346         /* Make dislayed cursor column equal the actual cursor column
347          * for commands which arn't simple vertical movements */
348         if (cmd->flag & EFIXXCOL)
349                 bw->cursor->xcol = piscol(bw->cursor);
350
351         /* Recenter cursor to middle of screen */
352         if (cmd->flag & EMID) {
353                 int omid = mid;
354
355                 mid = 1;
356                 dofollows();
357                 mid = omid;
358         }
359
360         if (dobeep && ret)
361                 ttputc(7);
362         return ret;
363 }
364
365 /* Return command table index for given command name */
366
367 HASH *cmdhash = NULL;
368
369 static void izcmds(void)
370 {
371         int x;
372
373         cmdhash = htmk(256);
374         for (x = 0; x != sizeof(cmds) / sizeof(CMD); ++x)
375                 htadd(cmdhash, cmds[x].name, cmds + x);
376 }
377
378 CMD *findcmd(const unsigned char *s)
379 {
380         if (!cmdhash)
381                 izcmds();
382         return (CMD *) htfind(cmdhash, s);
383 }
384
385 void addcmd(const unsigned char *s, MACRO *m)
386 {
387         CMD *cmd = malloc(sizeof(CMD));
388
389         if (!cmdhash)
390                 izcmds();
391         cmd->name = (unsigned char *)strdup((const char *)s);
392         cmd->flag = 0;
393         cmd->func = NULL;
394         cmd->m = m;
395         cmd->arg = 1;
396         cmd->negarg = NULL;
397         htadd(cmdhash, cmd->name, cmd);
398 }
399
400 static unsigned char **getcmds(void)
401 {
402         unsigned char **s = vaensure(NULL, sizeof(cmds) / sizeof(CMD));
403         int x;
404         HENTRY *e;
405
406         for (x = 0; x != cmdhash->len; ++x)
407                 for (e = cmdhash->tab[x]; e; e = e->next)
408                         s = vaadd(s, vsncpy(NULL, 0, sz(e->name)));
409         vasort(s, aLen(s));
410         return s;
411 }
412
413 /* Command line */
414
415 unsigned char **scmds = NULL;   /* Array of command names */
416
417 static int cmdcmplt(BW *bw)
418 {
419         if (!scmds)
420                 scmds = getcmds();
421         /*XXX simple_cmplt does p_goto_bol, better only to last comma */
422         return simple_cmplt(bw, scmds);
423 }
424
425 static int docmd(BW *bw, unsigned char *s, void *object, int *notify)
426 {
427         MACRO *mac;
428         int ret = -1;
429
430         if (s) {
431                 mac = mparse(NULL, s, &ret);
432                 if (ret < 0 || !mac)
433                         msgnw(bw->parent, UC "No such command");
434                 else {
435                         ret = exmacro(mac, 1);
436                         rmmacro(mac);
437                 }
438         }
439         vsrm(s);        /* allocated in pw.c::rtnpw() */
440         if (notify)
441                 *notify = 1;
442         return ret;
443 }
444
445 B *cmdhist = NULL;
446
447 int uexecmd(BW *bw)
448 {
449         if (wmkpw(bw->parent, UC "cmd: ", &cmdhist, docmd, UC "cmd", NULL, cmdcmplt, NULL, NULL, locale_map)) {
450                 return 0;
451         } else {
452                 return -1;
453         }
454 }
455
456 /*
457  * Show help screen at a specific card
458  */
459 static int do_helpcard(BASE *base, unsigned char *s, void *object, int *notify)
460 {
461         struct help *new_help;
462
463         if (notify)
464                 *notify = 1;
465         if (!*s) {
466                 vsrm(s);
467                 while (help_actual->prev != NULL)
468                         /* find the first help entry */
469                         help_actual = help_actual->prev;
470                 help_off(base->parent->t);
471                 return (0);
472         }
473         if ((new_help = find_context_help(s)) != NULL) {
474                 vsrm(s);
475                 help_actual = new_help;
476                 return (help_on(base->parent->t));
477         }
478         vsrm(s);
479         return (-1);
480 }
481 int u_helpcard(BASE *base)
482 {
483         if (wmkpw(base->parent, UC "Name of help card to show: ", NULL,
484             do_helpcard, NULL, NULL, utypebw, NULL, NULL, locale_map)) {
485                 return (0);
486         }
487         return (-1);
488 }