[m-rev.] for review: module initialisation for erlang
Peter Wang
wangp at students.csse.unimelb.edu.au
Thu Jun 7 17:30:47 AEST 2007
On 2007-06-07, Julien Fischer <juliensf at csse.unimelb.edu.au> wrote:
> >@@ -784,7 +785,11 @@
> > int i;
> > String_List *tmp_slist;
> >
> >- while ((c = getopt(argc, argv, "A:c:g:iI:lo:r:tw:xX:ks")) != EOF) {
> >+ /*
> >+ ** The set of options for mkinit and mkinit_erl should be
> >+ ** kept in sync, even if they may not necessarily make sense.
> >+ */
>
> Why? The compiler shouldn't be invoking mkinit_erl (mkinit) with
> options that don't make sense.
It would be confusing using the same option name for two different
purposes. I made them abort instead of ignoring unknown options.
> The code that is common to mkinit.c mkinit_erl could (and should) be
> factored out and placed in a separate source file.
Here is the diff.
Peter
--- util/.cvsignore 7 May 2007 06:59:24 -0000 1.3
+++ util/.cvsignore 7 Jun 2007 05:31:27 -0000
@@ -1,4 +1,5 @@
mkinit
+mkinit_erl
mdemangle
info_to_mdb
pad_backslash
--- util/Mmakefile 29 Nov 2006 04:51:41 -0000 1.19
+++ util/Mmakefile 7 Jun 2007 05:31:27 -0000
@@ -19,7 +19,7 @@
# we need -I ../runtime for "mercury_std.h", etc.
# the -O0 is to get around a stupid compiler bug in gcc 2.7.2.3 on cyclone
-PROGS=mkinit mdemangle info_to_mdb
+PROGS=mkinit mkinit_erl mdemangle info_to_mdb
PROGFILENAMES=$(PROGS:%=%$(EXT_FOR_EXE))
SRC=$(PROGS:%=%.c)
@@ -33,6 +33,7 @@
# mkinit.c needs `struct stat'
MGNUCFLAGS-mkinit = --no-ansi
+MGNUCFLAGS-mkinit_erl = --no-ansi
#-----------------------------------------------------------------------------#
@@ -42,6 +43,14 @@
$(MGNUC) --no-mercury-stdlib-dir \
$(GRADEFLAGS) $(ALL_MGNUCFLAGS) -o $@ $< $(GETOPT_SRC)
+mkinit$(EXT_FOR_EXE): mkinit.c mkinit_common.c mkinit_common.h
+ $(MGNUC) --no-mercury-stdlib-dir \
+ $(GRADEFLAGS) $(ALL_MGNUCFLAGS) -o $@ $^ $(GETOPT_SRC)
+
+mkinit_erl$(EXT_FOR_EXE): mkinit_erl.c mkinit_common.c mkinit_common.h
+ $(MGNUC) --no-mercury-stdlib-dir \
+ $(GRADEFLAGS) $(ALL_MGNUCFLAGS) -o $@ $^ $(GETOPT_SRC)
+
tags:
ctags $(SRC)
--- util/mkinit.c 9 Feb 2007 04:05:18 -0000 1.115
+++ util/mkinit.c 7 Jun 2007 05:31:27 -0000
@@ -31,6 +31,7 @@
** - compiler/compile_target_code.m
** in particular the predicates make_init_obj/7 and
** make_standalone_interface/3.
+** - util/mkinit_erl.c
**
*/
@@ -41,17 +42,7 @@
#include "mercury_std.h"
#include "getopt.h"
#include "mercury_array_macros.h"
-
-/*
-** mercury_array_macros.h uses the MR_NEW_ARRAY and MR_RESIZE_ARRAY macros.
-*/
-
-#define MR_NEW_ARRAY(type, num) \
- ((type *) malloc((num) * sizeof(type)))
-
-#define MR_RESIZE_ARRAY(ptr, type, num) \
- ((type *) realloc((ptr), (num) * sizeof(type)))
-
+#include "mkinit_common.h"
#include <stdio.h>
#include <stdlib.h>
@@ -69,16 +60,9 @@
/* --- adjustable limits --- */
#define MAXCALLS 40 /* maximum number of calls per function */
-#define MAXLINE 256 /* maximum number of characters per line */
- /* (characters after this limit are ignored) */
/* --- used to collect a list of strings --- */
-typedef struct String_List_struct {
- char *data;
- struct String_List_struct *next;
-} String_List;
-
static const char if_need_to_init[] =
"#if defined(MR_MAY_NEED_INITIALIZATION)\n";
@@ -134,7 +118,9 @@
"_type_tables",
"_debugger",
"_complexity",
- "write_out_proc_statics"
+ "write_out_proc_statics",
+ "",
+ ""
};
const char *bunch_function_guard[] =
@@ -217,8 +203,6 @@
/* --- global variables --- */
-static const char *MR_progname = NULL;
-
/*
** List of names of the modules to call all the usual initialization
** functions for: "init", "init_type_tables", "init_debugger" and (with
@@ -302,20 +286,12 @@
static int num_experimental_complexity_procs = 0;
-static int num_errors = 0;
-
/* List of options to pass to the runtime */
static String_List *runtime_flags = NULL;
/* Pointer to tail of the runtime_flags list */
static String_List **runtime_flags_tail = &runtime_flags;
- /* List of directories to search for init files */
-static String_List *init_file_dirs = NULL;
-
- /* Pointer to tail of the init_file_dirs list */
-static String_List **init_file_dirs_tail = &init_file_dirs;
-
/* List of functions to always execute at initialization */
static String_List *always_exec_funcs = NULL;
@@ -553,11 +529,6 @@
/* --- function prototypes --- */
static void parse_options(int argc, char *argv[]);
static void usage(void);
-static void set_output_file(void);
-static void do_path_search(void);
-static char *find_init_file(const char *base_name);
-static MR_bool file_exists(const char *filename);
-static char *read_line(const char *filename, FILE *fp, int max);
static void output_complexity_proc(const char *procname);
static void output_complexity_experiment_table(const char *filename);
static void output_headers(void);
@@ -572,39 +543,6 @@
static void output_init_function(const char *func_name,
int *num_bunches_ptr, int *num_calls_in_cur_bunch_ptr,
Purpose purpose);
-static int get_line(FILE *file, char *line, int line_max);
-static void *checked_malloc(size_t size);
-static char *checked_strdup(const char *str);
-static char *checked_strdupcat(const char *str, const char *suffix);
-
-/*---------------------------------------------------------------------------*/
-
-#ifndef MR_HAVE_STRERROR
-
-/*
-** Apparently SunOS 4.1.3 doesn't have strerror()
-** (!%^&!^% non-ANSI systems, grumble...)
-**
-** This code is duplicated in runtime/mercury_prof.c.
-*/
-
-extern int sys_nerr;
-extern char *sys_errlist[];
-
-char *
-strerror(int errnum)
-{
- if (errnum >= 0 && errnum < sys_nerr && sys_errlist[errnum] != NULL) {
- return sys_errlist[errnum];
- } else {
- static char buf[30];
-
- sprintf(buf, "Error %d", errnum);
- return buf;
- }
-}
-
-#endif
/*---------------------------------------------------------------------------*/
@@ -626,7 +564,7 @@
/* If the open fails, we won't write to the file */
#endif
- set_output_file();
+ set_output_file(output_file_name);
switch (output_task) {
case TASK_OUTPUT_LIB_INIT:
@@ -704,7 +642,7 @@
int num_bunches;
int i;
- do_path_search();
+ do_path_search(files, num_files);
output_headers();
for (filenum = 0; filenum < num_files; filenum++) {
@@ -784,7 +722,11 @@
int i;
String_List *tmp_slist;
- while ((c = getopt(argc, argv, "A:c:g:iI:lo:r:tw:xX:ks")) != EOF) {
+ /*
+ ** The set of options for mkinit and mkinit_erl should be
+ ** kept in sync, even if they may not necessarily make sense.
+ */
+ while ((c = getopt(argc, argv, "A:c:g:iI:lm:o:r:tw:xX:ks")) != EOF) {
switch (c) {
case 'A':
/*
@@ -818,16 +760,7 @@
break;
case 'I':
- /*
- ** Add the directory name to the end of the
- ** search path for `.init' files.
- */
- tmp_slist = (String_List *) checked_malloc(sizeof(String_List));
- tmp_slist->next = NULL;
- tmp_slist->data = (char *) checked_malloc(strlen(optarg) + 1);
- strcpy(tmp_slist->data, optarg);
- *init_file_dirs_tail = tmp_slist;
- init_file_dirs_tail = &tmp_slist->next;
+ add_init_file_dir(optarg);
break;
case 'l':
@@ -883,6 +816,10 @@
output_main_func = MR_FALSE; /* -s implies -l */
break;
+ case 'm':
+ /* Used by mkinit_erl. */
+ usage();
+
default:
usage();
}
@@ -912,171 +849,12 @@
fputs(" -I dir:\tadd dir to the search path for init files\n", stderr);
fputs(" -k:\t\tgenerate the .init for a library\n", stderr);
fputs(" -s:\t\tgenerate a standalone runtime interface\n", stderr);
+ fputs(" -m:\t\t(error)\n", stderr);
exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
-/*
-** If the `-o' option was used to specify the output file,
-** and the file name specified is not `-' (which we take to mean stdout),
-** then reassign stdout to the specified file.
-*/
-
-static void
-set_output_file(void)
-{
- if (output_file_name != NULL) {
- FILE *result = freopen(output_file_name, "w", stdout);
- if (result == NULL) {
- fprintf(stderr,
- "%s: error opening output file `%s': %s\n",
- MR_progname, output_file_name,
- strerror(errno));
- exit(EXIT_FAILURE);
- }
- }
-}
-
-/*---------------------------------------------------------------------------*/
-
-/*
-** Scan the list of files for ones not found in the current directory,
-** and replace them with their full path equivalent if they are found
-** in the list of search directories.
-*/
-
-static void
-do_path_search(void)
-{
- int filenum;
- char *init_file;
-
- for (filenum = 0; filenum < num_files; filenum++) {
- init_file = find_init_file(files[filenum]);
- if (init_file != NULL) {
- files[filenum] = init_file;
- }
- }
-}
-
-/*
-** Search the init file directory list to locate the file.
-** If the file is in the current directory or is not in any of the
-** search directories, then return NULL. Otherwise return the full
-** path name to the file.
-**
-** It is the caller's responsibility to free the returned buffer
-** holding the full path name when it is no longer needed.
-*/
-
-static char *
-find_init_file(const char *base_name)
-{
- char *filename;
- char *dirname;
- String_List *dir_ptr;
- int dirlen;
- int baselen;
- int len;
-
- if (file_exists(base_name)) {
- /* File is in current directory, so no search required */
- return NULL;
- }
-
- baselen = strlen(base_name);
-
- for (dir_ptr = init_file_dirs; dir_ptr != NULL; dir_ptr = dir_ptr->next) {
- dirname = dir_ptr->data;
- dirlen = strlen(dirname);
- len = dirlen + 1 + baselen;
-
- filename = (char *) checked_malloc(len + 1);
- strcpy(filename, dirname);
- filename[dirlen] = '/';
- strcpy(filename + dirlen + 1, base_name);
-
- if (file_exists(filename)) {
- return filename;
- }
-
- free(filename);
- }
-
- /* Did not find file */
- return NULL;
-}
-
-/*
-** Check whether a file exists.
-*/
-
-static MR_bool
-file_exists(const char *filename)
-{
-#ifdef MR_HAVE_SYS_STAT_H
- struct stat buf;
-
- return (stat(filename, &buf) == 0);
-#else
- FILE *f;
-
- f = fopen(filename, "rb");
- if (f != NULL) {
- fclose(f);
- return MR_TRUE;
- } else {
- return MR_FALSE;
- }
-#endif
-}
-
-/*---------------------------------------------------------------------------*/
-
-/*
-** Read a line from a file, and return a pointer to a malloc'd buffer
-** holding the line (without the final newline). If EOF occurs on a
-** nonempty line, treat the EOF as a newline; if EOF occurs on an empty
-** line, return NULL.
-*/
-
-char *
-read_line(const char *filename, FILE *fp, int max)
-{
- char *buf;
- int c;
- int i;
-
- buf = checked_malloc(max + 1);
- i = 0;
- while ((c = getc(fp)) != EOF && c != '\n') {
- if (i >= max) {
- fprintf(stderr, "%s: line too long in file `%s'\n",
- MR_progname, filename);
- num_errors++;
- return NULL;
- }
-
- buf[i++] = c;
- }
-
- if (c == '\n' || i > 0) {
- if (i >= max) {
- fprintf(stderr, "%s: line too long in file `%s'\n",
- MR_progname, filename);
- num_errors++;
- return NULL;
- }
-
- buf[i] = '\0';
- return buf;
- } else {
- free(buf);
- return NULL;
- }
-}
-
#define MAX_PROCNAME_LEN 1024
static void
@@ -1478,81 +1256,3 @@
}
/*---------------------------------------------------------------------------*/
-
-static int
-get_line(FILE *file, char *line, int line_max)
-{
- int c;
- int num_chars;
- int limit;
-
- num_chars = 0;
- limit = line_max - 2;
- while ((c = getc(file)) != EOF && c != '\n') {
- if (num_chars < limit) {
- line[num_chars++] = c;
- }
- }
-
- if (c == '\n' || num_chars > 0) {
- line[num_chars++] = '\n';
- }
-
- line[num_chars] = '\0';
-
-#ifdef CHECK_GET_LINE
- if (check_fp != NULL) {
- fprintf(check_fp, "%s", line);
- }
-#endif
-
- return num_chars;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static void *
-checked_malloc(size_t size)
-{
- void *mem;
-
- mem = malloc(size);
- if (mem == NULL) {
- fprintf(stderr, "Out of memory\n");
- exit(EXIT_FAILURE);
- }
- return mem;
-}
-
-static char *
-checked_strdup(const char *str)
-{
- char *mem;
-
- mem = malloc(strlen(str) + 1);
- if (mem == NULL) {
- fprintf(stderr, "Out of memory\n");
- exit(EXIT_FAILURE);
- }
-
- strcpy(mem, str);
- return mem;
-}
-
-static char *
-checked_strdupcat(const char *str, const char *suffix)
-{
- char *mem;
-
- mem = malloc(strlen(str) + strlen(suffix) + 1);
- if (mem == NULL) {
- fprintf(stderr, "Out of memory\n");
- exit(EXIT_FAILURE);
- }
-
- strcpy(mem, str);
- strcat(mem, suffix);
- return mem;
-}
-
-/*---------------------------------------------------------------------------*/
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ util/mkinit_common.c 7 Jun 2007 05:31:27 -0000
@@ -0,0 +1,339 @@
+/*
+** vim:sw=4 ts=4 expandtab
+*/
+/*
+** Copyright (C) 1995-2007 The University of Melbourne.
+** This file may only be copied under the terms of the GNU General
+** Public License - see the file COPYING in the Mercury distribution.
+*/
+
+/*
+** File: mkinit_common.c
+** Main authors: zs, fjh
+**
+** Common code shared by mkinit.c and mkinit_erl.c.
+**
+*/
+
+/* mercury_std.h includes mercury_regs.h, and must precede system headers */
+#include "mercury_conf.h"
+#include "mercury_std.h"
+#include "mercury_array_macros.h"
+#include "mkinit_common.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef MR_HAVE_SYS_STAT_H
+ #include <sys/stat.h>
+#endif
+
+#ifdef MR_HAVE_UNISTD_H
+ #include <unistd.h>
+#endif
+
+/* --- global variables --- */
+
+const char *MR_progname = NULL;
+int num_errors = 0;
+
+ /* List of directories to search for init files */
+static String_List *init_file_dirs = NULL;
+
+ /* Pointer to tail of the init_file_dirs list */
+static String_List **init_file_dirs_tail = &init_file_dirs;
+
+/* --- function prototypes --- */
+
+static char *find_init_file(const char *base_name);
+static MR_bool file_exists(const char *filename);
+
+/*---------------------------------------------------------------------------*/
+
+/*
+** If the `-o' option was used to specify the output file,
+** and the file name specified is not `-' (which we take to mean stdout),
+** then reassign stdout to the specified file.
+*/
+
+void
+set_output_file(const char *output_file_name)
+{
+ if (output_file_name != NULL) {
+ FILE *result = freopen(output_file_name, "w", stdout);
+ if (result == NULL) {
+ fprintf(stderr,
+ "%s: error opening output file `%s': %s\n",
+ MR_progname, output_file_name,
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+** Add the directory name to the end of the ** search path for `.init' files.
+*/
+
+void
+add_init_file_dir(const char *dir_name)
+{
+ String_List *tmp_slist;
+
+ tmp_slist = (String_List *) checked_malloc(sizeof(String_List));
+ tmp_slist->next = NULL;
+ tmp_slist->data = (char *) checked_malloc(strlen(dir_name) + 1);
+ strcpy(tmp_slist->data, dir_name);
+ *init_file_dirs_tail = tmp_slist;
+ init_file_dirs_tail = &tmp_slist->next;
+}
+
+/*
+** Scan the list of files for ones not found in the current directory,
+** and replace them with their full path equivalent if they are found
+** in the list of search directories.
+*/
+
+void
+do_path_search(char **files, int num_files)
+{
+ int filenum;
+ char *init_file;
+
+ for (filenum = 0; filenum < num_files; filenum++) {
+ init_file = find_init_file(files[filenum]);
+ if (init_file != NULL) {
+ files[filenum] = init_file;
+ }
+ }
+}
+
+/*
+** Search the init file directory list to locate the file.
+** If the file is in the current directory or is not in any of the
+** search directories, then return NULL. Otherwise return the full
+** path name to the file.
+**
+** It is the caller's responsibility to free the returned buffer
+** holding the full path name when it is no longer needed.
+*/
+
+static char *
+find_init_file(const char *base_name)
+{
+ char *filename;
+ char *dirname;
+ String_List *dir_ptr;
+ int dirlen;
+ int baselen;
+ int len;
+
+ if (file_exists(base_name)) {
+ /* File is in current directory, so no search required */
+ return NULL;
+ }
+
+ baselen = strlen(base_name);
+
+ for (dir_ptr = init_file_dirs; dir_ptr != NULL; dir_ptr = dir_ptr->next) {
+ dirname = dir_ptr->data;
+ dirlen = strlen(dirname);
+ len = dirlen + 1 + baselen;
+
+ filename = (char *) checked_malloc(len + 1);
+ strcpy(filename, dirname);
+ filename[dirlen] = '/';
+ strcpy(filename + dirlen + 1, base_name);
+
+ if (file_exists(filename)) {
+ return filename;
+ }
+
+ free(filename);
+ }
+
+ /* Did not find file */
+ return NULL;
+}
+
+/*
+** Check whether a file exists.
+*/
+
+static MR_bool
+file_exists(const char *filename)
+{
+#ifdef MR_HAVE_SYS_STAT_H
+ struct stat buf;
+
+ return (stat(filename, &buf) == 0);
+#else
+ FILE *f;
+
+ f = fopen(filename, "rb");
+ if (f != NULL) {
+ fclose(f);
+ return MR_TRUE;
+ } else {
+ return MR_FALSE;
+ }
+#endif
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+** Read a line from a file, and return a pointer to a malloc'd buffer
+** holding the line (without the final newline). If EOF occurs on a
+** nonempty line, treat the EOF as a newline; if EOF occurs on an empty
+** line, return NULL.
+*/
+
+char *
+read_line(const char *filename, FILE *fp, int max)
+{
+ char *buf;
+ int c;
+ int i;
+
+ buf = checked_malloc(max + 1);
+ i = 0;
+ while ((c = getc(fp)) != EOF && c != '\n') {
+ if (i >= max) {
+ fprintf(stderr, "%s: line too long in file `%s'\n",
+ MR_progname, filename);
+ num_errors++;
+ return NULL;
+ }
+
+ buf[i++] = c;
+ }
+
+ if (c == '\n' || i > 0) {
+ if (i >= max) {
+ fprintf(stderr, "%s: line too long in file `%s'\n",
+ MR_progname, filename);
+ num_errors++;
+ return NULL;
+ }
+
+ buf[i] = '\0';
+ return buf;
+ } else {
+ free(buf);
+ return NULL;
+ }
+}
+
+
+int
+get_line(FILE *file, char *line, int line_max)
+{
+ int c;
+ int num_chars;
+ int limit;
+
+ num_chars = 0;
+ limit = line_max - 2;
+ while ((c = getc(file)) != EOF && c != '\n') {
+ if (num_chars < limit) {
+ line[num_chars++] = c;
+ }
+ }
+
+ if (c == '\n' || num_chars > 0) {
+ line[num_chars++] = '\n';
+ }
+
+ line[num_chars] = '\0';
+
+#ifdef CHECK_GET_LINE
+ if (check_fp != NULL) {
+ fprintf(check_fp, "%s", line);
+ }
+#endif
+
+ return num_chars;
+}
+
+/*---------------------------------------------------------------------------*/
+
+void *
+checked_malloc(size_t size)
+{
+ void *mem;
+
+ mem = malloc(size);
+ if (mem == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+ return mem;
+}
+
+char *
+checked_strdup(const char *str)
+{
+ char *mem;
+
+ mem = malloc(strlen(str) + 1);
+ if (mem == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(mem, str);
+ return mem;
+}
+
+char *
+checked_strdupcat(const char *str, const char *suffix)
+{
+ char *mem;
+
+ mem = malloc(strlen(str) + strlen(suffix) + 1);
+ if (mem == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ strcpy(mem, str);
+ strcat(mem, suffix);
+ return mem;
+}
+
+/*---------------------------------------------------------------------------*/
+
+#ifndef MR_HAVE_STRERROR
+
+/*
+** Apparently SunOS 4.1.3 doesn't have strerror()
+** (!%^&!^% non-ANSI systems, grumble...)
+**
+** This code is duplicated in runtime/mercury_prof.c.
+*/
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(int errnum)
+{
+ if (errnum >= 0 && errnum < sys_nerr && sys_errlist[errnum] != NULL) {
+ return sys_errlist[errnum];
+ } else {
+ static char buf[30];
+
+ sprintf(buf, "Error %d", errnum);
+ return buf;
+ }
+}
+
+#endif
+
+/*---------------------------------------------------------------------------*/
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ util/mkinit_common.h 7 Jun 2007 05:31:27 -0000
@@ -0,0 +1,45 @@
+#ifndef MKINIT_COMMON_H
+#define MKINIT_COMMON_H
+
+#include <stdio.h>
+
+/*
+** mercury_array_macros.h uses the MR_NEW_ARRAY and MR_RESIZE_ARRAY macros.
+*/
+
+#define MR_NEW_ARRAY(type, num) \
+ ((type *) malloc((num) * sizeof(type)))
+
+#define MR_RESIZE_ARRAY(ptr, type, num) \
+ ((type *) realloc((ptr), (num) * sizeof(type)))
+
+/* --- adjustable limits --- */
+#define MAXLINE 256 /* maximum number of characters per line */
+ /* (characters after this limit are ignored) */
+
+/* --- used to collect a list of strings --- */
+
+typedef struct String_List_struct {
+ char *data;
+ struct String_List_struct *next;
+} String_List;
+
+/* --- global variables --- */
+
+extern const char *MR_progname;
+extern int num_errors;
+
+extern void set_output_file(const char *output_file_name);
+extern void add_init_file_dir(const char *dir_name);
+extern void do_path_search(char **files, int num_files);
+extern char *read_line(const char *filename, FILE *fp, int max);
+extern int get_line(FILE *file, char *line, int line_max);
+extern void *checked_malloc(size_t size);
+extern char *checked_strdup(const char *str);
+extern char *checked_strdupcat(const char *str, const char *suffix);
+
+#ifndef MR_HAVE_STRERROR
+extern char *strerror(int errnum);
+#endif
+
+#endif /* MKINIT_COMMON_H */
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ util/mkinit_erl.c 7 Jun 2007 05:31:27 -0000
@@ -0,0 +1,609 @@
+/*
+** vim:sw=4 ts=4 expandtab
+*/
+/*
+** Copyright (C) 2007 The University of Melbourne.
+** This file may only be copied under the terms of the GNU General
+** Public License - see the file COPYING in the Mercury distribution.
+*/
+
+/*
+** File: mkinit_erl.c
+** Main authors: zs, fjh, wangp
+**
+** Given a list of .erl or .init files on the command line, this program
+** produces the initialization file (usually called *_init.erl) on stdout.
+** The initialization file is a small program that calls the initialization
+** functions for all the modules in a Mercury program.
+**
+** Alternatively, if invoked with the -k option, this program produces a
+** list of intialization directives on stdout. This mode of operation is
+** is used when building .init files for libraries.
+**
+** If invoked with the -s option, this program produces a standalone
+** runtime interface on stdout. This mode of operation is used when
+** using Mercury libraries from applications written in foreign languages.
+**
+** NOTE: any changes to this program may need to be reflected in the
+** following places:
+**
+** - scripts/c2init.in
+** - compiler/compile_target_code.m
+** in particular the predicates make_init_obj/7 and
+** make_standalone_interface/3.
+** - util/mkinit.c
+**
+*/
+
+/*---------------------------------------------------------------------------*/
+
+/* mercury_std.h includes mercury_regs.h, and must precede system headers */
+#include "mercury_conf.h"
+#include "mercury_std.h"
+#include "getopt.h"
+#include "mercury_array_macros.h"
+#include "mkinit_common.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef MR_HAVE_SYS_STAT_H
+ #include <sys/stat.h>
+#endif
+
+#ifdef MR_HAVE_UNISTD_H
+ #include <unistd.h>
+#endif
+
+typedef enum
+{
+ TASK_OUTPUT_INIT_PROG = 0,
+ TASK_OUTPUT_LIB_INIT = 1
+} Task;
+
+typedef enum
+{
+ PURPOSE_INIT = 0,
+ PURPOSE_REQ_INIT = 1,
+ PURPOSE_REQ_FINAL = 2
+} Purpose;
+
+const char *main_func_name[] =
+{
+ "init_modules",
+ "init_modules_required",
+ "final_modules_required"
+};
+
+const char *module_suffix[] =
+{
+ "init",
+ "",
+ "",
+};
+
+/*
+** List of names of the modules to call all the usual initialization
+** functions for: "init", "init_type_tables", "init_debugger" and (with
+** the right #defines) "init_complexity_procs" and "write_out_proc_statics".
+*/
+
+static const char **std_modules = NULL;
+static int std_module_max = 0;
+static int std_module_next = 0;
+#define MR_INIT_STD_MODULE_SIZE 100
+
+/*
+** List of names of handwritten modules, for which we call a limited set
+** of initialization functions: "init", "init_type_tables" and (with
+** the right #defines) "write_out_proc_statics". We don't call
+** "init_debugger" functions since handwritten modules don't have module
+** layouts, and we don't generate "init_complexity_procs" since they have
+** no Mercury code to measure the complexity of.
+*/
+
+static const char **special_modules = NULL;
+static int special_module_max = 0;
+static int special_module_next = 0;
+#define MR_INIT_SPECIAL_MODULE_SIZE 10
+
+/*
+** The concatenation of std_modules and special_modules; created with the
+** right size (std_module_next + special_module_next).
+*/
+static const char **std_and_special_modules = NULL;
+
+/*
+** List of names of modules that have initialization functions that should
+** always be run. This is currently used to initialize the states of constraint
+** solvers. We call an "init_required" function for each such module.
+*/
+static const char **req_init_modules = NULL;
+static int req_init_module_max = 0;
+static int req_init_module_next = 0;
+#define MR_INIT_REQ_MODULE_SIZE 10
+
+/*
+** List of names of modules that have finalisation functions that should
+** always be run. We call a "final_required" function for each such module.
+*/
+static const char **req_final_modules = NULL;
+static int req_final_module_max = 0;
+static int req_final_module_next = 0;
+#define MR_FINAL_REQ_MODULE_SIZE 10
+
+/*
+** List of names of environment variables whose values should be sampled
+** at initialization.
+** NOTE: the Erlang backend does not yet use these.
+*/
+static const char **mercury_env_vars = NULL;
+static int mercury_env_var_max = 0;
+static int mercury_env_var_next = 0;
+#define MR_ENV_VAR_LIST_SIZE 10
+
+/* options and arguments, set by parse_options() */
+static const char *output_file_name = NULL;
+static const char *grade = "";
+static const char *module_name = "unknown_module_name";
+static int num_files;
+static char **files;
+static Task output_task = TASK_OUTPUT_INIT_PROG;
+
+static int num_errors = 0;
+
+ /* List of directories to search for init files */
+static String_List *init_file_dirs = NULL;
+
+ /* Pointer to tail of the init_file_dirs list */
+static String_List **init_file_dirs_tail = &init_file_dirs;
+
+/* --- code fragments to put in the output file --- */
+static const char header1[] =
+ "%%\n"
+ "%% This code was automatically generated by mkinit_erl - do not edit.\n"
+ "%%\n"
+ "%% Grade: %s\n"
+ "%% Input files:\n"
+ "%%\n"
+ ;
+
+/* --- function prototypes --- */
+static void parse_options(int argc, char *argv[]);
+static void usage(void);
+static void output_headers(void);
+static void output_init_function(Purpose purpose,
+ const char **func_names, int num_func_names);
+static void output_main(void);
+static int output_lib_init_file(void);
+static int output_init_program(void);
+static void process_file(const char *filename);
+static void process_init_file(const char *filename, const char *prefix);
+
+/*---------------------------------------------------------------------------*/
+
+int
+main(int argc, char **argv)
+{
+ int exit_status;
+
+ MR_progname = argv[0];
+
+ parse_options(argc, argv);
+
+ set_output_file(output_file_name);
+
+ switch (output_task) {
+ case TASK_OUTPUT_LIB_INIT:
+ /* Output a .init file */
+ exit_status = output_lib_init_file();
+ break;
+
+ case TASK_OUTPUT_INIT_PROG:
+ /* Output a _init.erl file. */
+ exit_status = output_init_program();
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown task\n", MR_progname);
+ exit(EXIT_FAILURE);
+ }
+
+ return exit_status;
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+** Output the initialisation file for a Mercury library, the .init file.
+*/
+static int
+output_lib_init_file(void)
+{
+ int filenum;
+ int i;
+
+ for (filenum = 0; filenum < num_files; filenum++) {
+ process_file(files[filenum]);
+ }
+
+ for (i = 0; i < std_module_next; i++) {
+ printf("INIT %s%s\n", std_modules[i], module_suffix[PURPOSE_INIT]);
+ }
+
+ for (i = 0; i < req_init_module_next; i++) {
+ printf("REQUIRED_INIT %s\n", req_init_modules[i]);
+ }
+
+ for (i = 0; i < req_final_module_next; i++) {
+ printf("REQUIRED_FINAL %s\n", req_final_modules[i]);
+ }
+
+ for (i = 0; i < mercury_env_var_next; i++) {
+ printf("ENVVAR %s\n", mercury_env_vars[i]);
+ }
+
+ if (num_errors > 0) {
+ fprintf(stderr, "%s: error while creating .init file.\n", MR_progname);
+ return EXIT_FAILURE;
+ } else {
+ return EXIT_SUCCESS;
+ }
+
+}
+
+/*---------------------------------------------------------------------------*/
+
+/*
+** Output the initialisation program for a Mercury executable, the *_init.erl
+** file.
+*/
+static int
+output_init_program(void)
+{
+ int filenum;
+ int num_bunches;
+ int i;
+
+ do_path_search(files, num_files);
+ output_headers();
+
+ for (filenum = 0; filenum < num_files; filenum++) {
+ process_file(files[filenum]);
+ }
+
+ std_and_special_modules = MR_NEW_ARRAY(const char *,
+ std_module_next + special_module_next);
+
+ for (i = 0; i < std_module_next; i++) {
+ std_and_special_modules[i] = std_modules[i];
+ }
+
+ for (i = 0; i < special_module_next; i++) {
+ std_and_special_modules[std_module_next + i] = special_modules[i];
+ }
+
+ fputs("\n", stdout);
+ fputs("-module('", stdout);
+ /* Make some effort at printing weird module names. */
+ for (i = 0; module_name[i] != '\0'; i++) {
+ switch (module_name[i]) {
+ case '\'':
+ case '\\':
+ fputc('\\', stdout);
+ }
+ fputc(module_name[i], stdout);
+ }
+ fputs("').\n", stdout);
+ fputs("-compile(export_all).\n\n", stdout);
+
+ output_init_function(PURPOSE_INIT,
+ std_and_special_modules, std_module_next + special_module_next);
+
+ output_init_function(PURPOSE_REQ_INIT,
+ req_init_modules, req_init_module_next);
+
+ output_init_function(PURPOSE_REQ_FINAL,
+ req_final_modules, req_final_module_next);
+
+ if (num_errors > 0) {
+ fputs("% Force syntax error, since there were\n", stdout);
+ fputs("% errors in the generation of this file\n", stdout);
+ fputs("#error \"You need to remake this file\"\n", stdout);
+ if (output_file_name != NULL) {
+ (void) fclose(stdout);
+ (void) remove(output_file_name);
+ }
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void
+parse_options(int argc, char *argv[])
+{
+ int c;
+ int i;
+ String_List *tmp_slist;
+
+ /*
+ ** The set of options for mkinit and mkinit_erl should be
+ ** kept in sync, even if they may not necessarily make sense.
+ */
+ while ((c = getopt(argc, argv, "A:c:g:iI:lo:r:tw:xX:ksm:")) != EOF) {
+ switch (c) {
+ case 'g':
+ grade = optarg;
+ break;
+
+ case 'I':
+ add_init_file_dir(optarg);
+ break;
+
+ case 'm':
+ module_name = optarg;
+ break;
+
+ case 'o':
+ if (strcmp(optarg, "-") == 0) {
+ output_file_name = NULL; /* output to stdout */
+ } else {
+ output_file_name = optarg;
+ }
+ break;
+
+ case 'x':
+ /* We always assume this option. */
+ break;
+
+ case 'k':
+ output_task = TASK_OUTPUT_LIB_INIT;
+ break;
+
+ case 'A':
+ case 'c':
+ case 'l':
+ case 'i':
+ case 'r':
+ case 't':
+ case 'w':
+ case 'X':
+ case 's':
+ /* Used by mkinit. */
+ usage();
+
+ default:
+ usage();
+ }
+ }
+
+ num_files = argc - optind;
+ if (num_files <= 0) {
+ usage();
+ }
+
+ files = argv + optind;
+}
+
+static void
+usage(void)
+{
+ fputs("Usage: mkinit_erl [options] files...\n", stderr);
+ fputs("Options:\n", stderr);
+ fputs(" -c maxcalls:\t(error)\n", stderr);
+ fputs(" -g grade:\tset the grade of the executable\n", stderr);
+ fputs(" -i:\t\t(error)\n", stderr);
+ fputs(" -l:\t\t(error)\n", stderr);
+ fputs(" -o file:\toutput to the named file\n", stderr);
+ fputs(" -r word:\t(error)\n", stderr);
+ fputs(" -t:\t\t(error)\n", stderr);
+ fputs(" -w entry:\t(error)\n", stderr);
+ fputs(" -I dir:\tadd dir to the search path for init files\n", stderr);
+ fputs(" -k:\t\tgenerate the .init for a library\n", stderr);
+ fputs(" -s:\t\t(error)\n", stderr);
+ fputs(" -m:\t\tset the name of the module\n", stderr);
+ exit(EXIT_FAILURE);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void
+output_headers(void)
+{
+ int filenum;
+
+ printf(header1, grade);
+
+ for (filenum = 0; filenum < num_files; filenum++) {
+ fputs("% ", stdout);
+ fputs(files[filenum], stdout);
+ putc('\n', stdout);
+ }
+}
+
+static void
+output_init_function(Purpose purpose, const char **func_names,
+ int num_func_names)
+{
+ int funcnum;
+
+ printf("%s() ->\n",
+ main_func_name[purpose]);
+
+ for (funcnum = 0; funcnum < num_func_names; funcnum++) {
+ printf("\t%s%s(),\n",
+ func_names[funcnum], module_suffix[purpose]);
+ }
+
+ fputs("\tvoid.\n", stdout);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void
+process_file(const char *filename)
+{
+ int len;
+
+ len = strlen(filename);
+ if (len >= 4 && strcmp(filename + len - 4, ".erl") == 0) {
+ process_init_file(filename, "% ");
+ } else if (len >= 5 && strcmp(filename + len - 5, ".init") == 0) {
+ process_init_file(filename, "");
+ } else {
+ fprintf(stderr,
+ "%s: filename `%s' must end in `.erl' or `.init'\n",
+ MR_progname, filename);
+ num_errors++;
+ }
+}
+
+static void
+process_init_file(const char *filename, const char *prefix_str)
+{
+ /*
+ ** The strings that are supposed to be followed by other information
+ ** (INIT, REQUIRED_INIT, and REQUIRED_FINAL) should end with
+ ** the space that separates the keyword from the following data.
+ ** The string that is not supposed to be following by other information
+ ** (ENDINIT) should not have a following space, since llds_out.m and
+ ** mlds_to_c.m do not add that space.
+ */
+
+ const char * const init_str = "INIT ";
+ const char * const reqinit_str = "REQUIRED_INIT ";
+ const char * const reqfinal_str = "REQUIRED_FINAL ";
+ const char * const envvar_str = "ENVVAR ";
+ const char * const endinit_str = "ENDINIT";
+ const int prefix_strlen = strlen(prefix_str);
+ const int init_strlen = strlen(init_str);
+ const int reqinit_strlen = strlen(reqinit_str);
+ const int reqfinal_strlen = strlen(reqfinal_str);
+ const int envvar_strlen = strlen(envvar_str);
+ const int endinit_strlen = strlen(endinit_str);
+ char line0[MAXLINE];
+ char * line;
+ int len;
+ FILE *cfile;
+
+ cfile = fopen(filename, "r");
+ if (cfile == NULL) {
+ fprintf(stderr, "%s: error opening file `%s': %s\n",
+ MR_progname, filename, strerror(errno));
+ num_errors++;
+ return;
+ }
+
+ while (get_line(cfile, line0, MAXLINE) > 0) {
+ if (strncmp(line0, prefix_str, prefix_strlen) != 0) {
+ continue;
+ }
+ line = line0 + prefix_strlen;
+
+ /* Remove trailing whitespace. */
+ len = strlen(line);
+ while (len > 0 && isspace(line[len - 1])) {
+ line[len - 1] = '\0';
+ len--;
+ }
+
+ if (strncmp(line, init_str, init_strlen) == 0) {
+ char *func_name;
+ int func_name_len;
+ int j;
+ MR_bool special;
+
+ func_name = line + init_strlen;
+ func_name_len = strlen(func_name);
+ if (MR_strneq(&func_name[func_name_len - 4], "init", 4)) {
+ func_name[func_name_len - 4] = '\0';
+ MR_ensure_room_for_next(std_module, const char *,
+ MR_INIT_STD_MODULE_SIZE);
+ std_modules[std_module_next] = checked_strdup(func_name);
+ std_module_next++;
+ } else {
+ MR_ensure_room_for_next(special_module, const char *,
+ MR_INIT_SPECIAL_MODULE_SIZE);
+ special_modules[special_module_next] =
+ checked_strdupcat(func_name, "_");
+ special_module_next++;
+ }
+ } else if (strncmp(line, reqinit_str, reqinit_strlen) == 0) {
+ char *func_name;
+ int j;
+
+ func_name = line + reqinit_strlen;
+ MR_ensure_room_for_next(req_init_module, const char *,
+ MR_INIT_REQ_MODULE_SIZE);
+ req_init_modules[req_init_module_next] = checked_strdup(func_name);
+ req_init_module_next++;
+ } else if (strncmp(line, reqfinal_str, reqfinal_strlen) == 0) {
+ char *func_name;
+ int j;
+
+ func_name = line + reqfinal_strlen;
+ MR_ensure_room_for_next(req_final_module, const char *,
+ MR_FINAL_REQ_MODULE_SIZE);
+ req_final_modules[req_final_module_next] =
+ checked_strdup(func_name);
+ req_final_module_next++;
+ } else if (strncmp(line, envvar_str, envvar_strlen) == 0) {
+ char *envvar_name;
+ int i;
+ int j;
+ MR_bool found;
+
+ /*
+ ** Check that all characters in the name of the environment
+ ** variable are acceptable as components of a C variable name.
+ ** Note that the variable name doesn't have to start with a letter
+ ** because the variable name has a prefix.
+ */
+ for (j = envvar_strlen; MR_isalnumunder(line[j]); j++) {
+ /* VOID */
+ }
+
+ if (line[j] != '\n') {
+ printf("%s: error: bad environment variable name %s\n",
+ MR_progname, line);
+ }
+
+ line[j] = '\0'; /* overwrite the newline */
+
+ envvar_name = line + envvar_strlen;
+
+ /*
+ ** Since the number of distinct environment variables used by
+ ** a program is likely to be in the single digits, linear search
+ ** should be efficient enough.
+ */
+ found = MR_FALSE;
+ for (i = 0; i < mercury_env_var_next; i++) {
+ if (strcmp(envvar_name, mercury_env_vars[i]) == 0) {
+ found = MR_TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ MR_ensure_room_for_next(mercury_env_var, const char *,
+ MR_ENV_VAR_LIST_SIZE);
+ mercury_env_vars[mercury_env_var_next] =
+ checked_strdup(envvar_name);
+ mercury_env_var_next++;
+ }
+ } else if (strncmp(line, endinit_str, endinit_strlen) == 0) {
+ break;
+ }
+ }
+
+ fclose(cfile);
+}
+
+/*---------------------------------------------------------------------------*/
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list