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