mirror of https://gerrit.osmocom.org/asn1c
522 lines
13 KiB
C
522 lines
13 KiB
C
#include "asn1c_internal.h"
|
|
#include "asn1c_compat.h"
|
|
#include "asn1c_fdeps.h"
|
|
#include "asn1c_lang.h"
|
|
#include "asn1c_misc.h"
|
|
#include "asn1c_save.h"
|
|
#include "asn1c_out.h"
|
|
|
|
static int asn1c_dump_streams(arg_t *arg, asn1c_fdeps_t *, int, char **);
|
|
static int asn1c_print_streams(arg_t *arg);
|
|
static int asn1c_save_streams(arg_t *arg, asn1c_fdeps_t *, int, char **);
|
|
static int asn1c_copy_over(arg_t *arg, char *path);
|
|
static int identical_files(const char *fname1, const char *fname2);
|
|
static int generate_pdu_collection_file(arg_t *arg);
|
|
static int generate_preamble(arg_t *, FILE *, int optc, char **argv);
|
|
|
|
int
|
|
asn1c_save_compiled_output(arg_t *arg, const char *datadir,
|
|
int argc, int optc, char **argv) {
|
|
asn1c_fdeps_t *deps = 0;
|
|
asn1c_fdeps_t *dlist;
|
|
asn1p_module_t *mod;
|
|
FILE *mkf; /* Makefile.am.sample */
|
|
int i;
|
|
|
|
deps = asn1c_read_file_dependencies(arg, datadir);
|
|
if(!deps && datadir) {
|
|
WARNING("Cannot read file-dependencies information "
|
|
"from %s\n", datadir);
|
|
}
|
|
|
|
TQ_FOR(mod, &(arg->asn->modules), mod_next) {
|
|
TQ_FOR(arg->expr, &(mod->members), next) {
|
|
if(asn1_lang_map[arg->expr->meta_type]
|
|
[arg->expr->expr_type].type_cb) {
|
|
if(asn1c_dump_streams(arg, deps, optc, argv))
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Dump out the Makefile template and the rest of the support code.
|
|
*/
|
|
if((arg->flags & A1C_PRINT_COMPILED)
|
|
|| (arg->flags & A1C_OMIT_SUPPORT_CODE)) {
|
|
return 0; /* Finished */
|
|
}
|
|
|
|
mkf = asn1c_open_file("Makefile.am", ".sample", 0);
|
|
if(mkf == NULL) {
|
|
perror("Makefile.am.sample");
|
|
return -1;
|
|
}
|
|
|
|
fprintf(mkf, "ASN_MODULE_SOURCES=");
|
|
TQ_FOR(mod, &(arg->asn->modules), mod_next) {
|
|
TQ_FOR(arg->expr, &(mod->members), next) {
|
|
if(asn1_lang_map[arg->expr->meta_type]
|
|
[arg->expr->expr_type].type_cb) {
|
|
fprintf(mkf, "\t\\\n\t%s.c",
|
|
arg->expr->Identifier);
|
|
}
|
|
}
|
|
}
|
|
fprintf(mkf, "\n\nASN_MODULE_HEADERS=");
|
|
TQ_FOR(mod, &(arg->asn->modules), mod_next) {
|
|
TQ_FOR(arg->expr, &(mod->members), next) {
|
|
if(asn1_lang_map[arg->expr->meta_type]
|
|
[arg->expr->expr_type].type_cb) {
|
|
fprintf(mkf, "\t\\\n\t%s.h",
|
|
arg->expr->Identifier);
|
|
}
|
|
}
|
|
}
|
|
fprintf(mkf, "\n\n");
|
|
|
|
/*
|
|
* Move necessary skeleton files and add them to Makefile.am.sample.
|
|
*/
|
|
dlist = asn1c_deps_makelist(deps);
|
|
if(dlist) {
|
|
char buf[8129];
|
|
char *dir_end;
|
|
size_t dlen = strlen(datadir);
|
|
|
|
assert(dlen < (sizeof(buf) / 2 - 2));
|
|
memcpy(buf, datadir, dlen);
|
|
dir_end = buf + dlen;
|
|
*dir_end++ = '/';
|
|
|
|
for(i = 0; i < dlist->el_count; i++) {
|
|
char *what_class; /* MODULE or CONVERTER */
|
|
char *what_kind; /* HEADERS or SOURCES */
|
|
char *fname = dlist->elements[i]->filename;
|
|
char *dotH;
|
|
|
|
assert(strlen(fname) < (sizeof(buf) / 2));
|
|
strcpy(dir_end, fname);
|
|
|
|
if(asn1c_copy_over(arg, buf) == -1) {
|
|
fprintf(mkf, ">>>ABORTED<<<");
|
|
fclose(mkf);
|
|
return -1;
|
|
}
|
|
|
|
/* MODULE data versus CONVERTER data */
|
|
switch(dlist->elements[i]->usage) {
|
|
case FDEP_CONVERTER: what_class = "CONVERTER"; break;
|
|
default: what_class= "MODULE"; break;
|
|
}
|
|
|
|
/* HEADERS versus SOURCES */
|
|
dotH = strrchr(fname, 'h');
|
|
if(dotH && fname<dotH && dotH[-1] == '.' && !dotH[1])
|
|
what_kind = "HEADERS";
|
|
else
|
|
what_kind = "SOURCES";
|
|
fprintf(mkf, "ASN_%s_%s+=%s\n",
|
|
what_class, what_kind, fname);
|
|
}
|
|
}
|
|
|
|
if(arg->flags & A1C_PDU_AUTO) {
|
|
fprintf(mkf, "ASN_CONVERTER_SOURCES+=pdu_collection.c\n");
|
|
if(generate_pdu_collection_file(arg))
|
|
return -1;
|
|
}
|
|
|
|
fprintf(mkf, "\n\n"
|
|
"lib_LTLIBRARIES=libsomething.la\n"
|
|
"libsomething_la_SOURCES="
|
|
"$(ASN_MODULE_SOURCES) $(ASN_MODULE_HEADERS)\n"
|
|
"\n"
|
|
"# This file may be used as an input for make(3)\n"
|
|
"# Remove the lines below to convert it into a pure .am file\n"
|
|
"TARGET = progname\n"
|
|
"CFLAGS +=%s -I.\n"
|
|
"OBJS=${ASN_MODULE_SOURCES:.c=.o}"
|
|
" ${ASN_CONVERTER_SOURCES:.c=.o}\n"
|
|
"\nall: $(TARGET)\n"
|
|
"\n$(TARGET): ${OBJS}"
|
|
"\n\t$(CC) $(CFLAGS) -o $(TARGET) ${OBJS} $(LDFLAGS) $(LIBS)\n"
|
|
"\n.SUFFIXES:"
|
|
"\n.SUFFIXES: .c .o\n"
|
|
"\n.c.o:"
|
|
"\n\t$(CC) $(CFLAGS) -o $@ -c $<\n"
|
|
"\nclean:"
|
|
"\n\trm -f $(TARGET)"
|
|
"\n\trm -f $(OBJS)\n"
|
|
"\nregen: regenerate-from-asn1-source\n"
|
|
"\nregenerate-from-asn1-source:\n\t"
|
|
, (arg->flags & A1C_PDU_AUTO) ? " -DASN_PDU_COLLECTION" : ""
|
|
);
|
|
|
|
for(i = 0; i < argc; i++)
|
|
fprintf(mkf, "%s%s", i ? " " : "", argv[i]);
|
|
fprintf(mkf, "\n\n");
|
|
|
|
fclose(mkf);
|
|
fprintf(stderr, "Generated Makefile.am.sample\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Dump the streams.
|
|
*/
|
|
static int
|
|
asn1c_dump_streams(arg_t *arg, asn1c_fdeps_t *deps, int optc, char **argv) {
|
|
if(arg->flags & A1C_PRINT_COMPILED) {
|
|
return asn1c_print_streams(arg);
|
|
} else {
|
|
return asn1c_save_streams(arg, deps, optc, argv);
|
|
}
|
|
}
|
|
|
|
static int
|
|
asn1c_print_streams(arg_t *arg) {
|
|
compiler_streams_t *cs = arg->expr->data;
|
|
asn1p_expr_t *expr = arg->expr;
|
|
int i;
|
|
|
|
for(i = 1; i < OT_MAX; i++) {
|
|
out_chunk_t *ot;
|
|
if(TQ_FIRST(&cs->destination[i].chunks) == NULL)
|
|
continue;
|
|
|
|
printf("\n/*** <<< %s [%s] >>> ***/\n\n",
|
|
_compiler_stream2str[i],
|
|
expr->Identifier);
|
|
|
|
TQ_FOR(ot, &(cs->destination[i].chunks), next) {
|
|
fwrite(ot->buf, ot->len, 1, stdout);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
asn1c_save_streams(arg_t *arg, asn1c_fdeps_t *deps, int optc, char **argv) {
|
|
asn1p_expr_t *expr = arg->expr;
|
|
compiler_streams_t *cs = expr->data;
|
|
out_chunk_t *ot;
|
|
FILE *fp_c, *fp_h;
|
|
char *tmpname_c, *tmpname_h;
|
|
char *name_buf;
|
|
char *header_id;
|
|
const char *c_retained = "";
|
|
const char *h_retained = "";
|
|
|
|
if(cs == NULL) {
|
|
fprintf(stderr, "Cannot compile %s at line %d\n",
|
|
expr->Identifier, expr->_lineno);
|
|
return -1;
|
|
}
|
|
|
|
fp_c = asn1c_open_file(expr->Identifier, ".c", &tmpname_c);
|
|
fp_h = asn1c_open_file(expr->Identifier, ".h", &tmpname_h);
|
|
if(fp_c == NULL || fp_h == NULL) {
|
|
if(fp_c) { unlink(tmpname_c); free(tmpname_c); fclose(fp_c); }
|
|
if(fp_h) { unlink(tmpname_h); free(tmpname_h); fclose(fp_h); }
|
|
return -1;
|
|
}
|
|
|
|
generate_preamble(arg, fp_c, optc, argv);
|
|
generate_preamble(arg, fp_h, optc, argv);
|
|
|
|
header_id = asn1c_make_identifier(0, expr, NULL);
|
|
fprintf(fp_h,
|
|
"#ifndef\t_%s_H_\n"
|
|
"#define\t_%s_H_\n"
|
|
"\n", header_id, header_id);
|
|
|
|
fprintf(fp_h, "\n#include <asn_application.h>\n");
|
|
|
|
#define SAVE_STREAM(fp, idx, msg, actdep) do { \
|
|
if(TQ_FIRST(&(cs->destination[idx].chunks)) && *msg) \
|
|
fprintf(fp, "\n/* %s */\n", msg); \
|
|
TQ_FOR(ot, &(cs->destination[idx].chunks), next) { \
|
|
if(actdep) asn1c_activate_dependency(deps, 0, ot->buf); \
|
|
fwrite(ot->buf, ot->len, 1, fp); \
|
|
} \
|
|
} while(0)
|
|
|
|
SAVE_STREAM(fp_h, OT_INCLUDES, "Including external dependencies", 1);
|
|
|
|
fprintf(fp_h, "\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n");
|
|
SAVE_STREAM(fp_h, OT_DEPS, "Dependencies", 0);
|
|
SAVE_STREAM(fp_h, OT_FWD_DECLS, "Forward declarations", 0);
|
|
SAVE_STREAM(fp_h, OT_TYPE_DECLS, expr->Identifier, 0);
|
|
SAVE_STREAM(fp_h, OT_FUNC_DECLS,"Implementation", 0);
|
|
fprintf(fp_h, "\n#ifdef __cplusplus\n}\n#endif\n");
|
|
|
|
if(!(arg->flags & A1C_NO_INCLUDE_DEPS))
|
|
SAVE_STREAM(fp_h, OT_POST_INCLUDE, "Referred external types", 1);
|
|
|
|
fprintf(fp_h, "\n#endif\t/* _%s_H_ */\n", header_id);
|
|
|
|
fprintf(fp_c, "#include <asn_internal.h>\n\n");
|
|
fprintf(fp_c, "#include \"%s.h\"\n\n", expr->Identifier);
|
|
if(arg->flags & A1C_NO_INCLUDE_DEPS)
|
|
SAVE_STREAM(fp_c, OT_POST_INCLUDE, "", 1);
|
|
TQ_FOR(ot, &(cs->destination[OT_CTABLES].chunks), next)
|
|
fwrite(ot->buf, ot->len, 1, fp_c);
|
|
TQ_FOR(ot, &(cs->destination[OT_CODE].chunks), next)
|
|
fwrite(ot->buf, ot->len, 1, fp_c);
|
|
TQ_FOR(ot, &(cs->destination[OT_STAT_DEFS].chunks), next)
|
|
fwrite(ot->buf, ot->len, 1, fp_c);
|
|
|
|
assert(OT_MAX == 10); /* Protection from reckless changes */
|
|
|
|
fclose(fp_c);
|
|
fclose(fp_h);
|
|
|
|
name_buf = alloca(strlen(expr->Identifier) + 3);
|
|
|
|
sprintf(name_buf, "%s.c", expr->Identifier);
|
|
if(identical_files(name_buf, tmpname_c)) {
|
|
c_retained = " (contents unchanged)";
|
|
unlink(tmpname_c);
|
|
} else {
|
|
if(rename(tmpname_c, name_buf)) {
|
|
unlink(tmpname_c);
|
|
perror(tmpname_c);
|
|
free(tmpname_c);
|
|
free(tmpname_h);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
sprintf(name_buf, "%s.h", expr->Identifier);
|
|
if(identical_files(name_buf, tmpname_h)) {
|
|
h_retained = " (contents unchanged)";
|
|
unlink(tmpname_h);
|
|
} else {
|
|
if(rename(tmpname_h, name_buf)) {
|
|
unlink(tmpname_h);
|
|
perror(tmpname_h);
|
|
free(tmpname_c);
|
|
free(tmpname_h);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
free(tmpname_c);
|
|
free(tmpname_h);
|
|
|
|
fprintf(stderr, "Compiled %s.c%s\n",
|
|
expr->Identifier, c_retained);
|
|
fprintf(stderr, "Compiled %s.h%s\n",
|
|
expr->Identifier, h_retained);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
generate_preamble(arg_t *arg, FILE *fp, int optc, char **argv) {
|
|
fprintf(fp,
|
|
"/*\n"
|
|
" * Generated by asn1c-" VERSION " (http://lionet.info/asn1c)\n"
|
|
" * From ASN.1 module \"%s\"\n"
|
|
" * \tfound in \"%s\"\n",
|
|
arg->expr->module->ModuleName,
|
|
arg->expr->module->source_file_name);
|
|
if(optc > 1) {
|
|
int i;
|
|
fprintf(fp, " * \t`asn1c ");
|
|
for(i = 1; i < optc; i++)
|
|
fprintf(fp, "%s%s", i>1?" ":"", argv[i]);
|
|
fprintf(fp, "`\n");
|
|
}
|
|
fprintf(fp, " */\n\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
identical_files(const char *fname1, const char *fname2) {
|
|
char buf[2][4096];
|
|
FILE *fp1, *fp2;
|
|
size_t olen, nlen;
|
|
int retval = 1; /* Files are identical */
|
|
|
|
#ifndef WIN32
|
|
struct stat sb;
|
|
|
|
if(lstat(fname1, &sb) || !S_ISREG(sb.st_mode)
|
|
|| lstat(fname2, &sb) || !S_ISREG(sb.st_mode)) {
|
|
return 0; /* Files are not identical */
|
|
}
|
|
#endif
|
|
|
|
fp1 = fopen(fname1, "r");
|
|
if(!fp1) { return 0; }
|
|
fp2 = fopen(fname2, "r");
|
|
if(!fp2) { fclose(fp1); return 0; }
|
|
|
|
while((olen = fread(buf[0], 1, sizeof(buf[0]), fp1))) {
|
|
nlen = fread(buf[1], 1, olen, fp2);
|
|
if(nlen != olen || memcmp(buf[0], buf[1], nlen)) {
|
|
retval = 0;
|
|
break;
|
|
}
|
|
}
|
|
nlen = fread(buf[1], 1, 1, fp2);
|
|
if(nlen) retval = 0;
|
|
|
|
fclose(fp1);
|
|
fclose(fp2);
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* Copy file for real.
|
|
*/
|
|
static int
|
|
real_copy(const char *src, const char *dst) {
|
|
unsigned char buf[4096];
|
|
char *tmpname;
|
|
FILE *fpsrc, *fpdst;
|
|
size_t len;
|
|
int retval = 0;
|
|
|
|
if(identical_files(src, dst))
|
|
return retval; /* Success, no need to copy for real. */
|
|
|
|
fpsrc = fopen(src, "r");
|
|
if(!fpsrc) { errno = EIO; return -1; }
|
|
fpdst = asn1c_open_file(dst, "", &tmpname);
|
|
if(!fpdst) { fclose(fpsrc); errno = EIO; return -1; }
|
|
|
|
while(!feof(fpsrc)) {
|
|
len = fread(buf, 1, sizeof(buf), fpsrc);
|
|
if(fwrite(buf, 1, len, fpdst) != len) {
|
|
perror(tmpname);
|
|
errno = EIO;
|
|
retval = -1;
|
|
break;
|
|
}
|
|
}
|
|
fclose(fpsrc);
|
|
fclose(fpdst);
|
|
|
|
/* Check if copied correctly, and rename into a permanent name */
|
|
if(retval) {
|
|
unlink(tmpname);
|
|
} else if(rename(tmpname, dst)) {
|
|
unlink(tmpname);
|
|
perror(tmpname);
|
|
retval = -1;
|
|
}
|
|
free(tmpname);
|
|
return retval;
|
|
}
|
|
|
|
static int
|
|
asn1c_copy_over(arg_t *arg, char *path) {
|
|
char *fname;
|
|
#ifdef WIN32
|
|
int use_real_copy = 1;
|
|
#else
|
|
int use_real_copy = (arg->flags & A1C_SKELETONS_COPY);
|
|
#endif
|
|
|
|
fname = a1c_basename(path);
|
|
if(!fname
|
|
|| (use_real_copy ? real_copy(path, fname) : symlink(path, fname))
|
|
) {
|
|
if(errno == EEXIST) {
|
|
struct stat sb1, sb2;
|
|
if(stat(path, &sb1) == 0
|
|
&& stat(fname, &sb2) == 0
|
|
&& sb1.st_dev == sb2.st_dev
|
|
&& sb1.st_ino == sb2.st_ino) {
|
|
/*
|
|
* Nothing to do.
|
|
*/
|
|
fprintf(stderr,
|
|
"File %s is already here as %s\n",
|
|
path, fname);
|
|
return 1;
|
|
} else {
|
|
fprintf(stderr,
|
|
"Retaining local %s (%s suggested)\n",
|
|
fname, path);
|
|
return 1;
|
|
}
|
|
} else if(errno == ENOENT) {
|
|
/* Ignore this */
|
|
return 0;
|
|
} else {
|
|
fprintf(stderr, "%s %s -> %s failed: %s\n",
|
|
use_real_copy ? "Copy" : "Symlink",
|
|
path, fname, strerror(errno));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "%s %s\t-> %s\n",
|
|
use_real_copy ? "Copied" : "Symlinked", path, fname);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
generate_pdu_collection_file(arg_t *arg) {
|
|
asn1p_module_t *mod;
|
|
FILE *fp;
|
|
|
|
fp = asn1c_open_file("pdu_collection", ".c", 0);
|
|
if(fp == NULL) {
|
|
perror("pdu_collection.c");
|
|
return -1;
|
|
}
|
|
|
|
fprintf(fp,
|
|
"/*\n"
|
|
" * Generated by asn1c-" VERSION " (http://lionet.info/asn1c)\n"
|
|
" */\n\n");
|
|
fprintf(fp, "struct asn_TYPE_descriptor_s;\t"
|
|
"/* Forward declaration */\n\n");
|
|
|
|
TQ_FOR(mod, &(arg->asn->modules), mod_next) {
|
|
TQ_FOR(arg->expr, &(mod->members), next) {
|
|
if(arg->expr->_type_referenced
|
|
|| !asn1_lang_map[arg->expr->meta_type]
|
|
[arg->expr->expr_type].type_cb)
|
|
continue;
|
|
fprintf(fp, "extern struct asn_TYPE_descriptor_s "
|
|
"asn_DEF_%s;\n",
|
|
asn1c_make_identifier(0, arg->expr, NULL));
|
|
}
|
|
}
|
|
|
|
fprintf(fp, "\n\n");
|
|
fprintf(fp, "struct asn_TYPE_descriptor_s *asn_pdu_collection[] = {\n");
|
|
TQ_FOR(mod, &(arg->asn->modules), mod_next) {
|
|
int mod_printed = 0;
|
|
TQ_FOR(arg->expr, &(mod->members), next) {
|
|
if(arg->expr->_type_referenced
|
|
|| !asn1_lang_map[arg->expr->meta_type]
|
|
[arg->expr->expr_type].type_cb)
|
|
continue;
|
|
if(!mod_printed++)
|
|
fprintf(fp, "\t/* From module %s in %s */\n",
|
|
arg->expr->module->ModuleName,
|
|
arg->expr->module->source_file_name);
|
|
fprintf(fp, "\t&asn_DEF_%s,\t\n",
|
|
asn1c_make_identifier(0, arg->expr, NULL));
|
|
}
|
|
}
|
|
|
|
fprintf(fp, "\t0\n};\n\n");
|
|
|
|
fclose(fp);
|
|
fprintf(stderr, "Generated pdu_collection.c\n");
|
|
|
|
return 0;
|
|
}
|
|
|