Suporta comentários.
[cascardo/declara.git] / lib / token.c
1 /*
2  *  Copyright (C) 2015  Thadeu Lima de Souza Cascardo <cascardo@cascardo.eti.br>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include "token.h"
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 static char * token_unescape(char *start, char *end)
25 {
26         int escape = 0;
27         char *s, *r, *n;
28         s = n = malloc(end - start);
29         if (!s)
30                 return NULL;
31         for (r = start + 1; r < end; r++) {
32                 if (escape) {
33                         *s = *r;
34                         s++;
35                         escape = 0;
36                 } else if (*r == '\\') {
37                         escape = 1;
38                 } else {
39                         *s = *r;
40                         s++;
41                 }
42         }
43         *s = 0;
44         return n;
45 }
46
47 static char * token_next(char *line, char **end)
48 {
49         char *start;
50         char *lend;
51         char cend;
52         int escape = 0;
53         start = line;
54         while (isspace(*start))
55                 start++;
56         if (*start == '#') {
57                 lend = start;
58         } else if (*start == '"') {
59                 lend = start + 1;
60                 while (*lend) {
61                         cend = *lend;
62                         lend++;
63                         if (escape)
64                                 escape = 0;
65                         else if (cend == '\\')
66                                 escape = 1;
67                         else if (cend == '"')
68                                 break;
69                 }
70         } else {
71                 lend = start;
72                 while (*lend && !isspace(*lend))
73                         lend++;
74         }
75         *end = lend;
76         return start;
77 }
78
79 char ** tokens_new(char *line)
80 {
81         char **args = NULL;
82         size_t alloc = 0;
83         size_t next = 0;
84         const int inc = 8;
85         int more = 0;
86         char *start;
87         char *end;
88         int i;
89         end = line;
90         do {
91                 start = token_next(end, &end);
92                 if (start == end)
93                         break;
94                 if (next <= alloc) {
95                         char **nargs;
96                         alloc += inc;
97                         nargs = realloc(args, alloc * sizeof(char *));
98                         if (!nargs) {
99                                 for (i = 0; i < alloc - inc; i++)
100                                         if (args[i])
101                                                 free(args[i]);
102                                 free(args);
103                                 return NULL;
104                         }
105                         for (i = alloc - inc; i < alloc; i++)
106                                 nargs[i] = NULL;
107                         args = nargs;
108                 }
109                 if (*start == '"') {
110                         args[next] = token_unescape(start, end - 1);
111                 } else {
112                         args[next] = strndup(start, end - start);
113                 }
114                 if (!args[next]) {
115                         for (i = 0; i < alloc; i++)
116                                 if (args[i])
117                                         free(args[i]);
118                         free(args);
119                         return NULL;
120                 }
121                 next++;
122         } while (1);
123         return args;
124 }
125
126 void tokens_free(char **args)
127 {
128         char **arg;
129         for (arg = args; *arg; arg++)
130                 free(*arg);
131         free(args);
132 }