file dependencies


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@147 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1compiler/asn1c_fdeps.c b/libasn1compiler/asn1c_fdeps.c
new file mode 100644
index 0000000..cb53b3c
--- /dev/null
+++ b/libasn1compiler/asn1c_fdeps.c
@@ -0,0 +1,180 @@
+#include "asn1c_internal.h"
+#include "asn1c_fdeps.h"
+
+static asn1c_fdeps_t *asn1c_new_dep(const char *filename);
+static int asn1c_dep_add(asn1c_fdeps_t *deps, asn1c_fdeps_t *d);
+
+int
+asn1c_activate_dependency(asn1c_fdeps_t *deps, asn1c_fdeps_t *cur, const char *data) {
+	char *fname;
+	int i;
+
+	if(!deps || !data || !*data)
+		return 0;
+	if(!cur) cur = deps;
+
+	if(cur->used_somewhere)
+		return 1;	/* Already activated */
+
+	(const char *)fname = data;
+	if(*data == '#') {
+		const char *start = data;
+		const char *end = 0;
+
+		start = strchr(data, '<');
+		if(start) {
+			start++;
+			end = strchr(start, '>');
+		}
+		if(end) {
+			fname = alloca((end - start) + 1);
+			memcpy(fname, start, end - start);
+			fname[end-start] = '\0';
+		} else {
+			return 0;
+		}
+	}
+
+	if(cur->filename && strcmp(cur->filename, fname) == 0) {
+		cur->used_somewhere = 1;
+
+		/* Activate subdependencies */
+		for(i = 0; i < cur->el_count; i++) {
+			asn1c_activate_dependency(deps,
+				cur->elements[i],
+				cur->elements[i]->filename);
+		}
+
+		/*
+		 * This might be a link to someplace else.
+		 */
+		return asn1c_activate_dependency(deps, NULL, fname);
+	} else {
+		for(i = 0; i < cur->el_count; i++) {
+			asn1c_activate_dependency(deps,
+				cur->elements[i], fname);
+		}
+	}
+
+	return 0;
+}
+
+asn1c_fdeps_t *
+asn1c_read_file_dependencies(arg_t *arg, const char *datadir) {
+	asn1c_fdeps_t *deps;
+	asn1c_fdeps_t *cur;
+	char buf[4096];
+	FILE *f;
+	int hit_COMMON_FILES = 0;
+
+	(void)arg;
+
+	if(!datadir || strlen(datadir) > sizeof(buf) / 2) {
+		errno = EINVAL;
+		return NULL;
+	} else {
+		sprintf(buf, "%s/file-dependencies", datadir);
+	}
+
+	f = fopen(buf, "r");
+	if(!f) return NULL;
+
+	deps = asn1c_new_dep(0);
+	assert(deps);
+
+	while(fgets(buf, sizeof(buf), f)) {
+		char *p = strchr(buf, '#');
+		if(p) *p = '\0';	/* Remove comments */
+
+		cur = deps;
+		for(p = strtok(buf, " \t\r\n"); p;
+				p = strtok(NULL, " \t\r\n")) {
+			asn1c_fdeps_t *d;
+			/*
+			 * If hit "COMMON-FILES:", treat everything else
+			 * as a huge dependency.
+			 */
+			if(strcmp(p, "COMMON-FILES:") == 0) {
+				hit_COMMON_FILES = 1;
+				break;
+			}
+			d = asn1c_new_dep(p);
+			assert(d);
+			d->used_somewhere = hit_COMMON_FILES;
+
+			if(asn1c_dep_add(cur, d) == 1)
+				cur = d;
+		}
+	}
+
+	fclose(f);
+
+	return deps;
+}
+
+static asn1c_fdeps_t *
+asn1c_new_dep(const char *filename) {
+	asn1c_fdeps_t *d;
+
+	d = calloc(1, sizeof(*d));
+	if(filename) {
+		d->filename = strdup(filename);
+		if(!d->filename) return NULL;
+	}
+
+	return d;
+}
+
+static int
+asn1c_dep_add(asn1c_fdeps_t *deps, asn1c_fdeps_t *d) {
+	int n;
+
+	/* Check for duplicates */
+	for(n = 0; n < deps->el_count; n++) {
+		if(strcmp(deps->elements[n]->filename, d->filename) == 0)
+			return 0;
+	}
+
+	if(deps->el_count == deps->el_size) {
+		n = deps->el_size?deps->el_size << 2:16;
+		void *p = realloc(deps->elements,
+			n * sizeof(deps->elements[0]));
+		assert(p);
+		deps->elements = p;
+		deps->el_size = n;
+	}
+
+	deps->elements[deps->el_count++] = d;
+	return 1;
+}
+
+asn1c_fdeps_t *
+asn1c_deps_makelist(asn1c_fdeps_t *deps) {
+	asn1c_fdeps_t *dlist;
+	asn1c_fdeps_t *d;
+	int i;
+
+	if(!deps) {
+		errno = EINVAL;
+		return 0;
+	}
+
+	dlist = asn1c_new_dep(0);
+
+	if(deps->filename && deps->used_somewhere) {
+		d = asn1c_new_dep(deps->filename);
+		asn1c_dep_add(dlist, d);
+	}
+
+	for(i = 0; i < deps->el_count; i++) {
+		int j;
+		d = asn1c_deps_makelist(deps->elements[i]);
+		assert(!d->filename);
+		for(j = 0; j < d->el_count; j++) {
+			asn1c_dep_add(dlist, d->elements[j]);
+		}
+	}
+
+	return dlist;
+}
+
diff --git a/libasn1compiler/asn1c_fdeps.h b/libasn1compiler/asn1c_fdeps.h
new file mode 100644
index 0000000..bb9496f
--- /dev/null
+++ b/libasn1compiler/asn1c_fdeps.h
@@ -0,0 +1,22 @@
+#ifndef	_ASN1C_FDEPS_H_
+#define	_ASN1C_FDEPS_H_
+
+typedef struct asn1c_fdeps_s {
+	char *filename;		/* Or 0, if root. */
+
+	int used_somewhere;	/* Somefile refers to it */
+
+	struct asn1c_fdeps_s **elements;
+	int el_size;
+	int el_count;
+} asn1c_fdeps_t;
+
+asn1c_fdeps_t *asn1c_read_file_dependencies(arg_t *arg, const char *datadir);
+
+/* Data may be a filename or an "#include <>" string. */
+int asn1c_activate_dependency(asn1c_fdeps_t *deps, asn1c_fdeps_t *cur,
+	const char *data);
+
+asn1c_fdeps_t *asn1c_deps_makelist(asn1c_fdeps_t *deps);
+
+#endif	/* _ASN1C_FDEPS_H_ */
diff --git a/libasn1compiler/asn1c_save.c b/libasn1compiler/asn1c_save.c
index 15d1b67..3c63053 100644
--- a/libasn1compiler/asn1c_save.c
+++ b/libasn1compiler/asn1c_save.c
@@ -1,21 +1,29 @@
 #include "asn1c_internal.h"
 #include "asn1c_compat.h"
+#include "asn1c_fdeps.h"
 
-static int asn1c_dump_streams(arg_t *arg);
+static int asn1c_dump_streams(arg_t *arg, asn1c_fdeps_t *);
 static int asn1c_print_streams(arg_t *arg);
-static int asn1c_save_streams(arg_t *arg);
+static int asn1c_save_streams(arg_t *arg, asn1c_fdeps_t *);
 static int asn1c_copy_over(arg_t *arg, char *path);
 
 int
 asn1c_save_compiled_output(arg_t *arg, const char *datadir) {
+	asn1c_fdeps_t *deps = 0;
+	FILE *mkf;
+	asn1c_fdeps_t *dlist;
 
-	(void)datadir;
+	deps = asn1c_read_file_dependencies(arg, datadir);
+	if(!deps && datadir) {
+		WARNING("Cannot read file-dependencies information "
+			"from %s\n", datadir);
+	}
 
 	TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
 		TQ_FOR(arg->expr, &(arg->mod->members), next) {
 			if(asn1_lang_map[arg->expr->meta_type]
 				[arg->expr->expr_type].type_cb) {
-				if(asn1c_dump_streams(arg))
+				if(asn1c_dump_streams(arg, deps))
 					return -1;
 			}
 		}
@@ -24,68 +32,65 @@
 	/*
 	 * Dump out the Makefile template and the rest of the support code.
 	 */
-	if((arg->flags & A1C_PRINT_COMPILED) == 0
-	&& (arg->flags & A1C_OMIT_SUPPORT_CODE) == 0) {
-		glob_t pg;
-		FILE *mkf;
-		char *p;
-		int i;
+	if((arg->flags & A1C_PRINT_COMPILED)
+	|| (arg->flags & A1C_OMIT_SUPPORT_CODE)) {
+		return 0;	/* Finished */
+	}
 
-		i = strlen(datadir) + sizeof("/*.[ch]");
-		p = alloca(i);
-		snprintf(p, i, "%s/*.[ch]", datadir);
+	mkf = asn1c_open_file("Makefile.am", ".sample");
+	if(mkf == NULL) {
+		perror("Makefile.am.sample");
+		return -1;
+	}
 
-		memset(&pg, 0, sizeof(pg));
-		if(glob(p, GLOB_ERR
-#ifdef	GLOB_TILDE
-			| GLOB_TILDE
-#endif	/* GLOB_TILDE */
-		, NULL, &pg)) {
-			fprintf(stderr,
-				"Bad skeletons directory (-S) %s: %s\n",
-				datadir, strerror(errno));
-			return -1;
-		}
-
-		mkf = asn1c_open_file(arg, "Makefile.am", ".sample");
-		if(mkf == NULL) {
-			globfree(&pg);
-			perror("Makefile.am.sample");
-			return -1;
-		}
-
-		fprintf(mkf, "ASN_SRCS=");
-		TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
-			TQ_FOR(arg->expr, &(arg->mod->members), next) {
-				if(asn1_lang_map[arg->expr->meta_type]
-					[arg->expr->expr_type].type_cb) {
-					fprintf(mkf, "\t\\\n\t%s.c %s.h",
-					arg->expr->Identifier,
-					arg->expr->Identifier);
-				}
+	fprintf(mkf, "ASN_MODULE_SOURCES=");
+	TQ_FOR(arg->mod, &(arg->asn->modules), mod_next) {
+		TQ_FOR(arg->expr, &(arg->mod->members), next) {
+			if(asn1_lang_map[arg->expr->meta_type]
+				[arg->expr->expr_type].type_cb) {
+				fprintf(mkf, "\t\\\n\t%s.c %s.h",
+				arg->expr->Identifier,
+				arg->expr->Identifier);
 			}
 		}
+	}
 
-		for(i = 0; i < pg.gl_pathc; i++) {
-			if(asn1c_copy_over(arg, pg.gl_pathv[i])) {
+	/*
+	 * Move necessary skeleton files and add them to Makefile.am.sample.
+	 */
+	dlist = asn1c_deps_makelist(deps);
+	if(dlist) {
+		char buf[8129];
+		char *dir_end;
+		int i = strlen(datadir);
+
+		assert(i < (int)(sizeof(buf) / 2 - 2));
+		memcpy(buf, datadir, i);
+		dir_end = buf + i;
+		*dir_end++ = '/';
+
+		for(i = 0; i < dlist->el_count; i++) {
+			char *fname = dlist->elements[i]->filename;
+
+			assert(strlen(fname) < (sizeof(buf) / 2));
+			strcpy(dir_end, fname);
+
+			if(asn1c_copy_over(arg, buf) == -1) {
 				fprintf(mkf, ">>>ABORTED<<<");
 				fclose(mkf);
-				globfree(&pg);
 				return -1;
 			} else {
-				fprintf(mkf, "\t\\\n\t%s",
-					a1c_basename(pg.gl_pathv[i]));
+				fprintf(mkf, "\t\\\n\t%s", fname);
 			}
 		}
-
-		fprintf(mkf, "\n\n");
-		fprintf(mkf, "lib_LTLIBRARIES=libsomething.la\n");
-		fprintf(mkf, "libsomething_la_SOURCES=${ASN_SRCS}\n");
-		fclose(mkf);
-		fprintf(stderr, "Generated Makefile.am.sample\n");
-		globfree(&pg);
 	}
 
+	fprintf(mkf, "\n\n");
+	fprintf(mkf, "lib_LTLIBRARIES=libsomething.la\n");
+	fprintf(mkf, "libsomething_la_SOURCES=$(ASN_MODULE_SOURCES)\n");
+	fclose(mkf);
+	fprintf(stderr, "Generated Makefile.am.sample\n");
+
 	return 0;
 }
 
@@ -93,11 +98,11 @@
  * Dump the streams.
  */
 static int
-asn1c_dump_streams(arg_t *arg)  {
+asn1c_dump_streams(arg_t *arg, asn1c_fdeps_t *deps)  {
 	if(arg->flags & A1C_PRINT_COMPILED) {
 		return asn1c_print_streams(arg);
 	} else {
-		return asn1c_save_streams(arg);
+		return asn1c_save_streams(arg, deps);
 	}
 }
 
@@ -125,7 +130,7 @@
 }
 
 static int
-asn1c_save_streams(arg_t *arg)  {
+asn1c_save_streams(arg_t *arg, asn1c_fdeps_t *deps) {
 	asn1p_expr_t *expr = arg->expr;
 	compiler_streams_t *cs = expr->data;
 	out_chunk_t *ot;
@@ -138,8 +143,8 @@
 		return -1;
 	}
 
-	fp_c = asn1c_open_file(arg, expr->Identifier, ".c");
-	fp_h = asn1c_open_file(arg, expr->Identifier, ".h");
+	fp_c = asn1c_open_file(expr->Identifier, ".c");
+	fp_h = asn1c_open_file(expr->Identifier, ".h");
 	if(fp_c == NULL || fp_h == NULL) {
 		if(fp_c) fclose(fp_c);	/* lacks unlink() */
 		if(fp_h) fclose(fp_h);	/* lacks unlink() */
@@ -163,15 +168,7 @@
 		arg->mod->source_file_name
 	);
 
-	header_id = alloca(strlen(expr->Identifier) + 1);
-	if(1) {
-		char *src, *dst;
-		for(src = expr->Identifier, dst = header_id;
-				(*dst=*src); src++, dst++)
-			if(!isalnum(*src)) *dst = '_';
-		*dst = '\0';
-	}
-
+	header_id = asn1c_make_identifier(0, expr->Identifier, NULL);
 	fprintf(fp_h,
 		"#ifndef\t_%s_H_\n"
 		"#define\t_%s_H_\n"
@@ -181,8 +178,10 @@
 
 	fprintf(fp_h, "#include <constr_TYPE.h>\n\n");
 
-	TQ_FOR(ot, &(cs->targets[OT_INCLUDES]), next)
+	TQ_FOR(ot, &(cs->targets[OT_INCLUDES]), next) {
+		asn1c_activate_dependency(deps, 0, ot->buf);
 		fwrite(ot->buf, ot->len, 1, fp_h);
+	}
 	fprintf(fp_h, "\n");
 	TQ_FOR(ot, &(cs->targets[OT_DEPS]), next)
 		fwrite(ot->buf, ot->len, 1, fp_h);
@@ -232,13 +231,16 @@
 				fprintf(stderr,
 					"File %s is already here as %s\n",
 					path, fname);
-				return 0;
+				return 1;
 			} else {
 				fprintf(stderr,
 					"Retaining local %s (%s suggested)\n",
 					fname, path);
-				return 0;
+				return 1;
 			}
+		} else if(errno == ENOENT) {
+			/* Ignore this */
+			return 0;
 		} else {
 			fprintf(stderr, "Symlink %s -> %s failed: %s\n",
 				path, fname, strerror(errno));
@@ -248,6 +250,6 @@
 
 	fprintf(stderr, "Symlinked %s\t-> %s\n", path, fname);
 
-	return 0;
+	return 1;
 }
 
diff --git a/libasn1compiler/asn1c_save.h b/libasn1compiler/asn1c_save.h
index 487e625..8d4b1f5 100644
--- a/libasn1compiler/asn1c_save.h
+++ b/libasn1compiler/asn1c_save.h
@@ -1,6 +1,6 @@
-#ifndef	_ASN1_SAVE_H_
-#define	_ASN1_SAVE_H_
+#ifndef	_ASN1C_SAVE_H_
+#define	_ASN1C_SAVE_H_
 
 int asn1c_save_compiled_output(arg_t *arg, const char *datadir);
 
-#endif	/* _ASN1_SAVE_H_ */
+#endif	/* _ASN1C_SAVE_H_ */