1 /* mkheader.c - Create a header file for libgpg-error
2  * Copyright (C) 2010 Free Software Foundation, Inc.
3  * Copyright (C) 2014 g10 Code GmbH
4  *
5  * This file is free software; as a special exception the author gives
6  * unlimited permission to copy and/or distribute it, with or without
7  * modifications, as long as this notice is preserved.
8  *
9  * This file is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
11  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  */
13 
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <unistd.h>
20 
21 #define PGM "mkheader"
22 
23 #define LINESIZE 1024
24 
25 static char *host_triplet; /* malloced.  */
26 static char *host_os;      /* points into host_triplet.  */
27 static char *srcdir;
28 static const char *hdr_version;
29 static const char *hdr_version_number;
30 static int cross_building; /* Command line flag.  */
31 
32 /* Values take from the supplied config.h.  */
33 static int have_stdint_h;
34 static int have_sys_types_h;
35 static int have_w32_system;
36 static int have_w64_system;
37 static char *replacement_for_off_type;
38 static int use_posix_threads;
39 
40 /* Various state flags.  */
41 static int stdint_h_included;
42 static int sys_types_h_included;
43 
44 
45 /* The usual free wrapper.  */
46 static void
xfree(void * a)47 xfree (void *a)
48 {
49   if (a)
50     free (a);
51 }
52 
53 
54 static char *
xmalloc(size_t n)55 xmalloc (size_t n)
56 {
57   char *p;
58 
59   p = malloc (n);
60   if (!p)
61     {
62       fputs (PGM ": out of core\n", stderr);
63       exit (1);
64     }
65   return p;
66 }
67 
68 
69 static char *
xstrdup(const char * string)70 xstrdup (const char *string)
71 {
72   char *p;
73   size_t len = strlen (string) + 1;
74 
75   p = xmalloc (len);
76   memcpy (p, string, len);
77   return p;
78 }
79 
80 
81 /* Return a malloced string with TRIPLET.  If TRIPLET has an alias
82  * return that instead.  In general build-aux/config.sub should do the
83  * aliasing but some returned triplets are anyway identical and thus
84  * we use this function to map it to the canonical form.  A pointer to
85  * the OS part of the returned value is stored at R_OS.
86  * NO_VENDOR_HACK is for internal use; caller must call with 0. */
87 static char *
canon_host_triplet(const char * triplet,int no_vendor_hack,char ** r_os)88 canon_host_triplet (const char *triplet, int no_vendor_hack, char **r_os)
89 {
90   struct {
91     const char *name;
92     const char *alias;
93   } tbl[] = {
94     {"i486-pc-linux-gnu", "i686-unknown-linux-gnu" },
95     {"i586-pc-linux-gnu" },
96     {"i686-pc-linux-gnu" },
97     {"arc-oe-linux-gnu"    }, /* Other CPU but same struct.  */
98     {"arc-oe-linux-uclibc" }, /* and uclibc is also the same.  */
99 
100     {"i486-pc-gnu", "i686-unknown-gnu"},
101     {"i586-pc-gnu"},
102     {"i686-pc-gnu"},
103 
104     {"i486-pc-kfreebsd-gnu", "i686-unknown-kfreebsd-gnu"},
105     {"i586-pc-kfreebsd-gnu"},
106     {"i686-pc-kfreebsd-gnu"},
107 
108     {"x86_64-pc-linux-gnuhardened1", "x86_64-unknown-linux-gnu" },
109     {"x86_64-pc-linux-gnu" },
110 
111     {"powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-gnu" },
112 
113     {"arm-unknown-linux-gnueabihf",  "arm-unknown-linux-gnueabi" },
114     {"armv7-unknown-linux-gnueabihf"  },
115     {"armv7a-unknown-linux-gnueabihf" },
116     {"armv5-unknown-linux-musleabi"   },
117     {"armv6-unknown-linux-musleabihf" },
118 
119     { NULL }
120   };
121   int i;
122   const char *lastalias = NULL;
123   const char *s;
124   char *p;
125   char *result;
126 
127   for (i=0; tbl[i].name; i++)
128     {
129       if (tbl[i].alias)
130         lastalias = tbl[i].alias;
131       if (!strcmp (tbl[i].name, triplet))
132         {
133           if (!lastalias)
134             break; /* Ooops: first entry has no alias.  */
135           result = xstrdup (lastalias);
136           goto leave;
137         }
138     }
139   for (i=0, s=triplet; *s; s++)
140     if (*s == '-')
141       i++;
142   if (i > 2 && !no_vendor_hack)
143     {
144       /* We have a 4 part "triplet": CPU-VENDOR-KERNEL-SYSTEM where
145        * the last two parts replace the OS part of a real triplet.
146        * The VENDOR part is then in general useless because
147        * KERNEL-SYSTEM is specific enough.  We now do a second pass by
148        * replacing VENDOR with "unknown".  */
149       char *buf = xmalloc (strlen (triplet) + 7 + 1);
150 
151       for (p=buf,s=triplet,i=0; *s; s++)
152         {
153           *p++ = *s;
154           if (*s == '-' && ++i == 1)
155             {
156               memcpy (p, "unknown-",8);
157               p += 8;
158               for (s++; *s != '-'; s++)
159                 ;
160             }
161         }
162       *p = 0;
163       result = canon_host_triplet (buf, 1, NULL);
164       xfree (buf);
165       goto leave;
166     }
167 
168   result = xstrdup (triplet);
169  leave:
170   /* Find the OS part.  */
171   if (r_os)
172     {
173       *r_os = result + strlen (result); /* Default to the empty string.  */
174       for (i=0, p=result; *p; p++)
175         if (*p == '-' && ++i == 2)
176           {
177             *r_os = p+1;
178             break;
179           }
180     }
181 
182   return result;
183 }
184 
185 
186 /* Parse the supplied config.h file and extract required info.
187    Returns 0 on success.  */
188 static int
parse_config_h(const char * fname)189 parse_config_h (const char *fname)
190 {
191   FILE *fp;
192   char line[LINESIZE];
193   int lnr = 0;
194   char *p1;
195 
196   fp = fopen (fname, "r");
197   if (!fp)
198     {
199       fprintf (stderr, "%s:%d: can't open file: %s\n",
200                fname, lnr, strerror (errno));
201       return 1;
202     }
203 
204   while (fgets (line, LINESIZE, fp))
205     {
206       size_t n = strlen (line);
207 
208       lnr++;
209       if (!n || line[n-1] != '\n')
210         {
211           fprintf (stderr,
212                    "%s:%d: trailing linefeed missing, line too long or "
213                    "embedded nul character\n", fname, lnr);
214           break;
215         }
216       line[--n] = 0;
217 
218       if (strncmp (line, "#define ", 8))
219         continue; /* We are only interested in define lines.  */
220       p1 = strtok (line + 8, " \t");
221       if (!*p1)
222         continue; /* oops */
223       if (!strcmp (p1, "HAVE_STDINT_H"))
224         have_stdint_h = 1;
225       else if (!strcmp (p1, "HAVE_SYS_TYPES_H"))
226         have_sys_types_h = 1;
227       else if (!strcmp (p1, "HAVE_W32_SYSTEM"))
228         have_w32_system = 1;
229       else if (!strcmp (p1, "HAVE_W64_SYSTEM"))
230         have_w64_system = 1;
231       else if (!strcmp (p1, "REPLACEMENT_FOR_OFF_T"))
232         {
233           p1 = strtok (NULL, "\"");
234           if (!*p1)
235             continue; /* oops */
236           xfree (replacement_for_off_type);
237           replacement_for_off_type = xstrdup (p1);
238         }
239       else if (!strcmp (p1, "USE_POSIX_THREADS"))
240         use_posix_threads = 1;
241     }
242 
243   if (ferror (fp))
244     {
245       fprintf (stderr, "%s:%d: error reading file: %s\n",
246                fname, lnr, strerror (errno));
247       return 1;
248     }
249 
250   fclose (fp);
251   return 0;
252 }
253 
254 
255 /* Write LINE to stdout.  The function is allowed to modify LINE.  */
256 static void
write_str(char * line)257 write_str (char *line)
258 {
259   if (fputs (line, stdout) == EOF)
260     {
261       fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno));
262       exit (1);
263     }
264 }
265 
266 static void
write_line(char * line)267 write_line (char *line)
268 {
269   if (puts (line) == EOF)
270     {
271       fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno));
272       exit (1);
273     }
274 }
275 
276 
277 /* Write SOURCE or CODES line to stdout.  The function is allowed to
278    modify LINE.  Trailing white space is already removed.  Passing
279    NULL resets the internal state.  */
280 static void
write_sources_or_codes(char * line)281 write_sources_or_codes (char *line)
282 {
283   static int in_intro;
284   char *p1, *p2;
285 
286   if (!line)
287     {
288       in_intro = 1;
289       return;
290     }
291 
292   if (!*line)
293     return;
294 
295   if (in_intro)
296     {
297       if (!strchr ("0123456789", *line))
298         return;
299       in_intro = 0;
300     }
301 
302   p1 = strtok (line, " \t");
303   p2 = p1? strtok (NULL, " \t") : NULL;
304 
305   if (p1 && p2 && strchr ("0123456789", *p1) && *p2)
306     {
307       write_str ("    ");
308       write_str (p2);
309       write_str (" = ");
310       write_str (p1);
311       write_str (",\n");
312     }
313 }
314 
315 
316 /* Write system errnos to stdout.  The function is allowed to
317    modify LINE.  Trailing white space is already removed.  Passing
318    NULL resets the internal state.  */
319 static void
write_errnos_in(char * line)320 write_errnos_in (char *line)
321 {
322   static int state;
323   char *p1, *p2;
324 
325   if (!line)
326     {
327       state = 0;
328       return;
329     }
330 
331   if (!*line)
332     return;
333 
334   if (!state && strchr ("0123456789", *line))
335     state = 1;
336   else if (state == 1 && !strchr ("0123456789", *line))
337     state = 2;
338 
339   if (state != 1)
340     return;
341 
342   p1 = strtok (line, " \t");
343   p2 = p1? strtok (NULL, " \t") : NULL;
344 
345   if (p1 && p2 && strchr ("0123456789", *p1) && *p2)
346     {
347       write_str ("    GPG_ERR_");
348       write_str (p2);
349       write_str (" = GPG_ERR_SYSTEM_ERROR | ");
350       write_str (p1);
351       write_str (",\n");
352     }
353 }
354 
355 
356 /* Create the full file name for NAME and return a newly allocated
357    string with it.  If name contains a '&' and REPL is not NULL
358    replace '&' with REPL. */
359 static char *
mk_include_name(const char * name,const char * repl)360 mk_include_name (const char *name, const char *repl)
361 {
362   FILE *fp;
363   char *incfname, *p;
364   const char *s;
365 
366   incfname = malloc (strlen (srcdir) + strlen (name)
367                      + (repl?strlen (repl):0) + 1);
368   if (!incfname)
369     {
370       fputs (PGM ": out of core\n", stderr);
371       exit (1);
372     }
373 
374   if (*name == '.' && name[1] == '/')
375     *incfname = 0;
376   else
377     strcpy (incfname, srcdir);
378   p = incfname + strlen (incfname);
379   for (s=name; *s; s++)
380     {
381       if (*s == '&' && repl)
382         {
383           while (*repl)
384             *p++ = *repl++;
385           repl = NULL;  /* Replace only once.  */
386         }
387       else
388         *p++ = *s;
389     }
390   *p = 0;
391   return incfname;
392 }
393 
394 
395 /* Include the file NAME from the source directory.  The included file
396    is not further expanded.  It may have comments indicated by a
397    double hash mark at the begin of a line.  OUTF is called for each
398    read line and passed a buffer with the content of line sans line
399    line endings.  If NAME is prefixed with "./" it is included from
400    the current directory and not from the source directory. */
401 static void
include_file(const char * fname,int lnr,const char * name,void (* outf)(char *))402 include_file (const char *fname, int lnr, const char *name, void (*outf)(char*))
403 {
404   FILE *fp;
405   char *incfname;
406   int inclnr;
407   char line[LINESIZE];
408   int repl_flag;
409 
410   repl_flag = !!strchr (name, '&');
411   incfname = mk_include_name (name, repl_flag? host_triplet : NULL);
412   fp = fopen (incfname, "r");
413   if (!fp && repl_flag)
414     {
415       /* Try again using the OS string.  */
416       free (incfname);
417       incfname = mk_include_name (name, host_os);
418       fp = fopen (incfname, "r");
419     }
420   if (!fp)
421     {
422       fprintf (stderr, "%s:%d: error including `%s': %s\n",
423                fname, lnr, incfname, strerror (errno));
424       exit (1);
425     }
426 
427   if (repl_flag)
428     fprintf (stderr,"%s:%d: note: including '%s'\n",
429              fname, lnr, incfname);
430 
431   inclnr = 0;
432   while (fgets (line, LINESIZE, fp))
433     {
434       size_t n = strlen (line);
435 
436       inclnr++;
437       if (!n || line[n-1] != '\n')
438         {
439           fprintf (stderr,
440                    "%s:%d: trailing linefeed missing, line too long or "
441                    "embedded nul character\n", incfname, inclnr);
442           fprintf (stderr,"%s:%d: note: file '%s' included from here\n",
443                    fname, lnr, incfname);
444           exit (1);
445         }
446       line[--n] = 0;
447       while (line[n] == ' ' || line[n] == '\t' || line[n] == '\r')
448         {
449           line[n] = 0;
450           if (!n)
451             break;
452           n--;
453         }
454 
455       if (line[0] == '#' && line[1] == '#')
456         {
457           if (!strncmp (line+2, "EOF##", 5))
458             break; /* Forced EOF.  */
459         }
460       else
461         outf (line);
462     }
463   if (ferror (fp))
464     {
465       fprintf (stderr, "%s:%d: error reading `%s': %s\n",
466                fname, lnr, incfname, strerror (errno));
467       exit (1);
468     }
469   fclose (fp);
470   free (incfname);
471 }
472 
473 
474 /* Try to include the file NAME.  Returns true if it does not
475    exist. */
476 static int
try_include_file(const char * fname,int lnr,const char * name,void (* outf)(char *))477 try_include_file (const char *fname, int lnr, const char *name,
478                   void (*outf)(char*))
479 {
480   int rc;
481   char *incfname;
482   int repl_flag;
483 
484   repl_flag = !!strchr (name, '&');
485   incfname = mk_include_name (name, repl_flag? host_triplet : NULL);
486   rc = access (incfname, R_OK);
487   if (rc && repl_flag)
488     {
489       free (incfname);
490       incfname = mk_include_name (name, host_os);
491       rc = access (incfname, R_OK);
492     }
493   if (!rc)
494     include_file (fname, lnr, name, outf);
495 
496   free (incfname);
497   return rc;
498 }
499 
500 
501 static int
write_special(const char * fname,int lnr,const char * tag)502 write_special (const char *fname, int lnr, const char *tag)
503 {
504   if (!strcmp (tag, "version"))
505     {
506       putchar ('\"');
507       fputs (hdr_version, stdout);
508       putchar ('\"');
509     }
510   else if (!strcmp (tag, "version-number"))
511     {
512       fputs (hdr_version_number, stdout);
513     }
514   else if (!strcmp (tag, "define:gpgrt_off_t"))
515     {
516       if (!replacement_for_off_type)
517         {
518           fprintf (stderr, "%s:%d: replacement for off_t not defined\n",
519                    fname, lnr);
520           exit (1);
521         }
522       else
523         {
524           if (!strcmp (replacement_for_off_type, "int64_t")
525               && !stdint_h_included && have_stdint_h)
526             {
527               fputs ("#include <stdint.h>\n\n", stdout);
528               stdint_h_included = 1;
529             }
530           printf ("typedef %s gpgrt_off_t;\n", replacement_for_off_type);
531         }
532     }
533   else if (!strcmp (tag, "define:gpgrt_ssize_t"))
534     {
535       if (have_w64_system)
536         {
537           if (!stdint_h_included && have_stdint_h)
538             {
539               fputs ("# include <stdint.h>\n", stdout);
540               stdint_h_included = 1;
541             }
542           fputs ("typedef int64_t gpgrt_ssize_t;\n", stdout);
543         }
544       else if (have_w32_system)
545         {
546           fputs ("typedef long    gpgrt_ssize_t;\n", stdout);
547         }
548       else
549         {
550           if (!sys_types_h_included)
551             {
552               fputs ("#include <sys/types.h>\n", stdout);
553               sys_types_h_included = 1;
554             }
555           fputs ("typedef ssize_t gpgrt_ssize_t;\n", stdout);
556         }
557     }
558   else if (!strcmp (tag, "api_ssize_t"))
559     {
560       if (have_w32_system)
561         fputs ("gpgrt_ssize_t", stdout);
562       else
563         fputs ("ssize_t", stdout);
564     }
565   else if (!strcmp (tag, "define:pid_t"))
566     {
567       if (have_sys_types_h)
568         {
569           if (!sys_types_h_included)
570             {
571               fputs ("#include <sys/types.h>\n", stdout);
572               sys_types_h_included = 1;
573             }
574         }
575       else if (have_w64_system)
576         {
577           if (!stdint_h_included && have_stdint_h)
578             {
579               fputs ("#include <stdint.h>\n", stdout);
580               stdint_h_included = 1;
581             }
582           fputs ("typedef int64_t pid_t\n", stdout);
583         }
584       else
585         {
586           fputs ("typedef int     pid_t\n", stdout);
587         }
588     }
589   else if (!strcmp (tag, "include:err-sources"))
590     {
591       write_sources_or_codes (NULL);
592       include_file (fname, lnr, "err-sources.h.in", write_sources_or_codes);
593     }
594   else if (!strcmp (tag, "include:err-codes"))
595     {
596       write_sources_or_codes (NULL);
597       include_file (fname, lnr, "err-codes.h.in", write_sources_or_codes);
598     }
599   else if (!strcmp (tag, "include:errnos"))
600     {
601       include_file (fname, lnr, "errnos.in", write_errnos_in);
602     }
603   else if (!strcmp (tag, "include:os-add"))
604     {
605       if (!strcmp (host_os, "mingw32"))
606         {
607           include_file (fname, lnr, "w32-add.h", write_line);
608         }
609       else if (!strcmp (host_os, "mingw32ce"))
610         {
611           include_file (fname, lnr, "w32-add.h", write_line);
612           include_file (fname, lnr, "w32ce-add.h", write_line);
613         }
614     }
615   else if (!strcmp (tag, "include:lock-obj"))
616     {
617       /* If we are not cross compiling and the native file exists we
618        * prefer that over one from syscfg.  */
619       if (cross_building
620           || try_include_file (fname, lnr,
621                                "./lock-obj-pub.native.h", write_line))
622         include_file (fname, lnr, "syscfg/lock-obj-pub.&.h", write_line);
623     }
624   else
625     return 0; /* Unknown tag.  */
626 
627   return 1; /* Tag processed.  */
628 }
629 
630 
631 int
main(int argc,char ** argv)632 main (int argc, char **argv)
633 {
634   FILE *fp = NULL;
635   char line[LINESIZE];
636   int lnr = 0;
637   const char *fname, *s;
638   char *p1, *p2;
639   const char *config_h;
640   const char *host_triplet_raw;
641 
642   if (argc)
643     {
644       argc--; argv++;
645     }
646   if (argc && !strcmp (argv[0], "--cross"))
647     {
648       cross_building = 1;
649       argc--; argv++;
650     }
651 
652   if (argc == 1)
653     {
654       /* Print just the canonicalized host triplet.  */
655       host_triplet = canon_host_triplet (argv[0], 0, &host_os);
656       printf ("%s\n", host_triplet);
657       goto leave;
658     }
659   else if (argc == 5)
660     ; /* Standard operation.  */
661   else
662     {
663       fputs ("usage: " PGM
664              " host_triplet template.h config.h version version_number\n"
665              "       " PGM
666              " host_triplet\n",
667              stderr);
668       return 1;
669     }
670   host_triplet_raw = argv[0];
671   fname = argv[1];
672   config_h = argv[2];
673   hdr_version = argv[3];
674   hdr_version_number = argv[4];
675 
676   host_triplet = canon_host_triplet (host_triplet_raw, 0, &host_os);
677 
678   srcdir = malloc (strlen (fname) + 2 + 1);
679   if (!srcdir)
680     {
681       fputs (PGM ": out of core\n", stderr);
682       return 1;
683     }
684   strcpy (srcdir, fname);
685   p1 = strrchr (srcdir, '/');
686   if (p1)
687     p1[1] = 0;
688   else
689     strcpy (srcdir, "./");
690 
691   if (parse_config_h (config_h))
692     return 1;
693 
694   fp = fopen (fname, "r");
695   if (!fp)
696     {
697       fprintf (stderr, "%s:%d: can't open file: %s\n",
698                fname, lnr, strerror (errno));
699       return 1;
700     }
701 
702   while (fgets (line, LINESIZE, fp))
703     {
704       size_t n = strlen (line);
705 
706       lnr++;
707       if (!n || line[n-1] != '\n')
708         {
709           fprintf (stderr,
710                    "%s:%d: trailing linefeed missing, line too long or "
711                    "embedded nul character\n", fname, lnr);
712           break;
713         }
714       line[--n] = 0;
715 
716       p1 = strchr (line, '@');
717       p2 = p1? strchr (p1+1, '@') : NULL;
718       if (!p1 || !p2 || p2-p1 == 1)
719         {
720           puts (line);
721           continue;
722         }
723       *p1++ = 0;
724       *p2++ = 0;
725       fputs (line, stdout);
726 
727       if (!strcmp (p1, "configure_input"))
728         {
729           s = strrchr (fname, '/');
730           printf ("Do not edit.  Generated from %s for:\n%*s",
731                   s? s+1 : fname, (int)(p1 - line) + 13, "");
732           if (!strcmp (host_triplet, host_triplet_raw))
733             printf ("%s", host_triplet);
734           else
735             printf ("%s (%s)", host_triplet, host_triplet_raw);
736           if (!use_posix_threads && !have_w32_system && !have_w64_system)
737             fputs (" NO-THREADS", stdout);
738           fputs (p2, stdout);
739         }
740       else if (!write_special (fname, lnr, p1))
741         {
742           putchar ('@');
743           fputs (p1, stdout);
744           putchar ('@');
745           fputs (p2, stdout);
746         }
747       else if (*p2)
748         {
749           fputs (p2, stdout);
750         }
751       putchar ('\n');
752     }
753 
754   if (ferror (fp))
755     {
756       fprintf (stderr, "%s:%d: error reading file: %s\n",
757                fname, lnr, strerror (errno));
758       return 1;
759     }
760 
761   fputs ("/*\n"
762          "Loc" "al Variables:\n"
763          "buffer-read-only: t\n"
764          "End:\n"
765          "*/\n", stdout);
766 
767  leave:
768   if (ferror (stdout))
769     {
770       fprintf (stderr, PGM ": error writing to stdout: %s\n", strerror (errno));
771       return 1;
772     }
773 
774   if (fp)
775     fclose (fp);
776 
777   xfree (host_triplet);
778   return 0;
779 }
780