]> git.draconx.ca Git - cdecl99.git/blob - t/crossparse.c
Avoid gnulib getline module.
[cdecl99.git] / t / crossparse.c
1 /*
2  * Test that libcdecl can parse its own output.
3  * Copyright © 2012, 2020, 2022-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 <errno.h>
24 #include <stdbool.h>
25 #include <getopt.h>
26
27 #include "cdecl.h"
28
29 #define PROGNAME "crossparse"
30 #include "test.h"
31 #include "getline.h"
32
33 static const char sopts[] = "f:ECVH";
34 static const struct option lopts[] = {
35         { "cdecl",   0, NULL, 'C' },
36         { "english", 0, NULL, 'E' },
37         { "file",    2, NULL, 'f' },
38         { "version", 0, NULL, 'V' },
39         { "help",    0, NULL, 'H' },
40         { 0 }
41 };
42
43 static void print_usage(FILE *f)
44 {
45         fprintf(f, "Usage: %s [options]\n", progname);
46 }
47
48 static void print_help(void)
49 {
50         print_usage(stdout);
51         puts("Test that libcdecl can parse its own output.\n");
52         test_print_options(lopts);
53 }
54
55 enum {
56         MODE_CDECL,
57         MODE_ENGLISH
58 };
59
60 typedef struct cdecl *parse_func(const char *);
61 typedef size_t render_func(char *, size_t, struct cdecl *);
62
63 char *rerender(const char *str, const char *parse_name,  parse_func *parse,
64                                 const char *render_name, render_func *render)
65 {
66         struct cdecl *decl = NULL;
67         char *buf = NULL;
68         size_t len;
69
70         decl = parse(str);
71         if (!decl) {
72                 print_error("%s: failed to parse input: %s", parse_name,
73                             cdecl_get_error()->str);
74                 goto err;
75         }
76
77         len = render(NULL, 0, decl);
78         buf = malloc_nofail(len+1);
79         if (render(buf, len+1, decl) != len) {
80                 print_error("%s: inconsistent length returned", render_name);
81                 goto err;
82         }
83
84         cdecl_free(decl);
85         return buf;
86 err:
87         cdecl_free(decl);
88         free(buf);
89
90         print_error("the failed input was: %s", str);
91         return NULL;
92 }
93 #define rerender(str, p, r) (rerender(str, #p, p, #r, r))
94
95 static bool test_crossparse(const char *str, int mode)
96 {
97         char *buf1 = NULL, *buf2 = NULL, *buf3 = NULL;
98         bool ret = false;
99
100         if (mode == MODE_ENGLISH) {
101                 buf1 = rerender(str, cdecl_parse_english, cdecl_declare);
102                 if (!buf1)
103                         goto out;
104                 buf2 = rerender(buf1, cdecl_parse_decl, cdecl_explain);
105                 if (!buf2)
106                         goto out;
107                 buf3 = rerender(buf2, cdecl_parse_english, cdecl_declare);
108                 if (!buf3)
109                         goto out;
110         } else {
111                 buf1 = rerender(str, cdecl_parse_decl, cdecl_explain);
112                 if (!buf1)
113                         goto out;
114                 buf2 = rerender(buf1, cdecl_parse_english, cdecl_declare);
115                 if (!buf2)
116                         goto out;
117                 buf3 = rerender(buf2, cdecl_parse_decl, cdecl_explain);
118                 if (!buf3)
119                         goto out;
120         }
121
122         if (!strcmp(buf1, buf3))
123                 ret = true;
124 out:
125         free(buf1);
126         free(buf2);
127         free(buf3);
128
129         if (!ret) {
130                 print_error("failed cross-parse check of: %s", str);
131         }
132
133         return ret;
134 }
135
136 int main(int argc, char **argv)
137 {
138         int opt, mode = MODE_CDECL;
139         int ret = EXIT_SUCCESS;
140         int i;
141
142         const char *filename = NULL;
143
144         if (argc > 0)
145                 progname = argv[0];
146
147         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
148                 switch (opt) {
149                 case 'C':
150                         mode = MODE_CDECL;
151                         break;
152                 case 'E':
153                         mode = MODE_ENGLISH;
154                         break;
155                 case 'f':
156                         filename = optarg;
157                         break;
158                 case 'V':
159                         test_print_version(PROGNAME);
160                         return EXIT_SUCCESS;
161                 case 'H':
162                         print_help();
163                         return EXIT_SUCCESS;
164                 default:
165                         print_usage(stderr);
166                         return EXIT_FAILURE;
167                 }
168         }
169
170         if (filename && !freopen(filename, "r", stdin)) {
171                 print_error("%s: %s", filename, strerror(errno));
172                 return EXIT_FAILURE;
173         } else if (filename) {
174                 char *line = NULL;
175                 size_t n = 0;
176
177                 while (do_getline(&line, &n)) {
178                         if (!test_crossparse(line, mode))
179                                 ret = EXIT_FAILURE;
180                 }
181
182                 free(line);
183         } else if (argv[optind]) {
184                 for (i = optind; i < argc; i++) {
185                         if (!test_crossparse(argv[i], mode))
186                                 ret = EXIT_FAILURE;
187                 }
188         } else {
189                 print_usage(stderr);
190                 return EXIT_FAILURE;
191         }
192
193         return ret;
194 }