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