Move c_flag() etc. to cmd2.c: they belong
[s-mailx.git] / cmd_arg.c
blob7ef9616fd19afa5f734caf867bdc8b520c615453
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Command argument list parser(s). TODO partial: no msglist in here, etc.
4 * Copyright (c) 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 /* getrawlist(): */
20 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
21 * Copyright (c) 2012 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
24 * Copyright (c) 1980, 1993
25 * The Regents of the University of California. All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. Neither the name of the University nor the names of its contributors
36 * may be used to endorse or promote products derived from this software
37 * without specific prior written permission.
39 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
42 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
49 * SUCH DAMAGE.
51 #undef n_FILE
52 #define n_FILE cmd_arg
54 #ifndef HAVE_AMALGAMATION
55 # include "nail.h"
56 #endif
58 FL int
59 getrawlist(bool_t wysh, char **res_dat, size_t res_size,
60 char const *line, size_t linesize){
61 int res_no;
62 NYD_ENTER;
64 pstate &= ~PS_ARGLIST_MASK;
66 if(res_size == 0){
67 res_no = -1;
68 goto jleave;
69 }else if(UICMP(z, res_size, >, INT_MAX))
70 res_size = INT_MAX;
71 else
72 --res_size;
73 res_no = 0;
75 if(!wysh){
76 /* And assuming result won't grow input */
77 char c2, c, quotec, *cp2, *linebuf;
79 linebuf = salloc(linesize);
81 for(;;){
82 for(; blankchar(*line); ++line)
84 if(*line == '\0')
85 break;
87 if(UICMP(z, res_no, >=, res_size)){
88 n_err(_("Too many input tokens for result storage\n"));
89 res_no = -1;
90 break;
93 cp2 = linebuf;
94 quotec = '\0';
96 /* TODO v15: complete switch in order mirror known behaviour */
97 while((c = *line++) != '\0'){
98 if(quotec != '\0'){
99 if(c == quotec){
100 quotec = '\0';
101 continue;
102 }else if(c == '\\'){
103 if((c2 = *line++) == quotec)
104 c = c2;
105 else
106 --line;
108 }else if(c == '"' || c == '\''){
109 quotec = c;
110 continue;
111 }else if(c == '\\'){
112 if((c2 = *line++) != '\0')
113 c = c2;
114 else
115 --line;
116 }else if(blankchar(c))
117 break;
118 *cp2++ = c;
121 res_dat[res_no++] = savestrbuf(linebuf, PTR2SIZE(cp2 - linebuf));
122 if(c == '\0')
123 break;
125 }else{
126 /* sh(1) compat mode. Prepare shell token-wise */
127 struct n_string store;
128 struct str input;
130 n_string_creat_auto(&store);
131 input.s = UNCONST(line);
132 input.l = linesize;
134 for(;;){
135 for(; blankchar(*line); ++line)
137 if(*line == '\0')
138 break;
140 if(UICMP(z, res_no, >=, res_size)){
141 n_err(_("Too many input tokens for result storage\n"));
142 res_no = -1;
143 break;
146 input.l -= PTR2SIZE(line - input.s);
147 input.s = UNCONST(line);
148 /* C99 */{
149 enum n_shexp_state shs;
151 if((shs = n_shell_parse_token(&store, &input, n_SHEXP_PARSE_LOG)) &
152 n_SHEXP_STATE_ERR_MASK){
153 if((shs & n_SHEXP_STATE_ERR_MASK) == n_SHEXP_STATE_ERR_UNICODE){
154 pstate |= PS_WYSHLIST_SAW_UNICODE;
155 }else{
156 res_no = -1;
157 break;
161 if(shs & n_SHEXP_STATE_OUTPUT){
162 if(shs & n_SHEXP_STATE_UNICODE)
163 pstate |= PS_WYSHLIST_SAW_UNICODE;
164 if(shs & n_SHEXP_STATE_CONTROL)
165 pstate |= PS_WYSHLIST_SAW_CONTROL;
167 res_dat[res_no++] = n_string_cp(&store);
168 n_string_drop_ownership(&store);
171 if(shs & n_SHEXP_STATE_STOP)
172 break;
174 line = input.s;
177 n_string_gut(&store);
180 if(res_no >= 0)
181 res_dat[(size_t)res_no] = NULL;
182 jleave:
183 NYD_LEAVE;
184 return res_no;
187 FL bool_t
188 n_cmd_arg_parse(struct n_cmd_arg_ctx *cacp){
189 struct n_cmd_arg ncap, *lcap;
190 struct str shin_orig, shin;
191 bool_t addca;
192 size_t cad_no, parsed_args;
193 struct n_cmd_arg_desc const *cadp;
194 NYD_ENTER;
196 assert(cacp->cac_inlen == 0 || cacp->cac_indat != NULL);
197 assert(cacp->cac_desc->cad_no > 0);
198 #ifdef HAVE_DEBUG
199 /* C99 */{
200 bool_t opt_seen = FAL0;
202 for(cadp = cacp->cac_desc, cad_no = 0; cad_no < cadp->cad_no; ++cad_no){
203 assert(cadp->cad_ent_flags[cad_no][0] & n__CMD_ARG_DESC_TYPE_MASK);
204 assert(!opt_seen ||
205 (cadp->cad_ent_flags[cad_no][0] & n_CMD_ARG_DESC_OPTION));
206 if(cadp->cad_ent_flags[cad_no][0] & n_CMD_ARG_DESC_OPTION)
207 opt_seen = TRU1;
208 assert(!(cadp->cad_ent_flags[cad_no][0] & n_CMD_ARG_DESC_GREEDY) ||
209 cad_no + 1 == cadp->cad_no);
212 #endif
214 shin.s = UNCONST(cacp->cac_indat); /* "logical" only */
215 shin.l = (cacp->cac_inlen == UIZ_MAX ? strlen(shin.s) : cacp->cac_inlen);
216 shin_orig = shin;
217 cacp->cac_no = 0;
218 cacp->cac_arg = lcap = NULL;
220 parsed_args = 0;
221 for(cadp = cacp->cac_desc, cad_no = 0; shin.l > 0 && cad_no < cadp->cad_no;
222 ++cad_no){
223 jredo:
224 memset(&ncap, 0, sizeof ncap);
225 ncap.ca_indat = shin.s;
226 /* >ca_inline once we know */
227 memcpy(&ncap.ca_ent_flags[0], &cadp->cad_ent_flags[cad_no][0],
228 sizeof ncap.ca_ent_flags);
229 addca = FAL0;
231 switch(ncap.ca_ent_flags[0] & n__CMD_ARG_DESC_TYPE_MASK){
232 case n_CMD_ARG_DESC_STRING:{
233 char /*const*/ *cp = shin.s;
234 size_t i = shin.l;
236 while(i > 0 && blankspacechar(*cp))
237 ++cp, --i;
239 ncap.ca_arg.ca_str.s = cp;
240 while(i > 0 && !blankspacechar(*cp))
241 ++cp, --i;
242 ncap.ca_arg.ca_str.s = savestrbuf(ncap.ca_arg.ca_str.s,
243 ncap.ca_arg.ca_str.l = PTR2SIZE(cp - ncap.ca_arg.ca_str.s));
245 while(i > 0 && blankspacechar(*cp))
246 ++cp, --i;
247 ncap.ca_inlen = PTR2SIZE(cp - ncap.ca_indat);
248 shin.s = cp;
249 shin.l = i;
250 addca = TRU1;
251 } break;
252 default:
253 case n_CMD_ARG_DESC_WYSH:{
254 struct n_string shou, *shoup;
255 enum n_shexp_state shs;
257 shoup = n_string_creat_auto(&shou);
258 ncap.ca_arg_flags =
259 shs = n_shell_parse_token(shoup, &shin, ncap.ca_ent_flags[1] |
260 n_SHEXP_PARSE_TRIMSPACE | n_SHEXP_PARSE_LOG);
261 ncap.ca_inlen = PTR2SIZE(shin.s - ncap.ca_indat);
262 if((shs & (n_SHEXP_STATE_OUTPUT | n_SHEXP_STATE_ERR_MASK)) ==
263 n_SHEXP_STATE_OUTPUT){
264 ncap.ca_arg.ca_str.s = n_string_cp(shoup);
265 ncap.ca_arg.ca_str.l = shou.s_len;
266 shoup = n_string_drop_ownership(shoup);
268 n_string_gut(shoup);
270 if(shs & n_SHEXP_STATE_ERR_MASK)
271 goto jerr;
272 if((shs & n_SHEXP_STATE_STOP) && /* XXX delay if output */
273 (ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_HONOUR_STOP)){
274 if(!(shs & n_SHEXP_STATE_OUTPUT))
275 goto jleave;
276 addca = TRUM1;
277 }else
278 addca = TRU1;
279 } break;
281 ++parsed_args;
283 if(addca){
284 struct n_cmd_arg *cap;
286 cap = salloc(sizeof *cap);
287 memcpy(cap, &ncap, sizeof ncap);
288 if(lcap == NULL)
289 cacp->cac_arg = cap;
290 else
291 lcap->ca_next = cap;
292 lcap = cap;
293 ++cacp->cac_no;
295 if(addca == TRUM1)
296 goto jleave;
299 if(shin.l > 0 && (ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY))
300 goto jredo;
303 if(cad_no < cadp->cad_no &&
304 !(cadp->cad_ent_flags[cad_no][0] & n_CMD_ARG_DESC_OPTION))
305 goto jerr;
307 jleave:
308 NYD_LEAVE;
309 return (lcap != NULL);
311 jerr:{
312 size_t i;
314 for(i = 0; (i < cadp->cad_no &&
315 !(cadp->cad_ent_flags[i][0] & n_CMD_ARG_DESC_OPTION)); ++i)
318 n_err(_("`%s': parsing stopped after %" PRIuZ " arguments "
319 "(need %" PRIuZ "%s)\n"
320 " Input: %.*s\n"
321 " Stopped: %.*s\n"),
322 cadp->cad_name, parsed_args, i, (i == cadp->cad_no ? "" : "+"),
323 (int)shin_orig.l, shin_orig.s,
324 (int)shin.l, shin.s);
326 lcap = NULL;
327 goto jleave;
330 FL struct n_string *
331 n_cmd_arg_join_greedy(struct n_cmd_arg_ctx const *cacp, struct n_string *store){
332 struct n_cmd_arg *cap;
333 NYD_ENTER;
335 for(cap = cacp->cac_arg;
336 (cap != NULL && !(cap->ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY));
337 cap = cap->ca_next)
339 /* Can only join strings */
340 assert(cap == NULL ||
341 (cap->ca_ent_flags[0] & (n_CMD_ARG_DESC_STRING | n_CMD_ARG_DESC_WYSH)));
343 while(cap != NULL){
344 store = n_string_push_buf(store,
345 cap->ca_arg.ca_str.s, cap->ca_arg.ca_str.l);
346 if((cap = cap->ca_next) != NULL)
347 store = n_string_push_c(store, ' ');
349 NYD_LEAVE;
350 return store;
353 /* s-it-mode */