]> git.sur5r.net Git - cc65/blob - testcode/lib/scanf-test.c
Merge remote-tracking branch 'irgendwer/AtariOS_Structure' into master
[cc65] / testcode / lib / scanf-test.c
1 /*
2 ** scanf-test.c
3 **
4 ** Tests that the scanf family of functions scans and converts its input data
5 ** correctly.
6 **
7 ** Note:  When this program uses conio, it doesn't guard against printing off
8 ** the bottom of the screen.  So, you might have a problem on platforms with
9 ** "short" screens.
10 **
11 ** 2005-01-26, Greg King
12 */
13
14 /* Define USE_STDIO, when you want to use the stdio functions.
15 ** Do not define it, when you want to use the conio functions.
16 */
17 /*
18 #define USE_STDIO
19 */
20
21 #include <stdio.h>
22 #include <string.h>
23
24 #ifdef USE_STDIO
25 # define SCANF scanf
26 # define PRINTF printf
27
28 #else
29 # include <conio.h>
30
31 /* Unlike other conio input functions, cscanf() echoes what you type. */
32 # define SCANF cscanf
33 # define PRINTF cprintf
34 #endif
35
36 #define ARRAYSIZE(a) (sizeof (a) / sizeof (a)[0])
37
38 static const struct {
39         const char *input, *format;
40         int rvalue;
41         enum TYPE {
42                 INT,
43                 CHAR
44                 } type1;
45         union {
46                 int nvalue;
47                 const char *svalue;
48                 } v1;
49         enum TYPE type2;
50         union {
51                 int nvalue;
52                 const char *svalue;
53                 } v2;
54         } test_data[] = {
55 /* Input sequences for character specifiers must be less than 80 characters
56 ** long.  These format strings are allowwed a maximum of two assignment
57 ** specifications.
58 */
59         /* Test that literals match, and that they aren't seen as conversions.
60         ** Test that integer specifiers can handle end-of-file.
61         */
62         {"qwerty   Dvorak", "qwerty  Dvorak", 0  , INT, {0}, INT, {0}},
63         {"qwerty"         , "qwerty  %d%i"  , EOF, INT, {0}, INT, {0}},
64         {"qwerty   "      , "qwerty  %d%i"  , EOF, INT, {0}, INT, {0}},
65
66         /* Test that integer specifiers scan properly. */
67         {"qwerty   a"     , "qwerty  %d%i", 0, INT, {0}    , INT, {0}},
68         {"qwerty   -"     , "qwerty  %d%i", 0, INT, {0}    , INT, {0}},
69         {"qwerty   -9"    , "qwerty  %d%i", 1, INT, {-9}   , INT, {0}},
70         {"qwerty   -95"   , "qwerty  %d%i", 1, INT, {-95}  , INT, {0}},
71         {"qwerty   -95a"  , "qwerty  %d%i", 1, INT, {-95}  , INT, {0}},
72         {"qwerty   -95a 1", "qwerty  %d%i", 1, INT, {-95}  , INT, {0}},
73         {"qwerty   -a"    , "qwerty  %d%i", 0, INT, {0}    , INT, {0}},
74         {"qwerty   -95 1" , "qwerty  %d%i", 2, INT, {-95}  , INT, {1}},
75         {"qwerty    95  2", "qwerty  %i"  , 1, INT, {95}   , INT, {0}},
76         {"qwerty   -95 +2", "qwerty  %x%o", 2, INT, {-0x95}, INT, {02}},
77         {"qwerty  0X9E 02", "qwerty  %i%i", 2, INT, {0x9e} , INT, {2}},
78         {"qwerty   095  2", "qwerty  %i%i", 2, INT, {0}    , INT, {95}},
79         {"qwerty   0e5  2", "qwerty  %i%i", 1, INT, {0}    , INT, {0}},
80
81         /* [String pointers are cast as (int),
82         ** in order to avoid cc65 warnings.]
83         */
84
85         /* Test that character specifiers can handle end-of-file. */
86         {"qwerty"   , "qwerty  %s%s"     , EOF, CHAR, {(int)""}, CHAR, {(int)""}},
87         {"qwerty   ", "qwerty  %s%s"     , EOF, CHAR, {(int)""}, CHAR, {(int)""}},
88         {"qwerty"   , "qwerty  %c%c"     , EOF, CHAR, {(int)""}, CHAR, {(int)""}},
89         {"qwerty   ", "qwerty  %c%c"     , EOF, CHAR, {(int)""}, CHAR, {(int)""}},
90         {"qwerty"   , "qwerty  %[ a-z]%c", EOF, CHAR, {(int)""}, CHAR, {(int)""}},
91         {"qwerty   ", "qwerty  %[ a-z]%c", EOF, CHAR, {(int)""}, CHAR, {(int)""}},
92         {"qwerty   ", "qwerty%s%s"       , EOF, CHAR, {(int)""}, CHAR, {(int)""}},
93
94         /* Test that character specifiers scan properly. */
95         {"123456qwertyasdfghzxcvbn!@#$%^QWERTYASDFGHZXCV"
96             "BN7890-=uiop[]\\jkl;'m,./&*()_+UIOP{}|JKL:\"M<>?",
97             "%79s%79s", 2,
98             CHAR, {(int)"123456qwertyasdfghzxcvbn!@#$%^QWERTYASDFGHZXCV"
99             "BN7890-=uiop[]\\jkl;'m,./&*()_+UIO"}, CHAR, {(int)"P{}|JKL:\"M<>?"}},
100         {"qwerty   ", "qwerty%c%c"     , 2, CHAR, {(int)" "}  , CHAR, {(int)" "}},
101         {"qwerty   ", "qwerty%2c%c"    , 2, CHAR, {(int)"  "} , CHAR, {(int)" "}},
102         {"qwerty   ", "qwerty%2c%2c"   , 1, CHAR, {(int)"  "} , CHAR, {(int)" "}},
103         {"qwerty   ", "qwerty%[ a-z]%c", 1, CHAR, {(int)"   "}, CHAR, {(int)""}},
104         {"qwerty  q", "qwerty%[ a-z]%c", 1, CHAR, {(int)"  q"}, CHAR, {(int)""}},
105         {"qwerty  Q", "qwerty%[ a-z]%c", 2, CHAR, {(int)"  "},  CHAR, {(int)"Q"}},
106         {"qwerty-QWERTY-", "%[q-ze-]%[-A-Z]" , 2, CHAR, {(int)"qwerty-"},
107             CHAR, {(int)"QWERTY-"}},
108
109         /* Test the space-separation of strings. */
110         {"qwerty qwerty"       , "qwerty%s%s", 1, CHAR, {(int)"qwerty"},
111             CHAR, {(int)""}},
112         {"qwerty qwerty Dvorak", "qwerty%s%s", 2, CHAR, {(int)"qwerty"},
113             CHAR, {(int)"Dvorak"}},
114
115         /* Test the mixxing of types. */
116         {"qwerty  abc3", "qwerty%s%X"      , 1, CHAR, {(int)"abc3"}  , INT , {0}},
117         {"qwerty  abc3", "qwerty%[ a-z]%X" , 2, CHAR, {(int)"  abc"} , INT , {3}},
118         {"qwerty  abc3", "qwerty%[ a-z3]%X", 1, CHAR, {(int)"  abc3"}, INT , {0}},
119         {"qwerty  abc3", "qwerty%[ A-Z]%X" , 2, CHAR, {(int)"  "}    , INT ,
120             {0xabc3}},
121         {"qwerty  3abc", "qwerty%i%[ a-z]" , 2, INT , {3}            , CHAR,
122             {(int)"abc"}},
123         {"qwerty  3abc", "qwerty%i%[ A-Z]" , 1, INT , {3}            , CHAR,
124             {(int)""}},
125
126         /* Test the character-count specifier. */
127         {"  95 5", "%n%i"  , 1, INT , {0}          , INT, {95}},
128         {"  a5 5", "%n%i"  , 0, INT , {0}          , INT, {0}},
129         {"  a5 5", "%x%n"  , 1, INT , {0xa5}       , INT, {4}},
130         {"  a5 5", " %4c%n", 1, CHAR, {(int)"a5 5"}, INT, {6}},
131         {" 05a9" , "%i%n"  , 1, INT , {5}          , INT, {3}},
132
133         /* Test assignment-suppression. */
134         {"  95 6", "%n%*i"  , 0, INT , {0}      , INT, {0}},
135         {"  a5 6", "%*x%n"  , 0, INT , {4}      , INT, {0}},
136         {"  a5 6", "%*x%n%o", 1, INT , {4}      , INT, {6}},
137         {"  a5 6", " %*4c%d", 0, CHAR, {(int)""}, INT, {0}},
138         {"The first number is 7.  The second number is 8.\n",
139             "%*[ .A-Za-z]%d%*[ .A-Za-z]%d", 2, INT, {7}, INT, {8}},
140         };
141
142 /* Test the char, short, and long specification-modifiers. */
143 static const struct {
144         const char *input, *format;
145         long value;
146         } type_data[] = {
147         {"+123456789", "%hhd", (signed char)123456789L},
148         {" 123456789", "%hd" , (unsigned short)123456789L},
149         {" 123456789", "%ld" ,  123456789L},
150         {"-123456789", "%lld", -123456789L},
151         };
152
153 static void Pause(void) {
154 #ifdef USE_STDIO
155         printf("\n");
156 #else
157         cprintf("\r\nTap a key to see the next test. ");
158         cgetc();
159         clrscr();
160 #endif
161         }
162
163 int main(void) {
164         long n0;
165         unsigned t;
166         int c, n1 = 12345, n2, n3;
167         char s1[80], s2[80];
168         void *p1 = main, *p2 = main, *p3 = main, *p4 = main;
169
170 #ifndef USE_STDIO
171         clrscr();
172         cursor(1);
173 #endif
174
175         /* Test that scanf() can recognize percent-signs in the input.
176         ** Test that integer converters skip white-space.
177         ** Test that "%i" can scan a single zero digit (followed by EOF).
178         */
179         sscanf("%  \r\n\f\v\t  0", "%%%i", &n1);
180         if (n1 != 0)
181                 PRINTF("sscanf()'s \"%%%%%%i\" couldn't scan either a \"%%\" "
182                        "or a single zero digit.\r\n\n");
183
184         /* Test scanf()'s return-value:  EOF if input ends before the first
185         ** conversion-attempt begins; an assignment-count, otherwise.
186         ** Test that scanf() properly converts and assigns the correct number
187         ** of arguments.
188         */
189         PRINTF("Testing scanf()'s return-value,\r\nconversions, and assignments...\r\n");
190         for (t = 0; t < ARRAYSIZE(test_data); ++t) {
191
192                 /* Prefill the arguments with zeroes. */
193                 n1 = n2 = 0;
194                 memset(s1, '\0', sizeof s1);
195                 memset(s2, '\0', sizeof s2);
196
197                 c=sscanf(test_data[t].input, test_data[t].format,
198                          /* Avoid warning messages about different
199                          ** pointer-types, by casting them to void-pointers.
200                          */
201                          test_data[t].type1 == INT ? (void *)&n1 : (void *)s1,
202                          test_data[t].type2 == INT ? (void *)&n2 : (void *)s2);
203                 if (c != test_data[t].rvalue)
204                         PRINTF("Test #%u returned %d instead of %d.\r\n",
205                                t + 1, c, test_data[t].rvalue);
206
207                 if (test_data[t].type1 == INT) {
208                         if (test_data[t].v1.nvalue != n1)
209                                 PRINTF("Test #%u assigned %i, instead of %i,\r\n"
210                                        "\tto the first argument.\r\n\n",
211                                        t + 1, n1, test_data[t].v1.nvalue);
212                         }
213                 else {          /* test_data[t].type1 == CHAR */
214                         if (strcmp(test_data[t].v1.svalue, s1))
215                                 PRINTF("Test #%u assigned\r\n\"%s\",\r\n"
216                                        "\tinstead of\r\n\"%s\",\r\n"
217                                        "\tto the first argument.\r\n\n",
218                                        t + 1, s1, test_data[t].v1.svalue);
219                         }
220
221                 if (test_data[t].type2 == INT) {
222                         if (test_data[t].v2.nvalue != n2)
223                                 PRINTF("Test #%u assigned %i, instead of %i,\r\n"
224                                        "\tto the second argument.\r\n\n",
225                                        t + 1, n2, test_data[t].v2.nvalue);
226                         }
227                 else {          /* test_data[t].type2 == CHAR */
228                         if (strcmp(test_data[t].v2.svalue, s2))
229                                 PRINTF("Test #%u assigned\r\n\"%s\",\r\n"
230                                        "\tinstead of\r\n\"%s\",\r\n"
231                                        "\tto the second argument.\r\n\n",
232                                        t + 1, s2, test_data[t].v2.svalue);
233                         }
234                 }
235         Pause();
236
237         /* Test the char, short, and long specification-modifiers. */
238         PRINTF("Testing scanf()'s type-modifiers...\r\n");
239         for (t = 0; t < ARRAYSIZE(type_data); ++t) {
240                 n0 = 0L;
241                 sscanf(type_data[t].input, type_data[t].format, &n0);
242                 if (type_data[t].value != n0)
243                         PRINTF("Test #%u assigned %li instead of %li.\r\n",
244                                t + 1, n0, type_data[t].value);
245                 }
246         Pause();
247
248         /* Test that the pointer specification
249         ** can convert what printf() generates.
250         */
251         PRINTF("Testing \"%%p\"...\r\n");
252         sprintf(s1, "%p %p %p %p", NULL, NULL,
253                 Pause,                  /* static (program) storage */
254                 &c);                    /* automatic (stack) storage */
255         sscanf(s1, "%p%p%p %p", &p1, &p2, &p3, &p4);
256         if (p1 != NULL || p2 != NULL ||
257             p3 != (void *)Pause || p4 != (void *)&c)
258                 PRINTF("p1 is %p, p2 is %p; they should be %p.\r\n"
259                        "scanf() assigned %p to p3, instead of %p.\r\n"
260                        "scanf() assigned %p to p4, instead of %p.\r\n",
261                        p1, p2, NULL,
262                        p3, Pause,
263                        p4, &c);
264
265         /* Test that scanf() can scan typed input.
266         ** Retest that "%i" can decode radix prefixxes.
267         */
268         do {
269                 Pause();
270                 PRINTF("Type 3 signed numbers,\r\n"
271                        "separated by white-space:\r\n"
272                        "octal decimal hexadecimal\r\n"
273                        "? ");
274                 c = SCANF("%i %i %i", &n1, &n2, &n3);
275                 PRINTF("\r\n\nscanf() returned %i.\r\n"
276                        "The numbers are:\r\n"
277                        " %+o octal,\r\n"
278                        " %+d decimal,\r\n"
279                        " %+#X hexadecimal.\r\n",
280                        c, n1, n2, n3);
281                 } while (c > 0);
282         return 0;
283         }