1/* Call Windows NT 3.x linker.
2   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
3   Contributed by Douglas B. Rupp (drupp@cs.washington.edu).
4
5This file is part of GNU CC.
6
7GNU CC is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU CC is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU CC; see the file COPYING.  If not, write to
19the Free Software Foundation, 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, USA.  */
21
22#include "config.h"
23#include <stdio.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <stdlib.h>
27#include <string.h>
28#include <process.h>
29
30static char *concat ();
31static char *concat3 ();
32
33/* These can be set by command line arguments */
34char *linker_path = 0;
35int verbose = 0;
36int subsystem = 0;
37int entry = 0;
38
39int link_arg_max = -1;
40char **link_args = (char **) 0;
41int link_arg_index = -1;
42
43char *search_dirs = ".";
44
45static int is_regular_file (char *name);
46
47/* Add the argument contained in STR to the list of arguments to pass to the
48   linker */
49
50static void
51addarg (str)
52     char *str;
53{
54  int i;
55
56  if (++link_arg_index >= link_arg_max)
57    {
58      char **new_link_args
59	= (char **) calloc (link_arg_max + 1000, sizeof (char *));
60
61      for (i = 0; i <= link_arg_max; i++)
62	new_link_args [i] = link_args [i];
63
64      if (link_args)
65	free (link_args);
66
67      link_arg_max += 1000;
68      link_args = new_link_args;
69    }
70
71  link_args [link_arg_index] = str;
72}
73
74/* Locate the file named in FILE_NAME in the set of paths contained in
75   PATH_VAL */
76
77static char *
78locate_file (file_name, path_val)
79     char *file_name;
80     char *path_val;
81{
82  char buf [1000];
83  int file_len = strlen (file_name);
84  char *end_path = path_val + strlen (path_val);
85  char *ptr;
86
87  /* Handle absolute pathnames */
88  if (file_name [0] == '/' || file_name [0] == DIR_SEPARATOR
89      || isalpha (file_name [0]) && file_name [1] == ':')
90    {
91      strncpy (buf, file_name, sizeof buf);
92      buf[sizeof buf - 1] = '\0';
93      if (is_regular_file (buf))
94	return strdup (buf);
95      else
96	return 0;
97  }
98
99  if (! path_val)
100    return 0;
101
102  for (;;)
103    {
104      for (; *path_val == PATH_SEPARATOR ; path_val++)
105	;
106      if (! *path_val)
107	return 0;
108
109      for (ptr = buf; *path_val && *path_val != PATH_SEPARATOR; )
110	*ptr++ = *path_val++;
111
112      ptr--;
113      if (*ptr != '/' && *ptr != DIR_SEPARATOR)
114	*++ptr = DIR_SEPARATOR;
115
116      strcpy (++ptr, file_name);
117
118      if (is_regular_file (buf))
119	return strdup (buf);
120    }
121
122  return 0;
123}
124
125/* Given a library name in NAME, i.e. foo.  Look first for libfoo.lib and then
126   libfoo.a in the set of directories we are allowed to search in */
127
128static char *
129expand_lib (name)
130     char *name;
131{
132  char *lib, *lib_path;
133
134  lib = malloc (strlen (name) + 8);
135  strcpy (lib, "lib");
136  strcat (lib, name);
137  strcat (lib, ".lib");
138  lib_path = locate_file (lib, search_dirs);
139  if (!lib_path)
140    {
141      strcpy (lib, "lib");
142      strcat (lib, name);
143      strcat (lib, ".a");
144      lib_path = locate_file (lib, search_dirs);
145      if (!lib_path)
146        {
147          fprintf
148            (stderr,
149             "Couldn't locate library: lib%s.a or lib%s.lib\n", name, name);
150          exit (1);
151        }
152    }
153
154  return lib_path;
155}
156
157/* Check to see if the file named in NAME is a regular file, i.e. not a
158   directory */
159
160static int
161is_regular_file (name)
162     char *name;
163{
164  int ret;
165  struct stat statbuf;
166
167  ret = stat(name, &statbuf);
168  return !ret && S_ISREG (statbuf.st_mode);
169}
170
171/* Process the number of args in P_ARGC and contained in ARGV.  Look for
172   special flags, etc. that must be handled for the Microsoft linker */
173
174static void
175process_args (p_argc, argv)
176     int *p_argc;
177     char *argv[];
178{
179  int i, j;
180
181  for (i = 1; i < *p_argc; i++)
182    {
183      /* -v turns on verbose option here and is passed on to gcc */
184      if (! strcmp (argv [i], "-v"))
185	verbose = 1;
186      else if (! strncmp (argv [i], "-g", 2))
187      {
188        addarg ("-debugtype:coff -debug:full");
189      }
190      else if (! strncmp (argv [i], "-stack", 6))
191      {
192        i++;
193        addarg (concat ("-stack:",argv[i]));
194      }
195      else if (! strncmp (argv [i], "-subsystem", 10))
196      {
197	subsystem = 1;
198        i++;
199        addarg (concat ("-subsystem:",argv[i]));
200      }
201      else if (! strncmp (argv [i], "-e", 2))
202      {
203	entry = 1;
204        i++;
205        addarg (concat ("-entry:",&argv[i][1]));
206      }
207    }
208}
209
210/* The main program.  Spawn the Microsoft linker after fixing up the
211   Unix-like flags and args to be what the Microsoft linker wants */
212
213main (argc, argv)
214     int argc;
215     char *argv[];
216{
217  int i;
218  int done_an_ali = 0;
219  int file_name_index;
220  char *pathval = getenv ("PATH");
221  char *spawn_args [5];
222  char *tmppathval = malloc (strlen (pathval) + 3);
223
224  strcpy (tmppathval, ".;");
225  pathval = strcat (tmppathval, pathval);
226
227  linker_path = locate_file ("link32.exe", pathval);
228  if (!linker_path)
229    {
230      linker_path = locate_file ("link.exe", pathval);
231      if (!linker_path)
232	{
233	  fprintf (stderr, "Couldn't locate link32 or link\n");
234	  exit (1);
235	}
236    }
237
238  addarg (linker_path);
239
240  process_args (&argc , argv);
241  if (! subsystem) addarg ("-subsystem:console");
242  if (! entry) addarg ("-entry:mainCRTStartup");
243
244  for (i = 1; i < argc; i++)
245    {
246      int arg_len = strlen (argv [i]);
247
248      if (!strcmp (argv [i], "-o"))
249	{
250	  char *buff, *ptr;
251	  int out_len;
252
253	  i++;
254	  out_len = strlen (argv[i]) + 10;
255	  buff = malloc (out_len);
256	  strcpy (buff, "-out:");
257	  strcat (buff, argv[i]);
258	  ptr = strstr (buff, ".exe");
259	  if (ptr == NULL || strlen (ptr) != 4)
260	    strcat (buff, ".exe");
261	  addarg (buff);
262	}
263      else if (arg_len > 2 && !strncmp (argv [i], "-L", 2))
264	{
265	  char *nbuff, *sdbuff;
266	  int j, new_len, search_dirs_len;
267
268	  new_len = strlen (&argv[i][2]);
269	  search_dirs_len = strlen (search_dirs);
270
271	  nbuff = malloc (new_len + 1);
272	  strcpy (nbuff, &argv[i][2]);
273
274	  for (j = 0; j < new_len; j++)
275	    if (nbuff[j] == '/') nbuff[j] = DIR_SEPARATOR;
276
277	  sdbuff = malloc (search_dirs_len + new_len + 2);
278	  strcpy (sdbuff, search_dirs);
279	  sdbuff[search_dirs_len] = PATH_SEPARATOR;
280	  sdbuff[search_dirs_len+1] = 0;
281	  strcat (sdbuff, nbuff);
282
283	  search_dirs = sdbuff;
284	}
285
286      else if (arg_len > 2 && !strncmp (argv [i], "-l", 2))
287        {
288	  addarg (expand_lib (&argv[i][2]));
289        }
290      else if (!strcmp (argv [i], "-v")
291            || !strcmp (argv [i], "-g")
292            || !strcmp (argv [i], "-noinhibit-exec"))
293        {
294          ;
295        }
296      else if (!strcmp (argv [i], "-stack")
297            || !strcmp (argv [i], "-subsystem")
298            || !strcmp (argv [i], "-e"))
299        {
300          i++;
301        }
302      else
303        {
304          addarg (argv [i]);
305        }
306    }
307
308  addarg (NULL);
309
310  if (verbose)
311    {
312      int i;
313
314      for (i = 0; i < link_arg_index; i++)
315	printf ("%s ", link_args [i]);
316      putchar ('\n');
317    }
318
319  if (spawnvp (P_WAIT, linker_path, (const char * const *)link_args) != 0)
320    {
321      fprintf (stderr, "Error executing %s\n", link_args[0]);
322      exit (1);
323    }
324
325  exit (0);
326}
327
328static char *
329concat (s1, s2)
330     char *s1, *s2;
331{
332  int len1 = strlen (s1);
333  int len2 = strlen (s2);
334  char *result = malloc (len1 + len2 + 1);
335
336  strcpy (result, s1);
337  strcpy (result + len1, s2);
338  *(result + len1 + len2) = 0;
339
340  return result;
341}
342
343static char *
344concat3 (s1, s2, s3)
345     char *s1, *s2, *s3;
346{
347  return concat (concat (s1, s2), s3);
348}
349