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