gpg: Reflow long texts.
[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 "stringhelp.h"
44
45 #include "t-support.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       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   if (home)
403     {
404       if (strlen (out) < homelen + 7)
405         fail (2);
406       if (strncmp (out, home, homelen))
407         fail (2);
408       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   if (home)
422     {
423       if (strlen (out) < homelen + 3)
424         fail (2);
425       if (strncmp (out, home, homelen))
426         fail (2);
427       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   if (strlen (out) < cwdlen + 7)
450     fail (0);
451   if (strncmp (out, cwd, cwdlen))
452     fail (0);
453   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   if (strlen (out) < cwdlen + 5)
461     fail (1);
462   if (strncmp (out, cwd, cwdlen))
463     fail (1);
464   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   if (strlen (out) < cwdlen)
472     fail (2);
473   if (strncmp (out, cwd, cwdlen))
474     fail (2);
475   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 static char *
681 stresc (char *s)
682 {
683   char *p;
684   int l = strlen (s) + 1;
685
686   for (p = s; *p; p ++)
687     if (*p == '\n')
688       l ++;
689
690   p = xmalloc (l);
691   for (l = 0; *s; s ++, l ++)
692     {
693       if (*s == ' ')
694         p[l] = '_';
695       else if (*p == '\n')
696         {
697           p[l ++] = '\\';
698           p[l ++] = 'n';
699           p[l] = '\n';
700         }
701       else
702         p[l] = *s;
703     }
704   p[l] = *s;
705
706   return p;
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 int
818 main (int argc, char **argv)
819 {
820   (void)argc;
821   (void)argv;
822
823   test_percent_escape ();
824   test_compare_filenames ();
825   test_strconcat ();
826   test_xstrconcat ();
827   test_make_filename_try ();
828   test_make_absfilename_try ();
829   test_strsplit ();
830   test_strtokenize ();
831   test_format_text ();
832
833   xfree (home_buffer);
834   return 0;
835 }