1 /* t-stringhelp.c - Regression tests for stringhelp.c
2  * Copyright (C) 2007 Free Software Foundation, Inc.
3  *               2015, 2021  g10 Code GmbH
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute and/or modify this
8  * part of GnuPG under the terms of either
9  *
10  *   - the GNU Lesser General Public License as published by the Free
11  *     Software Foundation; either version 3 of the License, or (at
12  *     your option) any later version.
13  *
14  * or
15  *
16  *   - the GNU General Public License as published by the Free
17  *     Software Foundation; either version 2 of the License, or (at
18  *     your option) any later version.
19  *
20  * or both in parallel, as here.
21  *
22  * GnuPG is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * General Public License for more details.
26  *
27  * You should have received a copies of the GNU General Public License
28  * and the GNU Lesser General Public License along with this program;
29  * if not, see <https://www.gnu.org/licenses/>.
30  * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later)
31  */
32 
33 #include <config.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #ifdef HAVE_PWD_H
39 # include <pwd.h>
40 #endif
41 #include <unistd.h>
42 #include <sys/types.h>
43 #include <limits.h>
44 
45 #include "t-support.h"
46 #include "sysutils.h"
47 #include "stringhelp.h"
48 
49 
50 static char *home_buffer;
51 
52 
53 const char *
gethome(void)54 gethome (void)
55 {
56   if (!home_buffer)
57     {
58       char *home = getenv("HOME");
59 
60       if(home)
61         home_buffer = xstrdup (home);
62 #if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
63       else
64         {
65           struct passwd *pwd;
66 
67           pwd = getpwuid (getuid());
68           if (pwd)
69             home_buffer = xstrdup (pwd->pw_dir);
70         }
71 #endif
72     }
73   return home_buffer;
74 }
75 
76 
77 static char *
mygetcwd(void)78 mygetcwd (void)
79 {
80   char *buffer;
81   size_t size = 100;
82 
83   for (;;)
84     {
85       buffer = xmalloc (size+1);
86 #ifdef HAVE_W32CE_SYSTEM
87       strcpy (buffer, "/");  /* Always "/".  */
88       return buffer;
89 #else
90       if (getcwd (buffer, size) == buffer)
91         return buffer;
92       xfree (buffer);
93       if (errno != ERANGE)
94         {
95           fprintf (stderr,"error getting current cwd: %s\n",
96                    strerror (errno));
97           exit (2);
98         }
99       size *= 2;
100 #endif
101     }
102 }
103 
104 
105 static void
test_percent_escape(void)106 test_percent_escape (void)
107 {
108   char *result;
109   static struct {
110     const char *extra;
111     const char *value;
112     const char *expected;
113   } tests[] =
114     {
115       { NULL, "", "" },
116       { NULL, "%", "%25" },
117       { NULL, "%%", "%25%25" },
118       { NULL, " %", " %25" },
119       { NULL, ":", "%3a" },
120       { NULL, " :", " %3a" },
121       { NULL, ": ", "%3a " },
122       { NULL, " : ", " %3a " },
123       { NULL, "::", "%3a%3a" },
124       { NULL, ": :", "%3a %3a" },
125       { NULL, "%:", "%25%3a" },
126       { NULL, ":%", "%3a%25" },
127       { "\\\n:", ":%", "%3a%25" },
128       { "\\\n:", "\\:%", "%5c%3a%25" },
129       { "\\\n:", "\n:%", "%0a%3a%25" },
130       { "\\\n:", "\xff:%", "\xff%3a%25" },
131       { "\\\n:", "\xfe:%", "\xfe%3a%25" },
132       { "\\\n:", "\x01:%", "\x01%3a%25" },
133       { "\x01",  "\x01:%", "%01%3a%25" },
134       { "\xfe",  "\xfe:%", "%fe%3a%25" },
135       { "\xfe",  "\xff:%", "\xff%3a%25" },
136 
137       { NULL, NULL, NULL }
138     };
139   int testno;
140 
141   result = percent_escape (NULL, NULL);
142   if (result)
143     fail (0);
144   for (testno=0; tests[testno].value; testno++)
145     {
146       result = percent_escape (tests[testno].value, tests[testno].extra);
147       if (!result)
148         fail (testno);
149       else if (strcmp (result, tests[testno].expected))
150         fail (testno);
151       xfree (result);
152     }
153 
154 }
155 
156 
157 static void
test_compare_filenames(void)158 test_compare_filenames (void)
159 {
160   struct {
161     const char *a;
162     const char *b;
163     int result;
164   } tests[] = {
165     { "", "", 0 },
166     { "", "a", -1 },
167     { "a", "", 1 },
168     { "a", "a", 0 },
169     { "a", "aa", -1 },
170     { "aa", "a", 1 },
171     { "a",  "b", -1  },
172 
173 #ifdef HAVE_W32_SYSTEM
174     { "a", "A", 0 },
175     { "A", "a", 0 },
176     { "foo/bar", "foo\\bar", 0 },
177     { "foo\\bar", "foo/bar", 0 },
178     { "foo\\", "foo/", 0 },
179     { "foo/", "foo\\", 0 },
180 #endif /*HAVE_W32_SYSTEM*/
181     { NULL, NULL, 0}
182   };
183   int testno, result;
184 
185   for (testno=0; tests[testno].a; testno++)
186     {
187       result = compare_filenames (tests[testno].a, tests[testno].b);
188       result = result < 0? -1 : result > 0? 1 : 0;
189       if (result != tests[testno].result)
190         fail (testno);
191     }
192 }
193 
194 
195 static void
test_strconcat(void)196 test_strconcat (void)
197 {
198   char *out;
199 
200   out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
201                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
202                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
203                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
204                    "1", "2", "3", "4", "5", "6", "7", NULL);
205   if (!out)
206     fail (0);
207   else
208     xfree (out);
209   out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
210                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
211                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
212                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
213                    "1", "2", "3", "4", "5", "6", "7", "8", NULL);
214   if (out)
215     fail (0);
216   else if (errno != EINVAL)
217     fail (0);
218 
219   out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
220                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
221                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
222                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
223                    "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL);
224   if (out)
225     fail (0);
226   else if (errno != EINVAL)
227     fail (0);
228   xfree (out);
229 
230 #if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute.  */
231   out = strconcat (NULL);
232   if (!out || *out)
233     fail (1);
234 #endif
235   out = strconcat (NULL, NULL);
236   if (!out || *out)
237     fail (1);
238   xfree (out);
239 
240   out = strconcat ("", NULL);
241   if (!out || *out)
242     fail (1);
243   xfree (out);
244 
245   out = strconcat ("", "", NULL);
246   if (!out || *out)
247     fail (2);
248   xfree (out);
249 
250   out = strconcat ("a", "b", NULL);
251   if (!out || strcmp (out, "ab"))
252     fail (3);
253   xfree (out);
254   out = strconcat ("a", "b", "c", NULL);
255   if (!out || strcmp (out, "abc"))
256     fail (3);
257   xfree (out);
258 
259   out = strconcat ("a", "b", "cc", NULL);
260   if (!out || strcmp (out, "abcc"))
261     fail (4);
262   xfree (out);
263   out = strconcat ("a1", "b1", "c1", NULL);
264   if (!out || strcmp (out, "a1b1c1"))
265     fail (4);
266   xfree (out);
267 
268   out = strconcat ("", " long b ", "", "--even-longer--", NULL);
269   if (!out || strcmp (out, " long b --even-longer--"))
270     fail (5);
271   xfree (out);
272 
273   out = strconcat ("", " long b ", "", "--even-longer--", NULL);
274   if (!out || strcmp (out, " long b --even-longer--"))
275     fail (5);
276   xfree (out);
277 }
278 
279 static void
test_xstrconcat(void)280 test_xstrconcat (void)
281 {
282   char *out;
283 
284   out = xstrconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
285                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
286                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
287                    "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
288                    "1", "2", "3", "4", "5", "6", "7", NULL);
289   if (!out)
290     fail (0);
291   xfree (out);
292 
293 #if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute.  */
294   out = xstrconcat (NULL);
295   if (!out)
296     fail (1);
297 #endif
298   out = xstrconcat (NULL, NULL);
299   if (!out)
300     fail (1);
301   xfree (out);
302 
303   out = xstrconcat ("", NULL);
304   if (!out || *out)
305     fail (1);
306   xfree (out);
307 
308   out = xstrconcat ("", "", NULL);
309   if (!out || *out)
310     fail (2);
311   xfree (out);
312 
313   out = xstrconcat ("a", "b", NULL);
314   if (!out || strcmp (out, "ab"))
315     fail (3);
316   xfree (out);
317   out = xstrconcat ("a", "b", "c", NULL);
318   if (!out || strcmp (out, "abc"))
319     fail (3);
320   xfree (out);
321 
322   out = xstrconcat ("a", "b", "cc", NULL);
323   if (!out || strcmp (out, "abcc"))
324     fail (4);
325   xfree (out);
326   out = xstrconcat ("a1", "b1", "c1", NULL);
327   if (!out || strcmp (out, "a1b1c1"))
328     fail (4);
329   xfree (out);
330 
331   out = xstrconcat ("", " long b ", "", "--even-longer--", NULL);
332   if (!out || strcmp (out, " long b --even-longer--"))
333     fail (5);
334   xfree (out);
335 
336   out = xstrconcat ("", " long b ", "", "--even-longer--", NULL);
337   if (!out || strcmp (out, " long b --even-longer--"))
338     fail (5);
339   xfree (out);
340 }
341 
342 
343 static void
test_make_filename_try(void)344 test_make_filename_try (void)
345 {
346   char *out;
347   const char *home = gethome ();
348   size_t homelen = home? strlen (home):0;
349 
350   out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
351                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
352                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
353                            "1", "2", "3", NULL);
354   if (out)
355     fail (0);
356   else if (errno != EINVAL)
357     fail (0);
358   xfree (out);
359   out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
360                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
361                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
362                            "1", "2", "3", "4", NULL);
363   if (out)
364     fail (0);
365   else if (errno != EINVAL)
366     fail (0);
367   xfree (out);
368 
369   out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
370                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
371                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
372                            "1", "2", NULL);
373   if (!out || strcmp (out,
374                       "1/2/3/4/5/6/7/8/9/10/"
375                       "1/2/3/4/5/6/7/8/9/10/"
376                       "1/2/3/4/5/6/7/8/9/10/"
377                       "1/2"))
378     fail (0);
379   xfree (out);
380 
381   out = make_filename_try ("foo", "~/bar", "baz/cde", NULL);
382   if (!out || strcmp (out, "foo/~/bar/baz/cde"))
383     fail (1);
384   xfree (out);
385 
386   out = make_filename_try ("foo", "~/bar", "baz/cde/", NULL);
387   if (!out || strcmp (out, "foo/~/bar/baz/cde/"))
388     fail (1);
389   xfree (out);
390 
391   out = make_filename_try ("/foo", "~/bar", "baz/cde/", NULL);
392   if (!out || strcmp (out, "/foo/~/bar/baz/cde/"))
393     fail (1);
394   xfree (out);
395 
396   out = make_filename_try ("//foo", "~/bar", "baz/cde/", NULL);
397   if (!out || strcmp (out, "//foo/~/bar/baz/cde/"))
398     fail (1);
399   xfree (out);
400 
401   out = make_filename_try ("", "~/bar", "baz/cde", NULL);
402   if (!out || strcmp (out, "/~/bar/baz/cde"))
403     fail (1);
404   xfree (out);
405 
406 
407   out = make_filename_try ("~/foo", "bar", NULL);
408   if (!out)
409     fail (2);
410   else if (home)
411     {
412       if (strlen (out) < homelen + 7)
413         fail (2);
414       else if (strncmp (out, home, homelen))
415         fail (2);
416       else if (strcmp (out+homelen, "/foo/bar"))
417         fail (2);
418     }
419   else
420     {
421       if (strcmp (out, "~/foo/bar"))
422         fail (2);
423     }
424   xfree (out);
425 
426   out = make_filename_try ("~", "bar", NULL);
427   if (!out)
428     fail (2);
429   else if (home)
430     {
431       if (strlen (out) < homelen + 3)
432         fail (2);
433       else if (strncmp (out, home, homelen))
434         fail (2);
435       else if (strcmp (out+homelen, "/bar"))
436         fail (2);
437     }
438   else
439     {
440       if (strcmp (out, "~/bar"))
441         fail (2);
442     }
443   xfree (out);
444 }
445 
446 
447 static void
test_make_absfilename_try(void)448 test_make_absfilename_try (void)
449 {
450   char *out;
451   char *cwd = mygetcwd ();
452   size_t cwdlen = strlen (cwd);
453 
454   out = make_absfilename_try ("foo", "bar", NULL);
455   if (!out)
456     fail (0);
457   else if (strlen (out) < cwdlen + 7)
458     fail (0);
459   else if (strncmp (out, cwd, cwdlen))
460     fail (0);
461   else if (strcmp (out+cwdlen, "/foo/bar"))
462     fail (0);
463   xfree (out);
464 
465   out = make_absfilename_try ("./foo", NULL);
466   if (!out)
467     fail (1);
468   else if (strlen (out) < cwdlen + 5)
469     fail (1);
470   else if (strncmp (out, cwd, cwdlen))
471     fail (1);
472   else if (strcmp (out+cwdlen, "/./foo"))
473     fail (1);
474   xfree (out);
475 
476   out = make_absfilename_try (".", NULL);
477   if (!out)
478     fail (2);
479   else if (strlen (out) < cwdlen)
480     fail (2);
481   else if (strncmp (out, cwd, cwdlen))
482     fail (2);
483   else if (strcmp (out+cwdlen, ""))
484     fail (2);
485   xfree (out);
486 
487   xfree (cwd);
488 }
489 
490 static void
test_strsplit(void)491 test_strsplit (void)
492 {
493   struct {
494     const char *s;
495     char delim;
496     char replacement;
497     const char *fields_expected[10];
498   } tv[] = {
499     {
500       "a:bc:cde:fghi:jklmn::foo:", ':', '\0',
501       { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL }
502     },
503     {
504       ",a,bc,,def,", ',', '!',
505       { "!a!bc!!def!", "a!bc!!def!", "bc!!def!", "!def!", "def!", "", NULL }
506     },
507     {
508       "", ':', ',',
509       { "", NULL }
510     }
511   };
512 
513   int tidx;
514 
515   for (tidx = 0; tidx < DIM(tv); tidx++)
516     {
517       char *s2;
518       int field_count;
519       char **fields;
520       int field_count_expected;
521       int i;
522 
523       /* Count the fields.  */
524       for (field_count_expected = 0;
525            tv[tidx].fields_expected[field_count_expected];
526            field_count_expected ++)
527         ;
528 
529       /* We need to copy s since strsplit modifies it in place.  */
530       s2 = xstrdup (tv[tidx].s);
531       fields = strsplit (s2, tv[tidx].delim, tv[tidx].replacement,
532                          &field_count);
533 
534       if (field_count != field_count_expected)
535         fail (tidx * 1000);
536 
537       for (i = 0; i < field_count_expected; i ++)
538         if (strcmp (tv[tidx].fields_expected[i], fields[i]) != 0)
539           {
540             printf ("For field %d, expected '%s', but got '%s'\n",
541                     i, tv[tidx].fields_expected[i], fields[i]);
542             fail (tidx * 1000 + i + 1);
543           }
544 
545       xfree (fields);
546       xfree (s2);
547     }
548 }
549 
550 
551 
552 static void
test_strtokenize(void)553 test_strtokenize (void)
554 {
555   struct {
556     const char *s;
557     const char *delim;
558     const char *fields_expected[10];
559   } tv[] = {
560     {
561       "", ":",
562       { "", NULL }
563     },
564     {
565       "a", ":",
566       { "a", NULL }
567     },
568     {
569       ":", ":",
570       { "", "", NULL }
571     },
572     {
573       "::", ":",
574       { "", "", "", NULL }
575     },
576     {
577       "a:b:c", ":",
578       { "a", "b", "c", NULL }
579     },
580     {
581       "a:b:", ":",
582       { "a", "b", "", NULL }
583     },
584     {
585       "a:b", ":",
586       { "a", "b", NULL }
587     },
588     {
589       "aa:b:cd", ":",
590       { "aa", "b", "cd", NULL }
591     },
592     {
593       "aa::b:cd", ":",
594       { "aa", "", "b", "cd", NULL }
595     },
596     {
597       "::b:cd", ":",
598       { "", "", "b", "cd", NULL }
599     },
600     {
601       "aa:   : b:cd ", ":",
602       { "aa", "", "b", "cd", NULL }
603     },
604     {
605       "  aa:   : b:  cd ", ":",
606       { "aa", "", "b", "cd", NULL }
607     },
608     {
609       "  ", ":",
610       { "", NULL }
611     },
612     {
613       "  :", ":",
614       { "", "", NULL }
615     },
616     {
617       "  : ", ":",
618       { "", "", NULL }
619     },
620     {
621       ": ", ":",
622       { "", "", NULL }
623     },
624     {
625       ": x ", ":",
626       { "", "x", NULL }
627     },
628     {
629       "a:bc:cde:fghi:jklmn::foo:", ":",
630       { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL }
631     },
632     {
633       ",a,bc,,def,", ",",
634       { "", "a", "bc", "", "def", "", NULL }
635     },
636     {
637       " a ", " ",
638       { "", "a", "", NULL }
639     },
640     {
641       " ", " ",
642       { "", "", NULL }
643     },
644     {
645       "", " ",
646       { "", NULL }
647     }
648   };
649 
650   int tidx;
651 
652   for (tidx = 0; tidx < DIM(tv); tidx++)
653     {
654       char **fields;
655       int field_count;
656       int field_count_expected;
657       int i;
658 
659       for (field_count_expected = 0;
660            tv[tidx].fields_expected[field_count_expected];
661            field_count_expected ++)
662         ;
663 
664       fields = strtokenize (tv[tidx].s, tv[tidx].delim);
665       if (!fields)
666         fail (tidx * 1000);
667       else
668         {
669           for (field_count = 0; fields[field_count]; field_count++)
670             ;
671           if (field_count != field_count_expected)
672             fail (tidx * 1000);
673           else
674             {
675               for (i = 0; i < field_count_expected; i++)
676                 if (strcmp (tv[tidx].fields_expected[i], fields[i]))
677                   {
678                     printf ("For field %d, expected '%s', but got '%s'\n",
679                             i, tv[tidx].fields_expected[i], fields[i]);
680                     fail (tidx * 1000 + i + 1);
681                   }
682             }
683           }
684 
685       xfree (fields);
686     }
687 }
688 
689 
690 static void
test_strtokenize_nt(void)691 test_strtokenize_nt (void)
692 {
693   struct {
694     const char *s;
695     const char *delim;
696     const char *fields_expected[10];
697   } tv[] = {
698     {
699       "", ":",
700       { "", NULL }
701     },
702     {
703       "a", ":",
704       { "a", NULL }
705     },
706     {
707       ":", ":",
708       { "", "", NULL }
709     },
710     {
711       "::", ":",
712       { "", "", "", NULL }
713     },
714     {
715       "a:b:c", ":",
716       { "a", "b", "c", NULL }
717     },
718     {
719       "a:b:", ":",
720       { "a", "b", "", NULL }
721     },
722     {
723       "a:b", ":",
724       { "a", "b", NULL }
725     },
726     {
727       "aa:b:cd", ":",
728       { "aa", "b", "cd", NULL }
729     },
730     {
731       "aa::b:cd", ":",
732       { "aa", "", "b", "cd", NULL }
733     },
734     {
735       "::b:cd", ":",
736       { "", "", "b", "cd", NULL }
737     },
738     {
739       "aa:   : b:cd ", ":",
740       { "aa", "   ", " b", "cd ", NULL }
741     },
742     {
743       "  aa:   : b:  cd ", ":",
744       { "  aa", "   ", " b", "  cd ", NULL }
745     },
746     {
747       "  ", ":",
748       { "  ", NULL }
749     },
750     {
751       "  :", ":",
752       { "  ", "", NULL }
753     },
754     {
755       "  : ", ":",
756       { "  ", " ", NULL }
757     },
758     {
759       ": ", ":",
760       { "", " ", NULL }
761     },
762     {
763       ": x ", ":",
764       { "", " x ", NULL }
765     },
766     {
767       "a:bc:cde:fghi:jklmn::foo:", ":",
768       { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL }
769     },
770     {
771       ",a,bc,,def,", ",",
772       { "", "a", "bc", "", "def", "", NULL }
773     },
774     {
775       " a ", " ",
776       { "", "a", "", NULL }
777     },
778     {
779       " ", " ",
780       { "", "", NULL }
781     },
782     {
783       "", " ",
784       { "", NULL }
785     }
786   };
787 
788   int tidx;
789 
790   for (tidx = 0; tidx < DIM(tv); tidx++)
791     {
792       char **fields;
793       int field_count;
794       int field_count_expected;
795       int i;
796 
797       for (field_count_expected = 0;
798            tv[tidx].fields_expected[field_count_expected];
799            field_count_expected ++)
800         ;
801 
802       fields = strtokenize_nt (tv[tidx].s, tv[tidx].delim);
803       if (!fields)
804         fail (tidx * 1000);
805       else
806         {
807           for (field_count = 0; fields[field_count]; field_count++)
808             ;
809           if (field_count != field_count_expected)
810             fail (tidx * 1000);
811           else
812             {
813               for (i = 0; i < field_count_expected; i++)
814                 if (strcmp (tv[tidx].fields_expected[i], fields[i]))
815                   {
816                     printf ("For field %d, expected '%s', but got '%s'\n",
817                             i, tv[tidx].fields_expected[i], fields[i]);
818                     fail (tidx * 1000 + i + 1);
819                   }
820             }
821           }
822 
823       xfree (fields);
824     }
825 }
826 
827 
828 static void
test_split_fields(void)829 test_split_fields (void)
830 {
831   struct {
832     const char *s;
833     int nfields;
834     const char *fields_expected[10];
835   } tv[] = {
836     {
837       "a bc cde fghi jklmn   foo ", 6,
838       { "a", "bc", "cde", "fghi", "jklmn", "foo", NULL }
839     },
840     {
841       " a bc  def ", 2,
842       { "a", "bc", "def", NULL }
843     },
844     {
845       " a bc  def ", 3,
846       { "a", "bc", "def", NULL }
847     },
848     {
849       " a bc  def ", 4,
850       { "a", "bc", "def", NULL }
851     },
852     {
853       "", 0,
854       { NULL }
855     }
856   };
857 
858   int tidx;
859   const char *fields[10];
860   int field_count_expected, nfields, field_count, i;
861   char *s2;
862 
863   for (tidx = 0; tidx < DIM(tv); tidx++)
864     {
865       nfields = tv[tidx].nfields;
866       log_assert (nfields <= DIM (fields));
867 
868       /* Count the fields.  */
869       for (field_count_expected = 0;
870            tv[tidx].fields_expected[field_count_expected];
871            field_count_expected ++)
872         ;
873       if (field_count_expected > nfields)
874         field_count_expected = nfields;
875 
876       /* We need to copy s since split_fields modifies in place.  */
877       s2 = xstrdup (tv[tidx].s);
878       field_count = split_fields (s2, fields, nfields);
879 
880       if (field_count != field_count_expected)
881         {
882           printf ("%s: tidx %d: expected %d, got %d\n",
883                   __func__, tidx, field_count_expected, field_count);
884           fail (tidx * 1000);
885         }
886       else
887         {
888           for (i = 0; i < field_count_expected; i ++)
889             if (strcmp (tv[tidx].fields_expected[i], fields[i]))
890               {
891                 printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n",
892                         __func__,
893                         tidx, i, tv[tidx].fields_expected[i], fields[i]);
894                 fail (tidx * 1000 + i + 1);
895               }
896         }
897 
898       xfree (s2);
899     }
900 }
901 
902 
903 static void
test_split_fields_colon(void)904 test_split_fields_colon (void)
905 {
906   struct {
907     const char *s;
908     int nfields;
909     const char *fields_expected[10];
910   } tv[] = {
911     {
912       "a:bc:cde:fghi:jklmn:  foo ", 6,
913       { "a", "bc", "cde", "fghi", "jklmn", "  foo ", NULL }
914     },
915     {
916       " a:bc: def ", 2,
917       { " a", "bc", NULL }
918     },
919     {
920       " a:bc :def ", 3,
921       { " a", "bc ", "def ", NULL }
922     },
923     {
924       " a:bc: def ", 4,
925       { " a", "bc", " def ", NULL }
926     },
927     {
928       "", 0,
929       { NULL }
930     }
931   };
932 
933   int tidx;
934   const char *fields[10];
935   int field_count_expected, nfields, field_count, i;
936   char *s2;
937 
938   for (tidx = 0; tidx < DIM(tv); tidx++)
939     {
940       nfields = tv[tidx].nfields;
941       log_assert (nfields <= DIM (fields));
942 
943       /* Count the fields.  */
944       for (field_count_expected = 0;
945            tv[tidx].fields_expected[field_count_expected];
946            field_count_expected ++)
947         ;
948       if (field_count_expected > nfields)
949         field_count_expected = nfields;
950 
951       /* We need to copy s since split_fields modifies in place.  */
952       s2 = xstrdup (tv[tidx].s);
953       field_count = split_fields_colon (s2, fields, nfields);
954 
955       if (field_count != field_count_expected)
956         {
957           printf ("%s: tidx %d: expected %d, got %d\n",
958                   __func__, tidx, field_count_expected, field_count);
959           fail (tidx * 1000);
960         }
961       else
962         {
963           for (i = 0; i < field_count_expected; i ++)
964             if (strcmp (tv[tidx].fields_expected[i], fields[i]))
965               {
966                 printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n",
967                         __func__,
968                         tidx, i, tv[tidx].fields_expected[i], fields[i]);
969                 fail (tidx * 1000 + i + 1);
970               }
971         }
972 
973       xfree (s2);
974     }
975 }
976 
977 
978 static char *
stresc(char * s)979 stresc (char *s)
980 {
981   char *p;
982   int l = strlen (s) + 1;
983 
984   for (p = s; *p; p ++)
985     if (*p == '\n')
986       l ++;
987 
988   p = xmalloc (l);
989   for (l = 0; *s; s ++, l ++)
990     {
991       if (*s == ' ')
992         p[l] = '_';
993       else if (*p == '\n')
994         {
995           p[l ++] = '\\';
996           p[l ++] = 'n';
997           p[l] = '\n';
998         }
999       else
1000         p[l] = *s;
1001     }
1002   p[l] = *s;
1003 
1004   return p;
1005 }
1006 
1007 
1008 static void
test_format_text(void)1009 test_format_text (void)
1010 {
1011   struct test
1012   {
1013     int target_cols, max_cols;
1014     char *input;
1015     char *expected;
1016   };
1017 
1018   struct test tests[] = {
1019     {
1020       10, 12,
1021       "",
1022       "",
1023     },
1024     {
1025       10, 12,
1026       " ",
1027       "",
1028     },
1029     {
1030       10, 12,
1031       "  ",
1032       "",
1033     },
1034     {
1035       10, 12,
1036       " \n ",
1037       " \n",
1038     },
1039     {
1040       10, 12,
1041       " \n  \n ",
1042       " \n  \n",
1043     },
1044     {
1045       10, 12,
1046       "0123456789 0123456789 0",
1047       "0123456789\n0123456789\n0",
1048     },
1049     {
1050       10, 12,
1051       "   0123456789   0123456789   0  ",
1052       "   0123456789\n0123456789\n0",
1053     },
1054     {
1055       10, 12,
1056       "01 34 67 90 23 56  89 12 45 67 89 1",
1057       "01 34 67\n90 23 56\n89 12 45\n67 89 1"
1058     },
1059     {
1060       10, 12,
1061       "01 34 67 90 23 56  89 12 45 67 89 1",
1062       "01 34 67\n90 23 56\n89 12 45\n67 89 1"
1063     },
1064     {
1065       72, 80,
1066       "Warning: if you think you've seen more than 10 messages "
1067       "signed by this key, then this key might be a forgery!  "
1068       "Carefully examine the email address for small variations "
1069       "(e.g., additional white space).  If the key is suspect, "
1070       "then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as being bad.\n",
1071       "Warning: if you think you've seen more than 10 messages signed by this\n"
1072       "key, then this key might be a forgery!  Carefully examine the email\n"
1073       "address for small variations (e.g., additional white space).  If the key\n"
1074       "is suspect, then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as\n"
1075       "being bad.\n"
1076 
1077     },
1078     {
1079       72, 80,
1080       "Normally, there is only a single key associated with an email "
1081       "address.  However, people sometimes generate a new key if "
1082       "their key is too old or they think it might be compromised.  "
1083       "Alternatively, a new key may indicate a man-in-the-middle "
1084       "attack!  Before accepting this key, you should talk to or "
1085       "call the person to make sure this new key is legitimate.",
1086       "Normally, there is only a single key associated with an email "
1087       "address.\nHowever, people sometimes generate a new key if "
1088       "their key is too old or\nthey think it might be compromised.  "
1089       "Alternatively, a new key may indicate\na man-in-the-middle "
1090       "attack!  Before accepting this key, you should talk\nto or "
1091       "call the person to make sure this new key is legitimate.",
1092     }
1093   };
1094 
1095   int i;
1096   int failed = 0;
1097 
1098   for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i ++)
1099     {
1100       struct test *test = &tests[i];
1101       char *result =
1102         format_text (test->input, test->target_cols, test->max_cols);
1103       if (!result)
1104         {
1105           fail (1);
1106           exit (2);
1107         }
1108       if (strcmp (result, test->expected) != 0)
1109         {
1110           printf ("%s: Test #%d failed.\nExpected: '%s'\nResult: '%s'\n",
1111                   __func__, i + 1, stresc (test->expected), stresc (result));
1112           failed ++;
1113         }
1114       xfree (result);
1115     }
1116 
1117   if (failed)
1118     fail(0);
1119 }
1120 
1121 
1122 static void
test_compare_version_strings(void)1123 test_compare_version_strings (void)
1124 {
1125   struct { const char *a; const char *b; int okay; } tests[] = {
1126     { "1.0.0",   "1.0.0", 0 },
1127     { "1.0.0-",  "1.0.0", 1 },
1128     { "1.0.0-1", "1.0.0", 1 },
1129     { "1.0.0.1", "1.0.0", 1 },
1130     { "1.0.0",   "1.0.1", -1 },
1131     { "1.0.0-",  "1.0.1", -1 },
1132     { "1.0.0-1", "1.0.1", -1 },
1133     { "1.0.0.1", "1.0.1", -1 },
1134     { "1.0.0",   "1.1.0", -1 },
1135     { "1.0.0-",  "1.1.0", -1 },
1136     { "1.0.0-1", "1.1.0", -1 },
1137     { "1.0.0.1", "1.1.0", -1 },
1138 
1139     { "1.0.0",   "1.0.0-", -1 },
1140     { "1.0.0",   "1.0.0-1", -1 },
1141     { "1.0.0",   "1.0.0.1", -1 },
1142     { "1.1.0",   "1.0.0", 1 },
1143     { "1.1.1",   "1.1.0", 1 },
1144     { "1.1.2",   "1.1.2", 0 },
1145     { "1.1.2",   "1.0.2", 1 },
1146     { "1.1.2",   "0.0.2", 1 },
1147     { "1.1.2",   "1.1.3", -1 },
1148 
1149     { "0.99.1",  "0.9.9", 1 },
1150     { "0.9.1",   "0.91.0", -1 },
1151 
1152     { "1.5.3",   "1.5",  1 },
1153     { "1.5.0",   "1.5",  0 },
1154     { "1.4.99",  "1.5",  -1 },
1155     { "1.5",     "1.4.99",  1 },
1156     { "1.5",     "1.5.0",  0 },
1157     { "1.5",     "1.5.1",  -1 },
1158 
1159     { "1.5.3-x17",   "1.5-23",  1 },
1160 
1161     { "1.5.3a",   "1.5.3",  1 },
1162     { "1.5.3a",   "1.5.3b",  -1 },
1163 
1164     { "3.1.4-ab", "3.1.4-ab", 0 },
1165     { "3.1.4-ab", "3.1.4-ac", -1 },
1166     { "3.1.4-ac", "3.1.4-ab", 1 },
1167     { "3.1.4-ab", "3.1.4-abb", -1 },
1168     { "3.1.4-abb", "3.1.4-ab", 1 },
1169 
1170     { "",       "",   INT_MIN },
1171     { NULL,     "",   INT_MIN },
1172     { "1.2.3",  "",   INT_MIN },
1173     { "1.2.3",  "2",  INT_MIN },
1174 
1175     /* Test cases for validity of A.  */
1176     { "",      NULL, INT_MIN },
1177     { "1",     NULL, INT_MIN },
1178     { "1.",    NULL, 0       },
1179     { "1.0",   NULL, 0       },
1180     { "1.0.",  NULL, 0       },
1181     { "a1.2",  NULL, INT_MIN },
1182     { NULL,    NULL, INT_MIN }
1183   };
1184   int idx;
1185   int res;
1186 
1187   for (idx=0; idx < DIM(tests); idx++)
1188     {
1189       res = compare_version_strings (tests[idx].a, tests[idx].b);
1190       /* printf ("test %d: '%s'  '%s'  %d  ->  %d\n", */
1191       /*         idx, tests[idx].a, tests[idx].b, tests[idx].okay, res); */
1192       if (res != tests[idx].okay)
1193         fail (idx);
1194     }
1195 }
1196 
1197 
1198 static void
test_substitute_envvars(void)1199 test_substitute_envvars (void)
1200 {
1201   struct {
1202     const char *name;
1203     const char *value;
1204   } envvars[] = {
1205     { "HOME", "/home/joe" },
1206     { "AVAR",  "avar" },
1207     { "AVAR1", "avarx" },
1208     { "AVAR2", "avarxy" },
1209     { "AVAR3", "avarxyz" },
1210     { "AVAR0", "ava" },
1211     { "MY_VAR", "my_vars_value" },
1212     { "STRANGE{X}VAR", "strange{x}vars-value" },
1213     { "ZERO",  "" }
1214   };
1215   struct {
1216     const char *string;
1217     const char *result;
1218   } tests[] = {
1219     { "foo bar",
1220       "foo bar"
1221     },
1222     { "foo $HOME",
1223       "foo /home/joe"
1224     },
1225     { "foo $HOME ",
1226       "foo /home/joe "
1227     },
1228     { "foo $HOME$$",
1229       "foo /home/joe$"
1230     },
1231     { "foo ${HOME}/.ssh",
1232       "foo /home/joe/.ssh"
1233     },
1234     { "foo $HOME/.ssh",
1235       "foo /home/joe/.ssh"
1236     },
1237     { "foo $HOME_/.ssh",
1238       "foo /.ssh"
1239     },
1240     { "foo $HOME/.ssh/$MY_VAR:1",
1241       "foo /home/joe/.ssh/my_vars_value:1"
1242     },
1243     { "foo $HOME${MY_VAR}:1",
1244       "foo /home/joemy_vars_value:1"
1245     },
1246     { "${STRANGE{X}VAR}-bla",
1247       "strange{x}vars-value-bla"
1248     },
1249     { "${STRANGE{X}{VAR}-bla", /* missing "}" */
1250       "${STRANGE{X}{VAR}-bla"
1251     },
1252     { "zero->$ZERO<-",
1253       "zero-><-"
1254     },
1255     { "->$AVAR.$AVAR1.$AVAR2.$AVAR3.$AVAR0<-",
1256       "->avar.avarx.avarxy.avarxyz.ava<-"
1257     },
1258     { "",
1259       ""
1260     }
1261   };
1262   int idx;
1263   char *res;
1264 
1265   for (idx=0; idx < DIM(envvars); idx++)
1266     if (gnupg_setenv (envvars[idx].name, envvars[idx].value, 1))
1267       {
1268         fprintf (stderr,"error setting envvar '%s' to '%s': %s\n",
1269                  envvars[idx].name, envvars[idx].value,
1270                  strerror (errno));
1271         exit (2);
1272       }
1273 
1274   for (idx=0; idx < DIM(tests); idx++)
1275     {
1276       res = substitute_envvars (tests[idx].string);
1277       if (!res)
1278         {
1279           fprintf (stderr,"error substituting '%s' (test %d): %s\n",
1280                    tests[idx].string, idx, strerror (errno));
1281           exit (2);
1282         }
1283       if (strcmp (res, tests[idx].result))
1284         {
1285           fprintf (stderr, "substituted '%s'\n", tests[idx].string);
1286           fprintf (stderr, "     wanted '%s'\n", tests[idx].result);
1287           fprintf (stderr, "        got '%s'\n", res);
1288           fail (idx);
1289         }
1290       xfree (res);
1291     }
1292 }
1293 
1294 
1295 int
main(int argc,char ** argv)1296 main (int argc, char **argv)
1297 {
1298   (void)argc;
1299   (void)argv;
1300 
1301   test_percent_escape ();
1302   test_compare_filenames ();
1303   test_strconcat ();
1304   test_xstrconcat ();
1305   test_make_filename_try ();
1306   test_make_absfilename_try ();
1307   test_strsplit ();
1308   test_strtokenize ();
1309   test_strtokenize_nt ();
1310   test_split_fields ();
1311   test_split_fields_colon ();
1312   test_compare_version_strings ();
1313   test_format_text ();
1314   test_substitute_envvars ();
1315 
1316   xfree (home_buffer);
1317   return !!errcount;
1318 }
1319