NativeEnumerated.c vars NULL init and check
[com/asn1c.git] / libasn1compiler / asn1c_save.c
1 #include "asn1c_internal.h"
2 #include "asn1c_compat.h"
3 #include "asn1c_fdeps.h"
4 #include "asn1c_lang.h"
5 #include "asn1c_misc.h"
6 #include "asn1c_save.h"
7 #include "asn1c_out.h"
8
9 #ifndef HAVE_SYMLINK
10 #define symlink(a,b) (errno=ENOSYS, -1)
11 #endif
12
13 /* Pedantically check fprintf's return value. */
14 static int safe_fprintf(FILE *fp, const char *fmt, ...) {
15     va_list ap;
16     va_start(ap, fmt);
17     int ret = vfprintf(fp, fmt, ap);
18     va_end(ap);
19     assert(ret >= 0);
20     return ret;
21 }
22
23 /* Pedantically check fwrite's return value. */
24 static size_t safe_fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream) {
25     size_t ret = fwrite(ptr, 1, size * nitems, stream);
26     assert(ret == size * nitems);
27     return ret;
28 }
29
30 #define HINCLUDE(s)                                             \
31         ((arg->flags & A1C_INCLUDES_QUOTED)                     \
32                 ? safe_fprintf(fp_h, "#include \"%s\"\n", s)            \
33                 : safe_fprintf(fp_h, "#include <%s>\n", s))             \
34
35 enum include_type_result {
36     TI_NOT_INCLUDED,
37     TI_INCLUDED_FROM_BULK,
38     TI_INCLUDED_FROM_CMDLINE
39 };
40
41 static int asn1c_dump_streams(arg_t *arg, asn1c_dep_chainset *, const char *, int, char **);
42 static int asn1c_print_streams(arg_t *arg);
43 static int asn1c_save_streams(arg_t *arg, asn1c_dep_chainset *, const char *,
44                               int, char **);
45 static int asn1c_copy_over(arg_t *arg, const char *destdir, const char *path,
46                            const char *msg);
47 static int identical_files(const char *fname1, const char *fname2);
48 static int need_to_generate_pdu_collection(arg_t *arg);
49 static abuf *generate_pdu_collection(arg_t *arg);
50 static int generate_pdu_collection_file(arg_t *arg, const char *destdir);
51 static int generate_preamble(arg_t *, FILE *, int optc, char **argv);
52 static enum include_type_result include_type_to_pdu_collection(arg_t *arg);
53 static int pdu_collection_has_unused_types(arg_t *arg);
54 static const char *generate_pdu_C_definition(void);
55 static void asn1c__cleanup_pdu_type(void);
56 static int asn1c__pdu_type_lookup(const char *typename);
57 static int generate_constant_file(arg_t *arg, const char *destdir);
58
59 static int
60 asn1c__save_library_makefile(arg_t *arg, const asn1c_dep_chainset *deps,
61                              const char *datadir, const char *destdir,
62                              const char *makefile_name) {
63     asn1p_module_t *mod;
64     FILE *mkf;
65
66         mkf = asn1c_open_file(destdir, makefile_name, "", 0);
67         if(mkf == NULL) {
68                 perror(makefile_name);
69                 return -1;
70         }
71
72         safe_fprintf(mkf, "ASN_MODULE_SRCS=");
73         TQ_FOR(mod, &(arg->asn->modules), mod_next) {
74                 TQ_FOR(arg->expr, &(mod->members), next) {
75                         if(asn1_lang_map[arg->expr->meta_type]
76                                 [arg->expr->expr_type].type_cb &&
77                                 (arg->expr->meta_type != AMT_VALUE)) {
78                                 safe_fprintf(mkf, "\t\\\n\t%s%s.c", destdir,
79                                 asn1c_make_identifier(AMI_MASK_ONLY_SPACES | AMI_USE_PREFIX, arg->expr, 0));
80                         }
81                 }
82         }
83         safe_fprintf(mkf, "\n\nASN_MODULE_HDRS=");
84         TQ_FOR(mod, &(arg->asn->modules), mod_next) {
85                 TQ_FOR(arg->expr, &(mod->members), next) {
86                         if(asn1_lang_map[arg->expr->meta_type]
87                                 [arg->expr->expr_type].type_cb &&
88                                 (arg->expr->meta_type != AMT_VALUE)) {
89                 safe_fprintf(
90                     mkf, "\t\\\n\t%s%s.h", destdir,
91                     asn1c_make_identifier(AMI_MASK_ONLY_SPACES | AMI_USE_PREFIX, arg->expr, 0));
92             }
93                 }
94         }
95         safe_fprintf(mkf, "\n\n");
96
97         /*
98          * Move necessary skeleton files and add them to Makefile.am.targets.
99          */
100     asn1c_dep_chain *dlist = asn1c_deps_flatten(deps, ~FDEP_CONVERTER);
101     if(dlist) {
102                 char dstpath[PATH_MAX];
103                 char *dir_end;
104                 size_t dlen = strlen(datadir);
105
106                 assert(dlen < (sizeof(dstpath) / 2 - 2));
107                 memcpy(dstpath, datadir, dlen);
108                 dir_end = dstpath + dlen;
109                 *dir_end++ = '/';
110
111                 for(size_t i = 0; i < dlist->deps_count; i++) {
112                         char where[32]; /* Location of the */
113                         char *what_kind;        /* HDRS or SRCS */
114                         const asn1c_dep_filename *dep_file = dlist->deps[i];
115                         char *fname = dep_file->filename;
116                         char *dotH;
117
118                         assert(strlen(fname) < (sizeof(dstpath) / 2));
119                         strcpy(dir_end, fname);
120
121                         if(arg->flags & A1C_DEBUG) {
122                                 snprintf(where, sizeof(where), "(line %d col %d)",
123                                 dep_file->lineno, dep_file->column);
124                         } else {
125                                 where[0] = '\0';
126                         }
127
128             if(asn1c_copy_over(arg, destdir, dstpath, where) == -1) {
129                 safe_fprintf(mkf, ">>>ABORTED<<<");
130                                 fclose(mkf);
131                                 return -1;
132                         }
133
134                         /* HDRS versus SRCS */
135                         dotH = strrchr(fname, 'h');
136                         if(dotH && fname < dotH && dotH[-1] == '.' && !dotH[1]) {
137                                 what_kind = "HDRS";
138                         } else {
139                                 what_kind = "SRCS";
140                         }
141             safe_fprintf(mkf, "ASN_MODULE_%s+=%s%s\n", what_kind, destdir,
142                          fname);
143         }
144
145                 asn1c_dep_chain_free(dlist);
146         }
147
148         safe_fprintf(
149                 mkf,
150                 "\n"
151                 "ASN_MODULE_CFLAGS=%s%s",
152                 (arg->flags & A1C_GEN_OER) ? "" : "-DASN_DISABLE_OER_SUPPORT ",
153                 (arg->flags & A1C_GEN_PER) ? "" : "-DASN_DISABLE_PER_SUPPORT ");
154
155         safe_fprintf(
156                 mkf,
157                 "\n\n"
158                 "lib_LTLIBRARIES+=libasncodec.la\n"
159                 "libasncodec_la_SOURCES="
160                 "$(ASN_MODULE_SRCS) $(ASN_MODULE_HDRS)\n"
161                 "libasncodec_la_CPPFLAGS=-I$(top_srcdir)/%s\n"
162                 "libasncodec_la_CFLAGS=$(ASN_MODULE_CFLAGS)\n"
163                 "libasncodec_la_LDFLAGS=-lm\n", destdir);
164         fclose(mkf);
165     safe_fprintf(stderr, "Generated %s%s\n", destdir, makefile_name);
166
167     return 0;
168 }
169
170 static int
171 asn1c__save_example_mk_makefile(arg_t *arg, const asn1c_dep_chainset *deps,
172                                 const char *datadir, const char *destdir,
173                                 const char *makefile_name,
174                                 const char *library_makefile_name, int argc,
175                                 char **argv) {
176     FILE *mkf;
177     asn1c_dep_chain *dlist = asn1c_deps_flatten(deps, FDEP_CONVERTER);
178
179     /* Generate converter-example.mk snippet */
180     mkf = asn1c_open_file(destdir, makefile_name, "", 0);
181     if(mkf == NULL) {
182         perror(makefile_name);
183         return -1;
184     }
185     safe_fprintf(
186         mkf,
187         "include %s%s\n\n"
188         "LIBS += -lm\n"
189         "CFLAGS += $(ASN_MODULE_CFLAGS) %s%s-I.\n"
190         "ASN_LIBRARY ?= libasncodec.a\n"
191         "ASN_PROGRAM ?= converter-example\n"
192         "ASN_PROGRAM_SRCS ?= ",
193         destdir, library_makefile_name,
194         (arg->flags & A1C_PDU_TYPE) ? generate_pdu_C_definition() : "",
195         need_to_generate_pdu_collection(arg) ? "-DASN_PDU_COLLECTION " : "");
196
197     if(dlist) {
198         for(size_t i = 0; i < dlist->deps_count; i++) {
199             char dstpath[PATH_MAX];
200             int ret = snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir,
201                                dlist->deps[i]->filename);
202             assert(ret > 0 && (size_t)ret < sizeof(dstpath));
203             if(asn1c_copy_over(arg, destdir, dstpath, "implicit") == -1) {
204                 safe_fprintf(mkf, ">>>ABORTED<<<");
205                 fclose(mkf);
206                 return -1;
207                         }
208             safe_fprintf(mkf, "\\\n\t%s%s", destdir, dlist->deps[i]->filename);
209         }
210         asn1c_dep_chain_free(dlist);
211     }
212
213     if(need_to_generate_pdu_collection(arg)) {
214         safe_fprintf(mkf, "\\\n\t%spdu_collection.c", destdir);
215         if(generate_pdu_collection_file(arg, destdir)) {
216             return -1;
217         }
218     }
219
220     safe_fprintf(
221         mkf,
222         "\n\nall: $(ASN_PROGRAM)\n"
223         "\n$(ASN_PROGRAM): $(ASN_LIBRARY) $(ASN_PROGRAM_SRCS:.c=.o)"
224         "\n\t$(CC) $(CFLAGS) $(CPPFLAGS) -o $(ASN_PROGRAM) "
225         "$(ASN_PROGRAM_SRCS:.c=.o) $(LDFLAGS) $(ASN_LIBRARY) $(LIBS)\n"
226         "\n$(ASN_LIBRARY): $(ASN_MODULE_SRCS:.c=.o)"
227         "\n\t$(AR) rcs $@ $(ASN_MODULE_SRCS:.c=.o)\n"
228         "\n.SUFFIXES:"
229         "\n.SUFFIXES: .c .o\n"
230         "\n.c.o:"
231         "\n\t$(CC) $(CFLAGS) -o $@ -c $<\n"
232         "\nclean:"
233         "\n\trm -f $(ASN_PROGRAM) $(ASN_LIBRARY)"
234         "\n\trm -f $(ASN_MODULE_SRCS:.c=.o) $(ASN_PROGRAM_SRCS:.c=.o)\n"
235         "\nregen: regenerate-from-asn1-source\n"
236         "\nregenerate-from-asn1-source:\n\t");
237
238     for(int i = 0; i < argc; i++) {
239         safe_fprintf(mkf, "%s%s", i ? " " : "", argv[i]);
240     }
241     safe_fprintf(mkf, "\n\n");
242
243     fclose(mkf);
244     safe_fprintf(stderr, "Generated %s%s\n", destdir, makefile_name);
245
246         return 0;
247 }
248
249 static int
250 asn1c__save_example_am_makefile(arg_t *arg, const asn1c_dep_chainset *deps, const char *datadir,
251                                 const char *destdir, const char *makefile_name,
252                                 const char *library_makefile_name, int argc,
253                                 char **argv) {
254         FILE *mkf;
255         asn1c_dep_chain *dlist = asn1c_deps_flatten(deps, FDEP_CONVERTER);
256
257         /* Generate example.am snippet */
258         mkf = asn1c_open_file(destdir, makefile_name, "", 0);
259         if(mkf == NULL) {
260                 return -1;
261         }
262         safe_fprintf(mkf,
263                      "include %s%s\n\n"
264                      "bin_PROGRAMS += asn1convert\n"
265                      "asn1convert_CFLAGS = $(ASN_MODULE_CFLAGS) %s%s\n"
266                      "asn1convert_CPPFLAGS = -I$(top_srcdir)/%s\n"
267                      "asn1convert_LDADD = libasncodec.la\n"
268                      "asn1convert_SOURCES = ",
269                      destdir, library_makefile_name,
270                      (arg->flags & A1C_PDU_TYPE) ? generate_pdu_C_definition() : "",
271                      need_to_generate_pdu_collection(arg) ? "-DASN_PDU_COLLECTION " : "", destdir);
272
273         if(dlist) {
274                 for(size_t i = 0; i < dlist->deps_count; i++) {
275                         char dstpath[PATH_MAX];
276                         int ret = snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir,
277                                            dlist->deps[i]->filename);
278                         assert(ret > 0 && (size_t)ret < sizeof(dstpath));
279                         if(asn1c_copy_over(arg, destdir, dstpath, "implicit") == -1) {
280                                 safe_fprintf(mkf, ">>>ABORTED<<<");
281                                 fclose(mkf);
282                                 return -1;
283                         }
284                         safe_fprintf(mkf, "\\\n\t%s%s", destdir, dlist->deps[i]->filename);
285                 }
286                 asn1c_dep_chain_free(dlist);
287         }
288
289         if(need_to_generate_pdu_collection(arg)) {
290                 safe_fprintf(mkf, "\\\n\t%spdu_collection.c", destdir);
291                 if(generate_pdu_collection_file(arg, destdir))
292                         return -1;
293         }
294
295         safe_fprintf(mkf,
296                      "\nregen: regenerate-from-asn1-source\n"
297                      "\nregenerate-from-asn1-source:\n\t");
298
299         for(int i = 0; i < argc; i++)
300                 safe_fprintf(mkf, "%s%s", i ? " " : "", argv[i]);
301         safe_fprintf(mkf, "\n\n");
302
303         fclose(mkf);
304         safe_fprintf(stderr, "Generated %s%s\n", destdir, makefile_name);
305
306         return 0;
307 }
308
309 static int
310 asn1c__save_autotools_example(const char *destdir,
311                               const char *program_makefile_name) {
312         FILE *fd;
313         const char* confac = "configure.ac";
314         const char* makeam = "Makefile.am";
315
316         if ((access(confac, F_OK) != -1)
317             || (access(makeam, F_OK) != -1))
318         {
319                 safe_fprintf(stderr, "Refusing to overwrite existing '%s' or '%s'\n", confac, makeam);
320                 return -1;
321         }
322
323         fd = asn1c_open_file("", confac, "", 0);
324         if(fd == NULL) {
325                 perror(confac);
326                 return -1;
327         }
328
329         safe_fprintf(fd,
330                      "AC_INIT([asn1convert],[0.1])\n"
331                      "AM_INIT_AUTOMAKE([-Werror foreign subdir-objects])\n"
332                      "AC_PREREQ([2.62])\n"
333                      "AC_PROG_CC\n"
334                      "LT_INIT\n"
335                      "AM_SILENT_RULES([yes])\n"
336                      "AC_CONFIG_FILES([Makefile])\n"
337                      "AC_OUTPUT\n");
338         fclose(fd);
339         safe_fprintf(stderr, "Generated minimal example %s\n", confac);
340
341         fd = asn1c_open_file("", makeam, "", 0);
342         if(fd == NULL) {
343                 perror(makeam);
344                 return -1;
345         }
346
347         safe_fprintf(fd,
348                      "bin_PROGRAMS =\n"
349                      "lib_LTLIBRARIES =\n"
350                      "include %s%s\n\n",
351                      destdir, program_makefile_name);
352
353         fclose(fd);
354         safe_fprintf(stderr, "Generated minimal example %s\n", makeam);
355         safe_fprintf(stderr, "\nRun the following to generate a configure script:\n");
356         safe_fprintf(stderr, "$ autoreconf --force --install\n");
357         return 0;
358 }
359
360 static int
361 can_generate_pdu_collection(arg_t *arg) {
362     abuf *buf = generate_pdu_collection(arg);
363     if(!buf) {
364         return -1;
365     }
366     abuf_free(buf);
367     return 0;
368 }
369
370 int
371 asn1c_save_compiled_output(arg_t *arg, const char *datadir, const char *destdir,
372                            int argc, int optc, char **argv) {
373     int ret = -1;
374
375     const char* example_am_makefile = "Makefile.am.asn1convert";
376     const char* program_makefile = "converter-example.mk";
377     const char* library_makefile = "Makefile.am.libasncodec";
378
379     /*
380      * Early check that we can properly generate PDU collection.
381      */
382     if(can_generate_pdu_collection(arg) == -1) {
383         return -1;
384     }
385
386     asn1c_dep_chainset *deps;
387     do {
388         asn1p_module_t *mod;
389
390         deps = asn1c_read_file_dependencies(arg, datadir);
391         if(!deps && datadir) {
392             WARNING(
393                 "Cannot read file-dependencies information "
394                 "from %s\n",
395                 datadir);
396         }
397
398         TQ_FOR(mod, &(arg->asn->modules), mod_next) {
399             TQ_FOR(arg->expr, &(mod->members), next) {
400                 if(asn1_lang_map[arg->expr->meta_type][arg->expr->expr_type]
401                        .type_cb &&
402                    (arg->expr->meta_type != AMT_VALUE)) {
403                     ret = asn1c_dump_streams(arg, deps, destdir, optc, argv);
404                     if(ret) break;
405                 }
406             }
407         }
408
409         /*
410          * Dump out the Makefile template and the rest of the support code.
411          */
412         if((arg->flags & A1C_PRINT_COMPILED)
413            || (arg->flags & A1C_OMIT_SUPPORT_CODE)) {
414             ret = 0;    /* Success */
415             break;
416         }
417
418         if(ret) break;
419
420         ret = asn1c__save_library_makefile(arg, deps, datadir, destdir,
421                                            library_makefile);
422         if(ret) break;
423
424         if(arg->flags & A1C_GEN_EXAMPLE) {
425             ret = asn1c__save_example_mk_makefile(arg, deps, datadir, destdir,
426                                                   program_makefile,
427                                                   library_makefile, argc, argv);
428             if(ret) break;
429             ret = asn1c__save_example_am_makefile(arg, deps, datadir, destdir,
430                                                   example_am_makefile,
431                                                   library_makefile, argc, argv);
432             if(ret) break;
433
434             if(arg->flags & A1C_GEN_AUTOTOOLS_EXAMPLE) {
435                 ret = asn1c__save_autotools_example(destdir, example_am_makefile);
436                 if(ret) break;
437             }
438         }
439     } while(0);
440
441     asn1c_dep_chainset_free(deps);
442     asn1c__cleanup_pdu_type();
443
444     generate_constant_file(arg, destdir);
445
446     return ret;
447 }
448
449 /*
450  * Dump the streams.
451  */
452 static int
453 asn1c_dump_streams(arg_t *arg, asn1c_dep_chainset *deps, const char *destdir,
454                    int optc, char **argv) {
455     if(arg->flags & A1C_PRINT_COMPILED) {
456                 return asn1c_print_streams(arg);
457         } else {
458                 return asn1c_save_streams(arg, deps, destdir, optc, argv);
459         }
460 }
461
462 static int
463 asn1c_print_streams(arg_t *arg)  {
464         compiler_streams_t *cs = arg->expr->data;
465         asn1p_expr_t *expr = arg->expr;
466         int i;
467
468         for(i = 1; i < OT_MAX; i++) {
469                 out_chunk_t *ot;
470                 if(TQ_FIRST(&cs->destination[i].chunks) == NULL)
471                         continue;
472
473                 printf("\n/*** <<< %s [%s] >>> ***/\n\n",
474                         _compiler_stream2str[i],
475                         expr->Identifier);
476
477                 TQ_FOR(ot, &(cs->destination[i].chunks), next) {
478                         safe_fwrite(ot->buf, ot->len, 1, stdout);
479                 }
480         }
481
482         return 0;
483 }
484
485 static int
486 asn1c_save_streams(arg_t *arg, asn1c_dep_chainset *deps, const char *destdir,
487                    int optc, char **argv) {
488     asn1p_expr_t *expr = arg->expr;
489         compiler_streams_t *cs = expr->data;
490         out_chunk_t *ot;
491         FILE *fp_c, *fp_h;
492         char *tmpname_c, *tmpname_h;
493         char name_buf[FILENAME_MAX];
494         const char *header_id;
495         const char *c_retained = "";
496         const char *h_retained = "";
497         char *filename;
498
499         if(cs == NULL) {
500                 safe_fprintf(stderr, "Cannot compile %s at line %d\n",
501                         expr->Identifier, expr->_lineno);
502                 return -1;
503         }
504
505         filename = strdup(asn1c_make_identifier(AMI_MASK_ONLY_SPACES | AMI_USE_PREFIX,
506                                                 expr, (char*)0));
507         fp_c = asn1c_open_file(destdir, filename, ".c", &tmpname_c);
508     if(fp_c == NULL) {
509         return -1;
510     }
511     fp_h = asn1c_open_file(destdir, filename, ".h", &tmpname_h);
512     if(fp_h == NULL) {
513         unlink(tmpname_c);
514         free(tmpname_c);
515         fclose(fp_c);
516         return -1;
517     }
518
519         generate_preamble(arg, fp_c, optc, argv);
520         generate_preamble(arg, fp_h, optc, argv);
521
522         header_id = asn1c_make_identifier(AMI_USE_PREFIX, expr, NULL);
523         safe_fprintf(fp_h,
524                 "#ifndef\t_%s_H_\n"
525                 "#define\t_%s_H_\n"
526                 "\n", header_id, header_id);
527
528         safe_fprintf(fp_h, "\n");
529         HINCLUDE("asn_application.h");
530
531 #define SAVE_STREAM(fp, idx, msg, actdep)       do {                    \
532         if(TQ_FIRST(&(cs->destination[idx].chunks)) && *msg)            \
533                 safe_fprintf(fp, "\n/* %s */\n", msg);                  \
534         TQ_FOR(ot, &(cs->destination[idx].chunks), next) {              \
535                 if(actdep) asn1c_activate_dependency(deps, ot->buf, header_id); \
536                 safe_fwrite(ot->buf, ot->len, 1, fp);                   \
537         }                                                               \
538 } while(0)
539
540         SAVE_STREAM(fp_h, OT_INCLUDES,  "Including external dependencies", 1);
541
542         safe_fprintf(fp_h, "\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
543         SAVE_STREAM(fp_h, OT_DEPS,      "Dependencies", 0);
544         SAVE_STREAM(fp_h, OT_FWD_DECLS, "Forward declarations", 0);
545         SAVE_STREAM(fp_h, OT_FWD_DEFS,  "Forward definitions", 0);
546         SAVE_STREAM(fp_h, OT_TYPE_DECLS, filename, 0);
547         SAVE_STREAM(fp_h, OT_FUNC_DECLS,"Implementation", 0);
548         safe_fprintf(fp_h, "\n#ifdef __cplusplus\n}\n#endif\n");
549
550         if(!(arg->flags & A1C_NO_INCLUDE_DEPS))
551         SAVE_STREAM(fp_h, OT_POST_INCLUDE, "Referred external types", 1);
552
553         safe_fprintf(fp_h, "\n#endif\t/* _%s_H_ */\n", header_id);
554
555         HINCLUDE("asn_internal.h");
556         safe_fprintf(fp_c, "#include \"%s.h\"\n\n", filename);
557         if(arg->flags & A1C_NO_INCLUDE_DEPS)
558                 SAVE_STREAM(fp_c, OT_POST_INCLUDE, "", 1);
559         TQ_FOR(ot, &(cs->destination[OT_IOC_TABLES].chunks), next)
560                 safe_fwrite(ot->buf, ot->len, 1, fp_c);
561         TQ_FOR(ot, &(cs->destination[OT_CTABLES].chunks), next)
562                 safe_fwrite(ot->buf, ot->len, 1, fp_c);
563         TQ_FOR(ot, &(cs->destination[OT_CODE].chunks), next)
564                 safe_fwrite(ot->buf, ot->len, 1, fp_c);
565         TQ_FOR(ot, &(cs->destination[OT_CTDEFS].chunks), next)
566                 safe_fwrite(ot->buf, ot->len, 1, fp_c);
567         TQ_FOR(ot, &(cs->destination[OT_STAT_DEFS].chunks), next)
568                 safe_fwrite(ot->buf, ot->len, 1, fp_c);
569
570         assert(OT_MAX == 13);   /* Protection from reckless changes */
571
572         fclose(fp_c);
573         fclose(fp_h);
574
575     int ret = snprintf(name_buf, sizeof(name_buf), "%s%s.c", destdir, filename);
576     assert(ret > 0 && ret < (ssize_t)sizeof(name_buf));
577
578         if(identical_files(name_buf, tmpname_c)) {
579                 c_retained = " (contents unchanged)";
580                 unlink(tmpname_c);
581         } else {
582                 if(rename(tmpname_c, name_buf)) {
583                         unlink(tmpname_c);
584                         perror(tmpname_c);
585                         free(tmpname_c);
586                         free(tmpname_h);
587                         return -1;
588                 }
589         }
590
591         sprintf(name_buf, "%s%s.h", destdir, filename);
592         if(identical_files(name_buf, tmpname_h)) {
593                 h_retained = " (contents unchanged)";
594                 unlink(tmpname_h);
595         } else {
596                 if(rename(tmpname_h, name_buf)) {
597                         unlink(tmpname_h);
598                         perror(tmpname_h);
599                         free(tmpname_c);
600                         free(tmpname_h);
601                         return -1;
602                 }
603         }
604
605         free(tmpname_c);
606         free(tmpname_h);
607
608         safe_fprintf(stderr, "Compiled %s%s.c%s\n",
609                 destdir, filename, c_retained);
610         safe_fprintf(stderr, "Compiled %s%s.h%s\n",
611                 destdir, filename, h_retained);
612         free(filename);
613         return 0;
614 }
615
616 static int
617 generate_preamble(arg_t *arg, FILE *fp, int optc, char **argv) {
618         safe_fprintf(fp,
619         "/*\n"
620         " * Generated by asn1c-" VERSION " (http://lionet.info/asn1c)\n"
621         " * From ASN.1 module \"%s\"\n"
622         " * \tfound in \"%s\"\n",
623                 arg->expr->module->ModuleName,
624                 arg->expr->module->source_file_name);
625         if(optc > 1) {
626                 int i;
627                 safe_fprintf(fp, " * \t`asn1c ");
628                 for(i = 1; i < optc; i++)
629                         safe_fprintf(fp, "%s%s", i>1?" ":"", argv[i]);
630                 safe_fprintf(fp, "`\n");
631         }
632         safe_fprintf(fp, " */\n\n");
633         return 0;
634 }
635
636 static int
637 identical_files(const char *fname1, const char *fname2) {
638         char buf[2][4096];
639         FILE *fp1, *fp2;
640         size_t olen, nlen;
641         int retval = 1; /* Files are identical */
642
643 #ifndef _WIN32
644         struct stat sb;
645
646         if(lstat(fname1, &sb) || !S_ISREG(sb.st_mode)
647         || lstat(fname2, &sb) || !S_ISREG(sb.st_mode)) {
648                 return 0;       /* Files are not identical */
649         }
650 #endif
651
652         fp1 = fopen(fname1, "r");
653         if(!fp1) { return 0; }
654         fp2 = fopen(fname2, "r");
655         if(!fp2) { fclose(fp1); return 0; }
656
657         while((olen = fread(buf[0], 1, sizeof(buf[0]), fp1))) {
658                 nlen = fread(buf[1], 1, olen, fp2);
659                 if(nlen != olen || memcmp(buf[0], buf[1], nlen)) {
660                         retval = 0;
661                         break;
662                 }
663         }
664         nlen = fread(buf[1], 1, 1, fp2);
665         if(nlen) retval = 0;
666
667         fclose(fp1);
668         fclose(fp2);
669         return retval;
670 }
671
672 /*
673  * Copy file for real.
674  */
675 static int
676 real_copy(const char *src, const char *dst) {
677         unsigned char buf[4096];
678         char *tmpname;
679         FILE *fpsrc, *fpdst;
680         size_t len;
681         int retval = 0;
682
683         if(identical_files(src, dst))
684                 return retval;  /* Success, no need to copy for real. */
685
686         fpsrc = fopen(src, "r");
687         if(!fpsrc) { errno = EIO; return -1; }
688         fpdst = asn1c_open_file(NULL, dst, "", &tmpname);
689         if(!fpdst) { fclose(fpsrc); errno = EIO; return -1; }
690
691         while(!feof(fpsrc)) {
692                 len = fread(buf, 1, sizeof(buf), fpsrc);
693                 if(safe_fwrite(buf, 1, len, fpdst) != len) {
694                         perror(tmpname);
695                         errno = EIO;
696                         retval = -1;
697                         break;
698                 }
699         }
700         fclose(fpsrc);
701         fclose(fpdst);
702
703         /* Check if copied correctly, and rename into a permanent name */
704         if(retval) {
705                 unlink(tmpname);
706         } else if(rename(tmpname, dst)) {
707                 unlink(tmpname);
708                 perror(tmpname);
709                 retval = -1;
710         }
711         free(tmpname);
712         return retval;
713 }
714
715 static int
716 asn1c_copy_over(arg_t *arg, const char *destdir, const char *path,
717                 const char *msg) {
718 #ifdef  _WIN32
719         int use_real_copy = 1;
720 #else
721         int use_real_copy = !(arg->flags & A1C_LINK_SKELETONS);
722 #endif
723
724         const char *fname = a1c_basename(path, destdir);
725         if(!fname
726         || (use_real_copy ? real_copy(path, fname) : symlink(path, fname))
727         ) {
728                 if(errno == EEXIST) {
729                         struct stat sb1, sb2;
730                         if(stat(path, &sb1) == 0
731                         && stat(fname, &sb2) == 0
732                         && sb1.st_dev == sb2.st_dev
733                         && sb1.st_ino == sb2.st_ino) {
734                                 /*
735                                  * Nothing to do.
736                                  */
737                                 safe_fprintf(stderr,
738                                         "File %s is already here as %s\n",
739                                         path, fname);
740                                 return 1;
741                         } else {
742                                 safe_fprintf(stderr,
743                                         "Retaining local %s (%s suggested)\n",
744                                         fname, path);
745                                 return 1;
746                         }
747                 } else if(errno == ENOENT) {
748                         /* Ignore this */
749                         return 0;
750                 } else {
751                         safe_fprintf(stderr, "%s %s -> %s failed: %s\n",
752                                 use_real_copy ? "Copy" : "Symlink",
753                                 path, fname, strerror(errno));
754                         return -1;
755                 }
756         }
757
758     const int has_msg = msg && *msg;
759     safe_fprintf(stderr, "%s %s\t-> %s%s%s\n",
760                  use_real_copy ? "Copied" : "Symlinked", path, fname,
761                  has_msg ? " " : "", has_msg ? msg : "");
762
763     return 1;
764 }
765
766
767 static int
768 generate_pdu_collection_file(arg_t *arg, const char *destdir) {
769     abuf *buf = generate_pdu_collection(arg);
770     assert(buf);
771
772     FILE *fp = asn1c_open_file(destdir, "pdu_collection", ".c", 0);
773         if(fp == NULL) {
774                 perror("pdu_collection.c");
775                 return -1;
776         }
777     safe_fwrite(buf->buffer, buf->length, 1, fp);
778     fclose(fp);
779
780         safe_fprintf(stderr, "Generated pdu_collection.c\n");
781     return 0;
782 }
783
784 static abuf *
785 generate_pdu_collection(arg_t *arg) {
786         asn1p_module_t *mod;
787     abuf *buf = abuf_new();
788
789     abuf_printf(buf, "/*\n * Generated by asn1c-" VERSION
790                      " (http://lionet.info/asn1c)\n */\n\n");
791     abuf_printf(buf,
792                 "struct asn_TYPE_descriptor_s;\t"
793                 "/* Forward declaration */\n\n");
794
795
796     TQ_FOR(mod, &(arg->asn->modules), mod_next) {
797         TQ_FOR(arg->expr, &(mod->members), next) {
798             if(include_type_to_pdu_collection(arg) == TI_NOT_INCLUDED) continue;
799             abuf_printf(buf,
800                         "extern struct asn_TYPE_descriptor_s "
801                         "asn_DEF_%s;\n",
802                         asn1c_make_identifier(AMI_USE_PREFIX, arg->expr, NULL));
803         }
804     }
805
806     abuf_printf(buf, "\n\n");
807         abuf_printf(buf, "struct asn_TYPE_descriptor_s *asn_pdu_collection[] = {\n");
808     TQ_FOR(mod, &(arg->asn->modules), mod_next) {
809         int mod_printed = 0;
810         TQ_FOR(arg->expr, &(mod->members), next) {
811             switch(include_type_to_pdu_collection(arg)) {
812             case TI_NOT_INCLUDED:
813                 continue;
814             case TI_INCLUDED_FROM_BULK:
815                 /* Increment */
816                 asn1c__pdu_type_lookup(arg->expr->Identifier);
817                 break;
818             case TI_INCLUDED_FROM_CMDLINE:
819                 break;
820             }
821             if(!mod_printed++) {
822                 abuf_printf(buf, "\t/* From module %s in %s */\n",
823                              arg->expr->module->ModuleName,
824                              arg->expr->module->source_file_name);
825             }
826             abuf_printf(buf, "\t&asn_DEF_%s,\t\n",
827                          asn1c_make_identifier(AMI_USE_PREFIX, arg->expr, NULL));
828         }
829     }
830
831     abuf_printf(buf, "\t0\n};\n\n");
832
833     if(pdu_collection_has_unused_types(arg)) {
834         abuf_free(buf);
835         return NULL;
836     }
837
838         return buf;
839 }
840
841 static struct PDUType {
842         char *typename;
843         int used;
844 } *pduType;
845 static size_t pduTypes;
846
847 static const char *
848 generate_pdu_C_definition(void) {
849     const char *src;
850     char *def;
851         char *dst;
852     if(pduTypes == 0) return "";
853     def = malloc(strlen(pduType[0].typename) + 20);
854     assert(def);
855     strcpy(def, "-DPDU=");
856         for(src = pduType[0].typename, dst = def + 6; *src; src++, dst++) {
857         if((*dst = *src) == '-') {
858             *dst = '_';
859         }
860     }
861     *dst++ = ' ';
862     *dst = 0;
863     return def;
864 }
865
866 void
867 asn1c__add_pdu_type(const char *ctypename) {
868         char *typename = strdup(ctypename);
869         assert(typename && *typename);
870
871         pduType = realloc(pduType, sizeof(pduType[0]) * (pduTypes + 1));
872         assert(pduType);
873         pduType[pduTypes].used = 0;
874         pduType[pduTypes].typename = typename;
875         pduTypes++;
876 }
877
878 static void
879 asn1c__cleanup_pdu_type() {
880     for(size_t i = 0; i < pduTypes; i++) {
881         free(pduType[i].typename);
882     }
883     free(pduType);
884         pduType = NULL;
885     pduTypes = 0;
886 }
887
888 static int
889 asn1c__pdu_type_lookup(const char *typename) {
890     for(size_t i = 0; i < pduTypes; i++) {
891         struct PDUType *pt = &pduType[i];
892         if(strcmp(pt->typename, typename) == 0) {
893             pt->used++;
894             return 1;
895         }
896     }
897     return 0;
898 }
899
900 static int
901 need_to_generate_pdu_collection(arg_t *arg) {
902     /* If -pdu=all or -pdu=auto are given, we need to generate one. */
903         if(arg->flags & (A1C_PDU_ALL|A1C_PDU_AUTO))
904                 return 1;
905
906     /*
907      * If no -pdu=... flags were given, need to do it, too,
908      * effectively producing -pdu=auto.
909      */
910     if(!(arg->flags & (A1C_PDU_ALL | A1C_PDU_AUTO | A1C_PDU_TYPE))) return 1;
911
912     if(arg->flags & A1C_PDU_TYPE) {
913         return (pduTypes > 1) ? 1 : 0;
914     }
915         return 0;
916 }
917
918 static int
919 pdu_collection_has_unused_types(arg_t *arg) {
920     int ret = 0;
921
922     for(size_t i = 0; i < pduTypes; i++) {
923         struct PDUType *pt = &pduType[i];
924         if(!pt->used) {
925             FATAL("Unknown ASN.1 type specified in -pdu=%s", pt->typename);
926             ret = -1;
927         }
928     }
929
930     return ret;
931 }
932
933 static enum include_type_result
934 include_type_to_pdu_collection(arg_t *arg) {
935     if(!asn1_lang_map[arg->expr->meta_type][arg->expr->expr_type].type_cb ||
936         (arg->expr->meta_type == AMT_VALUE))
937         return 0;
938
939     /* Parameterized types can't serve as PDU's without instantiation. */
940     if(arg->expr->lhs_params) {
941         return 0;
942     }
943
944     if((arg->flags & A1C_PDU_ALL)
945        /* -pdu=auto */
946        || ((arg->flags & A1C_PDU_AUTO) && !arg->expr->_type_referenced)
947        /* No -pdu=... whatsoever, act as if -pdu=auto */
948        || (!(arg->flags & (A1C_PDU_ALL | A1C_PDU_AUTO | A1C_PDU_TYPE))
949            && !arg->expr->_type_referenced)
950        || asn1c__pdu_type_lookup(arg->expr->Identifier)) {
951         return 1;
952     }
953
954     return 0;
955 }
956
957 static abuf *
958 generate_constant_collection(arg_t *arg) {
959     asn1p_module_t *mod;
960     abuf *buf = abuf_new();
961     int empty_file = 1;
962
963     abuf_printf(buf, "/*\n * Generated by asn1c-" VERSION
964                      " (http://lionet.info/asn1c)\n */\n\n");
965     abuf_printf(buf, "#ifndef _%sASN_CONSTANT_H\n#define _%sASN_CONSTANT_H\n\n", asn1c_prefix(), asn1c_prefix());
966
967     abuf_printf(buf, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");
968
969     TQ_FOR(mod, &(arg->asn->modules), mod_next) {
970         TQ_FOR(arg->expr, &(mod->members), next) {
971             if(arg->expr->meta_type != AMT_VALUE)
972                 continue;
973
974             if(arg->expr->expr_type == ASN_BASIC_INTEGER) {
975                 abuf_printf(buf, "#define %s (%s)\n",
976                             asn1c_make_identifier(AMI_USE_PREFIX, arg->expr, 0),
977                             asn1p_itoa(arg->expr->value->value.v_integer));
978                 empty_file = 0;
979             }
980         }
981     }
982
983     abuf_printf(buf, "\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _%sASN_CONSTANT_H */\n", asn1c_prefix());
984
985     if(empty_file) {
986         abuf_free(buf);
987         return 0;
988     }
989     return buf;
990 }
991
992 static int
993 generate_constant_file(arg_t *arg, const char *destdir) {
994     abuf *buf = generate_constant_collection(arg);
995     char *filename;
996     int filename_len;
997
998     if(!buf) return 0;
999
1000     filename_len = strlen(asn1c_prefix()) + strlen("asn_constant");
1001     filename = calloc(filename_len + 1, 1);
1002     snprintf(filename, filename_len + 1, "%sasn_constant", asn1c_prefix());
1003
1004     if(arg->flags & A1C_PRINT_COMPILED) {
1005         printf("\n/*** <<< asn_constant.h >>> ***/\n\n");
1006         safe_fwrite(buf->buffer, buf->length, 1, stdout);
1007     } else {
1008
1009         FILE *fp = asn1c_open_file(destdir, filename, ".h", 0);
1010         if(fp == NULL) {
1011             perror("asn_constant.h");
1012             return -1;
1013         }
1014         safe_fwrite(buf->buffer, buf->length, 1, fp);
1015         fclose(fp);
1016     }
1017     safe_fprintf(stderr, "Generated %s.h\n", filename);
1018     free(filename);
1019     abuf_free(buf);
1020     return 0;
1021 }