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 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
= 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
;
132 n_string_creat_auto(&store
);
133 input
.s
= n_UNCONST(line
);
137 for(; blankchar(*line
); ++line
)
142 if(UICMP(z
, res_no
, >=, res_size
)){
143 n_err(_("Too many input tokens for result storage\n"));
148 input
.l
-= PTR2SIZE(line
- input
.s
);
149 input
.s
= n_UNCONST(line
);
151 enum n_shexp_state shs
;
153 if((shs
= n_shexp_parse_token(&store
, &input
, n_SHEXP_PARSE_LOG
)) &
154 n_SHEXP_STATE_ERR_MASK
){
155 /* Simply ignore Unicode error, just keep the normalized \[Uu] */
156 if((shs
& n_SHEXP_STATE_ERR_MASK
) != n_SHEXP_STATE_ERR_UNICODE
){
162 if(shs
& n_SHEXP_STATE_OUTPUT
){
163 if(shs
& n_SHEXP_STATE_CONTROL
)
164 pstate
|= PS_WYSHLIST_SAW_CONTROL
;
166 res_dat
[res_no
++] = n_string_cp(&store
);
167 n_string_drop_ownership(&store
);
170 if(shs
& n_SHEXP_STATE_STOP
)
176 n_string_gut(&store
);
180 res_dat
[(size_t)res_no
] = NULL
;
187 n_cmd_arg_parse(struct n_cmd_arg_ctx
*cacp
){
188 struct n_cmd_arg ncap
, *lcap
;
189 struct str shin_orig
, shin
;
191 size_t cad_no
, parsed_args
;
192 struct n_cmd_arg_desc
const *cadp
;
195 assert(cacp
->cac_inlen
== 0 || cacp
->cac_indat
!= NULL
);
196 assert(cacp
->cac_desc
->cad_no
> 0);
199 bool_t opt_seen
= FAL0
;
201 for(cadp
= cacp
->cac_desc
, cad_no
= 0; cad_no
< cadp
->cad_no
; ++cad_no
){
202 assert(cadp
->cad_ent_flags
[cad_no
][0] & n__CMD_ARG_DESC_TYPE_MASK
);
204 (cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
));
205 if(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
)
207 assert(!(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_GREEDY
) ||
208 cad_no
+ 1 == cadp
->cad_no
);
213 shin
.s
= n_UNCONST(cacp
->cac_indat
); /* "logical" only */
214 shin
.l
= (cacp
->cac_inlen
== UIZ_MAX
? strlen(shin
.s
) : cacp
->cac_inlen
);
217 cacp
->cac_arg
= lcap
= NULL
;
220 for(cadp
= cacp
->cac_desc
, cad_no
= 0; shin
.l
> 0 && cad_no
< cadp
->cad_no
;
223 memset(&ncap
, 0, sizeof ncap
);
224 ncap
.ca_indat
= shin
.s
;
225 /* >ca_inline once we know */
226 memcpy(&ncap
.ca_ent_flags
[0], &cadp
->cad_ent_flags
[cad_no
][0],
227 sizeof ncap
.ca_ent_flags
);
230 switch(ncap
.ca_ent_flags
[0] & n__CMD_ARG_DESC_TYPE_MASK
){
231 case n_CMD_ARG_DESC_STRING
:{
232 char /*const*/ *cp
= shin
.s
;
235 while(i
> 0 && blankspacechar(*cp
))
238 ncap
.ca_arg
.ca_str
.s
= cp
;
239 while(i
> 0 && !blankspacechar(*cp
))
241 ncap
.ca_arg
.ca_str
.s
= savestrbuf(ncap
.ca_arg
.ca_str
.s
,
242 ncap
.ca_arg
.ca_str
.l
= PTR2SIZE(cp
- ncap
.ca_arg
.ca_str
.s
));
244 while(i
> 0 && blankspacechar(*cp
))
246 ncap
.ca_inlen
= PTR2SIZE(cp
- ncap
.ca_indat
);
252 case n_CMD_ARG_DESC_WYSH
:{
253 struct n_string shou
, *shoup
;
254 enum n_shexp_state shs
;
256 shoup
= n_string_creat_auto(&shou
);
258 shs
= n_shexp_parse_token(shoup
, &shin
, ncap
.ca_ent_flags
[1] |
259 n_SHEXP_PARSE_TRIMSPACE
| n_SHEXP_PARSE_LOG
);
260 ncap
.ca_inlen
= PTR2SIZE(shin
.s
- ncap
.ca_indat
);
261 if((shs
& (n_SHEXP_STATE_OUTPUT
| n_SHEXP_STATE_ERR_MASK
)) ==
262 n_SHEXP_STATE_OUTPUT
){
263 ncap
.ca_arg
.ca_str
.s
= n_string_cp(shoup
);
264 ncap
.ca_arg
.ca_str
.l
= shou
.s_len
;
265 shoup
= n_string_drop_ownership(shoup
);
269 if(shs
& n_SHEXP_STATE_ERR_MASK
)
271 if((shs
& n_SHEXP_STATE_STOP
) &&
272 (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_HONOUR_STOP
)){
273 if(!(shs
& n_SHEXP_STATE_OUTPUT
))
283 struct n_cmd_arg
*cap
;
285 cap
= salloc(sizeof *cap
);
286 memcpy(cap
, &ncap
, sizeof ncap
);
298 if(shin
.l
> 0 && (ncap
.ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
))
302 if(cad_no
< cadp
->cad_no
&&
303 !(cadp
->cad_ent_flags
[cad_no
][0] & n_CMD_ARG_DESC_OPTION
))
308 return (lcap
!= NULL
);
313 for(i
= 0; (i
< cadp
->cad_no
&&
314 !(cadp
->cad_ent_flags
[i
][0] & n_CMD_ARG_DESC_OPTION
)); ++i
)
317 n_err(_("`%s': parsing stopped after %" PRIuZ
" arguments "
318 "(need %" PRIuZ
"%s)\n"
321 cadp
->cad_name
, parsed_args
, i
, (i
== cadp
->cad_no
? n_empty
: "+"),
322 (int)shin_orig
.l
, shin_orig
.s
,
323 (int)shin
.l
, shin
.s
);
330 n_cmd_arg_join_greedy(struct n_cmd_arg_ctx
const *cacp
, struct n_string
*store
){
331 struct n_cmd_arg
*cap
;
334 for(cap
= cacp
->cac_arg
;
335 (cap
!= NULL
&& !(cap
->ca_ent_flags
[0] & n_CMD_ARG_DESC_GREEDY
));
338 /* Can only join strings */
339 assert(cap
== NULL
||
340 (cap
->ca_ent_flags
[0] & (n_CMD_ARG_DESC_STRING
| n_CMD_ARG_DESC_WYSH
)));
343 store
= n_string_push_buf(store
,
344 cap
->ca_arg
.ca_str
.s
, cap
->ca_arg
.ca_str
.l
);
345 if((cap
= cap
->ca_next
) != NULL
)
346 store
= n_string_push_c(store
, ' ');