home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
emxtutor.zip
/
emxsrcd1.zip
/
emx
/
src
/
emxomf
/
emxomfld.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-12-19
|
23KB
|
820 lines
/* emxomfld.c -- Provide an ld-like interface to LINK386
Copyright (c) 1992-1998 Eberhard Mattes
This file is part of emxomld.
emxomfld is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
emxomfld is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with emxomfld; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <io.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <getopt.h>
#include <errno.h>
#include <sys/moddef.h>
#include "defs.h"
#define VERSION "0.9d"
#define FALSE 0
#define TRUE 1
/* A member of a linked list of strings such as file names. */
typedef struct name_list
{
struct name_list *next;
char *name;
} name_list;
/* The output file name, specified by the -o option. */
static const char *output_fname = NULL;
/* The map file name (output), set by the -Zmap option. */
static const char *map_fname = NULL;
static int map_flag = FALSE;
/* The module definition file name (input), set if a file matching
*.def is given on the command line. */
static const char *def_fname = NULL;
/* The binary resource file name (input), set if a file matching *.res
is given on the command line. */
static const char *res_fname = NULL;
/* Base address of the excecutable file, specified by the -T
option. */
static const char *base = NULL;
/* List of directories searched for libraries. Each -L option adds a
directory to this list. add_libdirs is used to add another entry
at the end of the list. */
static name_list *libdirs = NULL;
static name_list **add_libdirs = &libdirs;
/* List of object files. Each file given on the command line which
does not match *.def, *.lib and *.res is added to this list.
add_obj_fnames is used to add another entry at the end of the
list. */
static name_list *obj_fnames = NULL;
static name_list **add_obj_fnames = &obj_fnames;
/* List of library files. Each file matching *.lib given on the
command line is added to this list. The -l option also adds an
entry to this list. add_lib_fnames is used to add another entry at
the end of the list. */
static name_list *lib_fnames = NULL;
static name_list **add_lib_fnames = &lib_fnames;
/* List of LINK386 options. LINK386 options can be specified with the
-O option. add_options is used to add another entry at the end of
the list. */
static name_list *options = NULL;
static name_list **add_options = &options;
/* The command line passed to LINK386. */
static char command_line[260];
/* The current length of the command line. */
static int line_len;
/* Non-zero if arguments go into the response file instead of
command_line. */
static int response_flag;
/* The name of the response file. */
static char response_fname[L_tmpnam] = "";
/* The response file. */
static FILE *response_file = NULL;
/* Non-zero if debugging information is to be omitted. Set by the -s
and -S options. */
static int strip_symbols = FALSE;
/* Non-zero if emxomfld should create an .exe file and touch the
output file. Set by the -Zexe option. */
static int exe_flag = FALSE;
/* Non-zero when creating a dynamic link library. Set by the -Zdll
option. */
static int dll_flag = FALSE;
/* The stack size, specified by the -Zstack option, in Kbyte. If the
-Zstack option is not used, this variable is 0. */
static long stack_size = 0;
/* The name of the linker to use. By default, LINK386 is used. This
can be overriden with the EMXOMFLD_LINKER environment variable. */
static const char *linker_name = "link386";
/* Prototypes. */
static void usage (void) NORETURN2;
static void *xmalloc (size_t n);
static char *xstrdup (const char *s);
static void add_name_list (name_list ***add, const char *src);
static void conv_path (char *name);
static void put_arg (const char *src, int path);
static void put_args (const name_list *list, int paths);
static void make_env (void);
static void cleanup (void);
static void arg_init (int rsp);
static void arg_end (void);
int main (int argc, char *argv[]);
/* Tell the user how to run this program. */
static void usage (void)
{
fprintf (stderr, "emxomfld " VERSION " -- "
"Copyright (c) 1992-1996 by Eberhard Mattes\n\n");
fprintf (stderr,
"Usage: emxomfld -o <file> [-l <lib>] [-L <libdir>] [-T <base>] [-sS]\n"
" [-Zexe] [-Zdll] [-Zstack <size>] [-Zmap[=<map_file>]]\n"
" [-O <option>] <file>...\n");
exit (1);
}
/* Allocate N bytes of memory. Quit on failure. This function is
used like malloc(), but we don't have to check the return value. */
static void *xmalloc (size_t n)
{
void *p;
p = malloc (n);
if (p == NULL)
{
fprintf (stderr, "emxomfld: out of memory\n");
exit (2);
}
return p;
}
/* Create a duplicate of the string S on the heap. Quit on failure.
This function is used like strdup(), but we don't have to check the
return value. */
static char *xstrdup (const char *s)
{
char *p;
p = xmalloc (strlen (s) + 1);
strcpy (p, s);
return p;
}
/* Add the name SRC to a list. ADD is a pointer to the pointer of the
end of the list. We duplicate the string before adding it to the
list. */
static void add_name_list (name_list ***add, const char *src)
{
name_list *new;
new = xmalloc (sizeof (name_list));
new->next = NULL;
new->name = xstrdup (src);
*(*add) = new;
(*add) = &new->next;
}
/* Replace forward slashes `/' in NAME with backslashes `\'. LINK386
needs backslashes in path names. */
static void conv_path (char *name)
{
char *p;
for (p = name; *p != 0; ++p)
if (*p == '/')
*p = '\\';
}
/* Add the argument SRC to the command line or to the response file.
If PATH is non-zero, SRC is a path name and slashes are to be
replaced by backslashes. If the command line gets too long, a
response file is created. */
static void put_arg (const char *src, int path)
{
int len, max_len;
char *tmp;
if (src != NULL)
{
/* Instead of a comma, we write a newline to the response
file. */
if (response_file != NULL && strcmp (src, ",") == 0)
{
fputc ('\n', response_file);
line_len = 0;
return;
}
/* Make a local copy of SRC to be able to modify it. Then,
translate forward slashes to backslashes if PATH is
non-zero. */
len = strlen (src);
tmp = alloca (len + 1);
strcpy (tmp, src);
if (path)
conv_path (tmp);
/* Check if we've reached the maximum line length. If the
maximum command line length is exceeded, create a response
file and write the remaining arguments to that file instead
of putting them on the command line. */
max_len = (response_file == NULL ? 110 : 52);
if (line_len + len + 1 > max_len)
{
/* If SRC is a single comma or a single semicolon, copy it
to the output, ignoring the maximum line length. This is
to meet the LINK386 command syntax. The maximum line
length allows for enough commas and semicolons added this
way. */
if ((*tmp == ',' || *tmp == ';') && tmp[1] == 0)
{
if (response_file == NULL)
{
command_line[line_len+0] = *tmp;
command_line[line_len+1] = 0;
}
else
fputc (*tmp, response_file);
++line_len;
return;
}
/* If a response file has not yet been opened, open it. */
if (response_file == NULL)
{
/* Complain if we are not allowed to use a response
file. */
if (!response_flag)
{
fprintf (stderr, "emxomfld: command line too long\n");
exit (2);
}
/* Choose a unique file name and create the response
file. */
strcpy (response_fname, "ldXXXXXX");
if (mktemp (response_fname) == NULL)
{
perror ("emxomfld");
exit (2);
}
response_file = fopen (response_fname, "wt");
if (response_file == NULL)
{
perror ("emxomfld");
exit (2);
}
/* Add the name of the response file to the command
line. */
command_line[line_len++] = ' ';
command_line[line_len++] = '@';
strcpy (command_line+line_len, response_fname);
}
else if (line_len != 0)
{
/* Start a new line in the response file. */
fputs (" +\n", response_file);
}
line_len = 0;
}
/* Separate command line arguments by spaces (unless the
argument to be added starts with a delimiter. */
if (line_len != 0 && *src != ',' && *src != ';')
{
if (response_file == NULL)
command_line[line_len++] = ' ';
else
fputc (' ', response_file);
}
/* Finally write the argument to the command line or to the
response file and adjust the current line length. */
if (response_file == NULL)
strcpy (command_line + line_len, tmp);
else
fputs (tmp, response_file);
line_len += len;
}
}
/* Put a list of arguments onto the command line or into the response
file. If PATHS is non-zero, the arguments are path names and
slashes are to be replaced by backslashes. */
static void put_args (const name_list *list, int paths)
{
while (list != NULL)
{
put_arg (list->name, paths);
list = list->next;
}
}
/* Build the environment for LINK386: define the LIB environment
variable. */
static void make_env (void)
{
static char tmp[4096];
char *p;
int len;
const name_list *list;
/* Create a string for putenv(). */
strcpy (tmp, "LIB=");
len = strlen (tmp);
/* Add the library directories to LIB, using `;' as separator. */
for (list = libdirs; list != NULL; list = list->next)
{
if (tmp[len-1] != ';')
tmp[len++] = ';';
strcpy (tmp+len, list->name);
conv_path (tmp+len);
len += strlen (list->name);
}
/* Append to the end the previous definition of LIB. */
p = getenv ("LIB");
if (p != NULL)
{
if (tmp[len-1] != ';')
tmp[len++] = ';';
strcpy (tmp+len, p);
}
/* Put the new value of LIB into the environment. */
putenv (tmp);
}
/* Start a new set of command line arguments. If RSP is non-zero, we
are allowed to use a response file. */
static void arg_init (int rsp)
{
command_line[0] = 0;
line_len = 0;
response_flag = rsp;
}
/* Call this after adding all the command line arguments. If a
response file has been created, add a newline and close it. */
static void arg_end (void)
{
if (response_file != NULL)
{
fputc ('\n', response_file);
if (fflush (response_file) != 0 || fclose (response_file) != 0)
{
perror ("emxomfld");
exit (2);
}
response_file = NULL;
}
}
/* Cleanup by closing (if open) and deleting (if pressent) the
response file. This function is used with atexit(). */
static void cleanup (void)
{
if (response_file != NULL)
{
fclose (response_file);
response_file = NULL;
}
if (response_fname[0] != 0)
{
remove (response_fname);
response_fname[0] = 0;
}
}
/* Main function of emxomf. Parse the command line and call LINK386
(and optionally RC). */
int main (int argc, char *argv[])
{
int c, rc, files;
int opt_i;
const char *ext;
char tmp[512], *t;
/* Close and delete the response file on exit. */
atexit (cleanup);
/* Prepare parsing of the command line. */
opt_i = FALSE; files = 0;
opterr = FALSE; optmode = GETOPT_KEEP;
if (argc < 2)
usage ();
/* Parse the command line options and other arguments. */
while ((c = getopt (argc, argv, "o:O:il:vL:T:sSxXZ:")) != EOF)
switch (c)
{
case 'i': /* Use /INFORMATION option of LINK386 */
opt_i = TRUE;
break;
case 'l': /* Add library */
add_name_list (&add_lib_fnames, optarg);
break;
case 'o': /* Set output file name */
output_fname = optarg;
break;
case 'L': /* Add library directory */
add_name_list (&add_libdirs, optarg);
break;
case 'T': /* Set base address */
base = optarg;
break;
case 's': /* Strip all symbols */
case 'S': /* Strip debugging symbols */
strip_symbols = TRUE;
break;
case 'x': /* Discard all local symbols */
case 'X': /* Discard local symbols starting with L */
break;
case 'v': /* For compatibility */
break;
case 'O': /* Specify LINK386 option */
add_name_list (&add_options, optarg);
break;
case 'Z': /* -Zdll, -Zexe, -Zmap, and -Zstack */
if (strcmp (optarg, "dll") == 0)
dll_flag = TRUE;
else if (strcmp (optarg, "exe") == 0)
exe_flag = TRUE;
else if (strcmp (optarg, "map") == 0)
map_flag = TRUE;
else if (strncmp (optarg, "map=", 4) == 0)
{
if (map_fname != NULL)
{
fprintf (stderr, "emxomfld: multiple map files files\n");
usage ();
}
map_fname = optarg + 4;
map_flag = TRUE;
}
else if (strcmp (optarg, "stack") == 0)
{
/* This makes assumptions on the internals of getopt(). */
if (optind >= argc || argv[optind][0] == '-')
usage ();
errno = 0;
stack_size = strtol (argv[optind], &t, 0);
if (errno != 0 || *t != 0 || t == argv[optind])
usage ();
++optind;
}
else
{
fprintf (stderr, "emxomfld: invalid option\n");
usage ();
}
break;
case 0: /* Non-option argument */
/* Extract the extension to see what to do with this
argument. */
ext = _getext (optarg);
if (ext == NULL)
{
/* GCC's temporary files don't have an extension. Add a
dot to the end of the name to prevent LINK386 from
adding `.obj'. */
sprintf (tmp, "%s.", optarg);
add_name_list (&add_obj_fnames, tmp);
}
/* If it's a .def file, use it as module definition file
(input). */
else if (stricmp (ext, ".def") == 0)
{
if (def_fname != NULL)
{
fprintf (stderr,
"emxomfld: multiple module definition files\n");
usage ();
}
def_fname = optarg;
}
/* If it's a .res file, use it as binary resource file
(input). */
else if (stricmp (ext, ".res") == 0)
{
if (res_fname != NULL)
{
fprintf (stderr,
"emxomfld: multiple binary resource files\n");
usage ();
}
res_fname = optarg;
}
/* If it's a .lib file, use it as library file. We also
accept .a files for those who use OMF files disguised as
a.out files (to simplify their make files). */
else if (stricmp (ext, ".lib") == 0 || stricmp (ext, ".a") == 0)
add_name_list (&add_lib_fnames, optarg);
/* Otherwise, assume it's an object file. */
else
add_name_list (&add_obj_fnames, optarg);
++files;
break;
default:
fprintf (stderr, "emxomfld: invalid option\n");
usage ();
}
/* Set default value for output file. */
if (output_fname == NULL)
{
fprintf (stderr,
"emxomfld: no output file, creating $$$.exe or $$$.dll\n");
output_fname = "$$$";
}
/* Check if there are any input files. */
if (files == 0)
{
fprintf (stderr, "emxomfld: no input files\n");
usage ();
}
/* Remove the output file if -Zexe is given. */
if (exe_flag)
remove (output_fname);
/* If neither -Zmap nor -Zmap=file is used, pass "nul" to LINK386 in
the map file field. If -Zmap is used, construct the name of the
.map file. If -Zmap=file is used, use `file' as the name of the
.map file. */
if (!map_flag)
map_fname = "nul";
else if (map_fname == NULL)
{
t = xstrdup (output_fname);
_remext (t);
map_fname = t;
}
t = getenv ("EMXOMFLD_LINKER");
if (t != NULL)
linker_name = t;
/* Start building the LINK386 command line. We can use a response
file if the command line gets too long. */
arg_init (TRUE);
/* Default options are:
/BATCH Run in batch mode (disable prompting, don't
echo response file)
/NOLOGO Don't display sign-on banner
/NOEXTDICTIONARY Don't use extended dictionary (redefining
library symbols is quite common)
/NOIGNORECASE Make symbols case-sensitive
/PACKCODE Group neighboring code segments (this is the
default unless the SEGMENTS module definition
statement is used for a segment of class
'CODE'). Not grouping neighboring code
segments would break sets */
put_arg (linker_name, TRUE);
put_arg ("/bat", FALSE);
put_arg ("/nol", FALSE);
put_arg ("/noe", FALSE);
put_arg ("/noi", FALSE);
put_arg ("/packc", FALSE);
/* Add the /INFORMATION option if the -i option was given. This is
for debugging. */
if (opt_i)
put_arg ("/i", FALSE);
/* Add the /DEBUG option if the -s option was not given. Without
this, LINK386 throws away debugging information. */
if (!strip_symbols)
put_arg ("/de", FALSE);
/* Add the /BASE:n option to set the base address. This specifies
the preferred load address of object 1. The base address being
used is 0x10000 unless a DLL is generated or the -T option was
given. -Tno can be used to suppress the /BASE:n option. */
if (base == NULL && !dll_flag)
{
struct _md *md;
if (def_fname != NULL)
{
md = _md_open (def_fname);
if (md == NULL)
{
fprintf (stderr, "emxomfld: cannot open `%s'\n", def_fname);
exit (2);
}
if (_md_next_token (md) == _MD_LIBRARY)
dll_flag = TRUE;
_md_close (md);
}
}
if (base == NULL && !dll_flag)
base = "0x10000";
if (base != NULL && strcmp (base, "no") != 0)
{
sprintf (tmp, "/bas:%s", base);
put_arg (tmp, FALSE);
}
/* Add the /STACK:n option if the -Zstack option was given. */
if (stack_size != 0)
{
sprintf (tmp, "/st:0x%lx", stack_size * 1024);
put_arg (tmp, FALSE);
}
/* Add the LINK386 options specified with -O. */
put_args (options, FALSE);
/* Put the object file names onto the command line. */
put_args (obj_fnames, TRUE);
put_arg (",", FALSE);
/* Put the output file name onto the command line. */
put_arg (output_fname, TRUE);
put_arg (",", FALSE);
/* Put the map file name onto the command line. */
put_arg (map_fname, TRUE);
put_arg (",", FALSE);
/* Put the library file names onto the command line. */
put_args (lib_fnames, TRUE);
put_arg (",", FALSE);
/* Put the name of the module definition file onto the command line. */
put_arg (def_fname, TRUE);
put_arg (";", FALSE);
arg_end ();
/* Build the environment for LINK386. */
make_env ();
/* Call LINK386 via CMD.EXE (what a waste -- but I'm lazy) and abort
on failure. */
rc = system (command_line);
if (rc < 0)
{
perror (linker_name);
exit (2);
}
/* Run RC if LINK386 completed successfully and a binary resource
file was given on the command line. */
if (rc == 0 && res_fname != NULL)
{
arg_init (TRUE);
put_arg ("rc", TRUE);
put_arg (res_fname, TRUE);
put_arg (output_fname, TRUE);
arg_end ();
rc = system (command_line);
if (rc < 0)
{
perror ("emxomfld: rc");
exit (2);
}
}
/* If both LINK386 and RC competed successfully and the -Zexe option
was given, touch the output file (without .exe) to keep `make'
happy. */
if (rc == 0 && exe_flag)
{
int h;
h = open (output_fname,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
if (h < 0)
{
perror ("emxomfld");
exit (2);
}
close (h);
}
/* Return the return code of LINK386 (or RC). */
return rc;
}