1/**
2 * \file
3 * \brief Commandline parameter parsing.
4 */
5
6/*
7 * Copyright (c) 2007, 2008, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <string.h>
17#include <stdlib.h>
18#include <stdbool.h>
19#include <assert.h>
20#include <limits.h>
21#include <getopt/getopt.h>
22
23static int handle_ulong(unsigned long *var, const char *val)
24{
25    assert(var != NULL);
26    int base;
27
28    // determine base (0x -> hex, anything else -> decimal)
29    if (val[0] == '0' && val[1] == 'x') {
30        base = 16;
31        val += 2;
32    } else {
33        base = 10;
34    }
35
36    unsigned long long x = strtoull(val, NULL, base);
37    if (x > ULONG_MAX) {
38        x = ULONG_MAX;
39    }
40    *var = (unsigned long)x;
41    return 0;
42}
43
44static int handle_long(long *var, const char *val)
45{
46    assert(var != NULL);
47    int base;
48
49    // determine base (0x -> hex, anything else -> decimal)
50    if (val[0] == '0' && val[1] == 'x') {
51        base = 16;
52        val += 2;
53    } else {
54        base = 10;
55    }
56
57    long long x = strtoll(val, NULL, base);
58    assert(x >= LONG_MIN && x <= LONG_MAX); // XXX
59    *var = x;
60
61    return 0;
62}
63
64static int handle_uint(unsigned int *var, const char *val)
65{
66    assert(var != NULL);
67    int base;
68
69    // determine base (0x -> hex, anything else -> decimal)
70    if (val[0] == '0' && val[1] == 'x') {
71        base = 16;
72        val += 2;
73    } else {
74        base = 10;
75    }
76
77    unsigned long x = strtoul(val, NULL, base);
78    if (x > UINT_MAX) {
79	x = UINT_MAX;
80    }
81    *var = (unsigned)x;
82    return 0;
83}
84
85static int handle_int(int *var, const char *val)
86{
87    assert(var != NULL);
88    int base;
89
90    // determine base (0x -> hex, anything else -> decimal)
91    if (val[0] == '0' && val[1] == 'x') {
92        base = 16;
93        val += 2;
94    } else {
95        base = 10;
96    }
97
98    long x = strtol(val, NULL, base);
99    assert(x >= INT_MIN && x <= INT_MAX); // XXX
100    *var = (int)x;
101
102    return 0;
103}
104
105static int handle_bool(bool *var, const char *val)
106{
107    assert(var != NULL);
108    if(!strncmp(val, "true", 4) || !strncmp(val, "yes", 3)) {
109        *var = true;
110        return 0;
111    } else if(!strncmp(val, "false", 5) || !strncmp(val, "no", 2)) {
112        *var = false;
113        return 0;
114    }
115
116    return -1;
117}
118
119static int handle_argument(const char *var, const char *val,
120                           struct cmdarg *cmdargs)
121{
122    // compare var against array of recognized arguments
123    for(int i = 0; cmdargs[i].arg != NULL; i++) {
124        struct cmdarg *a = &cmdargs[i];
125        if(!strncmp(var, a->arg, strlen(a->arg))) {
126            switch(a->type) {
127            case ArgType_Int:
128                return handle_int(a->var.integer, val);
129
130            case ArgType_UInt:
131                return handle_uint(a->var.uinteger, val);
132
133            case ArgType_Long:
134                return handle_long(a->var.longinteger, val);
135
136            case ArgType_ULong:
137                return handle_ulong(a->var.ulonginteger, val);
138
139            case ArgType_Bool:
140                return handle_bool(a->var.boolean, val);
141
142            case ArgType_Custom:
143                return a->var.handler(var, val);
144
145            default:
146                assert(!"Unknown type %d in kernel argument array!");
147                return -1;
148            }
149        }
150    }
151
152    return 0;
153}
154
155/**
156 * \brief Search backwards for character 'c' in string 'p'.
157 *
158 * Starts at the character, pointed to by 'p', within a string and searches
159 * backwards for 'c', returning a pointer to its position.
160 *
161 * WARNING: 'c' MUST exist prior to calling this function!!
162 *
163 * \param p     Character to start searching backwards from.
164 * \param c     Character to look for.
165 *
166 * \return Pointer to 'c' in string.
167 */
168static const char *look_back(const char *p, int c)
169{
170    while(*p != c) {
171        p--;
172    }
173
174    return p;
175}
176
177/**
178 * \brief Parse commandline parameters in 'cmdline'.
179 *
180 * Parses 'cmdline' for kernel commandline parameters and sets configuration
181 * variables accordingly.
182 *
183 * \param cmdline       Kernel commandline string.
184 */
185void parse_commandline(const char *cmdline, struct cmdarg *cmdargs)
186{
187    // Parse argument string into whitespace-separated 'var=val' tokens
188    for(const char *p = strchr(cmdline, '='); p != NULL;
189        p = strchr(p + 1, '=')) {
190        const char *var = look_back(p, ' ') + 1, *val = p + 1;
191
192        if(handle_argument(var, val, cmdargs) != 0) {
193            assert(!"parse_commandline: Parse error in commandline");
194        }
195    }
196}
197