[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