Make combinetime() overflow safe (Vincent Lefevre)..
[s-mailx.git] / cmd_arg.c
blobb9bde93054a69e8a56a2f4905e00d4a0b7aa7768
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_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){
155 res_no = -1;
156 break;
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)
169 break;
171 line = input.s;
174 n_string_gut(&store);
177 if(res_no >= 0)
178 res_dat[(size_t)res_no] = NULL;
179 jleave:
180 NYD_LEAVE;
181 return res_no;
184 FL bool_t
185 n_cmd_arg_parse(struct n_cmd_arg_ctx *cacp){
186 struct n_cmd_arg ncap, *lcap;
187 struct str shin_orig, shin;
188 bool_t addca;
189 size_t cad_no, parsed_args;
190 struct n_cmd_arg_desc const *cadp;
191 NYD_ENTER;
193 assert(cacp->cac_inlen == 0 || cacp->cac_indat != NULL);
194 assert(cacp->cac_desc->cad_no > 0);
195 #ifdef HAVE_DEBUG
196 /* C99 */{
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);
201 assert(!opt_seen ||
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)
204 opt_seen = TRU1;
205 assert(!(cadp->cad_ent_flags[cad_no][0] & n_CMD_ARG_DESC_GREEDY) ||
206 cad_no + 1 == cadp->cad_no);
209 #endif
211 shin.s = UNCONST(cacp->cac_indat); /* "logical" only */
212 shin.l = (cacp->cac_inlen == UIZ_MAX ? strlen(shin.s) : cacp->cac_inlen);
213 shin_orig = shin;
214 cacp->cac_no = 0;
215 cacp->cac_arg = lcap = NULL;
217 parsed_args = 0;
218 for(cadp = cacp->cac_desc, cad_no = 0; shin.l > 0 && cad_no < cadp->cad_no;
219 ++cad_no){
220 jredo:
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);
226 addca = FAL0;
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;
231 size_t i = shin.l;
233 while(i > 0 && blankspacechar(*cp))
234 ++cp, --i;
236 ncap.ca_arg.ca_str.s = cp;
237 while(i > 0 && !blankspacechar(*cp))
238 ++cp, --i;
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))
243 ++cp, --i;
244 ncap.ca_inlen = PTR2SIZE(cp - ncap.ca_indat);
245 shin.s = cp;
246 shin.l = i;
247 addca = TRU1;
248 } break;
249 default:
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);
255 ncap.ca_arg_flags =
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);
265 n_string_gut(shoup);
267 if(shs & n_SHEXP_STATE_ERR_MASK)
268 goto jerr;
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))
272 goto jleave;
273 addca = TRUM1;
274 }else
275 addca = TRU1;
276 } break;
278 ++parsed_args;
280 if(addca){
281 struct n_cmd_arg *cap;
283 cap = salloc(sizeof *cap);
284 memcpy(cap, &ncap, sizeof ncap);
285 if(lcap == NULL)
286 cacp->cac_arg = cap;
287 else
288 lcap->ca_next = cap;
289 lcap = cap;
290 ++cacp->cac_no;
292 if(addca == TRUM1)
293 goto jleave;
296 if(shin.l > 0 && (ncap.ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY))
297 goto jredo;
300 if(cad_no < cadp->cad_no &&
301 !(cadp->cad_ent_flags[cad_no][0] & n_CMD_ARG_DESC_OPTION))
302 goto jerr;
304 jleave:
305 NYD_LEAVE;
306 return (lcap != NULL);
308 jerr:{
309 size_t i;
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"
317 " Input: %.*s\n"
318 " Stopped: %.*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);
323 lcap = NULL;
324 goto jleave;
327 FL struct n_string *
328 n_cmd_arg_join_greedy(struct n_cmd_arg_ctx const *cacp, struct n_string *store){
329 struct n_cmd_arg *cap;
330 NYD_ENTER;
332 for(cap = cacp->cac_arg;
333 (cap != NULL && !(cap->ca_ent_flags[0] & n_CMD_ARG_DESC_GREEDY));
334 cap = cap->ca_next)
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)));
340 while(cap != NULL){
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, ' ');
346 NYD_LEAVE;
347 return store;
350 /* s-it-mode */