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