/* * link.c -- linker main program that controls the linking process. */ #include "link.h" #include "tproto.h" #include "tglobals.h" #include "../h/header.h" #include #include #ifdef BinaryHeader #include "hdr.h" #endif /* BinaryHeader */ static void setexe (char *fname); FILE *infile; /* input file (.u1 or .u2) */ FILE *outfile; /* interpreter code output file */ struct lfile *llfiles = NULL; /* List of files to link */ char inname[MaxPath]; /* input file name */ char icnname[MaxPath]; /* current icon source file name */ int colmno = 0; /* current source column number */ int lineno = 0; /* current source line number */ int fatals = 0; /* number of errors encountered */ /* * ilink - link a number of files, returning error count */ int ilink(ifiles,outname) char **ifiles; char *outname; { int i; struct lfile *lf,*lfls; char *filename; /* name of current input file */ linit(); /* initialize memory structures */ while (*ifiles) alsolink(*ifiles++); /* make initial list of files */ /* * Phase I: load global information contained in .u2 files into * data structures. * * The list of files to link is maintained as a queue with llfiles * as the base. lf moves along the list. Each file is processed * in turn by forming .u2 and .icn names from each file name, each * of which ends in .u1. The .u2 file is opened and globals is called * to process it. When the end of the list is reached, lf becomes * NULL and the loop is terminated, completing phase I. Note that * link instructions in the .u2 file cause files to be added to list * of files to link. */ for (lf = llfiles; lf != NULL; lf = lf->lf_link) { filename = lf->lf_name; makename(inname, SourceDir, filename, U2Suffix); makename(icnname, TargetDir, filename, SourceSuffix); infile = fopen(inname, "r"); if (infile == NULL) quitf("cannot open %s",inname); readglob(); fclose(infile); } /* Phase II (optional): scan code and suppress unreferenced procs. */ if (!strinv) scanrefs(); /* Phase III: resolve undeclared variables and generate code. */ /* * Open the output file. */ outfile = fopen(outname, "wb"); if (outfile == NULL) { /* may exist, but can't open for "w" */ ofile = NULL; /* so don't delete if it's there */ quitf("cannot create %s",outname); } /* * Write the bootstrap header to the output file. */ #ifdef BinaryHeader /* * With BinaryHeader defined, always write MaxHdr bytes. */ fwrite(iconxhdr, sizeof(char), MaxHdr, outfile); hdrsize = MaxHdr; #else /* BinaryHeader */ /* * Write a short shell header terminated by \n\f\n\0. * Use magic "#!/bin/sh" to ensure that $0 is set when run via $PATH. * Pad header to a multiple of 8 characters. * * The shell header searches for iconx in this order: * a. location specified by ICONX environment variable * (if specified, this MUST work, else the script exits) * b. iconx in same directory as executing binary * c. location specified in script * (as generated by icont or as patched later) * d. iconx in $PATH * * The ugly ${1+"$@"} is a workaround for non-POSIX handling * of "$@" by some shells in the absence of any arguments. * Thanks to the Unix-haters handbook for this trick. */ { char script[2 * MaxPath + 300]; sprintf(script, "%s\n%s%-72s\n%s\n\n%s\n%s\n%s\n%s\n\n%s\n", "#!/bin/sh", "IXBIN=", iconxloc, "IXLCL=`echo $0 | sed 's=[^/]*$=iconx='`", "[ -n \"$ICONX\" ] && exec \"$ICONX\" $0 ${1+\"$@\"}", "[ -x \"$IXLCL\" ] && exec \"$IXLCL\" $0 ${1+\"$@\"}", "[ -x \"$IXBIN\" ] && exec \"$IXBIN\" $0 ${1+\"$@\"}", "exec iconx $0 ${1+\"$@\"}", "[executable Icon binary follows]"); strcat(script, " \n\f\n" + ((int)(strlen(script) + 4) % 8)); hdrsize = strlen(script) + 1; /* length includes \0 at end */ fwrite(script, hdrsize, 1, outfile); /* write header */ } #endif /* BinaryHeader */ for (i = sizeof(struct header); i--;) putc(0, outfile); fflush(outfile); if (ferror(outfile) != 0) quit("unable to write to icode file"); /* * Loop through input files and generate code for each. */ lfls = llfiles; while ((lf = getlfile(&lfls)) != 0) { filename = lf->lf_name; makename(inname, SourceDir, filename, U1Suffix); makename(icnname, TargetDir, filename, SourceSuffix); infile = fopen(inname, "r"); if (infile == NULL) quitf("cannot open %s", inname); gencode(); fclose(infile); } gentables(); /* Generate record, field, global, global names, static, and identifier tables. */ fclose(outfile); lmfree(); if (fatals > 0) return fatals; setexe(outname); return 0; } /* * lwarn - issue a linker warning message. */ void lwarn(s1, s2, s3) char *s1, *s2, *s3; { fprintf(stderr, "%s: ", icnname); if (lineno) fprintf(stderr, "Line %d # :", lineno); fprintf(stderr, "\"%s\": %s%s\n", s1, s2, s3); fflush(stderr); } /* * lfatal - issue a fatal linker error message. */ void lfatal(s1, s2) char *s1, *s2; { fatals++; fprintf(stderr, "%s: ", icnname); if (lineno) fprintf(stderr, "Line %d # : ", lineno); fprintf(stderr, "\"%s\": %s\n", s1, s2); } /* * setexe - mark the output file as executable */ static void setexe(fname) char *fname; { struct stat stbuf; int u, r, m; /* * Set each of the three execute bits (owner,group,other) if allowed by * the current umask and if the corresponding read bit is set; do not * clear any bits already set. */ umask(u = umask(0)); /* get and restore umask */ if (stat(fname,&stbuf) == 0) { /* must first read existing mode */ r = (stbuf.st_mode & 0444) >> 2; /* get & position read bits */ m = stbuf.st_mode | (r & ~u); /* set execute bits */ chmod(fname,m); /* change file mode */ } }