+\f
+/* Name of linker. */
+#ifndef LD_NAME
+#define LD_NAME "ld"
+#endif
+
+/* Temporary file name base. */
+static char *temp_filename;
+
+/* The user has specified several input files. Invoke the linker to
+ link them all together, and convert and delete the resulting output
+ file. */
+
+static char *
+link_inputs (inputs, ld)
+ struct string_list *inputs;
+ char *ld;
+{
+ size_t c;
+ struct string_list *q;
+ char **argv;
+ size_t i;
+ int pid;
+ int status;
+
+ c = 0;
+ for (q = inputs; q != NULL; q = q->next)
+ ++c;
+
+ argv = (char **) alloca (c + 5);
+
+#ifndef __MSDOS__
+ if (ld == NULL)
+ {
+ char *p;
+
+ /* Find the linker to invoke based on how nlmconv was run. */
+ p = program_name + strlen (program_name);
+ while (p != program_name)
+ {
+ if (p[-1] == '/')
+ {
+ ld = (char *) xmalloc (p - program_name + strlen (LD_NAME) + 1);
+ memcpy (ld, program_name, p - program_name);
+ strcpy (ld + (p - program_name), LD_NAME);
+ break;
+ }
+ --p;
+ }
+ }
+#endif
+
+ if (ld == NULL)
+ ld = (char *) LD_NAME;
+
+ choose_temp_base ();
+
+ unlink_on_exit = xmalloc (strlen (temp_filename) + 3);
+ sprintf (unlink_on_exit, "%s.O", temp_filename);
+
+ argv[0] = ld;
+ argv[1] = (char *) "-r";
+ argv[2] = (char *) "-o";
+ argv[3] = unlink_on_exit;
+ i = 4;
+ for (q = inputs; q != NULL; q = q->next, i++)
+ argv[i] = q->string;
+ argv[i] = NULL;
+
+ if (debug)
+ {
+ for (i = 0; argv[i] != NULL; i++)
+ fprintf (stderr, " %s", argv[i]);
+ fprintf (stderr, "\n");
+ }
+
+ pid = pexecute (ld, argv);
+
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ perror ("waitpid");
+ unlink (unlink_on_exit);
+ exit (1);
+ }
+
+ if (status != 0)
+ {
+ fprintf (stderr, "%s: Execution of %s failed\n", program_name, ld);
+ unlink (unlink_on_exit);
+ exit (1);
+ }
+
+ return unlink_on_exit;
+}
+
+/* Choose a temporary file name. Stolen from gcc.c. */
+
+static const char *
+choose_temp_base_try (try, base)
+ const char *try;
+ const char *base;
+{
+ const char *rv;
+
+ if (base)
+ rv = base;
+ else if (try == NULL)
+ rv = NULL;
+ else if (access (try, R_OK | W_OK) != 0)
+ rv = NULL;
+ else
+ rv = try;
+ return rv;
+}
+
+static void
+choose_temp_base ()
+{
+ const char *base = NULL;
+ int len;
+
+ base = choose_temp_base_try (getenv ("TMPDIR"), base);
+ base = choose_temp_base_try (getenv ("TMP"), base);
+ base = choose_temp_base_try (getenv ("TEMP"), base);
+
+#ifdef P_tmpdir
+ base = choose_temp_base_try (P_tmpdir, base);
+#endif
+
+ base = choose_temp_base_try ("/usr/tmp", base);
+ base = choose_temp_base_try ("/tmp", base);
+
+ /* If all else fails, use the current directory! */
+ if (base == NULL)
+ base = "./";
+
+ len = strlen (base);
+ temp_filename = xmalloc (len + sizeof("/ccXXXXXX") + 1);
+ strcpy (temp_filename, base);
+ if (len > 0 && temp_filename[len-1] != '/')
+ temp_filename[len++] = '/';
+ strcpy (temp_filename + len, "ccXXXXXX");
+
+ mktemp (temp_filename);
+ if (*temp_filename == '\0')
+ abort ();
+}
+
+/* Execute a job. Stolen from gcc.c. */
+
+#ifndef OS2
+#ifdef __MSDOS__
+
+static int
+pexecute (program, argv)
+ char *program;
+ char *argv[];
+{
+ char *scmd, *rf;
+ FILE *argfile;
+ int i;
+
+ scmd = (char *)malloc (strlen (program) + strlen (temp_filename) + 10);
+ rf = scmd + strlen(program) + 2 + el;
+ sprintf (scmd, "%s.exe @%s.gp", program, temp_filename);
+ argfile = fopen (rf, "w");
+ if (argfile == 0)
+ pfatal_with_name (rf);
+
+ for (i=1; argv[i]; i++)
+ {
+ char *cp;
+ for (cp = argv[i]; *cp; cp++)
+ {
+ if (*cp == '"' || *cp == '\'' || *cp == '\\' || isspace (*cp))
+ fputc ('\\', argfile);
+ fputc (*cp, argfile);
+ }
+ fputc ('\n', argfile);
+ }
+ fclose (argfile);
+
+ i = system (scmd);
+
+ remove (rf);
+
+ if (i == -1)
+ {
+ perror (program);
+ return MIN_FATAL_STATUS << 8;
+ }
+
+ return i << 8;
+}
+
+#else /* not __MSDOS__ */
+
+static int
+pexecute (program, argv)
+ char *program;
+ char *argv[];
+{
+ int pid;
+ int retries, sleep_interval;
+
+ /* Fork a subprocess; wait and retry if it fails. */
+ sleep_interval = 1;
+ for (retries = 0; retries < 4; retries++)
+ {
+ pid = vfork ();
+ if (pid >= 0)
+ break;
+ sleep (sleep_interval);
+ sleep_interval *= 2;
+ }
+
+ switch (pid)
+ {
+ case -1:
+#ifdef vfork
+ perror ("fork");
+#else
+ perror ("vfork");
+#endif
+ exit (1);
+ /* NOTREACHED */
+ return 0;
+
+ case 0: /* child */
+ /* Exec the program. */
+ execvp (program, argv);
+ perror (program);
+ exit (1);
+ /* NOTREACHED */
+ return 0;
+
+ default:
+ /* Return child's process number. */
+ return pid;
+ }
+}
+
+#endif /* not __MSDOS__ */
+#else /* not OS2 */
+
+static int
+pexecute (program, argv)
+ char *program;
+ char *argv[];
+{
+ return spawnvp (1, program, argv);
+}
+#endif /* not OS2 */