xref: /nrf52832-nimble/packages/NimBLE-latest/apps/btshell/src/parse.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *  http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 #include <string.h>
21 #include <stdlib.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include "host/ble_hs.h"
27 #include "host/ble_uuid.h"
28 #include "host/ble_eddystone.h"
29 #include "cmd.h"
30 #include "btshell.h"
31 
32 #include "nimble/npl_shell.h"
33 
34 #define CMD_MAX_ARGS        16
35 
36 static char *cmd_args[CMD_MAX_ARGS][2];
37 static int cmd_num_args;
38 
39 int
parse_arg_find_idx(const char * key)40 parse_arg_find_idx(const char *key)
41 {
42     int i;
43 
44     for (i = 0; i < cmd_num_args; i++) {
45         if (strcmp(cmd_args[i][0], key) == 0) {
46             return i;
47         }
48     }
49 
50     return -1;
51 }
52 
53 char *
parse_arg_peek(const char * key)54 parse_arg_peek(const char *key)
55 {
56     int i;
57 
58     for (i = 0; i < cmd_num_args; i++) {
59         if (strcmp(cmd_args[i][0], key) == 0) {
60             return cmd_args[i][1];
61         }
62     }
63 
64     return NULL;
65 }
66 
67 char *
parse_arg_extract(const char * key)68 parse_arg_extract(const char *key)
69 {
70     int i;
71 
72     for (i = 0; i < cmd_num_args; i++) {
73         if (strcmp(cmd_args[i][0], key) == 0) {
74             /* Erase parameter. */
75             cmd_args[i][0][0] = '\0';
76 
77             return cmd_args[i][1];
78         }
79     }
80 
81     return NULL;
82 }
83 
84 /**
85  * Determines which number base to use when parsing the specified numeric
86  * string.  This just avoids base '0' so that numbers don't get interpreted as
87  * octal.
88  */
89 static int
parse_arg_long_base(char * sval)90 parse_arg_long_base(char *sval)
91 {
92     if (sval[0] == '0' && sval[1] == 'x') {
93         return 0;
94     } else {
95         return 10;
96     }
97 }
98 
99 long
parse_long_bounds(char * sval,long min,long max,int * out_status)100 parse_long_bounds(char *sval, long min, long max, int *out_status)
101 {
102     char *endptr;
103     long lval;
104 
105     lval = strtol(sval, &endptr, parse_arg_long_base(sval));
106     if (sval[0] != '\0' && *endptr == '\0' &&
107         lval >= min && lval <= max) {
108 
109         *out_status = 0;
110         return lval;
111     }
112 
113     *out_status = EINVAL;
114     return 0;
115 }
116 
117 long
parse_arg_long_bounds_peek(char * name,long min,long max,int * out_status)118 parse_arg_long_bounds_peek(char *name, long min, long max, int *out_status)
119 {
120     char *sval;
121 
122     sval = parse_arg_peek(name);
123     if (sval == NULL) {
124         *out_status = ENOENT;
125         return 0;
126     }
127     return parse_long_bounds(sval, min, max, out_status);
128 }
129 
130 long
parse_arg_long_bounds(char * name,long min,long max,int * out_status)131 parse_arg_long_bounds(char *name, long min, long max, int *out_status)
132 {
133     char *sval;
134 
135     sval = parse_arg_extract(name);
136     if (sval == NULL) {
137         *out_status = ENOENT;
138         return 0;
139     }
140     return parse_long_bounds(sval, min, max, out_status);
141 }
142 
143 long
parse_arg_long_bounds_dflt(char * name,long min,long max,long dflt,int * out_status)144 parse_arg_long_bounds_dflt(char *name, long min, long max,
145                               long dflt, int *out_status)
146 {
147     long val;
148     int rc;
149 
150     val = parse_arg_long_bounds(name, min, max, &rc);
151     if (rc == ENOENT) {
152         rc = 0;
153         val = dflt;
154     }
155 
156     *out_status = rc;
157 
158     return val;
159 }
160 
161 uint64_t
parse_arg_uint64_bounds(char * name,uint64_t min,uint64_t max,int * out_status)162 parse_arg_uint64_bounds(char *name, uint64_t min, uint64_t max, int *out_status)
163 {
164     char *endptr;
165     char *sval;
166     uint64_t lval;
167 
168     sval = parse_arg_extract(name);
169     if (sval == NULL) {
170         *out_status = ENOENT;
171         return 0;
172     }
173 
174     lval = strtoull(sval, &endptr, parse_arg_long_base(sval));
175     if (sval[0] != '\0' && *endptr == '\0' &&
176         lval >= min && lval <= max) {
177 
178         *out_status = 0;
179         return lval;
180     }
181 
182     *out_status = EINVAL;
183     return 0;
184 }
185 
186 long
parse_arg_long(char * name,int * out_status)187 parse_arg_long(char *name, int *out_status)
188 {
189     return parse_arg_long_bounds(name, LONG_MIN, LONG_MAX, out_status);
190 }
191 
192 uint8_t
parse_arg_bool(char * name,int * out_status)193 parse_arg_bool(char *name, int *out_status)
194 {
195     return parse_arg_long_bounds(name, 0, 1, out_status);
196 }
197 
198 uint8_t
parse_arg_bool_dflt(char * name,uint8_t dflt,int * out_status)199 parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status)
200 {
201     return parse_arg_long_bounds_dflt(name, 0, 1, dflt, out_status);
202 }
203 
204 uint8_t
parse_arg_uint8(char * name,int * out_status)205 parse_arg_uint8(char *name, int *out_status)
206 {
207     return parse_arg_long_bounds(name, 0, UINT8_MAX, out_status);
208 }
209 
210 uint16_t
parse_arg_uint16(char * name,int * out_status)211 parse_arg_uint16(char *name, int *out_status)
212 {
213     return parse_arg_long_bounds(name, 0, UINT16_MAX, out_status);
214 }
215 
216 uint16_t
parse_arg_uint16_peek(char * name,int * out_status)217 parse_arg_uint16_peek(char *name, int *out_status)
218 {
219     return parse_arg_long_bounds_peek(name, 0, UINT16_MAX, out_status);
220 }
221 
222 uint32_t
parse_arg_uint32(char * name,int * out_status)223 parse_arg_uint32(char *name, int *out_status)
224 {
225     return parse_arg_uint64_bounds(name, 0, UINT32_MAX, out_status);
226 }
227 
228 uint64_t
parse_arg_uint64(char * name,int * out_status)229 parse_arg_uint64(char *name, int *out_status)
230 {
231     return parse_arg_uint64_bounds(name, 0, UINT64_MAX, out_status);
232 }
233 
234 uint8_t
parse_arg_uint8_dflt(char * name,uint8_t dflt,int * out_status)235 parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status)
236 {
237     uint8_t val;
238     int rc;
239 
240     val = parse_arg_uint8(name, &rc);
241     if (rc == ENOENT) {
242         val = dflt;
243         rc = 0;
244     }
245 
246     *out_status = rc;
247     return val;
248 }
249 
250 uint16_t
parse_arg_uint16_dflt(char * name,uint16_t dflt,int * out_status)251 parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status)
252 {
253     uint16_t val;
254     int rc;
255 
256     val = parse_arg_uint16(name, &rc);
257     if (rc == ENOENT) {
258         val = dflt;
259         rc = 0;
260     }
261 
262     *out_status = rc;
263     return val;
264 }
265 
266 uint32_t
parse_arg_uint32_dflt(char * name,uint32_t dflt,int * out_status)267 parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status)
268 {
269     uint32_t val;
270     int rc;
271 
272     val = parse_arg_uint32(name, &rc);
273     if (rc == ENOENT) {
274         val = dflt;
275         rc = 0;
276     }
277 
278     *out_status = rc;
279     return val;
280 }
281 
282 const struct kv_pair *
parse_kv_find(const struct kv_pair * kvs,char * name)283 parse_kv_find(const struct kv_pair *kvs, char *name)
284 {
285     const struct kv_pair *kv;
286     int i;
287 
288     for (i = 0; kvs[i].key != NULL; i++) {
289         kv = kvs + i;
290         if (strcmp(name, kv->key) == 0) {
291             return kv;
292         }
293     }
294 
295     return NULL;
296 }
297 
298 int
parse_arg_kv(char * name,const struct kv_pair * kvs,int * out_status)299 parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status)
300 {
301     const struct kv_pair *kv;
302     char *sval;
303 
304     sval = parse_arg_extract(name);
305     if (sval == NULL) {
306         *out_status = ENOENT;
307         return -1;
308     }
309 
310     kv = parse_kv_find(kvs, sval);
311     if (kv == NULL) {
312         *out_status = EINVAL;
313         return -1;
314     }
315 
316     *out_status = 0;
317     return kv->val;
318 }
319 
320 int
parse_arg_kv_dflt(char * name,const struct kv_pair * kvs,int def_val,int * out_status)321 parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
322                      int *out_status)
323 {
324     int val;
325     int rc;
326 
327     val = parse_arg_kv(name, kvs, &rc);
328     if (rc == ENOENT) {
329         rc = 0;
330         val = def_val;
331     }
332 
333     *out_status = rc;
334 
335     return val;
336 }
337 
338 
339 static int
parse_arg_byte_stream_delim(char * sval,char * delims,int max_len,uint8_t * dst,int * out_len)340 parse_arg_byte_stream_delim(char *sval, char *delims, int max_len,
341                             uint8_t *dst, int *out_len)
342 {
343     unsigned long ul;
344     char *endptr;
345     char *token;
346     int i;
347 
348     i = 0;
349     for (token = strtok(sval, delims);
350          token != NULL;
351          token = strtok(NULL, delims)) {
352 
353         if (i >= max_len) {
354             return EINVAL;
355         }
356 
357         ul = strtoul(token, &endptr, 16);
358         if (sval[0] == '\0' || *endptr != '\0' || ul > UINT8_MAX) {
359             return -1;
360         }
361 
362         dst[i] = ul;
363         i++;
364     }
365 
366     *out_len = i;
367 
368     return 0;
369 }
370 
371 int
parse_arg_byte_stream(char * name,int max_len,uint8_t * dst,int * out_len)372 parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len)
373 {
374     char *sval;
375 
376     sval = parse_arg_extract(name);
377     if (sval == NULL) {
378         return ENOENT;
379     }
380 
381     return parse_arg_byte_stream_delim(sval, ":-", max_len, dst, out_len);
382 }
383 
384 int
parse_arg_byte_stream_exact_length(char * name,uint8_t * dst,int len)385 parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len)
386 {
387     int actual_len;
388     int rc;
389 
390     rc = parse_arg_byte_stream(name, len, dst, &actual_len);
391     if (rc != 0) {
392         return rc;
393     }
394 
395     if (actual_len != len) {
396         return EINVAL;
397     }
398 
399     return 0;
400 }
401 
402 static void
parse_reverse_bytes(uint8_t * bytes,int len)403 parse_reverse_bytes(uint8_t *bytes, int len)
404 {
405     uint8_t tmp;
406     int i;
407 
408     for (i = 0; i < len / 2; i++) {
409         tmp = bytes[i];
410         bytes[i] = bytes[len - i - 1];
411         bytes[len - i - 1] = tmp;
412     }
413 }
414 
415 int
parse_arg_mac(char * name,uint8_t * dst)416 parse_arg_mac(char *name, uint8_t *dst)
417 {
418     int rc;
419 
420     rc = parse_arg_byte_stream_exact_length(name, dst, 6);
421     if (rc != 0) {
422         return rc;
423     }
424 
425     parse_reverse_bytes(dst, 6);
426 
427     return 0;
428 }
429 
430 int
parse_arg_uuid(char * str,ble_uuid_any_t * uuid)431 parse_arg_uuid(char *str, ble_uuid_any_t *uuid)
432 {
433     uint16_t uuid16;
434     uint8_t val[16];
435     int len;
436     int rc;
437 
438     uuid16 = parse_arg_uint16_peek(str, &rc);
439     switch (rc) {
440     case ENOENT:
441         parse_arg_extract(str);
442         return ENOENT;
443 
444     case 0:
445         len = 2;
446         val[0] = uuid16;
447         val[1] = uuid16 >> 8;
448         parse_arg_extract(str);
449         break;
450 
451     default:
452         len = 16;
453         rc = parse_arg_byte_stream_exact_length(str, val, 16);
454         if (rc != 0) {
455             return EINVAL;
456         }
457         parse_reverse_bytes(val, 16);
458         break;
459     }
460 
461     rc = ble_uuid_init_from_buf(uuid, val, len);
462     if (rc != 0) {
463         return EINVAL;
464     } else {
465         return 0;
466     }
467 }
468 
469 int
parse_arg_all(int argc,char ** argv)470 parse_arg_all(int argc, char **argv)
471 {
472     char *key;
473     char *val;
474     int i;
475 
476     cmd_num_args = 0;
477 
478     for (i = 0; i < argc; i++) {
479         key = strtok(argv[i], "=");
480         val = strtok(NULL, "=");
481 
482         if (key != NULL && val != NULL) {
483             if (strlen(key) == 0) {
484                 console_printf("Error: invalid argument: %s\n", argv[i]);
485                 return -1;
486             }
487 
488             if (cmd_num_args >= CMD_MAX_ARGS) {
489                 console_printf("Error: too many arguments");
490                 return -1;
491             }
492 
493             cmd_args[cmd_num_args][0] = key;
494             cmd_args[cmd_num_args][1] = val;
495             cmd_num_args++;
496         }
497     }
498 
499     return 0;
500 }
501 
502 int
parse_eddystone_url(char * full_url,uint8_t * out_scheme,char * out_body,uint8_t * out_body_len,uint8_t * out_suffix)503 parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
504                     uint8_t *out_body_len, uint8_t *out_suffix)
505 {
506     static const struct {
507         char *s;
508         uint8_t scheme;
509     } schemes[] = {
510         { "http://www.", BLE_EDDYSTONE_URL_SCHEME_HTTP_WWW },
511         { "https://www.", BLE_EDDYSTONE_URL_SCHEME_HTTPS_WWW },
512         { "http://", BLE_EDDYSTONE_URL_SCHEME_HTTP },
513         { "https://", BLE_EDDYSTONE_URL_SCHEME_HTTPS },
514     };
515 
516     static const struct {
517         char *s;
518         uint8_t code;
519     } suffixes[] = {
520         { ".com/", BLE_EDDYSTONE_URL_SUFFIX_COM_SLASH },
521         { ".org/", BLE_EDDYSTONE_URL_SUFFIX_ORG_SLASH },
522         { ".edu/", BLE_EDDYSTONE_URL_SUFFIX_EDU_SLASH },
523         { ".net/", BLE_EDDYSTONE_URL_SUFFIX_NET_SLASH },
524         { ".info/", BLE_EDDYSTONE_URL_SUFFIX_INFO_SLASH },
525         { ".biz/", BLE_EDDYSTONE_URL_SUFFIX_BIZ_SLASH },
526         { ".gov/", BLE_EDDYSTONE_URL_SUFFIX_GOV_SLASH },
527         { ".com", BLE_EDDYSTONE_URL_SUFFIX_COM },
528         { ".org", BLE_EDDYSTONE_URL_SUFFIX_ORG },
529         { ".edu", BLE_EDDYSTONE_URL_SUFFIX_EDU },
530         { ".net", BLE_EDDYSTONE_URL_SUFFIX_NET },
531         { ".info", BLE_EDDYSTONE_URL_SUFFIX_INFO },
532         { ".biz", BLE_EDDYSTONE_URL_SUFFIX_BIZ },
533         { ".gov", BLE_EDDYSTONE_URL_SUFFIX_GOV },
534     };
535 
536     char *prefix;
537     char *suffix;
538     int full_url_len;
539     int prefix_len;
540     int suffix_len;
541     int suffix_idx;
542     int rc;
543     int i;
544 
545     full_url_len = strlen(full_url);
546 
547     rc = BLE_HS_EINVAL;
548     for (i = 0; i < sizeof schemes / sizeof schemes[0]; i++) {
549         prefix = schemes[i].s;
550         prefix_len = strlen(schemes[i].s);
551 
552         if (full_url_len >= prefix_len &&
553             memcmp(full_url, prefix, prefix_len) == 0) {
554 
555             *out_scheme = i;
556             rc = 0;
557             break;
558         }
559     }
560     if (rc != 0) {
561         return rc;
562     }
563 
564     rc = BLE_HS_EINVAL;
565     for (i = 0; i < sizeof suffixes / sizeof suffixes[0]; i++) {
566         suffix = suffixes[i].s;
567         suffix_len = strlen(suffixes[i].s);
568 
569         suffix_idx = full_url_len - suffix_len;
570         if (suffix_idx >= prefix_len &&
571             memcmp(full_url + suffix_idx, suffix, suffix_len) == 0) {
572 
573             *out_suffix = i;
574             rc = 0;
575             break;
576         }
577     }
578     if (rc != 0) {
579         *out_suffix = BLE_EDDYSTONE_URL_SUFFIX_NONE;
580         *out_body_len = full_url_len - prefix_len;
581     } else {
582         *out_body_len = full_url_len - prefix_len - suffix_len;
583     }
584 
585     memcpy(out_body, full_url + prefix_len, *out_body_len);
586 
587     return 0;
588 }
589