[scripting] Modified if-else-fi branches
[people/lynusvaz/gpxe.git] / src / hci / parse.c
1 #include <gpxe/parse.h>
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <gpxe/settings.h>
7
8 const struct char_table arith_table[21] = {
9         { .token = '\\', .type = FUNC, .next.parse_func = parse_escape },
10         { .token = '"', .type = TABLE, .next = { .next_table = { .ntable = dquote_table, .len = 3 } } },
11         { .token = '$', .type = FUNC, .next.parse_func = dollar_expand },
12         { .token = '\'', .type = TABLE, .next = { .next_table = { .ntable = squote_table, .len = 1 } } },
13         { .token = ' ', .type = ENDQUOTES },
14         { .token = '\t', .type = ENDQUOTES },
15         { .token = '~', .type = ENDTOK },
16         { .token = '!', .type = ENDTOK },
17         { .token = '*', .type = ENDTOK },
18         { .token = '/', .type = ENDTOK },
19         { .token = '%', .type = ENDTOK },
20         { .token = '+', .type = ENDTOK },
21         { .token = '-', .type = ENDTOK },
22         { .token = '<', .type = ENDTOK },
23         { .token = '=', .type = ENDTOK },
24         { .token = '>', .type = ENDTOK },
25         { .token = '&', .type = ENDTOK },
26         { .token = '|', .type = ENDTOK },
27         { .token = '^', .type = ENDTOK },
28         { .token = '(', .type = ENDTOK },
29         { .token = ')', .type = ENDTOK }
30 };
31
32 const struct char_table dquote_table[3] = {
33         { .token = '"', .type = ENDQUOTES },
34         { .token = '$', .type = FUNC, .next.parse_func = dollar_expand },
35         { .token = '\\', .type = FUNC, .next.parse_func = parse_escape }
36 };
37 const struct char_table squote_table[1] = {
38         { .token = '\'', .type = ENDQUOTES }
39 };
40 const struct char_table table[6] = {
41         { .token = '\\', .type = FUNC, .next.parse_func = parse_escape },
42         { .token = '"', .type = TABLE, .next = 
43                                 {.next_table = { .ntable = dquote_table, .len = 3 } } },
44         { .token = '$', .type = FUNC, .next.parse_func = dollar_expand },
45         { .token = '\'', .type = TABLE, .next = { .next_table = { .ntable = squote_table, .len = 1 } } },
46         { .token = ' ', .type = ENDTOK },
47         { .token = '\t', .type = ENDTOK }
48 };
49
50 char * string3cat ( struct string *s1, const char *s2, const char *s3 ) { /* The len is the size of s1 */
51         char *tmp = s1->value;
52         asprintf ( &s1->value, "%s%s%s", s1->value, s2, s3 );
53         free ( tmp );
54         return s1->value;
55 }
56
57 char * stringcpy ( struct string *s1, const char *s2 ) {
58         char *tmp = s1->value;
59         s1->value = strdup ( s2 );
60         free ( tmp );
61         return s1->value;
62 }
63
64 char * stringcat ( struct string *s1, const char *s2 ) {
65         char *tmp;
66         if ( !s1->value )
67                 return stringcpy ( s1, s2 );
68         if ( !s2 )
69                 return s1->value;
70         tmp = s1->value;
71         asprintf ( &s1->value, "%s%s", s1->value, s2 );
72         free ( tmp );
73         return s1->value;
74 }
75
76 void free_string ( struct string *s ) {
77         free ( s->value );
78         s->value = NULL;
79 }
80
81 /* struct string *s:
82 Before: [start]$<exp>[end]
83 End: [start]<expanded>[end]
84 and *end points to [end]
85 */
86 char * dollar_expand ( struct string *s, char *inp, char ** end ) {
87         char *name;
88         int setting_len;
89         int len;
90         
91         len = inp - s->value;
92         
93         if ( inp[1] == '{' ) {
94                 *inp = 0;
95                 name = ( inp + 2 );
96
97                 /* Locate closer */
98                 *end = strstr ( name, "}" );
99                 if ( ! *end ) {
100                         printf ( "can't find ending }\n" );
101                         free_string ( s );
102                         return NULL;
103                 }
104                 **end = 0;
105                 *end += 1;
106                 
107                 /* Determine setting length */
108                 setting_len = fetchf_named_setting ( name, NULL, 0 );
109                 if ( setting_len < 0 )
110                         setting_len = 0; /* Treat error as empty setting */
111
112                 /* Read setting into buffer */
113                 {
114                         char expdollar[setting_len + 1];
115                         expdollar[0] = '\0';
116                         fetchf_named_setting ( name, expdollar,
117                                                         setting_len + 1 );
118                         if ( string3cat ( s, expdollar, *end ) )
119                                 *end = s->value + len + strlen ( expdollar );
120                 }
121                 return s->value;
122         } else if ( inp[1] == '(' ) {
123                 name = inp;
124                 {
125                         int ret;
126                         ret = parse_arith ( s, name, end );
127                         return s->value;                /* if ret < 0, s->value = NULL */
128                 }
129         }
130         /* Can't find { or (, so preserve the $ */
131         *end = inp + 1;
132         return s->value;
133 }
134
135 char * parse_escape ( struct string *s, char *input, char **end ) {
136         char *exp;
137         if ( ! input[1] ) {
138                 printf ( "stray \\\n" );
139                 return s->value;
140         }
141         *input = 0;
142         *end = input + 2;
143         if ( input[1] == '\n' ) {
144                 int len = input - s->value;
145                 exp = stringcat ( s, *end );
146                 *end = exp + len;
147         } else {
148                 int len = input - s->value;
149                 *end = input + 1;
150                 exp = stringcat ( s, *end );
151                 *end = exp + len + 1;
152         }
153         return exp;
154 }
155
156 /* Both *head and *end point somewhere within s */
157 char * expand_string ( struct string *s, char **head, char **end, const struct char_table *table, int tlen, int in_quotes, int *success ) {
158         int i;
159         char *cur_pos;
160         int start;
161         
162         *success = 0;
163         start = *head - s->value;
164         cur_pos = *head;
165         
166         while ( *cur_pos ) {
167                 const struct char_table * tline = NULL;
168                 
169                 for ( i = 0; i < tlen; i++ ) {
170                         if ( table[i].token == *cur_pos ) {
171                                 tline = table + i;
172                                 break;
173                         }
174                 }
175                 
176                 if ( ! tline ) {
177                         *success = 1;
178                         cur_pos++;
179                 } else {
180                         switch ( tline->type ) {
181                                 case ENDQUOTES: /* 0 for end of input, where next char is to be discarded. Used for ending ' or " */
182                                 {
183                                         int pos = cur_pos - s->value;
184                                         char *t;
185                                         *success = 1;
186                                         *cur_pos = 0;
187                                         if ( ( t = stringcat ( s, cur_pos + 1 ) ) ) {
188                                                 *end = t + pos;
189                                                 *head = s->value + start;
190                                         }
191                                         return s->value;
192                                 }
193                                         break;
194                                 case TABLE: /* 1 for recursive call. Probably found quotes */
195                                         {
196                                                 int s2;
197                                                 char *end;
198                                                 char *tmp;
199                                                 
200                                                 *success = 1;
201                                                 *cur_pos = 0;
202                                                 tmp = s->value;
203                                                 
204                                                 if ( !stringcat ( s, cur_pos + 1 ) ) {
205                                                         printf ( "stringcat failed\n" );
206                                                         return NULL;
207                                                 }
208                                                 /* tmp is now invalid. Should not be dereferenced */
209                                                 cur_pos = s->value + ( cur_pos - tmp );
210                                                 if ( !expand_string ( s, &cur_pos, &end, tline->next.next_table.ntable, tline->next.next_table.len, 1, &s2 ) ) {
211                                                         return NULL;
212                                                 }
213                                                 cur_pos = end;
214                                         }
215                                         break;
216                                 case FUNC: /* Call another function */
217                                         {
218                                                 *success = 1;
219                                                 if ( ! tline->next.parse_func ( s, cur_pos, &cur_pos ) )
220                                                         return NULL;
221                                         }
222                                         break;
223                                         
224                                 case ENDTOK: /* End of input, and we also want next character */
225                                         *end = cur_pos;
226                                         *head = s->value + start;
227                                         return s->value;
228                                         break;
229                         }
230                 }
231                 
232         }
233         if ( in_quotes ) {
234                 printf ( "can't find closing '%c'\n", table[0].token );
235                 free_string ( s );
236                 return NULL;
237         }
238         *end = cur_pos;
239         *head = s->value + start;
240         return s->value;
241 }