wks: Allow reading of --install-key arguments from stdin.
[gnupg.git] / common / t-stringhelp.c
1 /* t-stringhelp.c - Regression tests for stringhelp.c
2  * Copyright (C) 2007 Free Software Foundation, Inc.
3  *               2015  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  */
31
32 #include <config.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <assert.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 "stringhelp.h"
47
48
49 static char *home_buffer;
50
51
52 const char *
53 gethome (void)
54 {
55   if (!home_buffer)
56     {
57       char *home = getenv("HOME");
58
59       if(home)
60         home_buffer = xstrdup (home);
61 #if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H)
62       else
63         {
64           struct passwd *pwd;
65
66           pwd = getpwuid (getuid());
67           if (pwd)
68             home_buffer = xstrdup (pwd->pw_dir);
69         }
70 #endif
71     }
72   return home_buffer;
73 }
74
75
76 static char *
77 mygetcwd (void)
78 {
79   char *buffer;
80   size_t size = 100;
81
82   for (;;)
83     {
84       buffer = xmalloc (size+1);
85 #ifdef HAVE_W32CE_SYSTEM
86       strcpy (buffer, "/");  /* Always "/".  */
87       return buffer;
88 #else
89       if (getcwd (buffer, size) == buffer)
90         return buffer;
91       xfree (buffer);
92       if (errno != ERANGE)
93         {
94           fprintf (stderr,"error getting current cwd: %s\n",
95                    strerror (errno));
96           exit (2);
97         }
98       size *= 2;
99 #endif
100     }
101 }
102
103
104 static void
105 test_percent_escape (void)
106 {
107   char *result;
108   static struct {
109     const char *extra;
110     const char *value;
111     const char *expected;
112   } tests[] =
113     {
114       { NULL, "", "" },
115       { NULL, "%", "%25" },
116       { NULL, "%%", "%25%25" },
117       { NULL, " %", " %25" },
118       { NULL, ":", "%3a" },
119       { NULL, " :", " %3a" },
120       { NULL, ": ", "%3a " },
121       { NULL, " : ", " %3a " },
122       { NULL, "::", "%3a%3a" },
123       { NULL, ": :", "%3a %3a" },
124       { NULL, "%:", "%25%3a" },
125       { NULL, ":%", "%3a%25" },
126       { "\\\n:", ":%", "%3a%25" },
127       { "\\\n:", "\\:%", "%5c%3a%25" },
128       { "\\\n:", "\n:%", "%0a%3a%25" },
129       { "\\\n:", "\xff:%", "\xff%3a%25" },
130       { "\\\n:", "\xfe:%", "\xfe%3a%25" },
131       { "\\\n:", "\x01:%", "\x01%3a%25" },
132       { "\x01",  "\x01:%", "%01%3a%25" },
133       { "\xfe",  "\xfe:%", "%fe%3a%25" },
134       { "\xfe",  "\xff:%", "\xff%3a%25" },
135
136       { NULL, NULL, NULL }
137     };
138   int testno;
139
140   result = percent_escape (NULL, NULL);
141   if (result)
142     fail (0);
143   for (testno=0; tests[testno].value; testno++)
144     {
145       result = percent_escape (tests[testno].value, tests[testno].extra);
146       if (!result)
147         fail (testno);
148       else if (strcmp (result, tests[testno].expected))
149         fail (testno);
150       xfree (result);
151     }
152
153 }
154
155
156 static void
157 test_compare_filenames (void)
158 {
159   struct {
160     const char *a;
161     const char *b;
162     int result;
163   } tests[] = {
164     { "", "", 0 },
165     { "", "a", -1 },
166     { "a", "", 1 },
167     { "a", "a", 0 },
168     { "a", "aa", -1 },
169     { "aa", "a", 1 },
170     { "a",  "b", -1  },
171
172 #ifdef HAVE_W32_SYSTEM
173     { "a", "A", 0 },
174     { "A", "a", 0 },
175     { "foo/bar", "foo\\bar", 0 },
176     { "foo\\bar", "foo/bar", 0 },
177     { "foo\\", "foo/", 0 },
178     { "foo/", "foo\\", 0 },
179 #endif /*HAVE_W32_SYSTEM*/
180     { NULL, NULL, 0}
181   };
182   int testno, result;
183
184   for (testno=0; tests[testno].a; testno++)
185     {
186       result = compare_filenames (tests[testno].a, tests[testno].b);
187       result = result < 0? -1 : result > 0? 1 : 0;
188       if (result != tests[testno].result)
189         fail (testno);
190     }
191 }
192
193
194 static void
195 test_strconcat (void)
196 {
197   char *out;
198
199   out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
200                    "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", NULL);
204   if (!out)
205     fail (0);
206   else
207     xfree (out);
208   out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
209                    "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", NULL);
213   if (out)
214     fail (0);
215   else if (errno != EINVAL)
216     fail (0);
217
218   out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
219                    "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", NULL);
223   if (out)
224     fail (0);
225   else if (errno != EINVAL)
226     fail (0);
227   xfree (out);
228
229 #if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute.  */
230   out = strconcat (NULL);
231   if (!out || *out)
232     fail (1);
233 #endif
234   out = strconcat (NULL, NULL);
235   if (!out || *out)
236     fail (1);
237   xfree (out);
238
239   out = strconcat ("", NULL);
240   if (!out || *out)
241     fail (1);
242   xfree (out);
243
244   out = strconcat ("", "", NULL);
245   if (!out || *out)
246     fail (2);
247   xfree (out);
248
249   out = strconcat ("a", "b", NULL);
250   if (!out || strcmp (out, "ab"))
251     fail (3);
252   xfree (out);
253   out = strconcat ("a", "b", "c", NULL);
254   if (!out || strcmp (out, "abc"))
255     fail (3);
256   xfree (out);
257
258   out = strconcat ("a", "b", "cc", NULL);
259   if (!out || strcmp (out, "abcc"))
260     fail (4);
261   xfree (out);
262   out = strconcat ("a1", "b1", "c1", NULL);
263   if (!out || strcmp (out, "a1b1c1"))
264     fail (4);
265   xfree (out);
266
267   out = strconcat ("", " long b ", "", "--even-longer--", NULL);
268   if (!out || strcmp (out, " long b --even-longer--"))
269     fail (5);
270   xfree (out);
271
272   out = strconcat ("", " long b ", "", "--even-longer--", NULL);
273   if (!out || strcmp (out, " long b --even-longer--"))
274     fail (5);
275   xfree (out);
276 }
277
278 static void
279 test_xstrconcat (void)
280 {
281   char *out;
282
283   out = xstrconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
284                    "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", NULL);
288   if (!out)
289     fail (0);
290   xfree (out);
291
292 #if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute.  */
293   out = xstrconcat (NULL);
294   if (!out)
295     fail (1);
296 #endif
297   out = xstrconcat (NULL, NULL);
298   if (!out)
299     fail (1);
300   xfree (out);
301
302   out = xstrconcat ("", NULL);
303   if (!out || *out)
304     fail (1);
305   xfree (out);
306
307   out = xstrconcat ("", "", NULL);
308   if (!out || *out)
309     fail (2);
310   xfree (out);
311
312   out = xstrconcat ("a", "b", NULL);
313   if (!out || strcmp (out, "ab"))
314     fail (3);
315   xfree (out);
316   out = xstrconcat ("a", "b", "c", NULL);
317   if (!out || strcmp (out, "abc"))
318     fail (3);
319   xfree (out);
320
321   out = xstrconcat ("a", "b", "cc", NULL);
322   if (!out || strcmp (out, "abcc"))
323     fail (4);
324   xfree (out);
325   out = xstrconcat ("a1", "b1", "c1", NULL);
326   if (!out || strcmp (out, "a1b1c1"))
327     fail (4);
328   xfree (out);
329
330   out = xstrconcat ("", " long b ", "", "--even-longer--", NULL);
331   if (!out || strcmp (out, " long b --even-longer--"))
332     fail (5);
333   xfree (out);
334
335   out = xstrconcat ("", " long b ", "", "--even-longer--", NULL);
336   if (!out || strcmp (out, " long b --even-longer--"))
337     fail (5);
338   xfree (out);
339 }
340
341
342 static void
343 test_make_filename_try (void)
344 {
345   char *out;
346   const char *home = gethome ();
347   size_t homelen = home? strlen (home):0;
348
349   out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
350                            "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", NULL);
353   if (out)
354     fail (0);
355   else if (errno != EINVAL)
356     fail (0);
357   xfree (out);
358   out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
359                            "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", NULL);
362   if (out)
363     fail (0);
364   else if (errno != EINVAL)
365     fail (0);
366   xfree (out);
367
368   out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
369                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
370                            "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
371                            "1", "2", NULL);
372   if (!out || strcmp (out,
373                       "1/2/3/4/5/6/7/8/9/10/"
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"))
377     fail (0);
378   xfree (out);
379
380   out = make_filename_try ("foo", "~/bar", "baz/cde", NULL);
381   if (!out || strcmp (out, "foo/~/bar/baz/cde"))
382     fail (1);
383   xfree (out);
384
385   out = make_filename_try ("foo", "~/bar", "baz/cde/", NULL);
386   if (!out || strcmp (out, "foo/~/bar/baz/cde/"))
387     fail (1);
388   xfree (out);
389
390   out = make_filename_try ("/foo", "~/bar", "baz/cde/", NULL);
391   if (!out || strcmp (out, "/foo/~/bar/baz/cde/"))
392     fail (1);
393   xfree (out);
394
395   out = make_filename_try ("//foo", "~/bar", "baz/cde/", NULL);
396   if (!out || strcmp (out, "//foo/~/bar/baz/cde/"))
397     fail (1);
398   xfree (out);
399
400   out = make_filename_try ("", "~/bar", "baz/cde", NULL);
401   if (!out || strcmp (out, "/~/bar/baz/cde"))
402     fail (1);
403   xfree (out);
404
405
406   out = make_filename_try ("~/foo", "bar", NULL);
407   if (!out)
408     fail (2);
409   else if (home)
410     {
411       if (strlen (out) < homelen + 7)
412         fail (2);
413       else if (strncmp (out, home, homelen))
414         fail (2);
415       else if (strcmp (out+homelen, "/foo/bar"))
416         fail (2);
417     }
418   else
419     {
420       if (strcmp (out, "~/foo/bar"))
421         fail (2);
422     }
423   xfree (out);
424
425   out = make_filename_try ("~", "bar", NULL);
426   if (!out)
427     fail (2);
428   else if (home)
429     {
430       if (strlen (out) < homelen + 3)
431         fail (2);
432       else if (strncmp (out, home, homelen))
433         fail (2);
434       else if (strcmp (out+homelen, "/bar"))
435         fail (2);
436     }
437   else
438     {
439       if (strcmp (out, "~/bar"))
440         fail (2);
441     }
442   xfree (out);
443 }
444
445
446 static void
447 test_make_absfilename_try (void)
448 {
449   char *out;
450   char *cwd = mygetcwd ();
451   size_t cwdlen = strlen (cwd);
452
453   out = make_absfilename_try ("foo", "bar", NULL);
454   if (!out)
455     fail (0);
456   else if (strlen (out) < cwdlen + 7)
457     fail (0);
458   else if (strncmp (out, cwd, cwdlen))
459     fail (0);
460   else if (strcmp (out+cwdlen, "/foo/bar"))
461     fail (0);
462   xfree (out);
463
464   out = make_absfilename_try ("./foo", NULL);
465   if (!out)
466     fail (1);
467   else if (strlen (out) < cwdlen + 5)
468     fail (1);
469   else if (strncmp (out, cwd, cwdlen))
470     fail (1);
471   else if (strcmp (out+cwdlen, "/./foo"))
472     fail (1);
473   xfree (out);
474
475   out = make_absfilename_try (".", NULL);
476   if (!out)
477     fail (2);
478   else if (strlen (out) < cwdlen)
479     fail (2);
480   else if (strncmp (out, cwd, cwdlen))
481     fail (2);
482   else if (strcmp (out+cwdlen, ""))
483     fail (2);
484   xfree (out);
485
486   xfree (cwd);
487 }
488
489 static void
490 test_strsplit (void)
491 {
492   struct {
493     const char *s;
494     char delim;
495     char replacement;
496     const char *fields_expected[10];
497   } tv[] = {
498     {
499       "a:bc:cde:fghi:jklmn::foo:", ':', '\0',
500       { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL }
501     },
502     {
503       ",a,bc,,def,", ',', '!',
504       { "!a!bc!!def!", "a!bc!!def!", "bc!!def!", "!def!", "def!", "", NULL }
505     },
506     {
507       "", ':', ',',
508       { "", NULL }
509     }
510   };
511
512   int tidx;
513
514   for (tidx = 0; tidx < DIM(tv); tidx++)
515     {
516       char *s2;
517       int field_count;
518       char **fields;
519       int field_count_expected;
520       int i;
521
522       /* Count the fields.  */
523       for (field_count_expected = 0;
524            tv[tidx].fields_expected[field_count_expected];
525            field_count_expected ++)
526         ;
527
528       /* We need to copy s since strsplit modifies it in place.  */
529       s2 = xstrdup (tv[tidx].s);
530       fields = strsplit (s2, tv[tidx].delim, tv[tidx].replacement,
531                          &field_count);
532
533       if (field_count != field_count_expected)
534         fail (tidx * 1000);
535
536       for (i = 0; i < field_count_expected; i ++)
537         if (strcmp (tv[tidx].fields_expected[i], fields[i]) != 0)
538           {
539             printf ("For field %d, expected '%s', but got '%s'\n",
540                     i, tv[tidx].fields_expected[i], fields[i]);
541             fail (tidx * 1000 + i + 1);
542           }
543
544       xfree (fields);
545       xfree (s2);
546     }
547 }
548
549
550
551 static void
552 test_strtokenize (void)
553 {
554   struct {
555     const char *s;
556     const char *delim;
557     const char *fields_expected[10];
558   } tv[] = {
559     {
560       "", ":",
561       { "", NULL }
562     },
563     {
564       "a", ":",
565       { "a", NULL }
566     },
567     {
568       ":", ":",
569       { "", "", NULL }
570     },
571     {
572       "::", ":",
573       { "", "", "", NULL }
574     },
575     {
576       "a:b:c", ":",
577       { "a", "b", "c", NULL }
578     },
579     {
580       "a:b:", ":",
581       { "a", "b", "", NULL }
582     },
583     {
584       "a:b", ":",
585       { "a", "b", NULL }
586     },
587     {
588       "aa:b:cd", ":",
589       { "aa", "b", "cd", NULL }
590     },
591     {
592       "aa::b:cd", ":",
593       { "aa", "", "b", "cd", NULL }
594     },
595     {
596       "::b:cd", ":",
597       { "", "", "b", "cd", NULL }
598     },
599     {
600       "aa:   : b:cd ", ":",
601       { "aa", "", "b", "cd", NULL }
602     },
603     {
604       "  aa:   : b:  cd ", ":",
605       { "aa", "", "b", "cd", NULL }
606     },
607     {
608       "  ", ":",
609       { "", NULL }
610     },
611     {
612       "  :", ":",
613       { "", "", NULL }
614     },
615     {
616       "  : ", ":",
617       { "", "", NULL }
618     },
619     {
620       ": ", ":",
621       { "", "", NULL }
622     },
623     {
624       ": x ", ":",
625       { "", "x", NULL }
626     },
627     {
628       "a:bc:cde:fghi:jklmn::foo:", ":",
629       { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL }
630     },
631     {
632       ",a,bc,,def,", ",",
633       { "", "a", "bc", "", "def", "", NULL }
634     },
635     {
636       " a ", " ",
637       { "", "a", "", NULL }
638     },
639     {
640       " ", " ",
641       { "", "", NULL }
642     },
643     {
644       "", " ",
645       { "", NULL }
646     }
647   };
648
649   int tidx;
650
651   for (tidx = 0; tidx < DIM(tv); tidx++)
652     {
653       char **fields;
654       int field_count;
655       int field_count_expected;
656       int i;
657
658       for (field_count_expected = 0;
659            tv[tidx].fields_expected[field_count_expected];
660            field_count_expected ++)
661         ;
662
663       fields = strtokenize (tv[tidx].s, tv[tidx].delim);
664       if (!fields)
665         fail (tidx * 1000);
666       else
667         {
668           for (field_count = 0; fields[field_count]; field_count++)
669             ;
670           if (field_count != field_count_expected)
671             fail (tidx * 1000);
672           else
673             {
674               for (i = 0; i < field_count_expected; i++)
675                 if (strcmp (tv[tidx].fields_expected[i], fields[i]))
676                   {
677                     printf ("For field %d, expected '%s', but got '%s'\n",
678                             i, tv[tidx].fields_expected[i], fields[i]);
679                     fail (tidx * 1000 + i + 1);
680                   }
681             }
682           }
683
684       xfree (fields);
685     }
686 }
687
688
689 static void
690 test_split_fields (void)
691 {
692   struct {
693     const char *s;
694     int nfields;
695     const char *fields_expected[10];
696   } tv[] = {
697     {
698       "a bc cde fghi jklmn   foo ", 6,
699       { "a", "bc", "cde", "fghi", "jklmn", "foo", NULL }
700     },
701     {
702       " a bc  def ", 2,
703       { "a", "bc", "def", NULL }
704     },
705     {
706       " a bc  def ", 3,
707       { "a", "bc", "def", NULL }
708     },
709     {
710       " a bc  def ", 4,
711       { "a", "bc", "def", NULL }
712     },
713     {
714       "", 0,
715       { NULL }
716     }
717   };
718
719   int tidx;
720   char *fields[10];
721   int field_count_expected, nfields, field_count, i;
722   char *s2;
723
724   for (tidx = 0; tidx < DIM(tv); tidx++)
725     {
726       nfields = tv[tidx].nfields;
727       assert (nfields <= DIM (fields));
728
729       /* Count the fields.  */
730       for (field_count_expected = 0;
731            tv[tidx].fields_expected[field_count_expected];
732            field_count_expected ++)
733         ;
734       if (field_count_expected > nfields)
735         field_count_expected = nfields;
736
737       /* We need to copy s since split_fields modifies in place.  */
738       s2 = xstrdup (tv[tidx].s);
739       field_count = split_fields (s2, fields, nfields);
740
741       if (field_count != field_count_expected)
742         {
743           printf ("%s: tidx %d: expected %d, got %d\n",
744                   __func__, tidx, field_count_expected, field_count);
745           fail (tidx * 1000);
746         }
747       else
748         {
749           for (i = 0; i < field_count_expected; i ++)
750             if (strcmp (tv[tidx].fields_expected[i], fields[i]))
751               {
752                 printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n",
753                         __func__,
754                         tidx, i, tv[tidx].fields_expected[i], fields[i]);
755                 fail (tidx * 1000 + i + 1);
756               }
757         }
758
759       xfree (s2);
760     }
761 }
762
763
764 static void
765 test_split_fields_colon (void)
766 {
767   struct {
768     const char *s;
769     int nfields;
770     const char *fields_expected[10];
771   } tv[] = {
772     {
773       "a:bc:cde:fghi:jklmn:  foo ", 6,
774       { "a", "bc", "cde", "fghi", "jklmn", "  foo ", NULL }
775     },
776     {
777       " a:bc: def ", 2,
778       { " a", "bc", NULL }
779     },
780     {
781       " a:bc :def ", 3,
782       { " a", "bc ", "def ", NULL }
783     },
784     {
785       " a:bc: def ", 4,
786       { " a", "bc", " def ", NULL }
787     },
788     {
789       "", 0,
790       { NULL }
791     }
792   };
793
794   int tidx;
795   char *fields[10];
796   int field_count_expected, nfields, field_count, i;
797   char *s2;
798
799   for (tidx = 0; tidx < DIM(tv); tidx++)
800     {
801       nfields = tv[tidx].nfields;
802       assert (nfields <= DIM (fields));
803
804       /* Count the fields.  */
805       for (field_count_expected = 0;
806            tv[tidx].fields_expected[field_count_expected];
807            field_count_expected ++)
808         ;
809       if (field_count_expected > nfields)
810         field_count_expected = nfields;
811
812       /* We need to copy s since split_fields modifies in place.  */
813       s2 = xstrdup (tv[tidx].s);
814       field_count = split_fields_colon (s2, fields, nfields);
815
816       if (field_count != field_count_expected)
817         {
818           printf ("%s: tidx %d: expected %d, got %d\n",
819                   __func__, tidx, field_count_expected, field_count);
820           fail (tidx * 1000);
821         }
822       else
823         {
824           for (i = 0; i < field_count_expected; i ++)
825             if (strcmp (tv[tidx].fields_expected[i], fields[i]))
826               {
827                 printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n",
828                         __func__,
829                         tidx, i, tv[tidx].fields_expected[i], fields[i]);
830                 fail (tidx * 1000 + i + 1);
831               }
832         }
833
834       xfree (s2);
835     }
836 }
837
838
839 static char *
840 stresc (char *s)
841 {
842   char *p;
843   int l = strlen (s) + 1;
844
845   for (p = s; *p; p ++)
846     if (*p == '\n')
847       l ++;
848
849   p = xmalloc (l);
850   for (l = 0; *s; s ++, l ++)
851     {
852       if (*s == ' ')
853         p[l] = '_';
854       else if (*p == '\n')
855         {
856           p[l ++] = '\\';
857           p[l ++] = 'n';
858           p[l] = '\n';
859         }
860       else
861         p[l] = *s;
862     }
863   p[l] = *s;
864
865   return p;
866 }
867
868
869 static void
870 test_format_text (void)
871 {
872   struct test
873   {
874     int target_cols, max_cols;
875     char *input;
876     char *expected;
877   };
878
879   struct test tests[] = {
880     {
881       10, 12,
882       "",
883       "",
884     },
885     {
886       10, 12,
887       " ",
888       "",
889     },
890     {
891       10, 12,
892       "  ",
893       "",
894     },
895     {
896       10, 12,
897       " \n ",
898       " \n",
899     },
900     {
901       10, 12,
902       " \n  \n ",
903       " \n  \n",
904     },
905     {
906       10, 12,
907       "0123456789 0123456789 0",
908       "0123456789\n0123456789\n0",
909     },
910     {
911       10, 12,
912       "   0123456789   0123456789   0  ",
913       "   0123456789\n0123456789\n0",
914     },
915     {
916       10, 12,
917       "01 34 67 90 23 56  89 12 45 67 89 1",
918       "01 34 67\n90 23 56\n89 12 45\n67 89 1"
919     },
920     {
921       10, 12,
922       "01 34 67 90 23 56  89 12 45 67 89 1",
923       "01 34 67\n90 23 56\n89 12 45\n67 89 1"
924     },
925     {
926       72, 80,
927       "Warning: if you think you've seen more than 10 messages "
928       "signed by this key, then this key might be a forgery!  "
929       "Carefully examine the email address for small variations "
930       "(e.g., additional white space).  If the key is suspect, "
931       "then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as being bad.\n",
932       "Warning: if you think you've seen more than 10 messages signed by this\n"
933       "key, then this key might be a forgery!  Carefully examine the email\n"
934       "address for small variations (e.g., additional white space).  If the key\n"
935       "is suspect, then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as\n"
936       "being bad.\n"
937
938     },
939     {
940       72, 80,
941       "Normally, there is only a single key associated with an email "
942       "address.  However, people sometimes generate a new key if "
943       "their key is too old or they think it might be compromised.  "
944       "Alternatively, a new key may indicate a man-in-the-middle "
945       "attack!  Before accepting this key, you should talk to or "
946       "call the person to make sure this new key is legitimate.",
947       "Normally, there is only a single key associated with an email "
948       "address.\nHowever, people sometimes generate a new key if "
949       "their key is too old or\nthey think it might be compromised.  "
950       "Alternatively, a new key may indicate\na man-in-the-middle "
951       "attack!  Before accepting this key, you should talk\nto or "
952       "call the person to make sure this new key is legitimate.",
953     }
954   };
955
956   int i;
957   int failed = 0;
958
959   for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i ++)
960     {
961       struct test *test = &tests[i];
962       char *result =
963         format_text (test->input, test->target_cols, test->max_cols);
964       if (!result)
965         {
966           fail (1);
967           exit (2);
968         }
969       if (strcmp (result, test->expected) != 0)
970         {
971           printf ("%s: Test #%d failed.\nExpected: '%s'\nResult: '%s'\n",
972                   __func__, i + 1, stresc (test->expected), stresc (result));
973           failed ++;
974         }
975       xfree (result);
976     }
977
978   if (failed)
979     fail(0);
980 }
981
982
983 static void
984 test_compare_version_strings (void)
985 {
986   struct { const char *a; const char *b; int okay; } tests[] = {
987     { "1.0.0",   "1.0.0", 0 },
988     { "1.0.0-",  "1.0.0", 1 },
989     { "1.0.0-1", "1.0.0", 1 },
990     { "1.0.0.1", "1.0.0", 1 },
991     { "1.0.0",   "1.0.1", -1 },
992     { "1.0.0-",  "1.0.1", -1 },
993     { "1.0.0-1", "1.0.1", -1 },
994     { "1.0.0.1", "1.0.1", -1 },
995     { "1.0.0",   "1.1.0", -1 },
996     { "1.0.0-",  "1.1.0", -1 },
997     { "1.0.0-1", "1.1.0", -1 },
998     { "1.0.0.1", "1.1.0", -1 },
999
1000     { "1.0.0",   "1.0.0-", -1 },
1001     { "1.0.0",   "1.0.0-1", -1 },
1002     { "1.0.0",   "1.0.0.1", -1 },
1003     { "1.1.0",   "1.0.0", 1 },
1004     { "1.1.1",   "1.1.0", 1 },
1005     { "1.1.2",   "1.1.2", 0 },
1006     { "1.1.2",   "1.0.2", 1 },
1007     { "1.1.2",   "0.0.2", 1 },
1008     { "1.1.2",   "1.1.3", -1 },
1009
1010     { "0.99.1",  "0.9.9", 1 },
1011     { "0.9.1",   "0.91.0", -1 },
1012
1013     { "1.5.3",   "1.5",  1 },
1014     { "1.5.0",   "1.5",  0 },
1015     { "1.4.99",  "1.5",  -1 },
1016     { "1.5",     "1.4.99",  1 },
1017     { "1.5",     "1.5.0",  0 },
1018     { "1.5",     "1.5.1",  -1 },
1019
1020     { "1.5.3-x17",   "1.5-23",  1 },
1021
1022     { "1.5.3a",   "1.5.3",  1 },
1023     { "1.5.3a",   "1.5.3b",  -1 },
1024
1025     { "3.1.4-ab", "3.1.4-ab", 0 },
1026     { "3.1.4-ab", "3.1.4-ac", -1 },
1027     { "3.1.4-ac", "3.1.4-ab", 1 },
1028     { "3.1.4-ab", "3.1.4-abb", -1 },
1029     { "3.1.4-abb", "3.1.4-ab", 1 },
1030
1031     { "",       "",   INT_MIN },
1032     { NULL,     "",   INT_MIN },
1033     { "1.2.3",  "",   INT_MIN },
1034     { "1.2.3",  "2",  INT_MIN },
1035
1036     /* Test cases for validity of A.  */
1037     { "",      NULL, INT_MIN },
1038     { "1",     NULL, INT_MIN },
1039     { "1.",    NULL, 0       },
1040     { "1.0",   NULL, 0       },
1041     { "1.0.",  NULL, 0       },
1042     { "a1.2",  NULL, INT_MIN },
1043     { NULL,    NULL, INT_MIN }
1044   };
1045   int idx;
1046   int res;
1047
1048   for (idx=0; idx < DIM(tests); idx++)
1049     {
1050       res = compare_version_strings (tests[idx].a, tests[idx].b);
1051       /* printf ("test %d: '%s'  '%s'  %d  ->  %d\n", */
1052       /*         idx, tests[idx].a, tests[idx].b, tests[idx].okay, res); */
1053       if (res != tests[idx].okay)
1054         fail (idx);
1055     }
1056 }
1057
1058
1059 int
1060 main (int argc, char **argv)
1061 {
1062   (void)argc;
1063   (void)argv;
1064
1065   test_percent_escape ();
1066   test_compare_filenames ();
1067   test_strconcat ();
1068   test_xstrconcat ();
1069   test_make_filename_try ();
1070   test_make_absfilename_try ();
1071   test_strsplit ();
1072   test_strtokenize ();
1073   test_split_fields ();
1074   test_split_fields_colon ();
1075   test_compare_version_strings ();
1076   test_format_text ();
1077
1078   xfree (home_buffer);
1079   return !!errcount;
1080 }