]> git.draconx.ca Git - cdecl99.git/blob - t/cdeclerr.c
libcdecl: Avoid vsnprintf for error reporting.
[cdecl99.git] / t / cdeclerr.c
1 /*
2  * Helper application to test internal library error reporting.
3  * Copyright © 2023 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 <assert.h>
23 #include <ctype.h>
24
25 #include "cdecl-internal.h"
26 #include "cdecl.h"
27 #include "errmsg.h"
28 #include "tap.h"
29
30 #if ENABLE_NLS
31 void cdecl__init_i18n(void) { }
32 #endif
33
34 static char *fmt_char(int c, char *buf)
35 {
36         int escape = 0;
37
38         switch (c) {
39         case '\a': escape = 'a';  break;
40         case '\b': escape = 'b';  break;
41         case '\t': escape = 't';  break;
42         case '\n': escape = 'n';  break;
43         case '\v': escape = 'v';  break;
44         case '\f': escape = 'f';  break;
45         case '\r': escape = 'r';  break;
46         case '\\': escape = '\\'; break;
47         case '\'': escape = '\''; break;
48         }
49
50         if (escape)
51                 sprintf(buf, "'\\%c'", escape);
52         else if (isprint((unsigned char)c))
53                 sprintf(buf, "'%c'", c);
54         else
55                 sprintf(buf, "'\\%.3o'", c & 0777u);
56
57         return buf;
58 }
59
60 static void check_fixed_string(size_t len)
61 {
62         const struct cdecl_error *err;
63         char *work1, *work2;
64         size_t errlen;
65
66         assert(len > 0);
67         work1 = calloc(2, len);
68         if (!work1)
69                 abort();
70
71         work2 = work1 + len;
72         memset(work1, 'X', len - 1);
73         memset(work2, 'X', len - 1);
74
75         tap_diag("cdecl__err w/ %lu-byte string", (unsigned long)len);
76         cdecl__err(1234, work1, "");
77         memset(work1, 0, len);
78         err = cdecl_get_error();
79
80         if (!tap_result(err->code == 1234, "returned error code")) {
81                 tap_diag("Failed, unexpected result");
82                 tap_diag("   Received: %u", err->code);
83                 tap_diag("   Expected: 1234");
84         }
85
86         errlen = strlen(err->str);
87         if (!tap_result(errlen == len-1, "returned string length")) {
88                 tap_diag("Failed, unexpected result");
89                 tap_diag("   Received: %lu", (unsigned long)errlen);
90                 tap_diag("   Expected: %lu", (unsigned long)len);
91         }
92
93         if (!tap_result(!memcmp(err->str, work2, len), "returned string")) {
94                 char buf[8];
95                 size_t n;
96
97                 n = strspn(err->str, "X");
98                 if (n >= len)
99                         n = len;
100
101                 tap_diag("Failed, first incorrect character at offset %lu",
102                          (unsigned long)n);
103                 tap_diag("   Received: %s", fmt_char(err->str[n], buf));
104                 tap_diag("   Expected: %s", fmt_char(work2[n], buf));
105         }
106
107         free(work1);
108 }
109
110 static void check_enomem()
111 {
112         const char expmsg[] = "failed to allocate memory";
113         const struct cdecl_error *err;
114
115         tap_diag("cdecl__errmsg(CDECL__ENOMEM)");
116         cdecl__errmsg(CDECL__ENOMEM);
117         err = cdecl_get_error();
118
119         if (!tap_result(err->code == CDECL_ENOMEM, "returned error code")) {
120                 tap_diag("Failed, unexpected result");
121                 tap_diag("   Received: %u", err->code);
122                 tap_diag("   Expected: %d", CDECL_ENOMEM);
123         }
124
125         if (!tap_result(!strcmp(err->str, "failed to allocate memory"),
126                         "returned string"))
127         {
128                 unsigned i;
129
130                 for (i = 0; i < sizeof expmsg; i++) {
131                         if (err->str[i] != expmsg[i])
132                                 break;
133                 }
134
135                 tap_diag("Failed, first incorrect character at offset %u", i);
136                 tap_diag("   Received: %.*s", (int)sizeof expmsg, err->str);
137                 tap_diag("   Expected: %s", expmsg);
138         }
139 }
140
141 int main(void)
142 {
143         tap_plan(3*3 + 2);
144
145         check_fixed_string(50);
146         check_fixed_string(500);
147         check_fixed_string(5000);
148
149         check_enomem();
150
151         tap_done();
152 }