]> git.draconx.ca Git - cdecl99.git/blob - t/normalize.c
e11e6484f94ba0ca6479678b8a02dd3249d488ec
[cdecl99.git] / t / normalize.c
1 /*
2  * Helper application to test normalization of declaration specifiers.
3  * Copyright © 2021-2024 Nick Bowler
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <getopt.h>
24
25 #include "cdecl.h"
26 #include "cdecl-internal.h"
27
28 #define PROGNAME "normalize"
29 #include "test.h"
30
31 static const char sopts[] = "f:VH";
32 static const struct option lopts[] = {
33         { "file", 1, NULL, 'f' },
34         { "version", 0, NULL, 'V' },
35         { "help", 0, NULL, 'H' },
36         { 0 }
37 };
38
39 static void print_usage(FILE *f)
40 {
41         fprintf(f, "Usage: %s [options]\n", progname);
42 }
43
44 static void print_help(void)
45 {
46         const struct option *opt;
47
48         print_usage(stdout);
49         puts("Test normalization of declaration specifiers.\n");
50         test_print_options(lopts);
51 }
52
53 static unsigned to_spectype(const char *tok)
54 {
55         if (!strcmp(tok, "typedef")) return CDECL_STOR_TYPEDEF;
56         if (!strcmp(tok, "extern")) return CDECL_STOR_EXTERN;
57         if (!strcmp(tok, "static")) return CDECL_STOR_STATIC;
58         if (!strcmp(tok, "auto")) return CDECL_STOR_AUTO;
59         if (!strcmp(tok, "register")) return CDECL_STOR_REGISTER;
60         if (!strcmp(tok, "inline")) return CDECL_FUNC_INLINE;
61         if (!strcmp(tok, "restrict")) return CDECL_QUAL_RESTRICT;
62         if (!strcmp(tok, "volatile")) return CDECL_QUAL_VOLATILE;
63         if (!strcmp(tok, "const")) return CDECL_QUAL_CONST;
64         if (!strcmp(tok, "void")) return CDECL_TYPE_VOID;
65         if (!strcmp(tok, "signed")) return CDECL_TYPE_SIGNED;
66         if (!strcmp(tok, "unsigned")) return CDECL_TYPE_UNSIGNED;
67         if (!strcmp(tok, "char")) return CDECL_TYPE_CHAR;
68         if (!strcmp(tok, "short")) return CDECL_TYPE_SHORT;
69         if (!strcmp(tok, "long")) return CDECL_TYPE_LONG;
70         if (!strcmp(tok, "int")) return CDECL_TYPE_INT;
71         if (!strcmp(tok, "float")) return CDECL_TYPE_FLOAT;
72         if (!strcmp(tok, "double")) return CDECL_TYPE_DOUBLE;
73         if (!strcmp(tok, "_Complex")) return CDECL_TYPE_COMPLEX;
74         if (!strcmp(tok, "_Imaginary")) return CDECL_TYPE_IMAGINARY;
75         if (!strcmp(tok, "_Bool")) return CDECL_TYPE_BOOL;
76         if (!strcmp(tok, "struct")) return CDECL_TYPE_STRUCT;
77         if (!strcmp(tok, "union")) return CDECL_TYPE_UNION;
78         if (!strcmp(tok, "enum")) return CDECL_TYPE_ENUM;
79
80         return CDECL_TYPE_IDENT;
81 }
82
83 static const char *to_specstring(unsigned type)
84 {
85         switch (type) {
86         case CDECL_STOR_TYPEDEF: return "typedef";
87         case CDECL_STOR_EXTERN: return "extern";
88         case CDECL_STOR_STATIC: return "static";
89         case CDECL_STOR_AUTO: return "auto";
90         case CDECL_STOR_REGISTER: return "register";
91         case CDECL_FUNC_INLINE: return "inline";
92         case CDECL_QUAL_RESTRICT: return "restrict";
93         case CDECL_QUAL_VOLATILE: return "volatile";
94         case CDECL_QUAL_CONST: return "const";
95         case CDECL_TYPE_VOID: return "void";
96         case CDECL_TYPE_SIGNED: return "signed";
97         case CDECL_TYPE_UNSIGNED: return "unsigned";
98         case CDECL_TYPE_CHAR: return "char";
99         case CDECL_TYPE_SHORT: return "short";
100         case CDECL_TYPE_LONG: return "long";
101         case CDECL_TYPE_INT: return "int";
102         case CDECL_TYPE_FLOAT: return "float";
103         case CDECL_TYPE_DOUBLE: return "double";
104         case CDECL_TYPE_COMPLEX: return "_Complex";
105         case CDECL_TYPE_IMAGINARY: return "_Imaginary";
106         case CDECL_TYPE_BOOL: return "_Bool";
107         case CDECL_TYPE_STRUCT: return "struct";
108         case CDECL_TYPE_UNION: return "union";
109         case CDECL_TYPE_ENUM: return "enum";
110         }
111
112         return "";
113 }
114
115 static void print_spec(struct cdecl_declspec *spec)
116 {
117         const char *s = to_specstring(spec->type);
118
119         printf("%s", s);
120         if (spec->ident) {
121                 if (s[0])
122                         putchar(' ');
123                 printf("%s", spec->ident);
124         }
125 }
126
127 int do_normalize(char *line, size_t n)
128 {
129         struct cdecl_declspec *s, *specs = NULL, **newspec = &specs;
130         char *tok = NULL;
131
132         while ((tok = strtok(tok ? NULL : line, " "))) {
133                 struct cdecl_declspec *spec = malloc_nofail(sizeof *spec);
134
135                 spec->next = NULL;
136                 spec->ident = NULL;
137                 spec->type = to_spectype(tok);
138                 switch (spec->type) {
139                 case CDECL_TYPE_STRUCT:
140                 case CDECL_TYPE_UNION:
141                 case CDECL_TYPE_ENUM:
142                         spec->ident = strtok(NULL, " ");
143                         break;
144                 case CDECL_TYPE_IDENT:
145                         spec->ident = tok;
146                 }
147
148                 *newspec = spec;
149                 newspec = &spec->next;
150         }
151
152         specs = cdecl__normalize_specs(specs);
153         for (s = specs; s; s = s->next) {
154                 print_spec(s);
155                 if (s->next)
156                         putchar(' ');
157         }
158         putchar('\n');
159
160         while (specs) {
161                 struct cdecl_declspec *c = specs;
162                 specs = specs->next;
163                 free(c);
164         }
165
166         return 0;
167 }
168
169 int main(int argc, char **argv)
170 {
171         int opt, ret = EXIT_SUCCESS;
172         char *filename = NULL, *line = NULL;
173         FILE *infile = stdin;
174         size_t n = 0;
175         ssize_t rc;
176
177         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
178                 switch (opt) {
179                 case 'f':
180                         filename = optarg;
181                         break;
182                 case 'V':
183                         test_print_version(PROGNAME);
184                         return EXIT_SUCCESS;
185                 case 'H':
186                         print_help();
187                         return EXIT_SUCCESS;
188                 default:
189                         print_usage(stderr);
190                         return EXIT_FAILURE;
191                 }
192         }
193
194         if (filename) {
195                 infile = fopen(filename, "r");
196                 if (!infile) {
197                         perror(filename);
198                         return EXIT_FAILURE;
199                 }
200         }
201
202         while ((rc = getline(&line, &n, infile)) >= 0) {
203                 if (rc > 0 && line[rc-1] == '\n')
204                         line[rc-1] = 0;
205                 if (do_normalize(line, n) < 0)
206                         ret = EXIT_FAILURE;
207         }
208
209         free(line);
210         return ret;
211 }