source: trunk/emx/src/emxomf/emxomfld.c@ 2693

Last change on this file since 2693 was 2693, checked in by bird, 19 years ago

#89: wlink is now default. Fixed exp=int$w$ problem with wlink.

  • Property cvs2svn:cvs-rev set to 1.42
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 63.9 KB
Line 
1/* emxomfld.c -- Provide an ld-like interface to the IBM and M$ linkers
2 Copyright (c) 1992-1998 Eberhard Mattes
3 Copyright (c) 2003 InnoTek Systemberatung GmbH
4 Copyright (c) 2003-2004 Knut St. Osmundsen
5
6This file is part of emxomld.
7
8emxomfld is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13emxomfld is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with emxomfld; see the file COPYING. If not, write to
20the Free Software Foundation, 59 Temple Place - Suite 330,
21Boston, MA 02111-1307, USA. */
22
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <alloca.h>
27#include <errno.h>
28#include <string.h>
29#include <process.h>
30#include <io.h>
31#include <fcntl.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/utime.h>
35#include <sys/moddef.h>
36#include <getopt.h>
37#include <alloca.h>
38#include <sys/omflib.h>
39#include "defs.h"
40#include "weakld.h"
41
42#define FALSE 0
43#define TRUE 1
44
45/* A member of a linked list of strings such as file names. */
46typedef struct name_list
47{
48 struct name_list *next;
49 unsigned flags;
50 char *name;
51} name_list;
52
53
54/* Whether or not linker tracing is enabled. */
55static int opt_t;
56
57/* Whether or not to include .dll in the shared library searching. */
58static int opt_dll_search;
59
60/* The output file name, specified by the -o option. */
61static const char *output_fname = NULL;
62
63/* The map file name (output), set by the -Zmap option. */
64static const char *map_fname = NULL;
65static int map_flag = FALSE;
66
67/* The module definition file name (input), set if a file matching
68 *.def is given on the command line. */
69static const char *def_fname = NULL;
70
71/* The binary resource file name (input), set if a file matching *.res
72 is given on the command line. */
73static const char *res_fname = NULL;
74
75/* Base address of the excecutable file, specified by the -T
76 option. */
77static const char *base = NULL;
78
79/* List of directories searched for libraries. Each -L option adds a
80 directory to this list. add_libdirs is used to add another entry
81 at the end of the list. */
82static name_list *libdirs = NULL;
83static name_list **add_libdirs = &libdirs;
84
85/* List of object files. Each file given on the command line which
86 does not match *.def, *.lib and *.res is added to this list.
87 add_obj_fnames is used to add another entry at the end of the
88 list. */
89static name_list *obj_fnames = NULL;
90static name_list **add_obj_fnames = &obj_fnames;
91
92/* List of library files. Each file matching *.lib given on the
93 command line is added to this list. The -l option also adds an
94 entry to this list. add_lib_fnames is used to add another entry at
95 the end of the list.
96 The flags member indicates library search method. If set search for
97 static lib, if clear search for shared lib before search for static lib. */
98static name_list *lib_fnames = NULL;
99static name_list **add_lib_fnames = &lib_fnames;
100
101/* List of linker options. Linker options can be specified with the
102 -O option. add_options is used to add another entry at the end of
103 the list. */
104static name_list *options = NULL;
105static name_list **add_options = &options;
106
107/* The command line passed to the linker. */
108static char command_line[260];
109
110/* The current length of the command line. */
111static int line_len;
112
113/* Non-zero if arguments go into the response file instead of
114 command_line. */
115static int response_flag;
116
117/* The name of the response file. */
118static char response_fname[L_tmpnam] = "";
119
120/* The response file. */
121static FILE *response_file = NULL;
122
123/* Force the use of a response file from the next put_arg(). */
124static int force_response_file = FALSE;
125
126/* Weak alias object file. */
127static char weakobj_fname[_MAX_PATH + 1];
128
129/* Weak definition file (modified def_fname). */
130static char weakdef_fname[_MAX_PATH + 1];
131
132/* list of converted libraries and objects which must be removed upon exit. */
133static name_list *conv_list = NULL;
134
135/* Non-zero if debugging information is to be omitted. Set by the -s
136 and -S options. */
137static int strip_symbols = FALSE;
138
139/* Non-zero if emxomfld should create an .exe file and touch the
140 output file. Set by the -Zexe option. */
141static int exe_flag = FALSE;
142
143/* Non-zero when creating a dynamic link library. Set by the -Zdll
144 option. */
145static int dll_flag = FALSE;
146
147/* The stack size, specified by the -Zstack option, in Kbyte. If the
148 -Zstack option is not used, this variable defaults to 1024 to match
149 the defaults of emxbind. */
150static long stack_size = 1024;
151
152/* The name of the linker to use. By default, ilink is used. This
153 can be overridden with the EMXOMFLD_LINKER environment variable. */
154static const char *linker_name = "wlink.exe";
155
156/* The type of linker to use. By default we assume it's VAC365 or later
157 version of ilink. This can be overridden with the EMXOMFLD_TYPE env.
158 var. using any of the values WLINK, VAC365, VAC308 and LINK386. */
159static const char *linker_type = "WLINK";
160
161/* Non-zero if emxomfld should automatically convert a.out objects and
162 archives to the OMF equivalents during linking. */
163static int autoconvert_flag = 1;
164
165
166/* Prototypes. */
167
168static void usage (void) NORETURN2;
169extern void *xmalloc (size_t n);
170extern void *xrealloc (void *ptr, size_t n);
171extern char *xstrdup (const char *s);
172static void add_name_list (name_list ***add, const char *src, unsigned flags);
173static void conv_path (char *name);
174static void put_arg (const char *src, int path, int quotable);
175static void put_args (const name_list *list, int paths);
176static void make_env (void);
177static void cleanup (void);
178static void arg_init (int rsp);
179static void arg_end (void);
180int main (int argc, char *argv[]);
181
182/* To avoid including os2.h... */
183#ifndef _System
184#define _System
185#endif
186extern int _System DosCopy (char *, char *, int);
187
188/* Allocate N bytes of memory. Quit on failure. This function is
189 used like malloc(), but we don't have to check the return value. */
190
191void *xmalloc (size_t n)
192{
193 void *p;
194
195 p = malloc (n);
196 if (p == NULL && n)
197 {
198 fprintf (stderr, "emxomfld: out of memory\n");
199 exit (2);
200 }
201 return p;
202}
203
204
205/* Change the allocation of PTR to N bytes. Quit on failure. This
206 function is used like realloc(), but we don't have to check the
207 return value. */
208
209void *xrealloc (void *ptr, size_t n)
210{
211 void *p;
212
213 p = realloc (ptr, n);
214 if (p == NULL && n)
215 {
216 fprintf (stderr, "emxomfld: out of memory\n");
217 exit (2);
218 }
219 return p;
220}
221
222
223
224/* Create a duplicate of the string S on the heap. Quit on failure.
225 This function is used like strdup(), but we don't have to check the
226 return value. */
227
228char *xstrdup (const char *s)
229{
230 char *p;
231 int cch = strlen (s) + 1;
232
233 p = xmalloc (cch);
234 memcpy (p, s, cch);
235 return p;
236}
237
238
239/* Add the name SRC to a list. ADD is a pointer to the pointer of the
240 end of the list. We duplicate the string before adding it to the
241 list. */
242
243static void add_name_list (name_list ***add, const char *src, unsigned flags)
244{
245 name_list *node;
246
247 node = xmalloc (sizeof (name_list));
248 node->next = NULL;
249 node->name = xstrdup (src);
250 node->flags = flags;
251 *(*add) = node;
252 (*add) = &node->next;
253}
254
255/* Opens a response file. */
256
257static void open_response_file(void)
258{
259 int fd;
260
261 if (response_file)
262 return;
263
264 /* Complain if we are not allowed to use a response
265 file. */
266
267 if (!response_flag)
268 {
269 fprintf (stderr, "emxomfld: command line too long\n");
270 exit (2);
271 }
272
273 /* Choose a unique file name and create the response
274 file. */
275
276 strcpy (response_fname, "ldXXXXXX");
277 fd = mkstemp (response_fname);
278 if (fd < 0)
279 {
280 perror ("emxomfld");
281 exit (2);
282 }
283 close(fd);
284 response_file = fopen (response_fname, "wt");
285 if (response_file == NULL)
286 {
287 perror ("emxomfld");
288 exit (2);
289 }
290
291 /* Add the name of the response file to the command
292 line. */
293
294 command_line[line_len++] = ' ';
295 command_line[line_len++] = '@';
296 strcpy (command_line+line_len, response_fname);
297 if (!stricmp (linker_type, "WLINK"))
298 strcat (command_line, ".");
299
300 if (force_response_file)
301 force_response_file = FALSE;
302}
303
304/* Replace forward slashes `/' in NAME with backslashes `\'. The linkers
305 requires backslashes in path names. */
306
307static void conv_path (char *name)
308{
309 char *p;
310
311 for (p = name; *p != 0; ++p)
312 if (*p == '/')
313 *p = '\\';
314}
315
316
317/* Add the argument SRC to the command line or to the response file.
318 If PATH is non-zero, SRC is a path name and slashes are to be
319 replaced by backslashes. If the command line gets too long, a
320 response file is created.
321 If quotable is non-zero SRC will be quoted. This is required for
322 supporting files names which includes '+' and spaces. */
323
324static void put_arg (const char *src, int path, int quotable)
325{
326 int len, max_len;
327 char *tmp;
328
329 if (src != NULL)
330 {
331
332 /* Instead of a comma, we write a newline to the response
333 file. */
334
335 if (response_file != NULL && strcmp (src, ",") == 0)
336 {
337 fputc ('\n', response_file);
338 line_len = 0;
339 return;
340 }
341
342 /* Make a local copy of SRC to be able to modify it. Then,
343 translate forward slashes to backslashes if PATH is
344 non-zero. */
345
346 len = strlen (src);
347 tmp = alloca (len + (quotable ? 3 : 1));
348 if (path)
349 {
350 /* needs quoting? */
351 if (quotable)
352 {
353 *tmp = '"';
354 strcpy (tmp+1, src);
355 tmp[++len] = '"';
356 tmp[++len] = '\0';
357 }
358 else
359 strcpy (tmp, src);
360 conv_path (tmp);
361 }
362 else
363 strcpy (tmp, src);
364
365
366 /* Check if we've reached the maximum line length. If the
367 maximum command line length is exceeded, create a response
368 file and write the remaining arguments to that file instead
369 of putting them on the command line. */
370
371 max_len = (response_file == NULL ? 110 : 52);
372 if ( line_len + len + 1 > max_len
373 || (force_response_file && !response_file))
374 {
375
376 /* If SRC is a single comma or a single semicolon, copy it
377 to the output, ignoring the maximum line length. This is
378 to meet the IBM/M$ linker command syntax. The maximum line
379 length allows for enough commas and semicolons added this
380 way. */
381
382 if ((*tmp == ',' || *tmp == ';') && tmp[1] == 0)
383 {
384 if (response_file == NULL)
385 {
386 command_line[line_len+0] = *tmp;
387 command_line[line_len+1] = 0;
388 }
389 else
390 fputc (*tmp, response_file);
391 ++line_len;
392 return;
393 }
394
395 /* If a response file has not yet been opened, open it. */
396
397 if (response_file == NULL)
398 open_response_file();
399 else if (line_len != 0)
400 {
401
402 /* Start a new line in the response file. */
403
404 fputs (" +\n", response_file);
405 }
406 line_len = 0;
407 }
408
409 /* Separate command line arguments by spaces (unless the
410 argument to be added starts with a delimiter. */
411
412 if (line_len != 0 && *src != ',' && *src != ';')
413 {
414 if (response_file == NULL)
415 command_line[line_len++] = ' ';
416 else
417 fputc (' ', response_file);
418 }
419
420 /* Finally write the argument to the command line or to the
421 response file and adjust the current line length. */
422
423 if (response_file == NULL)
424 strcpy (command_line + line_len, tmp);
425 else
426 fputs (tmp, response_file);
427 line_len += len;
428 }
429}
430
431
432/* Put a list of arguments onto the command line or into the response
433 file. If PATHS is non-zero, the arguments are path names and
434 slashes are to be replaced by backslashes. */
435
436static void put_args (const name_list *list, int paths)
437{
438 while (list != NULL)
439 {
440 put_arg (list->name, paths, paths);
441 list = list->next;
442 }
443}
444
445
446/* Build the environment for the IBM/M$ Linkers: define the LIB
447 environment variable. */
448
449static void make_env (void)
450{
451 static char tmp[4096];
452 char *p;
453 int len;
454 const name_list *list;
455
456 /* Create a string for putenv(). */
457
458 strcpy (tmp, "LIB=");
459 len = strlen (tmp);
460
461 /* Add the library directories to LIB, using `;' as separator. */
462
463 for (list = libdirs; list != NULL; list = list->next)
464 {
465 if (list != libdirs && tmp[len-1] != ';')
466 tmp[len++] = ';';
467 strcpy (tmp+len, list->name);
468 conv_path (tmp+len);
469 len += strlen (list->name);
470 }
471
472 /* Append to the end the previous definition of LIB. */
473
474 p = getenv ("LIB");
475 if (p != NULL)
476 {
477 if (tmp[len-1] != ';')
478 tmp[len++] = ';';
479 strcpy (tmp+len, p);
480 }
481
482
483 /* Put the new value of LIB into the environment. */
484
485 putenv (tmp);
486
487 if (opt_t)
488 fprintf(stderr, "*** %s\n", tmp);
489}
490
491/**
492 * Checks if the stream phFile is an OMF library.
493 *
494 * @returns 1 if OMF library.
495 * @returns 0 if not OMF library.
496 * @param phFile Filestream to check.
497 */
498static int check_omf_library(FILE *phFile)
499{
500#pragma pack(1)
501 struct
502 {
503 byte rec_type;
504 word rec_len;
505 dword dict_offset;
506 word dict_blocks;
507 byte flags;
508 } libhdr;
509#pragma pack()
510
511 if ( fread(&libhdr, 1, sizeof(libhdr), phFile) == sizeof (libhdr)
512 && !fseek(phFile, 0, SEEK_SET)
513 && libhdr.rec_type == LIBHDR
514 && libhdr.flags <= 1 /* ASSUME only first bit is used... */
515 )
516 {
517 int page_size = libhdr.rec_len + 3;
518 if (page_size >= 16
519 && page_size <= 32768
520 && !(page_size & (page_size - 1)) != 0)
521 return 1;
522 }
523 return 0;
524}
525
526
527/**
528 * Checks if the stream phFile is an OMF object or library.
529 *
530 * @returns 1 if OMF.
531 * @returns 0 if not OMF.
532 * @param phFile Filestream to check.
533 */
534static int check_omf(FILE *phFile)
535{
536#pragma pack(1)
537 struct
538 {
539 byte rec_type;
540 word rec_len;
541 } omfhdr;
542#pragma pack()
543 if ( fread(&omfhdr, 1, sizeof(omfhdr), phFile) == sizeof (omfhdr)
544 && omfhdr.rec_type == THEADR
545 && omfhdr.rec_len >= sizeof(omfhdr)
546 && !fseek(phFile, 0, SEEK_SET)
547 )
548 return 1;
549
550 return !fseek(phFile, 0, SEEK_SET)
551 && check_omf_library(phFile);
552}
553
554
555/**
556 * Checks if the stream phFile is an LX DLL.
557 *
558 * @returns 1 if LX DLL.
559 * @returns 0 if not LX DLL.
560 * @param phFile File stream to check.
561 */
562static int check_lx_dll(FILE *phFile)
563{
564 unsigned long ul;
565 char achMagic[2];
566
567 if ( fseek(phFile, 0, SEEK_SET)
568 || fread(&achMagic, 1, 2, phFile) != 2)
569 goto thats_not_it;
570
571 if (!memcmp(achMagic, "MZ", 2))
572 {
573 if ( fseek(phFile, 0x3c, SEEK_SET)
574 || fread(&ul, 1, 4, phFile) != 4 /* offset of the 'new' header */
575 || ul < 0x40
576 || ul >= 0x10000000 /* 512MB stubs sure */
577 || fseek(phFile, ul, SEEK_SET)
578 || fread(&achMagic, 1, 2, phFile) != 2)
579 goto thats_not_it;
580 }
581
582 if ( memcmp(achMagic, "LX", 2)
583 || fseek(phFile, 14, SEEK_CUR)
584 || fread(&ul, 1, 4, phFile) != 4) /*e32_mflags*/
585 goto thats_not_it;
586
587#define E32MODDLL 0x08000L
588#define E32MODPROTDLL 0x18000L
589#define E32MODMASK 0x38000L
590 if ( (ul & E32MODMASK) != E32MODDLL
591 && (ul & E32MODMASK) != E32MODPROTDLL)
592 goto thats_not_it;
593
594 /* it's a LX DLL! */
595 fseek(phFile, 0, SEEK_SET);
596 return 1;
597
598
599thats_not_it:
600 fseek(phFile, 0, SEEK_SET);
601 return 0;
602}
603
604
605/**
606 * Generates an unique temporary file.
607 *
608 * @returns 0 on success.
609 * @returns -1 on failure.
610 * @param pszFile Where to put the filename.
611 * @param pszPrefix Prefix.
612 * @param pszSuffix Suffix.
613 * @param pszLooklike Filename which name is to be incorporated into the temp filename.
614 * @remark The code is nicked from the weak linker.
615 */
616static int make_tempfile(char *pszFile, const char *pszPrefix, const char *pszSuffix, const char *pszLooklike)
617{
618 struct stat s;
619 unsigned c = 0;
620 char szLooklike[32];
621 pid_t pid = getpid();
622 const char * pszTmp = getenv("TMP");
623 if (!pszTmp) pszTmp = getenv("TMPDIR");
624 if (!pszTmp) pszTmp = getenv("TEMP");
625 if (!pszTmp) pszTmp = ".";
626 if (pszLooklike)
627 {
628 int cch;
629 char *psz = (char*)pszLooklike; /* we're nice fellows. */
630 while ((psz = strpbrk(psz, ":/\\")) != NULL)
631 pszLooklike = ++psz;
632 cch = strlen(pszLooklike);
633 if (cch + 3 > sizeof(szLooklike))
634 cch = sizeof(szLooklike) - 3;
635 szLooklike[0] = '_';
636 memcpy(&szLooklike[1], pszLooklike, cch);
637 szLooklike[cch + 1] = '_';
638 szLooklike[cch + 2] = '\0';
639 pszLooklike = psz = &szLooklike[0];
640 while ((psz = strpbrk(psz, ".@%^&#()")) != NULL)
641 *psz++ = '_';
642 }
643 else
644 pszLooklike = "";
645
646 do
647 {
648 struct timeval tv = {0,0};
649 if (c++ >= 200)
650 return -1;
651 gettimeofday(&tv, NULL);
652 sprintf(pszFile, "%s\\%s%s%x%lx%d%lx%s", pszTmp, pszPrefix, pszLooklike, pid, tv.tv_sec, c, tv.tv_usec, pszSuffix);
653 } while (!stat(pszFile, &s));
654
655 return 0;
656}
657
658
659/**
660 * Converts the file indicated by phFile & pszFilename to omf closing
661 * phFile and updating pszFilename with the new (temporary filename).
662 *
663 * @returns Pointer to an filestream for the converted file and pszFilename
664 * containing the name of the converted file.
665 * @returns exit the program
666 * @param phFile Filestream of the file to convert. (close this)
667 * @param pszFilename Name of the file to convert on entry.
668 * Name of the converted file on return.
669 */
670static FILE *aout_to_omf(FILE *pf, char *pszFilename, int fLibrary)
671{
672 int rc;
673 char * pszNewFile;
674 name_list *pName;
675
676 fclose(pf); /* don't need this! */
677
678 if (opt_t)
679 fprintf(stderr, "emxomfld: info: converting %s %s to OMF.\n",
680 fLibrary ? "lib" : "obj", pszFilename);
681
682 /*
683 * Make temporary file.
684 */
685 pName = xmalloc(sizeof(name_list));
686 pName->name = pszNewFile = xmalloc(_MAX_PATH);
687 if (make_tempfile(pszNewFile, "ldconv", fLibrary ? ".lib" : ".obj", pszFilename))
688 {
689 free(pszNewFile);
690 return NULL;
691 }
692
693 /*
694 * Do the conversion.
695 */
696 rc = spawnlp(P_WAIT, "emxomf.exe", "emxomf.exe", "-o", pszNewFile, pszFilename, NULL);
697 if (!rc)
698 {
699 /* open the file */
700 pf = fopen(pszNewFile, "rb");
701 if (pf)
702 {
703 /* add to auto delete list for removal on exit(). */
704 pName->next = conv_list;
705 conv_list = pName;
706
707 strcpy(pszFilename, pszNewFile);
708
709 if (opt_t)
710 fprintf(stderr, "emxomfld: info: convert result '%s'.\n",
711 pszFilename);
712 return pf;
713 }
714 remove(pszNewFile);
715 }
716 free(pszNewFile);
717 free(pName);
718
719 fprintf(stderr, "emxomfld: a.out to omf conversion failed for '%s'.\n",
720 pszFilename);
721 exit(1);
722 return NULL;
723}
724
725
726/**
727 * Converts the file indicated by phFile & pszFilename to omf closing
728 * phFile and updating pszFilename with the new (temporary filename).
729 *
730 * @returns Pointer to an filestream for the converted file and pszFilename
731 * containing the name of the converted file.
732 * @returns exit the program
733 * @param phFile Filestream of the file to convert. (close this)
734 * @param pszFilename Name of the file to convert on entry.
735 * Name of the converted file on return.
736 */
737static FILE *lx_to_omf(FILE *pf, char *pszFilename)
738{
739 int rc;
740 char * pszNewFile;
741 name_list *pName;
742
743 fclose(pf); /* don't need this! */
744
745 if (opt_t)
746 fprintf(stderr, "emxomfld: info: converting %s %s to an OMF import lib.\n",
747 "lib", pszFilename);
748
749 /*
750 * Make temporary file.
751 */
752 pName = xmalloc(sizeof(name_list));
753 pName->name = pszNewFile = xmalloc(_MAX_PATH);
754 if (make_tempfile(pszNewFile, "ldconv", ".lib", pszFilename))
755 {
756 free(pszNewFile);
757 return NULL;
758 }
759
760 /*
761 * Do the conversion.
762 */
763 rc = spawnlp(P_WAIT, "emximp.exe", "emximp.exe", "-o", pszNewFile, pszFilename, NULL);
764 if (!rc)
765 {
766 /* open the file */
767 pf = fopen(pszNewFile, "rb");
768 if (pf)
769 {
770 /* add to auto delete list for removal on exit(). */
771 pName->next = conv_list;
772 conv_list = pName;
773
774 strcpy(pszFilename, pszNewFile);
775
776 if (opt_t)
777 fprintf(stderr, "emxomfld: info: convert result '%s'.\n",
778 pszFilename);
779 return pf;
780 }
781 remove(pszNewFile);
782 }
783 free(pszNewFile);
784 free(pName);
785
786 fprintf(stderr, "emxomfld: lx dll to omf conversion failed for '%s'.\n",
787 pszFilename);
788 exit(2);
789 return NULL;
790}
791
792
793/**
794 * Finds the full path of a OMF object file and opens the file.
795 *
796 * This function may perform conversion from a.out to omf if that feature
797 * is enabled.
798 *
799 * We choose to be UNIX compatible her, and not search the LIB env.var.
800 * for unqualified objects. Nor will we add any suffixes to the name
801 * if it's witout any extension.
802 *
803 * @returns Pointer to a file stream for the object file to use in the link.
804 * @returns NULL on failure with pszFullname containing a copy of
805 * pszName (or something like that).
806 * @param pszFullname Where to store the name of the file to be used
807 * in the linking (and which stream is returned).
808 * @param pszName Object name given to on the linker commandline.
809 */
810static FILE *find_obj(char *pszFullname, const char *pszName)
811{
812 FILE *phFile;
813 char *psz;
814
815 /*
816 * Make abspath with slashes the desired way and such.
817 */
818 if (!_realrealpath(pszName, pszFullname, _MAX_PATH + 1))
819 {
820 printf("emxomfld: _abspath failed on '%s'!!!\n", pszName);
821 exit(1);
822 }
823
824 psz = pszFullname;
825 while ((psz = strchr(psz, '/')) != NULL)
826 *psz++ = '\\';
827
828 /*
829 * Try open the file.
830 */
831 phFile = fopen(pszFullname, "rb");
832 if (!phFile)
833 return NULL;
834
835 /*
836 * If autoconversion check if such is needed.
837 */
838 if ( autoconvert_flag
839 && !check_omf(phFile))
840 phFile = aout_to_omf(phFile, pszFullname, FALSE);
841
842 return phFile;
843}
844
845
846
847/* Finds the full path of a library file and opens the file.
848 *
849 * This function may perform conversion from a.out to omf if that feature
850 * is enabled.
851 *
852 * The function assumes that LIB has been updated with all the search paths
853 * specified on the commandline.
854 *
855 * Library names with no extension are given extensions after the rules
856 * indicated by the IS_SHARED parameter. If IS_SHARED is set then libraries
857 * with suffixes indicating shared libraries will be looked for before
858 * libraries with suffixes indicated static libraries. The list is as
859 * follows for set IS_SHARED:
860 * 1. _dll.lib
861 * 2. .lib
862 * 3. .dll (optional)
863 * 4. _s.lib
864 *
865 * If IS_SHARED is clear:
866 * 1. _s.lib
867 * 2. .lib
868 *
869 * Library names with no path is searched for in the semicolon separated list
870 * of paths the env.var. LIB contains. For each directory in LIB we'll start
871 * by see if it contains a 'lib' prefixed file, if not found we'll check for
872 * the unprefixed filename. If we're appending suffixes too, we'll loop thru
873 * all the possible suffixes for each directory before advancing to the next,
874 * having the prefixing as the inner most loop.
875 *
876 * @returns Pointer to a file stream for the library file to use in the link.
877 * @returns NULL on failure with pszFullname containing a copy of
878 * pszName (or something like that).
879 * @param pszFullname Where to store the name of the file to be used
880 * in the linking (and which stream is returned).
881 * @param pszName Library name given to on the linker commandline.
882 */
883static FILE *find_lib(char *pszFullname, const char *pszName, int fShared)
884{
885 /* Suffix list for shared linking. */
886 static const char *apszSharedSuff[] = { "_dll.lib", "_dll.a", ".lib", ".a", "_s.lib", "_s.a", NULL };
887 /* Suffix list for shared linking with .dll. */
888 static const char *apszSharedDllSuff[] = { "_dll.lib", "_dll.a", ".lib", ".a", ".dll", "_s.lib", "_s.a", NULL };
889 /* Suffix list for static linking. */
890 static const char *apszStaticSuff[] = { "_s.lib", "_s.a", ".lib", ".a", NULL };
891 /* Suffix list for names with extension. */
892 static const char *apszExtensionSuff[] = { "", NULL };
893 /* Prefix list for names with path. */
894 static const char *apszWithPathPref[] = { "", NULL };
895 /* Prefix list for names with no path. */
896 static const char *apszWithoutPathPref[]= { "lib", "", NULL };
897 int fPath; /* set if the library name have a path. */
898 int fExt; /* set if the library name have an extension. */
899 const char **papszSuffs; /* Pointer to the suffix list. */
900 const char **papszPrefs; /* Pointer to the prefix list. */
901 const char *pszLibPath; /* The path we're searching. */
902 size_t cchCurPath; /* Size of the current path. */
903 size_t cchName = strlen(pszName);
904 const char *psz;
905
906 /*
907 * Check if the file name has a path.
908 * (If it has, we won't check the LIB directories.)
909 * Choose the prefix list accordingly.
910 */
911 fPath = (strpbrk(pszName, ":/\\") != NULL);
912 papszPrefs = fPath ? apszWithPathPref : apszWithoutPathPref;
913
914 /*
915 * Check if the file has a real extension.
916 * Real extension means, .lib, .dll or .a. It also implies something
917 * before the dot.
918 * Choose the suffix list accordingly.
919 */
920 fExt = ( (cchName > 4 && !stricmp(pszName + cchName - 4, ".lib"))
921 || (cchName > 4 && !stricmp(pszName + cchName - 4, ".dll"))
922 || (cchName > 2 && !stricmp(pszName + cchName - 2, ".a")) );
923
924 if (!fExt)
925 {
926 if (fShared)
927 papszSuffs = opt_dll_search ? &apszSharedDllSuff[0] : &apszSharedSuff[0];
928 else
929 papszSuffs = &apszStaticSuff[0];
930 }
931 else
932 papszSuffs = apszExtensionSuff;
933
934 /*
935 * Loop 1: LIB (with a fake .\ as the first iteration)
936 * (Looping on pszLibPath, with preinitiated cchCurPath & pszFullname.)
937 */
938 cchCurPath = 0;
939 if (!fPath)
940 {
941 cchCurPath = 2;
942 memcpy(pszFullname, ".\\", 2);
943 }
944 pszLibPath = getenv("LIB");
945 do
946 {
947 /*
948 * Loop2: Suffixes.
949 */
950 int iSuff;
951 for (iSuff = 0; papszSuffs[iSuff]; iSuff++)
952 {
953 /*
954 * Loop3: Prefixes.
955 */
956 int iPref;
957 for (iPref = 0; papszPrefs[iPref]; iPref++)
958 {
959 FILE *phFile;
960 int cch = strlen(papszPrefs[iPref]);
961
962 /*
963 * Construct name.
964 */
965 memcpy(&pszFullname[cchCurPath], papszPrefs[iPref], cch);
966 cch = cchCurPath + cch;
967 memcpy(&pszFullname[cch], pszName, cchName);
968 cch += cchName;
969 strcpy(&pszFullname[cch], papszSuffs[iSuff]);
970
971 /*
972 * Open and if necessary convert it.
973 */
974 phFile = fopen(pszFullname, "rb");
975 if (phFile)
976 {
977 char *pszTmp;
978 if (autoconvert_flag)
979 {
980 if (check_lx_dll(phFile))
981 phFile = lx_to_omf(phFile, pszFullname);
982 else if (!check_omf(phFile))
983 phFile = aout_to_omf(phFile, pszFullname, TRUE);
984 }
985
986 /* Get the real native path. */
987 pszTmp = _realrealpath(pszFullname, NULL, 0);
988 if (pszTmp)
989 {
990 strcpy(pszFullname, pszTmp);
991 free(pszTmp);
992 }
993
994 /* Replace forward slashes with backslashes (link386). */
995 while ((pszFullname = strchr(pszFullname, '/')) != NULL)
996 *pszFullname++ = '\\';
997 return phFile;
998 }
999 } /* next prefix */
1000 } /* next suffix */
1001
1002 /*
1003 * If a path was specified or no LIB we're done now.
1004 */
1005 if (fPath || !pszLibPath)
1006 break;
1007
1008 /*
1009 * Next LIB part.
1010 */
1011 for (;;)
1012 {
1013 psz = strchr(pszLibPath, ';');
1014 if (!psz)
1015 psz = strchr(pszLibPath, '\0');
1016 cchCurPath = psz - pszLibPath;
1017 if (cchCurPath)
1018 {
1019 memcpy(pszFullname, pszLibPath, cchCurPath);
1020 pszLibPath = psz + (*psz == ';');
1021 /* Append last slash if it is not there */
1022 if ( pszFullname[cchCurPath - 1] != '/'
1023 && pszFullname[cchCurPath - 1] != '\\')
1024 pszFullname[cchCurPath++] = '\\';
1025 break;
1026 }
1027 if (!*psz)
1028 break;
1029 pszLibPath = psz + 1;
1030 }
1031 } while (cchCurPath);
1032
1033 /* failure */
1034 return NULL;
1035}
1036
1037
1038/* Weak prelinking for Method 2 Weak support. */
1039
1040static void weak_prelink ()
1041{
1042 int rc = 0;
1043 name_list * pOpt;
1044 PWLD pwld;
1045 unsigned fFlags = 0;
1046
1047 /* look for ilinker options. */
1048 if (opt_t)
1049 fFlags |= WLDC_VERBOSE;
1050 if (!stricmp(linker_type, "LINK386"))
1051 fFlags |= WLDC_LINKER_LINK386;
1052 else if (!stricmp(linker_type, "WLINK"))
1053 fFlags |= WLDC_LINKER_WLINK;
1054
1055 for (pOpt = options; pOpt; pOpt = pOpt->next)
1056 if ( !strnicmp(pOpt->name, "/NOE", 4)
1057 || !strnicmp(pOpt->name, "-NOE", 4))
1058 fFlags |= WLDC_NO_EXTENDED_DICTIONARY_SEARCH;
1059 else
1060 if ( !strnicmp(pOpt->name, "/INC", 4)
1061 || !strnicmp(pOpt->name, "-INC", 4))
1062 fFlags = fFlags; /* Ignore for now. */
1063 else
1064 if ( !strnicmp(pOpt->name, "/IG", 3)
1065 || !strnicmp(pOpt->name, "-IG", 3))
1066 fFlags |= WLDC_CASE_INSENSITIVE;
1067 else
1068 if ( !strnicmp(pOpt->name, "/I", 2)
1069 || !strnicmp(pOpt->name, "-I", 2))
1070 fFlags = fFlags; /* Ignore - require opt_t. */
1071 else
1072 if ( !strnicmp(pOpt->name, "/NOIN", 5)
1073 || !strnicmp(pOpt->name, "-NOIN", 5))
1074 fFlags &= ~WLDC_VERBOSE;
1075 else
1076 if ( !strnicmp(pOpt->name, "/NOI", 4)
1077 || !strnicmp(pOpt->name, "/NOI", 4))
1078 fFlags &= ~WLDC_CASE_INSENSITIVE;
1079
1080 /* create the linker and to the linking. */
1081 if (opt_t)
1082 fprintf(stderr, "*** Invoking weak prelinker with flags %x.\n", fFlags);
1083 pwld = WLDCreate (fFlags);
1084 if (pwld)
1085 {
1086 name_list * pcur;
1087 FILE *phfile;
1088 char szname[_MAX_PATH + 1];
1089
1090 /* definition file if any */
1091 if (def_fname && def_fname[0])
1092 {
1093 phfile = fopen (def_fname, "r");
1094 rc = WLDAddDefFile (pwld, phfile, def_fname);
1095 }
1096
1097 /* objects */
1098 for (pcur = obj_fnames; !rc && pcur; pcur = pcur->next)
1099 {
1100 phfile = find_obj (szname, pcur->name);
1101 rc = WLDAddObject (pwld, phfile, szname);
1102 }
1103
1104 /* libraries */
1105 for (pcur = lib_fnames; !rc && pcur; pcur = pcur->next)
1106 {
1107 phfile = find_lib (szname, pcur->name, !pcur->flags);
1108 rc = WLDAddLibrary (pwld, phfile, szname);
1109 free(pcur->name);
1110 pcur->name = xstrdup(szname);
1111 }
1112
1113 /* complete pass 1 */
1114 if (!rc)
1115 {
1116 rc = WLDPass1 (pwld);
1117 /* ignore unresolved externals for now. */
1118 if (rc == 42)
1119 {
1120 rc = 0;
1121 fprintf(stderr, "Ignoring unresolved externals reported from weak prelinker.\n");
1122 }
1123 }
1124
1125 /* generate weak aliases. */
1126 if (!rc)
1127 rc = WLDGenerateWeakAliases (pwld, weakobj_fname, weakdef_fname);
1128 if (!rc && weakobj_fname[0])
1129 {
1130 char *pszTmp = _realrealpath(weakobj_fname, NULL, 0);
1131 if (pszTmp)
1132 {
1133 strcpy(weakobj_fname, pszTmp);
1134 free(pszTmp);
1135 }
1136 add_name_list (&add_obj_fnames, weakobj_fname, 0);
1137 }
1138 if (!rc && weakdef_fname[0])
1139 {
1140 char *pszTmp = _realrealpath(weakdef_fname, NULL, 0);
1141 if (pszTmp)
1142 {
1143 strcpy(weakdef_fname, pszTmp);
1144 free(pszTmp);
1145 }
1146 def_fname = weakdef_fname;
1147 }
1148
1149 /* cleanup the linker */
1150 WLDDestroy (pwld);
1151
1152 /* last words */
1153 if (rc)
1154 fprintf (stderr, "emxomfld: weak prelinker failed. (rc=%d)\n", rc);
1155 }
1156 else
1157 {
1158 rc = 8;
1159 fprintf (stderr, "emxomfld: failed to create weak prelinker.\n");
1160 }
1161
1162 /* die on error. */
1163 if (rc)
1164 exit(rc);
1165
1166 /* verbose */
1167 if (opt_t)
1168 fprintf(stderr, "*** Weak prelinker done\n");
1169}
1170
1171
1172/* Start a new set of command line arguments. If RSP is non-zero, we
1173 are allowed to use a response file. */
1174
1175static void arg_init (int rsp)
1176{
1177 if (response_fname[0] != '\0')
1178 {
1179 remove (response_fname);
1180 response_fname[0] = '\0';
1181 }
1182 command_line[0] = '\0';
1183 line_len = 0;
1184 response_flag = rsp;
1185 force_response_file = FALSE;
1186}
1187
1188
1189/* Call this after adding all the command line arguments. If a
1190 response file has been created, add a newline and close it. */
1191
1192static void arg_end (void)
1193{
1194 if (response_file != NULL)
1195 {
1196 fputc ('\n', response_file);
1197 if (fflush (response_file) != 0 || fclose (response_file) != 0)
1198 {
1199 perror ("emxomfld");
1200 exit (2);
1201 }
1202 response_file = NULL;
1203 }
1204}
1205
1206/* Generates a definition file for a dll which doesn't have one. */
1207static void gen_deffile(void)
1208{
1209 char * psz;
1210 name_list *pName;
1211
1212 /*
1213 * Make temporary file.
1214 */
1215 pName = (name_list *)xmalloc(sizeof(*pName));
1216 pName->name = psz = xmalloc(_MAX_PATH);
1217 if (!make_tempfile(psz, "lddef", ".def", NULL))
1218 {
1219 FILE *pFile = fopen(psz, "w");
1220 if (pFile)
1221 {
1222 const char *pszName = _getname(output_fname);
1223 size_t cchName = strlen(pszName);
1224 if (cchName > 4 && !stricmp(pszName + cchName - 4, ".dll"))
1225 cchName -= 4;
1226 fprintf(pFile,
1227 ";; Autogenerated by emxomfld\n"
1228 "LIBRARY %.*s INITINSTANCE TERMINSTANCE\n"
1229 "DATA MULTIPLE\n"
1230 "CODE SHARED\n"
1231 "\n",
1232 cchName, pszName);
1233 fclose(pFile);
1234 def_fname = psz;
1235 if (opt_t)
1236 fprintf(stderr,
1237 "--- Generated def-file %s:\n"
1238 ";; Autogenerated by emxomfld\n"
1239 "LIBRARY %.*s INITINSTANCE TERMINSTANCE\n"
1240 "DATA MULTIPLE NONSHARED\n"
1241 "CODE SINGLE SHARED\n"
1242 "---- End of generated def-file.\n",
1243 psz, cchName, pszName);
1244
1245 /* add to auto delete list for removal on exit(). */
1246 pName->next = conv_list;
1247 conv_list = pName;
1248 return;
1249 }
1250 }
1251 free(psz);
1252 free(pName);
1253}
1254
1255/* converts a def file statement to watcom responsfile lingo. */
1256
1257static def_2_watcom(struct _md *md, const _md_stmt *stmt, _md_token token, void *arg)
1258{
1259 switch (token)
1260 {
1261 case _MD_BASE:
1262 fprintf (response_file, "OPTION OFFSET=%#lx\n", stmt->base.addr);
1263 break;
1264
1265 case _MD_CODE:
1266 break;
1267
1268 case _MD_DATA:
1269 break;
1270
1271 case _MD_DESCRIPTION:
1272 fprintf (response_file, "OPTION DESCRIPTION '%s'\n", stmt->descr.string);
1273 break;
1274
1275 case _MD_EXETYPE:
1276 break;
1277
1278 case _MD_EXPORTS:
1279 fprintf (response_file, "EXPORT '%s'", stmt->export.entryname);
1280 if (stmt->export.flags & _MDEP_ORDINAL)
1281 fprintf (response_file, ".%d", stmt->export.ordinal);
1282 if (stmt->export.internalname[0])
1283 fprintf (response_file, "='%s'", stmt->export.internalname);
1284 if (stmt->export.flags & _MDEP_RESIDENTNAME)
1285 fprintf (response_file, " RESIDENT", stmt->export.internalname);
1286 /** @todo _MDEP_NONAME */
1287 fprintf (response_file, "\n");
1288
1289 /* reference the internal name. */
1290 if (stmt->export.internalname[0])
1291 fprintf (response_file, "REFERENCE '%s'\n", stmt->export.internalname);
1292 break;
1293
1294 case _MD_HEAPSIZE:
1295 fprintf (response_file, "OPTION HEAPSIZE=%#lx\n", stmt->heapsize.size);
1296 break;
1297
1298 case _MD_IMPORTS:
1299 fprintf (response_file, "IMPORT '%s' '%s'", stmt->import.entryname,
1300 stmt->import.modulename);
1301 if (stmt->import.flags & _MDEP_ORDINAL)
1302 fprintf (response_file, ".%d", stmt->import.ordinal);
1303 else if (stmt->import.internalname[0])
1304 fprintf (response_file, ".'%s'", stmt->import.internalname);
1305 fprintf (response_file, "\n");
1306 break;
1307
1308 case _MD_LIBRARY:
1309 if (stmt->library.name[0])
1310 fprintf (response_file, "OPTION MODNAME='%s'\n", stmt->library.name);
1311 break;
1312
1313 case _MD_NAME:
1314 if (stmt->name.name[0])
1315 fprintf (response_file, "OPTION MODNAME='%s'\n", stmt->name.name);
1316 break;
1317
1318 case _MD_OLD:
1319 fprintf (response_file, "OPTION OLDLIBRARY='%s'\n", stmt->old.name);
1320 break;
1321
1322 case _MD_PROTMODE:
1323 fprintf (response_file, "OPTION PROTMODE\n");
1324 break;
1325
1326 case _MD_REALMODE:
1327 fprintf (response_file, "OPTION PROTMODE\n");
1328 break;
1329
1330 case _MD_SEGMENTS:
1331 fprintf (stderr, "emxomfld: ignoring SEGMENTS directive in .def-file\n");
1332 break;
1333
1334 case _MD_STACKSIZE:
1335 fprintf (response_file, "OPTION STACK=%#ld\n", stmt->stacksize.size);
1336 break;
1337
1338 case _MD_STUB:
1339 fprintf (response_file, "OPTION STUB='%s'\n", stmt->stub.name);
1340 break;
1341
1342 case _MD_VIRTUAL:
1343 case _MD_PHYSICAL:
1344 break;
1345
1346 case _MD_parseerror:
1347 fprintf (stderr, "emxomfld: %s (line %ld of %s)",
1348 _md_errmsg (stmt->error.code), _md_get_linenumber (md), def_fname);
1349 exit (2);
1350 break;
1351
1352 default:
1353 abort ();
1354 }
1355 return 0;
1356}
1357
1358/* -t output. We dump the commandline and responsefile. */
1359static void show_spawn(const char *pszwhat)
1360{
1361 if (!opt_t)
1362 return;
1363 fprintf(stderr, "*** Invoking %s\n %s\n", pszwhat, command_line);
1364 if (response_fname[0])
1365 { /* display the responsfile content. */
1366 char sz[4096];
1367 FILE *phfile = fopen(response_fname, "r");
1368 fprintf(stderr, "--- Response file %s:\n", response_fname);
1369 sz[0] = '\0';
1370 while (fgets(sz, sizeof(sz), phfile))
1371 fprintf(stderr, "%s", sz);
1372 fclose(phfile);
1373 if (sz[strlen(sz) - 1] != '\n')
1374 fprintf(stderr, "\n");
1375 fprintf(stderr, "--- End of Response File\n");
1376 }
1377}
1378
1379
1380/* Execute commandline and returns the result.
1381 pszwhat is used for opt_t trace information. */
1382
1383static int emxomfld_spawn(char *pszcmd, const char *pszwhat)
1384{
1385 int argi;
1386 char ** argv;
1387 char * psz;
1388 int rc;
1389
1390 if (opt_t)
1391 show_spawn(pszwhat);
1392
1393 /* construct spawnvp() argument array */
1394 argi = 0;
1395 argv = NULL;
1396 psz = pszcmd;
1397 while (psz && *psz)
1398 {
1399 char *psz2 = psz;
1400
1401 /* skip blanks. */
1402 while (*psz2 == '\t' || *psz2 == ' ')
1403 psz2++;
1404
1405 /* find end of argument taking in account in arg quoting. */
1406 while (*psz2 && *psz2 != '\t' && *psz2 != ' ')
1407 {
1408 if (*psz2 == '"' || *psz2 == '\'')
1409 {
1410 char chQuote = *psz2++;
1411 while (*psz2 && *psz2 != chQuote)
1412 psz2++;
1413 }
1414 psz2++;
1415 }
1416
1417 /* terminate and set psz2 to point to next */
1418 if (*psz2)
1419 *psz2++ = '\0';
1420
1421 /* add argument to argument vector. */
1422 if (!(argi % 32))
1423 argv = xrealloc(argv, sizeof(argv[0]) * (argi + 32 + 1));
1424 argv[argi++] = psz;
1425
1426 /* next */
1427 psz = psz2;
1428 }
1429 argv[argi] = NULL;
1430
1431 /* Spawn process. */
1432 rc = spawnvp(P_WAIT, argv[0], argv);
1433 if (opt_t)
1434 fprintf(stderr, "*** Return from %s is %d\n", pszwhat, rc);
1435
1436 free(argv);
1437 return rc;
1438}
1439
1440
1441/* Cleanup by closing (if open) and deleting (if pressent) the
1442 response file. This function is used with atexit(). */
1443
1444static void cleanup (void)
1445{
1446 if (response_file != NULL)
1447 {
1448 fclose (response_file);
1449 response_file = NULL;
1450 }
1451 if (opt_t <= 1)
1452 {
1453 if (response_fname[0] != '\0')
1454 {
1455 remove (response_fname);
1456 response_fname[0] = '\0';
1457 }
1458 if (weakobj_fname[0] != '\0')
1459 {
1460 remove (weakobj_fname);
1461 weakobj_fname[0] = '\0';
1462 }
1463 if (weakdef_fname[0] != '\0')
1464 {
1465 remove (weakdef_fname);
1466 weakdef_fname[0] = '\0';
1467 }
1468 for (; conv_list; conv_list = conv_list->next)
1469 remove (conv_list->name);
1470 }
1471}
1472
1473/* Tell the user how to run this program. */
1474
1475static void usage (void)
1476{
1477 fputs ("emxomfld " VERSION INNOTEK_VERSION "\n"
1478 "Copyright (c) 1992-1996 by Eberhard Mattes\n"
1479 "Copyright (c) 2003 by InnoTek Systemberatung GmbH\n"
1480 "Copyright (c) 2003-2006 by Knut St. Osmundsen\n"
1481 "\n", stderr);
1482 fputs ("Usage: emxomfld -o <file> [-l <lib>] [-L <libdir>] [-T <base>] [-igtsS]\n"
1483 " [-Zexe] [-Zdll] [-Zstack <size>] [-Zmap[=<map_file>]]\n"
1484 " [-Z[no-]autoconv] [-Zdll-search] [-O <option>] [-static]\n"
1485 " [-non_shared] [-Bstatic] [-dn] [call_shared] [-Bshared]\n"
1486 " [-dy] <file>...\n"
1487 "\n", stderr);
1488 fputs ("Options:\n"
1489 " -Zno-autoconv / -Zautoconv:\n"
1490 " Turns off/on the automatic conversion of a.out libs and objs.\n"
1491 " default: -Zautoconv\n"
1492 " -Bstatic, -non_shared, -dn, -static:\n"
1493 " Link with static libraries.\n"
1494 " The search order is then changed to: lib<name>_s.lib, <name>_s.lib,\n"
1495 " lib<name>.lib, <name>.lib\n", stderr);
1496 fputs (" -Bshared, -call_shared, -dy:\n"
1497 " Link with shared libraries. This is default.\n"
1498 " The search order is then changed to: lib<name>_dll.lib, <name>_dll.lib,\n"
1499 " lib<name>.lib, <name>.lib, <name>.dll, lib<name>_s.lib, <name>_s.lib.\n"
1500 " -Zdll-search:\n"
1501 " Enables dlls as valid libraries from shared linking. (default disabled)\n"
1502 "\n", stderr);
1503 fputs ("Environment variables:\n"
1504 " EMXOMFLD_TYPE:\n"
1505 " The type of linker we're using. Values: WLINK, VAC365, VAC308, LINK386.\n"
1506 " WLINK wlink.exe from Open Watcom v1.5 or later.\n"
1507 " VAC365 ilink.exe from IBM C and C++ Compilers for OS/2 v3.6 or later.\n"
1508 " VAC308 ilink.exe from Visual Age for C++ v3.08.\n"
1509 " LINK386 link386 form OS/2 install or DDK.\n", stderr);
1510 fputs (" EMXOMFLD_LINKER:\n"
1511 " Name of the linker to use and optionally extra parameters. Spaces in the\n"
1512 " linker name or path is not supported. Quotes are not supported either.\n"
1513 "The default values for these two variables are WLINK and wlink.exe.\n", stderr);
1514 exit (1);
1515}
1516
1517
1518
1519static struct option longopts[] =
1520{
1521#define OPT_LIBS_STATIC 0x1000
1522 {"Bstatic", 0, 0, OPT_LIBS_STATIC},
1523 {"non_shared", 0, 0, OPT_LIBS_STATIC},
1524 {"dn", 0, 0, OPT_LIBS_STATIC},
1525 {"static", 0, 0, OPT_LIBS_STATIC},
1526#define OPT_LIBS_SHARED 0x1001
1527 {"Bshared", 0, 0, OPT_LIBS_SHARED},
1528 {"call_shared", 0, 0, OPT_LIBS_SHARED},
1529 {"dy", 0, 0, OPT_LIBS_SHARED},
1530#define OPT_ZEXE 0x1002
1531 {"Zexe", 0, 0, OPT_ZEXE}, /* Create .exe file, touch `output file' */
1532#define OPT_ZDLL 0x1003
1533 {"Zdll", 0, 0, OPT_ZDLL}, /* Create .dll file, touch `output file' */
1534#define OPT_ZSTACK 0x1004
1535 {"Zstack", 1, 0, OPT_ZSTACK}, /* Set stack size */
1536#define OPT_ZMAP 0x1005
1537 {"Zmap", 2, 0, OPT_ZMAP}, /* Create .map file */
1538 {"Zmap=", 1, 0, OPT_ZMAP},
1539#define OPT_ZAUTOCONV 0x1006
1540 {"Zautoconv",0, 0, OPT_ZAUTOCONV},
1541#define OPT_ZNO_AUTOCONV 0x1007
1542 {"Zno-autoconv",0, 0, OPT_ZNO_AUTOCONV},
1543#define OPT_ZDLL_SEARCH 0x1008
1544 {"Zdll-search",0, 0, OPT_ZDLL_SEARCH},
1545/* {"e", 1, 0, 'e'}, entry point */
1546 {"i", 0, 0, 'i'},
1547 {"o", 1, 0, 'o'},
1548 {"O", 1, 0, 'O'},
1549/* {"u", 1, 0, 'u'}, reference symbol */
1550 {"s", 0, 0, 's'},
1551 {"S", 0, 0, 'S'},
1552 {"t", 0, 0, 't'},
1553 {"T", 1, 0, 'T'},
1554 {"v", 0, 0, 'v'},
1555 {"x", 0, 0, 'x'},
1556 {"X", 0, 0, 'X'},
1557 {NULL, 0, 0, 0}
1558};
1559
1560/* Main function of emxomf. Parse the command line and call the IBM/M$
1561 linker (and optionally RC). */
1562
1563int main (int argc, char *argv[])
1564{
1565 struct stat s;
1566 int c, rc, files;
1567 const char *ext;
1568 char tmp[512], *t;
1569 char execname[512];
1570 name_list *pcur;
1571 int opt_libs_static = 0;
1572 int longind;
1573
1574 /* Get options from response files (@filename) and wildcard (*.o) on the command. */
1575
1576 _response (&argc, &argv);
1577 _wildcard (&argc, &argv);
1578
1579 /* Close and delete the response file on exit. */
1580
1581 atexit (cleanup);
1582
1583 /* Prepare parsing of the command line. */
1584
1585 files = 0;
1586 opterr = FALSE;
1587 /*optmode = GETOPT_KEEP; */
1588 if (argc < 2)
1589 usage ();
1590
1591 /* Parse the command line options and other arguments. */
1592 while ((c = getopt_long_only (argc, argv, "-l:y:L:", longopts, &longind)) != EOF)
1593 {
1594 if (c == 0)
1595 c = longopts[longind].val;
1596 switch (c)
1597 {
1598 case 1: /* Non-option argument */
1599
1600 /* Extract the extension to see what to do with this
1601 argument. */
1602
1603 ext = _getext (optarg);
1604
1605 if (ext == NULL)
1606 {
1607 /* GCC's temporary files don't have an extension. Add a
1608 dot to the end of the name to prevent the linker from
1609 adding `.obj'. */
1610
1611 sprintf (tmp, "%s.", optarg);
1612 add_name_list (&add_obj_fnames, tmp, 0);
1613 }
1614
1615 /* If it's a .def file, use it as module definition file
1616 (input). */
1617
1618 else if (stricmp (ext, ".def") == 0)
1619 {
1620 if (def_fname != NULL)
1621 {
1622 fprintf (stderr,
1623 "emxomfld: multiple module definition files\n");
1624 return 1;
1625 }
1626 def_fname = _realrealpath(optarg, NULL, 0);
1627 if (!def_fname)
1628 def_fname = optarg;
1629 }
1630
1631 /* If it's a .res file, use it as binary resource file
1632 (input). */
1633
1634 else if (stricmp (ext, ".res") == 0)
1635 {
1636 if (res_fname != NULL)
1637 {
1638 fprintf (stderr,
1639 "emxomfld: multiple binary resource files\n");
1640 return 1;
1641 }
1642 res_fname = _realrealpath(optarg, NULL, 0);
1643 if (!def_fname)
1644 res_fname = optarg;
1645 }
1646
1647 /* If it's a .lib file, use it as library file. We also
1648 accept .a files for those who use OMF files disguised as
1649 a.out files (to simplify their make files). */
1650
1651 else if (stricmp (ext, ".lib") == 0 || stricmp (ext, ".a") == 0 || stricmp (ext, ".dll") == 0)
1652 add_name_list (&add_lib_fnames, optarg, opt_libs_static);
1653
1654 /* Otherwise, assume it's an object file. */
1655
1656 else
1657 add_name_list (&add_obj_fnames, optarg, 0);
1658 ++files;
1659 break;
1660
1661 case 't':
1662 case 'i': /* Trace the linking process, sending /INFO to the IBM/M$ linker. */
1663 opt_t++;
1664 break;
1665
1666 case 'l': /* Add library */
1667 add_name_list (&add_lib_fnames, optarg, opt_libs_static);
1668 break;
1669
1670 case 'o': /* Set output file name */
1671 output_fname = optarg;
1672 break;
1673
1674 case 'L': /* Add library directory */
1675 add_name_list (&add_libdirs, optarg, 0);
1676 break;
1677
1678 case 'T': /* Set base address */
1679 base = optarg;
1680 break;
1681
1682 case 's': /* Strip all symbols */
1683 case 'S': /* Strip debugging symbols */
1684 strip_symbols = TRUE;
1685 break;
1686
1687 case 'x': /* Discard all local symbols */
1688 case 'X': /* Discard local symbols starting with L */
1689 break;
1690
1691 case 'v': /* For compatibility */
1692 break;
1693
1694 case 'O': /* Specify Linker option */
1695 add_name_list (&add_options, optarg, 0);
1696 break;
1697
1698 case OPT_ZDLL:
1699 dll_flag = TRUE;
1700 break;
1701
1702 case OPT_ZEXE:
1703 exe_flag = TRUE;
1704 break;
1705
1706 case OPT_ZMAP:
1707 map_flag = TRUE;
1708 if (optarg)
1709 {
1710 if (map_fname != NULL)
1711 {
1712 fprintf (stderr, "emxomfld: multiple map files files\n");
1713 return 1;
1714 }
1715 map_fname = optarg;
1716 }
1717 break;
1718
1719 case OPT_ZSTACK:
1720 if (!optarg)
1721 return 1;
1722 errno = 0;
1723 stack_size = strtol (optarg, &t, 0);
1724 if (errno != 0 || *t != 0 || t == optarg)
1725 return 1;
1726 break;
1727
1728 case OPT_ZAUTOCONV:
1729 autoconvert_flag = 1;
1730 break;
1731 case OPT_ZNO_AUTOCONV:
1732 autoconvert_flag = 0;
1733 break;
1734
1735 case OPT_ZDLL_SEARCH:
1736 opt_dll_search = 1;
1737 break;
1738
1739 case OPT_LIBS_STATIC:
1740 opt_libs_static = 1;
1741 break;
1742 case OPT_LIBS_SHARED:
1743 opt_libs_static = 0;
1744 break;
1745
1746 case '?':
1747 default:
1748 if (optind > 1)
1749 fprintf (stderr, "emxomfld: invalid option (%s)\n", argv[optind - 1]);
1750 else
1751 usage ();
1752 return 1;
1753 }
1754 }
1755 /* Set default value for output file. */
1756
1757 if (output_fname == NULL)
1758 {
1759 fprintf (stderr,
1760 "emxomfld: no output file, creating $$$.exe or $$$.dll\n");
1761 output_fname = "$$$";
1762 }
1763
1764 /* Check if there are any input files. */
1765
1766 if (files == 0)
1767 {
1768 fprintf (stderr, "emxomfld: no input files\n");
1769 return 1;
1770 }
1771
1772 /* Remove the output file if -Zexe is given. */
1773
1774 if (exe_flag)
1775 remove (output_fname);
1776
1777 /* If neither -Zmap nor -Zmap=file is used, pass "nul" to the linker in
1778 the map file field. If -Zmap is used, construct the name of the
1779 .map file. If -Zmap=file is used, use `file' as the name of the
1780 .map file. */
1781
1782 if (!map_flag)
1783 map_fname = "nul";
1784 else if (map_fname == NULL)
1785 {
1786 int cch = strlen (output_fname) + 1;
1787 t = xmalloc (cch + 4);
1788 memcpy (t, output_fname, cch);
1789 _remext (t);
1790 strcat (t, ".map");
1791 map_fname = t;
1792 }
1793
1794 /* Build the environment for the linker. */
1795
1796 make_env ();
1797
1798 /* EMXOMFLD_TYPE contains VAC365, VAC308 or LINK386 if set. If non of these
1799 we assume VAC365.
1800 EMXOMFLD_LINKER contains the linker name and perhaps extra arguments. If
1801 not set we'll use the default linker, ilink. */
1802
1803 t = getenv ("EMXOMFLD_TYPE");
1804 if ( t
1805 && stricmp(t, "WLINK")
1806 && stricmp(t, "VAC365")
1807 && stricmp(t, "VAC308")
1808 && stricmp(t, "LINK386")
1809 )
1810 fprintf (stderr, "emxomfld: warning: '%s' is an invalid value for EMXOMFLD_TYPE.\n", t);
1811 else if (t)
1812 linker_type = t;
1813
1814 t = getenv ("EMXOMFLD_LINKER");
1815 if (t)
1816 linker_name = t;
1817 if (opt_t)
1818 fprintf(stderr, "*** Linker : %s\n"
1819 "*** Linker type: %s\n", linker_name, linker_type);
1820
1821 /* apply object & library hacks */
1822 for (pcur = obj_fnames, rc = 0; !rc && pcur; pcur = pcur->next)
1823 {
1824 char szname[_MAX_PATH + 1];
1825 FILE *phfile = find_obj (szname, pcur->name);
1826 if (!phfile)
1827 continue;
1828 free (pcur->name);
1829 pcur->name = xstrdup(szname);
1830 fclose(phfile);
1831 }
1832
1833 for (pcur = lib_fnames, rc = 0; !rc && pcur; pcur = pcur->next)
1834 {
1835 char szname[_MAX_PATH + 1];
1836 FILE *phfile = find_lib (szname, pcur->name, !pcur->flags);
1837 if (!phfile)
1838 continue;
1839 free (pcur->name);
1840 pcur->name = xstrdup(szname);
1841 fclose(phfile);
1842 }
1843
1844 /* generate .def-file for dlls. */
1845
1846 if (!def_fname && dll_flag)
1847 gen_deffile();
1848
1849 /* Do the weak prelinking. Important that this is done after make_env(). */
1850
1851 weak_prelink ();
1852
1853 /* Start building the linker command line. We can use a response
1854 file if the command line gets too long. */
1855
1856 arg_init (TRUE);
1857
1858 /* issue commandline */
1859 put_arg (linker_name, TRUE, FALSE);
1860
1861 if (stricmp (linker_type, "WLINK"))
1862 {
1863 /*
1864 For VAC365 and VAC308 the default options are:
1865
1866 /NOFR[EEFORMAT] Use /NOFREEFORMAT to allow a LINK386-compatible
1867 command line syntax, in which different types of file
1868 are grouped and separated by commas.
1869
1870 /DBGPACK If !strip_symbols then we'll add this option, which
1871 will cause type tables to be merged into one global
1872 table and so eliminating a lot of duplicate info.
1873
1874 For VAC365 additional default option is:
1875
1876 /STUB:<emxomfld-path>\os2stub.bin
1877 Causes this MZ stub to be used when linking the
1878 executables instead of the default on for the linker.
1879
1880 For LINK386 the default options are:
1881
1882 /BATCH Run in batch mode (disable prompting, don't
1883 echo response file)
1884
1885 The default options for all linkers are:
1886
1887 /NOLOGO Don't display sign-on banner
1888
1889 /NOEXTDICTIONARY Don't use extended dictionary (redefining
1890 library symbols is quite common)
1891
1892 /NOIGNORECASE Make symbols case-sensitive
1893
1894 /PACKCODE Group neighboring code segments (this is the
1895 default unless the SEGMENTS module definition
1896 statement is used for a segment of class
1897 'CODE'). Not grouping neighboring code
1898 segments would break sets
1899
1900 For non DLLs targets:
1901
1902 /BASE:0x10000 Base the executable an so removing extra fixups.
1903
1904 */
1905
1906 /* the next part depends on the linker type. */
1907 if (!stricmp (linker_type, "LINK386"))
1908 put_arg ("/bat", FALSE, FALSE);
1909 else /* vac3xx: */
1910 {
1911 put_arg ("/nofree", FALSE, FALSE);
1912 if (!strip_symbols)
1913 put_arg ("/db", FALSE, FALSE);
1914 if (map_flag)
1915 put_arg ("/map", FALSE, FALSE);
1916 }
1917 put_arg ("/nol", FALSE, FALSE);
1918 put_arg ("/noe", FALSE, FALSE);
1919 put_arg ("/noi", FALSE, FALSE);
1920 put_arg ("/packc", FALSE, FALSE);
1921
1922
1923 /* VAC365: check if we have os2stub.bin.
1924 We must to this after the above stuff else /nol might end up in the
1925 response file and we'll get the component output. */
1926
1927 if (!stricmp (linker_type, "VAC365"))
1928 {
1929 /* gklayout show that the linker isn't capable of determining a
1930 decent value for this parameter. 32MB makes gklayout link. */
1931 put_arg ("/ocache:0x02000000", FALSE, FALSE);
1932
1933 _execname (&execname[0], sizeof(execname));
1934 strcpy (_getname (&execname[0]), "os2stub.bin");
1935 if (!stat (execname, &s))
1936 {
1937 sprintf (tmp, "/STUB:%s", &execname[0]);
1938 put_arg (tmp, FALSE, FALSE);
1939 }
1940 }
1941
1942 /* Add the /INFORMATION option if the -i or -t option was given. This is
1943 for debugging. */
1944
1945 if (opt_t)
1946 put_arg ("/i", FALSE, FALSE);
1947
1948 /* Add the /DEBUG option if the -s option was not given. Without
1949 this, the linker throws away debugging information. */
1950
1951 if (!strip_symbols)
1952 put_arg ("/de", FALSE, FALSE);
1953
1954 /* Add the /BASE:n option to set the base address. This specifies
1955 the preferred load address of object 1. The base address being
1956 used is 0x10000 unless a DLL is generated or the -T option was
1957 given. -Tno can be used to suppress the /BASE:n option. */
1958
1959 if (base == NULL && !dll_flag)
1960 {
1961 struct _md *md;
1962
1963 if (def_fname != NULL)
1964 {
1965 int token;
1966 md = _md_open (def_fname);
1967 if (md == NULL)
1968 {
1969 fprintf (stderr, "emxomfld: cannot open `%s'\n", def_fname);
1970 exit (2);
1971 }
1972 token = _md_next_token (md);
1973 if (token == _MD_LIBRARY || token == _MD_PHYSICAL || token == _MD_VIRTUAL)
1974 dll_flag = TRUE;
1975 _md_close (md);
1976 }
1977 }
1978 if (base == NULL && !dll_flag)
1979 base = "0x10000";
1980 if (base != NULL && stricmp (base, "no") != 0)
1981 {
1982 sprintf (tmp, "/bas:%s", base);
1983 put_arg (tmp, FALSE, FALSE);
1984 }
1985
1986 /* Add the /STACK:n option if the -Zstack option was given. */
1987
1988 if (!dll_flag)
1989 {
1990 sprintf (tmp, "/st:0x%lx", stack_size * 1024);
1991 put_arg (tmp, FALSE, FALSE);
1992 }
1993
1994 /* Add the linker options specified with -O. */
1995
1996 put_args (options, FALSE);
1997
1998 /* Put the object file names onto the command line. */
1999
2000 force_response_file = TRUE; /* link386 workaround. */
2001 put_args (obj_fnames, TRUE);
2002 put_arg (",", FALSE, FALSE);
2003
2004 /* Put the output file name onto the command line. */
2005
2006 put_arg (output_fname, TRUE, TRUE);
2007 put_arg (",", FALSE, FALSE);
2008
2009 /* Put the map file name onto the command line. */
2010
2011 put_arg (map_fname, TRUE, TRUE);
2012 put_arg (",", FALSE, FALSE);
2013
2014 /* Put the library file names onto the command line. */
2015
2016 put_args (lib_fnames, TRUE);
2017 put_arg (",", FALSE, FALSE);
2018
2019 /* Put the name of the module definition file onto the command line. */
2020
2021 put_arg (def_fname, TRUE, TRUE);
2022 put_arg (";", FALSE, FALSE);
2023
2024 /* Call Linker and abort on failure. */
2025 }
2026 else /* wlink */
2027 {
2028 open_response_file ();
2029
2030 /* figure out what format options we're gonna use */
2031
2032 if (!def_fname && !dll_flag)
2033 fprintf (response_file, "FORMAT OS2 LX PMCompatible\n");
2034 else if (!def_fname && dll_flag)
2035 fprintf (response_file, "FORMAT OS2 LX DLL INITINSTANCE TERMINSTANCE\n");
2036 else
2037 {
2038 int token;
2039 struct _md *pMd = _md_open (def_fname);
2040 if (!pMd)
2041 {
2042 fprintf (stderr, "emxomfld: cannot open `%s'\n", def_fname);
2043 exit (2);
2044 }
2045 token = _md_next_token (pMd);
2046 if (token == _MD_LIBRARY || token == _MD_PHYSICAL || token == _MD_VIRTUAL)
2047 dll_flag = TRUE;
2048 if (dll_flag)
2049 {
2050 int fInitInstance = 1;
2051 int fTermInstance = 1;
2052 for (;;)
2053 {
2054 switch (_md_next_token (pMd))
2055 {
2056 case _MD_INITINSTANCE: fInitInstance = 1; continue;
2057 case _MD_INITGLOBAL: fInitInstance = 0; continue;
2058 case _MD_TERMINSTANCE: fTermInstance = 1; continue;
2059 case _MD_TERMGLOBAL: fTermInstance = 0; continue;
2060 default: break;
2061 }
2062 break;
2063 }
2064 fprintf (response_file, "FORMAT OS2 LX DLL %s %s\n",
2065 fInitInstance ? "INITINSTANCE" : "INITGLOBAL",
2066 fTermInstance ? "TERMINSTANCE" : "TERMGLOBAL");
2067 }
2068 else
2069 switch (_md_next_token (pMd))
2070 {
2071 case _MD_WINDOWAPI:
2072 fprintf (response_file, "FORMAT OS2 LX PM\n");
2073 break;
2074 default:
2075 case _MD_WINDOWCOMPAT:
2076 fprintf (response_file, "FORMAT OS2 LX PMCompatible\n");
2077 break;
2078 case _MD_NOTWINDOWCOMPAT:
2079 fprintf (response_file, "FORMAT OS2 LX FullScreen\n");
2080 break;
2081 }
2082 _md_close (pMd);
2083 }
2084
2085 /* output files */
2086
2087 fprintf (response_file, "NAME '%s'\n", output_fname);
2088
2089 if (map_flag && map_fname)
2090 fprintf (response_file, "OPTION MAP='%s'\n", map_fname);
2091 else if (map_flag)
2092 fprintf (response_file, "OPTION MAP\n", map_fname);
2093
2094 /* standard stuff */
2095
2096 if (!strip_symbols)
2097 fprintf (response_file, "DEBUG HLL\n");
2098 fprintf (response_file, "OPTION QUIET\n");
2099 fprintf (response_file, "OPTION OSNAME='OS/2 EMX'\n");
2100 fprintf (response_file, "OPTION CASEEXACT\n");
2101 if (!dll_flag)
2102 fprintf (response_file, "OPTION STACK=%#lx\n", stack_size * 1024);
2103 if (!dll_flag && !base)
2104 base = "0x10000";
2105 if (base)
2106 fprintf (response_file, "OPTION OFFSET=%s\n", base);
2107
2108 /* the stub */
2109
2110 _execname(&execname[0], sizeof(execname));
2111 strcpy (_getname (&execname[0]), "os2stub.bin");
2112 if (!stat (execname, &s))
2113 fprintf (response_file, "OPTION STUB='%s'\n", execname);
2114
2115 /* Add the /INFORMATION option if the -i or -t option was given. This is
2116 for debugging. */
2117
2118// if (opt_t)
2119// put_arg ("/i", FALSE, FALSE);
2120
2121 /* Add the linker options specified with -O. */
2122
2123 for (pcur = options; pcur; pcur = pcur->next)
2124 fprintf (response_file, "%s\n", pcur->name);
2125
2126 /* Put the object file names onto the command line. */
2127
2128 for (pcur = obj_fnames; pcur; pcur = pcur->next)
2129 fprintf (response_file, "FILE '%s'\n", pcur->name);
2130
2131 /* Put the library file names onto the command line. */
2132
2133 for (pcur = lib_fnames; pcur; pcur = pcur->next)
2134 fprintf (response_file, "LIBRARY '%s'\n", pcur->name);
2135
2136 /* Translate the essentials of the module definition file into wlink lingo. */
2137 if (def_fname)
2138 {
2139 _md_token token;
2140 struct _md *pMd = _md_open (def_fname);
2141 if (!pMd)
2142 {
2143 fprintf (stderr, "emxomfld: cannot open `%s'\n", def_fname);
2144 exit (2);
2145 }
2146 _md_next_token (pMd);
2147 _md_parse (pMd, def_2_watcom, NULL);
2148 _md_close (pMd);
2149 }
2150 }
2151
2152 /* End the arguments and run the linker. */
2153
2154 arg_end ();
2155
2156 rc = emxomfld_spawn (command_line, "Linker");
2157 if (rc == 4 && !strnicmp(linker_type, "VAC3", 4)) /* Ignore iLink warnings. */
2158 rc = 0;
2159 if (rc < 0)
2160 {
2161 perror (linker_name);
2162 exit (2);
2163 }
2164
2165 /* Run RC if Linker completed successfully and a binary resource
2166 file was given on the command line. */
2167
2168 if (rc == 0 && res_fname != NULL)
2169 {
2170 arg_init (TRUE);
2171 put_arg ("rc.exe", TRUE, FALSE);
2172 put_arg ("-n", FALSE, FALSE);
2173 put_arg (res_fname, TRUE, FALSE);
2174 put_arg (output_fname, TRUE, FALSE);
2175 arg_end ();
2176 rc = emxomfld_spawn (command_line, "Resource Linker");
2177 if (rc < 0)
2178 {
2179 perror ("emxomfld: rc");
2180 exit (2);
2181 }
2182 }
2183
2184 /* If both Linker and RC completed successfully and the -Zexe option
2185 was given, touch the output file (without .exe) to keep `make'
2186 happy. */
2187
2188 if (rc == 0 && exe_flag)
2189 {
2190 /* find target and source filenames. */
2191 t = xstrdup (output_fname);
2192 _remext (t);
2193 _execname(&execname[0], sizeof(execname));
2194 strcpy(_getname(&execname[0]), "ldstub.bin");
2195
2196 /* Copy stub into file */
2197 if (opt_t)
2198 fprintf(stderr, "*** copy %s to %s (-Zexe)", execname, t);
2199 DosCopy(&execname[0], t, 4);
2200
2201 /* Now touch it */
2202 if (utime(t, NULL))
2203 {
2204 perror ("emxomfld");
2205 exit (2);
2206 }
2207 free (t);
2208 }
2209
2210 /* Return the return code of Linker or RC. */
2211
2212 return rc;
2213}
Note: See TracBrowser for help on using the repository browser.