1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2  *@ Actual command table, `help', `list', etc., and the cmd_arg() parser.
3  *
4  * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
5  * SPDX-License-Identifier: ISC
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #ifndef mx_CMD_H
20 #define mx_CMD_H
21 
22 #include <mx/nail.h>
23 
24 #define mx_HEADER
25 #include <su/code-in.h>
26 
27 enum mx_cmd_arg_flags{ /* TODO Most of these need to change, in fact in v15
28    * TODO i rather see the mechanism that is used in c_bind() extended and used
29    * TODO anywhere, i.e. n_cmd_arg_parse().
30    * TODO Note we may NOT support arguments with su_cs_len()>=U32_MAX (?) */
31    mx_CMD_ARG_TYPE_MSGLIST = 0, /* Message list type */
32    mx_CMD_ARG_TYPE_NDMLIST = 1, /* Message list, no defaults */
33    mx_CMD_ARG_TYPE_RAWDAT = 2, /* The plain string in an argv[] */
34      mx_CMD_ARG_TYPE_STRING = 3, /* A pure string TODO obsolete */
35    mx_CMD_ARG_TYPE_WYSH = 4, /* getrawlist(), sh(1) compatible */
36       mx_CMD_ARG_TYPE_RAWLIST = 5, /* getrawlist(), old style TODO obsolete */
37      mx_CMD_ARG_TYPE_WYRA = 6, /* _RAWLIST or _WYSH (with `wysh') TODO obs. */
38    mx_CMD_ARG_TYPE_ARG = 7, /* n_cmd_arg_desc/n_cmd_arg() new-style */
39    mx_CMD_ARG_TYPE_MASK = 7, /* Mask of the above */
40 
41    mx_CMD_ARG_A = 1u<<4, /* Needs an active mailbox */
42    mx_CMD_ARG_F = 1u<<5, /* Is a conditional command */
43    mx_CMD_ARG_G = 1u<<6, /* Is supposed to produce "gabby" history */
44    mx_CMD_ARG_H = 1u<<7, /* Never place in `history' */
45    mx_CMD_ARG_I = 1u<<8, /* Interactive command bit */
46    mx_CMD_ARG_L = 1u<<9, /* Supports `local' prefix (only WYSH/WYRA) */
47    mx_CMD_ARG_M = 1u<<10, /* Legal from send mode bit */
48    mx_CMD_ARG_O = 1u<<11, /* n_OBSOLETE()d command */
49    mx_CMD_ARG_P = 1u<<12, /* Autoprint dot after command */
50    mx_CMD_ARG_R = 1u<<13, /* Forbidden in compose mode recursion */
51    mx_CMD_ARG_SC = 1u<<14, /* Forbidden pre-n_PSO_STARTED_CONFIG */
52    mx_CMD_ARG_S = 1u<<15, /* Forbidden pre-n_PSO_STARTED (POSIX) */
53    mx_CMD_ARG_T = 1u<<16, /* Is a transparent command */
54    mx_CMD_ARG_V = 1u<<17, /* Supports `vput' prefix (only WYSH/WYRA) */
55    mx_CMD_ARG_W = 1u<<18, /* Invalid when read only bit */
56    mx_CMD_ARG_X = 1u<<19, /* Valid command in n_PS_COMPOSE_FORKHOOK mode */
57    /* XXX Note that CMD_ARG_EM implies a _real_ return value for $! */
58    mx_CMD_ARG_EM = 1u<<30 /* If error: n_pstate_err_no (4 $! aka. ok_v___em) */
59 };
60 
61 enum mx_cmd_arg_desc_flags{
62    /* - A type */
63    mx_CMD_ARG_DESC_SHEXP = 1u<<0, /* sh(1)ell-style token */
64    /* TODO All MSGLIST arguments can only be used last and are always greedy
65     * TODO (but MUST be _GREEDY, and MUST NOT be _OPTION too!).
66     * MSGLIST_AND_TARGET may create a NULL target */
67    mx_CMD_ARG_DESC_MSGLIST = 1u<<1,  /* Message specification(s) */
68    mx_CMD_ARG_DESC_NDMSGLIST = 1u<<2,
69    mx_CMD_ARG_DESC_MSGLIST_AND_TARGET = 1u<<3,
70 
71    mx__CMD_ARG_DESC_TYPE_MASK = mx_CMD_ARG_DESC_SHEXP |
72          mx_CMD_ARG_DESC_MSGLIST | mx_CMD_ARG_DESC_NDMSGLIST |
73          mx_CMD_ARG_DESC_MSGLIST_AND_TARGET,
74 
75    /* - Optional flags */
76    /* It is not an error if an optional argument is missing; once an argument
77     * has been declared optional only optional arguments may follow */
78    mx_CMD_ARG_DESC_OPTION = 1u<<16,
79    /* GREEDY: parse as many of that type as possible; must be last entry */
80    mx_CMD_ARG_DESC_GREEDY = 1u<<17,
81    /* If greedy, join all given arguments separated by ASCII SP right away */
82    mx_CMD_ARG_DESC_GREEDY_JOIN = 1u<<18,
83    /* Honour an overall "stop" request in one of the arguments (\c@ or #) */
84    mx_CMD_ARG_DESC_HONOUR_STOP = 1u<<19,
85    /* With any MSGLIST, only one message may be give or ERR_NOTSUP (default) */
86    mx_CMD_ARG_DESC_MSGLIST_NEEDS_SINGLE = 1u<<20,
87 
88    mx__CMD_ARG_DESC_FLAG_MASK = mx_CMD_ARG_DESC_OPTION |
89          mx_CMD_ARG_DESC_GREEDY | mx_CMD_ARG_DESC_GREEDY_JOIN |
90          mx_CMD_ARG_DESC_HONOUR_STOP |
91          mx_CMD_ARG_DESC_MSGLIST_NEEDS_SINGLE,
92 
93    /* We may include something for n_pstate_err_no */
94    mx_CMD_ARG_DESC_ERRNO_SHIFT = 21u,
95    mx_CMD_ARG_DESC_ERRNO_MASK = (1u<<10) - 1
96 };
97 #define mx_CMD_ARG_DESC_ERRNO_TO_ORBITS(ENO) \
98    (S(u32,ENO) << mx_CMD_ARG_DESC_ERRNO)
99 #define mx_CMD_ARG_DESC_TO_ERRNO(FLAGCARRIER) \
100    ((S(u32,FLAGCARRIER) >> mx_CMD_ARG_DESC_ERRNO_SHIFT) &\
101       mx_CMD_ARG_DESC_ERRNO_MASK)
102 
103 struct mx_cmd_arg_desc{
104    char cad_name[12]; /* Name of command */
105    u32 cad_no; /* Number of entries in cad_ent_flags */
106    /* [enum cmd_arg_desc_flags,arg-dep] */
107    u32 cad_ent_flags[VFIELD_SIZE(0)][2];
108 };
109 
110 /* ISO C(99) does not allow initialization of "flex array" */
111 #define mx_CMD_ARG_DESC_SUBCLASS_DEF(CMD,NO,VAR) \
112    mx_CMD_ARG_DESC_SUBCLASS_DEF_NAME(CMD, su_STRING(CMD), NO, VAR)
113 #define mx_CMD_ARG_DESC_SUBCLASS_DEF_NAME(CMD,NAME,NO,VAR) \
114    static struct mx_cmd_arg_desc_ ## CMD {\
115       char cad_name[12];\
116       u32 cad_no;\
117       u32 cad_ent_flags[NO][2];\
118    } const VAR = { NAME "\0", NO,
119 #define mx_CMD_ARG_DESC_SUBCLASS_DEF_END }
120 #define mx_CMD_ARG_DESC_SUBCLASS_CAST(P) su_R(struct mx_cmd_arg_desc const*,P)
121 
122 struct mx_cmd_arg_ctx{
123    struct mx_cmd_arg_desc const *cac_desc; /* Input: description of command */
124    char const *cac_indat; /* Input that shall be parsed */
125    uz cac_inlen; /* Input length (UZ_MAX: do a su_cs_len()) */
126    u32 cac_msgflag; /* Input (option): required flags of messages */
127    u32 cac_msgmask; /* Input (option): relevant flags of messages */
128    uz cac_no; /* Output: number of parsed arguments */
129    struct mx_cmd_arg *cac_arg; /* Output: parsed arguments */
130    char const *cac_vput; /* "Output": vput prefix used: varname */
131 };
132 
133 struct mx_cmd_arg{
134    struct mx_cmd_arg *ca_next;
135    char const *ca_indat; /*[PRIV] Pointer into cmd_arg_ctx.cac_indat */
136    uz ca_inlen; /*[PRIV] of .ca_indat of this arg (no NUL) */
137    u32 ca_ent_flags[2]; /* Copy of cmd_arg_desc.cad_ent_flags[X] */
138    u32 ca_arg_flags; /* [Output: _WYSH: copy of parse result flags] */
139    u8 ca__dummy[4];
140    union{
141       struct str ca_str; /* _CMD_ARG_DESC_SHEXP */
142       int *ca_msglist; /* _CMD_ARG_DESC_MSGLIST+ */
143    } ca_arg; /* Output: parsed result */
144 };
145 
146 struct mx_cmd_desc{
147    char const *cd_name; /* Name of command */
148    int (*cd_func)(void*); /* Implementor of command */
149    enum mx_cmd_arg_flags cd_caflags;
150    u32 cd_mflags_o_minargs; /* Message flags or min. args WYSH/WYRA/RAWLIST */
151    u32 cd_mmask_o_maxargs; /* Ditto, mask or max. args */
152    /* XXX requires cmd.h initializer changes u8 cd__pad[4];*/
153    struct mx_cmd_arg_desc const *cd_cadp;
154 #ifdef mx_HAVE_DOCSTRINGS
155    char const *cd_doc; /* One line doc for command */
156 #endif
157 };
158 
159 /* Isolate the command from the arguments, return pointer to end of cmd name */
160 EXPORT char const *mx_cmd_isolate_name(char const *cmd);
161 
162 /* Whether cmd is a valid command name (and not a modifier, for example) */
163 EXPORT boole mx_cmd_is_valid_name(char const *cmd);
164 
165 /* First command which fits for cmd, or NULL */
166 EXPORT struct mx_cmd_desc const *mx_cmd_firstfit(char const *cmd);
167 
168 /* Get the default command for the empty line */
169 EXPORT struct mx_cmd_desc const *mx_cmd_default(void);
170 
171 /* Or NIL if cmd is invalid, or su_empty if !HAVE_DOCSTRINGS */
mx_cmd_get_brief_doc(struct mx_cmd_desc const * cdp_or_nil)172 INLINE char const *mx_cmd_get_brief_doc(struct mx_cmd_desc const *cdp_or_nil){
173    char const *rv;
174 
175    if(cdp_or_nil != NIL){
176 #ifdef mx_HAVE_DOCSTRINGS
177       rv = cdp_or_nil->cd_doc;
178 #else
179       rv = su_empty;
180 #endif
181    }else
182       rv = NIL;
183    return rv;
184 }
185 
186 /* True if I/O succeeded (or nothing was printed).
187  * If fp is NIL we dump via err(), and return true. */
188 EXPORT boole mx_cmd_print_synopsis(struct mx_cmd_desc const *cdp_or_nil,
189       FILE *fp_or_nil);
190 
191 /* Scan an entire command argument line, return whether result can be used,
192  * otherwise no resources are allocated (in ->cac_arg).
193  * For _WYSH arguments the flags _TRIM_SPACE (v15 _not_ _TRIM_IFSSPACE) and
194  * _LOG are implicit, _META_SEMICOLON is starting with the last (non-optional)
195  * argument, and then a trailing empty argument is ignored, too */
196 EXPORT boole mx_cmd_arg_parse(struct mx_cmd_arg_ctx *cacp);
197 
198 /* Save away the data from autorec memory, and restore it to that.
199  * The heap storage is a single pointer to be n_free() by users */
200 EXPORT void *mx_cmd_arg_save_to_heap(struct mx_cmd_arg_ctx const *cacp);
201 EXPORT struct mx_cmd_arg_ctx *mx_cmd_arg_restore_from_heap(void *vp);
202 
203 /* Scan out the list of string arguments according to rm, return -1 on error;
204  * res_dat is NULL terminated unless res_size is 0 or error occurred */
205 EXPORT int /* TODO v15*/ getrawlist(boole wysh, char **res_dat, uz res_size,
206                   char const *line, uz linesize);
207 
208 #include <su/code-ou.h>
209 #endif /* mx_CMD_H */
210 /* s-it-mode */
211