1 /*        $NetBSD: parse_cross.c,v 1.1.1.1 2024/06/11 09:15:38 wiz Exp $        */
2 
3 #if HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 #include <nbcompat.h>
7 #if HAVE_SYS_CDEFS_H
8 #include <sys/cdefs.h>
9 #endif
10 __RCSID("$NetBSD: parse_cross.c,v 1.1.1.1 2024/06/11 09:15:38 wiz Exp $");
11 
12 #if HAVE_ERR_H
13 #include <err.h>
14 #endif
15 #include "lib.h"
16 #include "add.h"
17 
18 /*
19  * ${OPSYS}/${MACHINE_ARCH} ${OS_VERSION}
20  *
21  * or just
22  *
23  * ${MACHINE_ARCH}
24  */
25 void
parse_cross(const char * text,char ** machine_arch,char ** opsys,char ** os_version)26 parse_cross(const char *text, char **machine_arch, char **opsys,
27     char **os_version)
28 {
29           static const char safeset[] = /* XXX */
30               "abcdefghijklmnopqrstuvwxyz"
31               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
32               "0123456789"
33               "-._";
34           char *copy = xstrdup(text);
35           char *p = copy, *q, *r;
36 
37           /*
38            * If there's no /, treat it as a single MACHINE_ARCH.
39            */
40           if ((q = strchr(p, '/')) == NULL) {
41                     *machine_arch = copy;
42                     *opsys = NULL;
43                     *os_version = NULL;
44           } else {
45                     /*
46                      * NUL-terminate at the slash so p := text[0..slash)
47                      * is the OPSYS.
48                      */
49                     *q++ = '\0';
50 
51                     /*
52                      * If there's no SPC, fail.
53                      */
54                     if (*(r = strchr(q, ' ')) == '\0') {
55                               goto fail;
56                     }
57 
58                     /*
59                      * NUL-terminate at the space so
60                      *
61                      *        q := text(slash..space)
62                      *
63                      * is the MACHINE_ARCH.
64                      */
65                     *r++ = '\0';
66 
67                     /*
68                      * The rest is already NUL-terminated, so
69                      *
70                      *        r := text(space..NUL)
71                      *
72                      * is the OS_VERSION.
73                      */
74                     *opsys = p;
75                     *machine_arch = q;
76                     *os_version = r;
77           }
78 
79           /*
80            * Verify that MACHINE_ARCH, and, if specified, OPSYS and
81            * OS_VERSION lie within the safe set, so we can reserve large
82            * amounts of the space of inputs for additional syntax.
83            * Ideally we would impose more constraints here with a
84            * regular expression to restrict the space even more, but
85            * this'll do for now.
86            */
87           if ((*machine_arch)[strspn(*machine_arch, safeset)] != '\0') {
88                     goto fail;
89           }
90           if (*opsys != NULL && (*opsys)[strspn(*opsys, safeset)] != '\0') {
91                     goto fail;
92           }
93           if (*os_version != NULL &&
94               (*os_version)[strspn(*os_version, safeset)] != '\0') {
95                     goto fail;
96           }
97           return;
98 
99 fail:     errx(1, "Invalid -m argument: ${OPSYS}/${MACHINE_ARCH} ${OS_VERSION}");
100 }
101