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