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 - 2017 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.
20 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
21 * Copyright (c) 2012 - 2017 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
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
52 #define n_FILE cmd_arg
54 #ifndef HAVE_AMALGAMATION
59 getrawlist(bool_t wysh
, char **res_dat
, size_t res_size
,
60 char const *line
, size_t linesize
){
64 n_pstate
&= ~n_PS_ARGLIST_MASK
;
69 }else if(UICMP(z
, res_size
, >, INT_MAX
))
76 /* And assuming result won't grow input */
77 char c2
, c
, quotec
, *cp2
, *linebuf
;
79 linebuf
= n_lofi_alloc(linesize
);
82 for(; blankchar(*line
); ++line
)
87 if(UICMP(z
, res_no
, >=, res_size
)){
88 n_err(_("Too many input tokens for result storage\n"));
96 /* TODO v15: complete switch in order mirror known behaviour */
97 while((c
= *line
++) != '\0'){
103 if((c2
= *line
++) == quotec
)
108 }else if(c
== '"' || c
== '\''){
112 if((c2
= *line
++) != '\0')
116 }else if(blankchar(c
))
121 res_dat
[res_no
++] = savestrbuf(linebuf
, PTR2SIZE(cp2
- linebuf
));
126 n_lofi_free(linebuf
);
128 /* sh(1) compat mode. Prepare shell token-wise */
129 struct n_string store
;
133 n_string_creat_auto(&store
);
134 input
.s
= n_UNCONST(line
);
140 for(; blankchar(*line
); ++line
)
146 if(UICMP(z
, res_no
, >=, res_size
)){
147 n_err(_("Too many input tokens for result storage\n"));
152 input
.l
-= PTR2SIZE(line
- input
.s
);
153 input
.s
= n_UNCONST(line
);
155 enum n_shexp_state shs
;
157 if((shs
= n_shexp_parse_token((n_SHEXP_PARSE_LOG
|
158 n_SHEXP_PARSE_META_SEMICOLON
), &store
, &input
, &cookie
)
159 ) & n_SHEXP_STATE_ERR_MASK
){
160 /* Simply ignore Unicode error, just keep the normalized \[Uu] */
161 if((shs
& n_SHEXP_STATE_ERR_MASK
) != n_SHEXP_STATE_ERR_UNICODE
){
167 if(shs
& n_SHEXP_STATE_OUTPUT
){
168 if(shs
& n_SHEXP_STATE_CONTROL
)
169 n_pstate
|= n_PS_WYSHLIST_SAW_CONTROL
;
171 res_dat
[res_no
++] = n_string_cp(&store
);
172 n_string_drop_ownership(&store
);
175 if(shs
& n_SHEXP_STATE_STOP
)
181 n_string_gut(&store
);
185 res_dat
[(size_t)res_no
] = NULL
;
192 n_cmd_arg_parse(struct n_cmd_arg_ctx
*cacp
){ /* TODO use this for cmd_tab! */
193 struct n_cmd_arg ncap
, *lcap
;
194 struct str shin_orig
, shin
;
197 size_t cad_no
, parsed_args
;
198 struct n_cmd_arg_desc
const *cadp
;
201 assert(cacp
->cac_inlen
== 0 || cacp
->cac_indat
!= NULL
);
202 assert(cacp
->cac_desc
->cad_no
> 0);
205 bool_t opt_seen
= FAL0
;
207 for(cadp
= cacp
->cac_desc
, cad_no
= 0; cad_no
< cadp
->cad_no
; ++cad_no
){
208 assert(cadp
->cad_ent_flags
[cad_no
][0] & n__CMD_ARG_DESC_TYPE_MASK
);
210 (cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
));
211 if(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
)
213 assert(!(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_GREEDY
) ||
214 cad_no
+ 1 == cadp
->cad_no
);
219 shin
.s
= n_UNCONST(cacp
->cac_indat
); /* "logical" only */
220 shin
.l
= (cacp
->cac_inlen
== UIZ_MAX
? strlen(shin
.s
) : cacp
->cac_inlen
);
223 cacp
->cac_arg
= lcap
= NULL
;
227 for(cadp
= cacp
->cac_desc
, cad_no
= 0; shin
.l
> 0 && cad_no
< cadp
->cad_no
;
230 memset(&ncap
, 0, sizeof ncap
);
231 ncap
.ca_indat
= shin
.s
;
232 /* >ca_inline once we know */
233 memcpy(&ncap
.ca_ent_flags
[0], &cadp
->cad_ent_flags
[cad_no
][0],
234 sizeof ncap
.ca_ent_flags
);
237 switch(ncap
.ca_ent_flags
[0] & n__CMD_ARG_DESC_TYPE_MASK
){
238 case n_CMD_ARG_DESC_STRING
:{ /* TODO \ escaping? additional type!? */
239 char /*const*/ *cp
= shin
.s
;
242 while(i
> 0 && blankspacechar(*cp
))
245 ncap
.ca_arg
.ca_str
.s
= cp
;
246 while(i
> 0 && !blankspacechar(*cp
))
248 ncap
.ca_arg
.ca_str
.s
= savestrbuf(ncap
.ca_arg
.ca_str
.s
,
249 ncap
.ca_arg
.ca_str
.l
= PTR2SIZE(cp
- ncap
.ca_arg
.ca_str
.s
));
251 while(i
> 0 && blankspacechar(*cp
))
253 ncap
.ca_inlen
= PTR2SIZE(cp
- ncap
.ca_indat
);
259 case n_CMD_ARG_DESC_WYSH
:{
260 struct n_string shou
, *shoup
;
261 enum n_shexp_state shs
;
263 shoup
= n_string_creat_auto(&shou
);
265 shs
= n_shexp_parse_token((ncap
.ca_ent_flags
[1] |
266 n_SHEXP_PARSE_TRIMSPACE
| n_SHEXP_PARSE_LOG
), shoup
, &shin
,
267 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
? &cookie
: NULL
));
268 ncap
.ca_inlen
= PTR2SIZE(shin
.s
- ncap
.ca_indat
);
269 if((shs
& (n_SHEXP_STATE_OUTPUT
| n_SHEXP_STATE_ERR_MASK
)) ==
270 n_SHEXP_STATE_OUTPUT
){
271 ncap
.ca_arg
.ca_str
.s
= n_string_cp(shoup
);
272 ncap
.ca_arg
.ca_str
.l
= shou
.s_len
;
273 shoup
= n_string_drop_ownership(shoup
);
277 if(shs
& n_SHEXP_STATE_ERR_MASK
)
279 if((shs
& n_SHEXP_STATE_STOP
) &&
280 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_HONOUR_STOP
)){
281 if(!(shs
& n_SHEXP_STATE_OUTPUT
))
291 struct n_cmd_arg
*cap
;
293 cap
= salloc(sizeof *cap
);
294 memcpy(cap
, &ncap
, sizeof ncap
);
306 if((shin
.l
> 0 || cookie
!= NULL
) &&
307 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
))
311 if(cad_no
< cadp
->cad_no
&&
312 !(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
))
317 return (lcap
!= NULL
);
322 for(i
= 0; (i
< cadp
->cad_no
&&
323 !(cadp
->cad_ent_flags
[i
][0] & n_CMD_ARG_DESC_OPTION
)); ++i
)
326 n_err(_("`%s': parsing stopped after %" PRIuZ
" arguments "
327 "(need %" PRIuZ
"%s)\n"
330 cadp
->cad_name
, parsed_args
, i
, (i
== cadp
->cad_no
? n_empty
: "+"),
331 (int)shin_orig
.l
, shin_orig
.s
,
332 (int)shin
.l
, shin
.s
);
339 n_cmd_arg_join_greedy(struct n_cmd_arg_ctx
const *cacp
, struct n_string
*store
){
340 struct n_cmd_arg
*cap
;
343 for(cap
= cacp
->cac_arg
;
344 (cap
!= NULL
&& !(cap
->ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
));
347 /* Can only join strings */
348 assert(cap
== NULL
||
349 (cap
->ca_ent_flags
[0] & (n_CMD_ARG_DESC_STRING
| n_CMD_ARG_DESC_WYSH
)));
352 store
= n_string_push_buf(store
,
353 cap
->ca_arg
.ca_str
.s
, cap
->ca_arg
.ca_str
.l
);
354 if((cap
= cap
->ca_next
) != NULL
)
355 store
= n_string_push_c(store
, ' ');