retaining old file if contents are the same


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@736 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1compiler/asn1c_C.c b/libasn1compiler/asn1c_C.c
index 5cd9e05..e35f534 100644
--- a/libasn1compiler/asn1c_C.c
+++ b/libasn1compiler/asn1c_C.c
@@ -7,6 +7,7 @@
 #include "asn1c_constraint.h"
 #include "asn1c_out.h"
 #include "asn1c_misc.h"
+#include "asn1c_compat.h"
 #include <asn1fix_export.h>	/* Stuff exported by libasn1fix */
 
 typedef struct tag2el_s {
@@ -1703,9 +1704,9 @@
 	int using_type_name = 0;
 	char *p;
 
-	OUT("/* %s defined in %s at line %d */\n",
+	OUT("/* %s defined in %s near line %d */\n",
 		MKID_nc(expr->Identifier),
-		arg->mod->source_file_name, expr->_lineno);
+		a1c_basename(arg->mod->source_file_name), expr->_lineno);
 	if(HIDE_INNER_DEFS)
 		OUT("static /* Use -fall-defs-global to expose */\n");
 	OUT("asn_TYPE_descriptor_t asn_DEF_%s", MKID_nc(expr->Identifier));
diff --git a/libasn1compiler/asn1c_compat.c b/libasn1compiler/asn1c_compat.c
index ebc6d3f..2a80251 100644
--- a/libasn1compiler/asn1c_compat.c
+++ b/libasn1compiler/asn1c_compat.c
@@ -14,7 +14,7 @@
 #endif
 
 FILE *
-asn1c_open_file(const char *name, const char *ext) {
+asn1c_open_file(const char *name, const char *ext, char **opt_tmpname) {
 	int created = 1;
 #ifndef	WIN32
 	struct stat sb;
@@ -22,22 +22,35 @@
 	char *fname;
 	size_t len;
 	FILE *fp;
+	int ret;
 	int fd;
 
 	/*
 	 * Compute filenames.
 	 */
-	len = strlen(name) + strlen(ext) + 1;
+	len = strlen(name) + strlen(ext) + sizeof(".XXXXXX");
 	fname = alloca(len);
-	snprintf(fname, len, "%s%s", name, ext);
+	ret = snprintf(fname, len, "%s%s%s", name, ext,
+		opt_tmpname ? ".XXXXXX" : "");
+	assert(ret > 0 && ret < len);
 
-	/*
-	 * Create files.
-	 */
-	fd = open(fname, O_CREAT | O_EXCL | O_WRONLY, DEFFILEMODE);
-	if(fd == -1 && errno == EEXIST) {
-		fd = open(fname, O_WRONLY, DEFFILEMODE);
-		created = 0;
+	if(opt_tmpname) {
+		/*
+		 * Create temporary file.
+		 */
+		fd = mkstemp(fname);
+#ifndef	WIN32
+		(void)fchmod(fd, DEFFILEMODE);
+#endif
+	} else {
+		/*
+		 * Create specified file, or open the old one.
+		 */
+		fd = open(fname, O_CREAT | O_EXCL | O_WRONLY, DEFFILEMODE);
+		if(fd == -1 && errno == EEXIST) {
+			fd = open(fname, O_WRONLY, DEFFILEMODE);
+			created = 0;
+		}
 	}
 	if(fd == -1) {
 		perror(fname);
@@ -68,6 +81,13 @@
 		if(created) unlink(fname);
 		close(fd);
 	}
+
+	/* Return the temporary file name */
+	if(opt_tmpname) {
+		*opt_tmpname = strdup(fname);
+		assert(*opt_tmpname);
+	}
+
 	return fp;
 }
 
diff --git a/libasn1compiler/asn1c_compat.h b/libasn1compiler/asn1c_compat.h
index 44ac438..8aa3865 100644
--- a/libasn1compiler/asn1c_compat.h
+++ b/libasn1compiler/asn1c_compat.h
@@ -3,8 +3,12 @@
 
 /*
  * Open the arbitrary file by its base name and extension.
+ * If opt_tmpname is given, a temporary file will be created and
+ * its name returned in (*opt_tmpname).
+ * The (*opt_tmpname) should then be subsequently freed by free(3).
  */
-FILE *asn1c_open_file(const char *base_part, const char *extension);
+FILE *asn1c_open_file(const char *base_part, const char *extension,
+	char **opt_tmpname);
 
 /*
  * Obtain base name and directory name of a path.
diff --git a/libasn1compiler/asn1c_save.c b/libasn1compiler/asn1c_save.c
index 1abf242..2d0410f 100644
--- a/libasn1compiler/asn1c_save.c
+++ b/libasn1compiler/asn1c_save.c
@@ -10,6 +10,7 @@
 static int asn1c_print_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);
+static int identical_files(const char *fname1, const char *fname2);
 
 int
 asn1c_save_compiled_output(arg_t *arg, const char *datadir) {
@@ -41,7 +42,7 @@
 		return 0;	/* Finished */
 	}
 
-	mkf = asn1c_open_file("Makefile.am", ".sample");
+	mkf = asn1c_open_file("Makefile.am", ".sample", 0);
 	if(mkf == NULL) {
 		perror("Makefile.am.sample");
 		return -1;
@@ -174,7 +175,11 @@
 	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",
@@ -182,11 +187,11 @@
 		return -1;
 	}
 
-	fp_c = asn1c_open_file(expr->Identifier, ".c");
-	fp_h = asn1c_open_file(expr->Identifier, ".h");
+	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) fclose(fp_c);	/* lacks unlink() */
-		if(fp_h) fclose(fp_h);	/* lacks unlink() */
+		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;
 	}
 
@@ -250,11 +255,70 @@
 
 	fclose(fp_c);
 	fclose(fp_h);
-	fprintf(stderr, "Compiled %s.c\n", expr->Identifier);
-	fprintf(stderr, "Compiled %s.h\n", expr->Identifier);
+
+	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);
+			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);
+			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
+identical_files(const char *fname1, const char *fname2) {
+	char buf[2][8192];
+	FILE *fp1, *fp2;
+	size_t olen, nlen;
+	int retval = 1;	/* Files are identical */
+
+	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.
  */
@@ -265,9 +329,12 @@
 	size_t len;
 	int retval = 0;
 
-	fpsrc = fopen(src, "rb");
+	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 = fopen(src, "wb");
+	fpdst = asn1c_open_file(dst, "", 0);
 	if(!fpdst) { fclose(fpsrc); errno = EIO; return -1; }
 
 	while(!feof(fpsrc)) {
diff --git a/libasn1compiler/asn1compiler.h b/libasn1compiler/asn1compiler.h
index 817e06c..d073517 100644
--- a/libasn1compiler/asn1compiler.h
+++ b/libasn1compiler/asn1compiler.h
@@ -42,7 +42,7 @@
 	/*
 	 * Generate type_id_PR_member things identifiers of id_PR_member.
 	 */
-	A1C_DOUBLE_IDENTIFIERS	= 0x0100,
+	A1C_COMPOUND_NAMES	= 0x0100,
 };
 
 /*