/* * link.c -- linker main program that controls the linking process. */ #include "..\h\config.h" #include "general.h" #include "tproto.h" #include "globals.h" #include "link.h" #include "..\h\paths.h" #include "..\h\header.h" #ifdef Header #include "hdr.h" #ifndef MaxHeader #define MaxHeader MaxHdr #endif /* MaxHeader */ #endif /* Header */ /* * Prototype. */ hidden novalue setexe Params((char *fname)); /* * The following code is operating-system dependent [@link.01]. Include * system-dependent files and declarations. */ #if PORT /* nothing to do */ Deliberate Syntax Error #endif /* PORT */ #if AMIGA || ATARI_ST || HIGHC_386 || MACINTOSH || VM || VMS /* nothing to do */ #endif /* AMIGA || ATARI_ST || HIGHC_386 ... */ #if MSDOS #if MICROSOFT || TURBO #include #endif /* MICROSOFT || TURBO */ #endif /* MSDOS */ #if MVS char *routname; /* real output file name */ #endif /* MVS */ #if OS2 #if MICROSOFT #include #endif /* MICROSOFT */ #endif /* OS2 */ #if UNIX #ifdef CRAY #define word word_fubar #include #include #undef word #else /* CRAY */ #include #include #endif /* CRAY */ #endif /* UNIX */ /* * End of operating-system specific code. */ FILE *infile; /* input file (.u1 or .u2) */ FILE *outfile; /* interpreter code output file */ #ifdef DeBugLinker FILE *dbgfile; /* debug file */ static char dbgname[MaxFileName]; /* debug file name */ #endif /* DeBugLinker */ char inname[MaxFileName]; /* input file name */ static char icnname[MaxFileName]; /* icon source file name */ struct lfile *llfiles = NULL; /* List of files to link */ #ifdef EvalTrace int colmno = 0; /* current source program colm number */ #endif /* EvalTrace */ int lineno = 0; /* current source program 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); #if MVS || VM /* * Even though the ucode data is all reasonable text characters, use * of text I/O may cause problems if a line is larger than LRECL. * This is likely to be true with any compiler, though the precise * disaster which results may vary. */ infile = fopen(inname, ReadBinary); #else infile = fopen(inname, ReadText); #endif /* MVS || VM */ if (infile == NULL) quitf("cannot open %s",inname); readglob(); fclose(infile); } /* Phase II: resolve undeclared variables and generate code. */ /* * Open the output file. */ #ifdef WATERLOO_C_V3_0 strcat(outname," (BIN"); #endif /* WATERLOO_C_V3_0 */ #if MVS routname = outname; outfile = tmpfile(); /* write icode to temporary file to avoid fseek-PDS limitations */ #else /* MVS */ outfile = fopen(outname, WriteBinary); #endif /* MVS */ /* * The following code is operating-system dependent [@link.02]. Set * untranslated mode if necessary. */ #if PORT /* probably nothing */ Deliberate Syntax Error #endif /* PORT */ #if AMIGA || ATARI_ST || HIGHC_386 || MACINTOSH || MVS || UNIX || VM || VMS /* nothing to do */ #endif /* AMIGA || ATARI_ST || ... */ #if MSDOS #if LATTICE fmode(outfile,1); /* set for untranslated mode */ #endif /* LATTICE */ #if MICROSOFT || TURBO setmode(fileno(outfile),O_BINARY); /* set for untranslated mode */ #endif /* MICROSOFT || TURBO */ #endif /* MSDOS */ #if OS2 #if MICROSOFT setmode(fileno(outfile),O_BINARY); #endif /* MICROSOFT */ #endif /* OS2 */ /* * End of operating-system specific code. */ if (outfile == NULL) quitf("cannot create %s",outname); #ifdef Header /* * Open Header, which contains the start-up program and copy it to the * output file. Then, write out null bytes to past the end of the * start-up program. */ { int hsize; char hname[MaxFileName]; char hdrdat[MaxHeader+1]; #if MACINTOSH #if MPW { /* Look for header in same directory as the linker */ char *p, *rindex(); p = (char *)getenv("Command"); if (p) { strcpy(hname,p); p = rindex(hname,':') + 1; } else p = hname; strcpy(p,HeaderPath); fprintf(stderr,HeaderPath); fflush(stderr); } #endif /* MPW */ #endif /* MACINTOSH */ fwrite(iconxhdr, sizeof(char), MaxHeader, outfile); } #endif /* Header */ for (i = sizeof(struct header); i--;) putc(0, outfile); fflush(outfile); if (ferror(outfile) != 0) quit("unable to write to icode file"); #ifdef DeBugLinker /* * Open the .ux file if debugging is on. */ if (Dflag) { makename(dbgname, TargetDir, llfiles->lf_name, ".ux"); dbgfile = fopen(dbgname, WriteText); if (dbgfile == NULL) quitf("cannot create %s", dbgname); } #endif /* DeBugLinker */ /* * Loop through input files and generate code for each. */ lfls = llfiles; while (lf = getlfile(&lfls)) { filename = lf->lf_name; makename(inname, SourceDir, filename, U1Suffix); makename(icnname, TargetDir, filename, SourceSuffix); #if MVS || VM infile = fopen(inname, ReadBinary); #else /* MVS || VM */ infile = fopen(inname, ReadText); #endif /* MVS || VM */ 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. */ novalue 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. */ novalue lfatal(s1, s2) char *s1, *s2; { fprintf(stderr, "%s: ", icnname); if (lineno) fprintf(stderr, "Line %d # : ", lineno); fprintf(stderr, "\"%s\": %s\n", s1, s2); fatals++; } /* * setexe - mark the output file as executable */ static novalue setexe(fname) char *fname; { /* * The following code is operating-system dependent [@link.03]. It changes the * mode of executable file so that it can be executed directly. */ #if PORT /* something is needed */ Deliberate Syntax Error #endif /* PORT */ #if AMIGA /* not necessary */ #endif /* AMIGA */ #if ATARI_ST || MSDOS || MVS || OS2 || VM || VMS /* can't be made executable */ /* note: VMS files can't be made executable, but see "iexe.com" under VMS. */ #endif /* ATARI_ST || MSDOS || VMS */ #if HIGHC_386 /* not implemented yet. */ #endif /* HIGHC_386 */ #if MACINTOSH #if MPW /* Nothing to do here -- file is set to type TEXT (so it can be executed as a script) in tmain.c. */ /* #pragma unused(fname) */ #endif /* MPW */ #endif /* MACINTOSH */ #if MSDOS #if LATTICE || MICROSOFT || TURBO chmod(fname,0755); /* probably could be smarter... */ #endif /* LATTICE || MICROSOFT || TURBO */ #if MWC /* can't handle */ #endif /* MWC */ #endif /* MSDOS */ #if UNIX { 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 */ } } #endif /* UNIX */ /* * End of operating-system specific code. */ }