/* * Helper application to test normalization of declaration specifiers. * Copyright © 2021-2024 Nick Bowler * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "cdecl.h" #include "cdecl-internal.h" #define PROGNAME "normalize" #include "test.h" static const char sopts[] = "f:VH"; static const struct option lopts[] = { { "file", 1, NULL, 'f' }, { "version", 0, NULL, 'V' }, { "help", 0, NULL, 'H' }, { 0 } }; static void print_usage(FILE *f) { fprintf(f, "Usage: %s [options]\n", progname); } static void print_help(void) { const struct option *opt; print_usage(stdout); puts("Test normalization of declaration specifiers.\n"); test_print_options(lopts); } static unsigned to_spectype(const char *tok) { if (!strcmp(tok, "typedef")) return CDECL_STOR_TYPEDEF; if (!strcmp(tok, "extern")) return CDECL_STOR_EXTERN; if (!strcmp(tok, "static")) return CDECL_STOR_STATIC; if (!strcmp(tok, "auto")) return CDECL_STOR_AUTO; if (!strcmp(tok, "register")) return CDECL_STOR_REGISTER; if (!strcmp(tok, "inline")) return CDECL_FUNC_INLINE; if (!strcmp(tok, "restrict")) return CDECL_QUAL_RESTRICT; if (!strcmp(tok, "volatile")) return CDECL_QUAL_VOLATILE; if (!strcmp(tok, "const")) return CDECL_QUAL_CONST; if (!strcmp(tok, "void")) return CDECL_TYPE_VOID; if (!strcmp(tok, "signed")) return CDECL_TYPE_SIGNED; if (!strcmp(tok, "unsigned")) return CDECL_TYPE_UNSIGNED; if (!strcmp(tok, "char")) return CDECL_TYPE_CHAR; if (!strcmp(tok, "short")) return CDECL_TYPE_SHORT; if (!strcmp(tok, "long")) return CDECL_TYPE_LONG; if (!strcmp(tok, "int")) return CDECL_TYPE_INT; if (!strcmp(tok, "float")) return CDECL_TYPE_FLOAT; if (!strcmp(tok, "double")) return CDECL_TYPE_DOUBLE; if (!strcmp(tok, "_Complex")) return CDECL_TYPE_COMPLEX; if (!strcmp(tok, "_Imaginary")) return CDECL_TYPE_IMAGINARY; if (!strcmp(tok, "_Bool")) return CDECL_TYPE_BOOL; if (!strcmp(tok, "struct")) return CDECL_TYPE_STRUCT; if (!strcmp(tok, "union")) return CDECL_TYPE_UNION; if (!strcmp(tok, "enum")) return CDECL_TYPE_ENUM; return CDECL_TYPE_IDENT; } static const char *to_specstring(unsigned type) { switch (type) { case CDECL_STOR_TYPEDEF: return "typedef"; case CDECL_STOR_EXTERN: return "extern"; case CDECL_STOR_STATIC: return "static"; case CDECL_STOR_AUTO: return "auto"; case CDECL_STOR_REGISTER: return "register"; case CDECL_FUNC_INLINE: return "inline"; case CDECL_QUAL_RESTRICT: return "restrict"; case CDECL_QUAL_VOLATILE: return "volatile"; case CDECL_QUAL_CONST: return "const"; case CDECL_TYPE_VOID: return "void"; case CDECL_TYPE_SIGNED: return "signed"; case CDECL_TYPE_UNSIGNED: return "unsigned"; case CDECL_TYPE_CHAR: return "char"; case CDECL_TYPE_SHORT: return "short"; case CDECL_TYPE_LONG: return "long"; case CDECL_TYPE_INT: return "int"; case CDECL_TYPE_FLOAT: return "float"; case CDECL_TYPE_DOUBLE: return "double"; case CDECL_TYPE_COMPLEX: return "_Complex"; case CDECL_TYPE_IMAGINARY: return "_Imaginary"; case CDECL_TYPE_BOOL: return "_Bool"; case CDECL_TYPE_STRUCT: return "struct"; case CDECL_TYPE_UNION: return "union"; case CDECL_TYPE_ENUM: return "enum"; } return ""; } static void print_spec(struct cdecl_declspec *spec) { const char *s = to_specstring(spec->type); printf("%s", s); if (spec->ident) { if (s[0]) putchar(' '); printf("%s", spec->ident); } } int do_normalize(char *line, size_t n) { struct cdecl_declspec *s, *specs = NULL, **newspec = &specs; char *tok = NULL; while ((tok = strtok(tok ? NULL : line, " "))) { struct cdecl_declspec *spec = malloc_nofail(sizeof *spec); spec->next = NULL; spec->ident = NULL; spec->type = to_spectype(tok); switch (spec->type) { case CDECL_TYPE_STRUCT: case CDECL_TYPE_UNION: case CDECL_TYPE_ENUM: spec->ident = strtok(NULL, " "); break; case CDECL_TYPE_IDENT: spec->ident = tok; } *newspec = spec; newspec = &spec->next; } specs = cdecl__normalize_specs(specs); for (s = specs; s; s = s->next) { print_spec(s); if (s->next) putchar(' '); } putchar('\n'); while (specs) { struct cdecl_declspec *c = specs; specs = specs->next; free(c); } return 0; } int main(int argc, char **argv) { int opt, ret = EXIT_SUCCESS; char *filename = NULL, *line = NULL; FILE *infile = stdin; size_t n = 0; ssize_t rc; while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { switch (opt) { case 'f': filename = optarg; break; case 'V': test_print_version(PROGNAME); return EXIT_SUCCESS; case 'H': print_help(); return EXIT_SUCCESS; default: print_usage(stderr); return EXIT_FAILURE; } } if (filename && !freopen(filename, "r", stdin)) { print_error("%s: %s", filename, strerror(errno)); return EXIT_FAILURE; } while (test_getline(&line, &n)) { if (do_normalize(line, n) < 0) ret = EXIT_FAILURE; } free(line); return ret; }