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.
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
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 pstate
&= ~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
= salloc(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 /* sh(1) compat mode. Prepare shell token-wise */
127 struct n_string store
;
130 n_string_creat_auto(&store
);
131 input
.s
= UNCONST(line
);
135 for(; blankchar(*line
); ++line
)
140 if(UICMP(z
, res_no
, >=, res_size
)){
141 n_err(_("Too many input tokens for result storage\n"));
146 input
.l
-= PTR2SIZE(line
- input
.s
);
147 input
.s
= UNCONST(line
);
149 enum n_shexp_state shs
;
151 if((shs
= n_shexp_parse_token(&store
, &input
, n_SHEXP_PARSE_LOG
)) &
152 n_SHEXP_STATE_ERR_MASK
){
153 /* Simply ignore Unicode error, just keep the normalized \[Uu] */
154 if((shs
& n_SHEXP_STATE_ERR_MASK
) != n_SHEXP_STATE_ERR_UNICODE
){
160 if(shs
& n_SHEXP_STATE_OUTPUT
){
161 if(shs
& n_SHEXP_STATE_CONTROL
)
162 pstate
|= PS_WYSHLIST_SAW_CONTROL
;
164 res_dat
[res_no
++] = n_string_cp(&store
);
165 n_string_drop_ownership(&store
);
168 if(shs
& n_SHEXP_STATE_STOP
)
174 n_string_gut(&store
);
178 res_dat
[(size_t)res_no
] = NULL
;
185 n_cmd_arg_parse(struct n_cmd_arg_ctx
*cacp
){
186 struct n_cmd_arg ncap
, *lcap
;
187 struct str shin_orig
, shin
;
189 size_t cad_no
, parsed_args
;
190 struct n_cmd_arg_desc
const *cadp
;
193 assert(cacp
->cac_inlen
== 0 || cacp
->cac_indat
!= NULL
);
194 assert(cacp
->cac_desc
->cad_no
> 0);
197 bool_t opt_seen
= FAL0
;
199 for(cadp
= cacp
->cac_desc
, cad_no
= 0; cad_no
< cadp
->cad_no
; ++cad_no
){
200 assert(cadp
->cad_ent_flags
[cad_no
][0] & n__CMD_ARG_DESC_TYPE_MASK
);
202 (cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
));
203 if(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
)
205 assert(!(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_GREEDY
) ||
206 cad_no
+ 1 == cadp
->cad_no
);
211 shin
.s
= UNCONST(cacp
->cac_indat
); /* "logical" only */
212 shin
.l
= (cacp
->cac_inlen
== UIZ_MAX
? strlen(shin
.s
) : cacp
->cac_inlen
);
215 cacp
->cac_arg
= lcap
= NULL
;
218 for(cadp
= cacp
->cac_desc
, cad_no
= 0; shin
.l
> 0 && cad_no
< cadp
->cad_no
;
221 memset(&ncap
, 0, sizeof ncap
);
222 ncap
.ca_indat
= shin
.s
;
223 /* >ca_inline once we know */
224 memcpy(&ncap
.ca_ent_flags
[0], &cadp
->cad_ent_flags
[cad_no
][0],
225 sizeof ncap
.ca_ent_flags
);
228 switch(ncap
.ca_ent_flags
[0] & n__CMD_ARG_DESC_TYPE_MASK
){
229 case n_CMD_ARG_DESC_STRING
:{
230 char /*const*/ *cp
= shin
.s
;
233 while(i
> 0 && blankspacechar(*cp
))
236 ncap
.ca_arg
.ca_str
.s
= cp
;
237 while(i
> 0 && !blankspacechar(*cp
))
239 ncap
.ca_arg
.ca_str
.s
= savestrbuf(ncap
.ca_arg
.ca_str
.s
,
240 ncap
.ca_arg
.ca_str
.l
= PTR2SIZE(cp
- ncap
.ca_arg
.ca_str
.s
));
242 while(i
> 0 && blankspacechar(*cp
))
244 ncap
.ca_inlen
= PTR2SIZE(cp
- ncap
.ca_indat
);
250 case n_CMD_ARG_DESC_WYSH
:{
251 struct n_string shou
, *shoup
;
252 enum n_shexp_state shs
;
254 shoup
= n_string_creat_auto(&shou
);
256 shs
= n_shexp_parse_token(shoup
, &shin
, ncap
.ca_ent_flags
[1] |
257 n_SHEXP_PARSE_TRIMSPACE
| n_SHEXP_PARSE_LOG
);
258 ncap
.ca_inlen
= PTR2SIZE(shin
.s
- ncap
.ca_indat
);
259 if((shs
& (n_SHEXP_STATE_OUTPUT
| n_SHEXP_STATE_ERR_MASK
)) ==
260 n_SHEXP_STATE_OUTPUT
){
261 ncap
.ca_arg
.ca_str
.s
= n_string_cp(shoup
);
262 ncap
.ca_arg
.ca_str
.l
= shou
.s_len
;
263 shoup
= n_string_drop_ownership(shoup
);
267 if(shs
& n_SHEXP_STATE_ERR_MASK
)
269 if((shs
& n_SHEXP_STATE_STOP
) && /* XXX delay if output */
270 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_HONOUR_STOP
)){
271 if(!(shs
& n_SHEXP_STATE_OUTPUT
))
281 struct n_cmd_arg
*cap
;
283 cap
= salloc(sizeof *cap
);
284 memcpy(cap
, &ncap
, sizeof ncap
);
296 if(shin
.l
> 0 && (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
))
300 if(cad_no
< cadp
->cad_no
&&
301 !(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
))
306 return (lcap
!= NULL
);
311 for(i
= 0; (i
< cadp
->cad_no
&&
312 !(cadp
->cad_ent_flags
[i
][0] & n_CMD_ARG_DESC_OPTION
)); ++i
)
315 n_err(_("`%s': parsing stopped after %" PRIuZ
" arguments "
316 "(need %" PRIuZ
"%s)\n"
319 cadp
->cad_name
, parsed_args
, i
, (i
== cadp
->cad_no
? "" : "+"),
320 (int)shin_orig
.l
, shin_orig
.s
,
321 (int)shin
.l
, shin
.s
);
328 n_cmd_arg_join_greedy(struct n_cmd_arg_ctx
const *cacp
, struct n_string
*store
){
329 struct n_cmd_arg
*cap
;
332 for(cap
= cacp
->cac_arg
;
333 (cap
!= NULL
&& !(cap
->ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
));
336 /* Can only join strings */
337 assert(cap
== NULL
||
338 (cap
->ca_ent_flags
[0] & (n_CMD_ARG_DESC_STRING
| n_CMD_ARG_DESC_WYSH
)));
341 store
= n_string_push_buf(store
,
342 cap
->ca_arg
.ca_str
.s
, cap
->ca_arg
.ca_str
.l
);
343 if((cap
= cap
->ca_next
) != NULL
)
344 store
= n_string_push_c(store
, ' ');