| /* mklink.c creates startup code and the link command line. |
| |
| Copyright (C) 2000-2025 Free Software Foundation, Inc. |
| Contributed by Gaius Mulley <gaius@glam.ac.uk>. |
| |
| This file is part of GNU Modula-2. |
| |
| GNU Modula-2 is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GNU Modula-2 is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Modula-2; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| |
| #include "config.h" |
| #include "system.h" |
| |
| #define MAX_FILE_NAME 8192 |
| #define MAXSTACK 4096 |
| #define STDIN 0 |
| #define STDOUT 1 |
| #define ENDOFILE ((char)-1) |
| #define ERROR(X) \ |
| (fprintf (stderr, "%s:%d error %s\n", __FILE__, __LINE__, X) \ |
| && (fflush (stderr))) |
| #define DEBUG(X) \ |
| ((Debug) && (fprintf (stderr, "%s\n", X) && (fflush (stderr)))) |
| |
| #if !defined(TRUE) |
| #define TRUE (1 == 1) |
| #endif |
| |
| #if !defined(FALSE) |
| #define FALSE (1 == 0) |
| #endif |
| |
| typedef struct functlist |
| { |
| char *functname; |
| struct functlist *next; |
| } functList; |
| |
| /* Prototypes. */ |
| |
| static void ParseFileLinkCommand (void); |
| static void ParseFileStartup (void); |
| static void ParseFile (char *Name); |
| static void ParseComments (void); |
| static void CopyUntilEof (void); |
| static void CopyUntilEol (void); |
| static int IsSym (char *s); |
| static int SymIs (char *s); |
| static int FindString (char *String); |
| static void GetNL (void); |
| static char GetChar (void); |
| static void ResetBuffer (void); |
| static int GetSingleChar (char *ch); |
| static int InRange (int Element, unsigned int Min, unsigned int Max); |
| static char PutChar (char ch); |
| static int IsSpace (char ch); |
| static void SkipSpaces (void); |
| static void SkipText (void); |
| static void SilentSkipSpaces (void); |
| static void SilentSkipText (void); |
| static void PushBack (char *s); |
| static int IsDigit (char ch); |
| static void GetName (char *Name); |
| static void OpenOutputFile (void); |
| static void CloseFile (void); |
| static void FindSource (char *Name); |
| static void CopyUntilEolInto (char *Buffer); |
| static void FindObject (char *Name); |
| static int IsExists (char *Name); |
| |
| /* Global variables. */ |
| |
| static char *NameOfFile = NULL; |
| static const char *NameOfMain = "main"; |
| static int StackPtr = 0; |
| static char Stack[MAXSTACK]; |
| static int CurrentFile = STDIN; |
| static int OutputFile; |
| static int LinkCommandLine = FALSE; |
| static int ProfilePCommand = FALSE; |
| static int ProfilePGCommand = FALSE; |
| static int ExitNeeded = TRUE; |
| static char *libraries = NULL; |
| static char *args = NULL; |
| static functList *head = NULL; |
| static functList *tail = NULL; |
| static int langC = FALSE; /* FALSE = C++, TRUE = C. */ |
| |
| /* addLibrary - adds libname to the list of libraries to be linked. */ |
| |
| static void |
| addLibrary (char *libname) |
| { |
| if (libraries == NULL) |
| libraries = strdup (libname); |
| else |
| { |
| char *old = libraries; |
| char *newlib |
| = (char *)malloc (strlen (libname) + strlen (libraries) + 1 + 1); |
| strcpy (newlib, libraries); |
| strcat (newlib, " "); |
| strcat (newlib, libname); |
| libraries = newlib; |
| free (old); |
| } |
| } |
| |
| /* addGccArg - adds arg to the list of gcc arguments. */ |
| |
| static void |
| addGccArg (char *arg) |
| { |
| if (args == NULL) |
| args = strdup (arg); |
| else |
| { |
| char *old = args; |
| char *newarg = (char *)malloc (strlen (old) + strlen (arg) + 1 + 1); |
| strcpy (newarg, old); |
| strcat (newarg, " "); |
| strcat (newarg, arg); |
| args = newarg; |
| free (old); |
| } |
| } |
| |
| int |
| main (int argc, char *argv[]) |
| { |
| int i; |
| |
| if (argc >= 3) |
| { |
| if (strcmp (argv[1], "-l") == 0) |
| LinkCommandLine = TRUE; |
| else if (strcmp (argv[1], "-s") == 0) |
| LinkCommandLine = FALSE; |
| else |
| { |
| fprintf (stderr, "Usage: mklink (-l|-s) [--langc|--langc++] [--pg|-p] " |
| "[--lib library] [--main name] [--exit] --name " |
| "filename <modulelistfile>\n"); |
| fprintf (stderr, " must supply -l or -s option\n"); |
| exit (1); |
| } |
| ProfilePCommand = FALSE; |
| ProfilePGCommand = FALSE; |
| i = 2; |
| while (i < argc - 1) |
| { |
| if (strcmp (argv[i], "--langc++") == 0) |
| langC = FALSE; |
| else if (strcmp (argv[i], "--langc") == 0) |
| langC = TRUE; |
| else if (strncmp (argv[i], "-f", 2) == 0) |
| addGccArg (argv[i]); |
| else if (strcmp (argv[i], "--pg") == 0) |
| ProfilePGCommand = TRUE; |
| else if (strcmp (argv[i], "-p") == 0) |
| ProfilePCommand = TRUE; |
| else if (strcmp (argv[i], "--exit") == 0) |
| ExitNeeded = FALSE; |
| else if (strcmp (argv[i], "--lib") == 0) |
| { |
| i++; |
| addLibrary (argv[i]); |
| } |
| else if (strcmp (argv[i], "--main") == 0) |
| { |
| i++; |
| NameOfMain = argv[i]; |
| } |
| else if (strcmp (argv[i], "--name") == 0) |
| { |
| i++; |
| NameOfFile = argv[i]; |
| } |
| i++; |
| } |
| ParseFile (argv[i]); |
| } |
| else |
| { |
| fprintf (stderr, "Usage: mklink (-l|-s) [--gcc|--g++] [--pg|-p] [--lib " |
| "library] [--main name] [--exit] --name filename " |
| "<modulelistfile>\n"); |
| exit (1); |
| } |
| if (NameOfFile == NULL) |
| { |
| fprintf (stderr, "mklink must have a --name argument\n"); |
| fprintf (stderr, "Usage: mklink (-l|-s) [--gcc|--g++] [--pg|-p] [--lib " |
| "library] [--main name] [--exit] --name filename " |
| "<modulelistfile>\n"); |
| exit (1); |
| } |
| exit (0); |
| } |
| |
| /* ParseFile - parses the input file and generates the output file. */ |
| |
| static void |
| ParseFile (char *Name) |
| { |
| FindSource (Name); |
| OpenOutputFile (); |
| if (LinkCommandLine) |
| ParseFileLinkCommand (); |
| else |
| ParseFileStartup (); |
| CloseFile (); |
| } |
| |
| /* ParseFileLinkCommand - generates the link command. */ |
| |
| static void |
| ParseFileLinkCommand (void) |
| { |
| char name[MAX_FILE_NAME]; |
| char *s = NULL; |
| char *l = NULL; |
| char *c = NULL; |
| |
| s = getenv ("CC"); |
| if (s == NULL) |
| { |
| if (langC) |
| printf ("gcc -g "); |
| else |
| printf ("g++ -g "); |
| } |
| else |
| printf ("%s -g ", s); |
| |
| if (args != NULL) |
| printf ("%s ", args); |
| |
| l = getenv ("LDFLAGS"); |
| if (l != NULL) |
| printf ("%s ", l); |
| |
| c = getenv ("CFLAGS"); |
| if (c != NULL) |
| printf ("%s ", c); |
| |
| if (ProfilePGCommand) |
| printf (" -pg"); |
| else if (ProfilePCommand) |
| printf (" -p"); |
| |
| while (PutChar (GetChar ()) != (char)EOF) |
| { |
| CopyUntilEolInto (name); |
| if ((strlen (name) > 0) && (name[0] != '#')) |
| FindObject (name); |
| } |
| printf (" %s\n", libraries); |
| } |
| |
| /* FindObject - searches the M2PATH variable to find the object file. |
| If it finds the object file it prints it to stdout otherwise it |
| writes an error on stderr. */ |
| |
| static void |
| FindObject (char *Name) |
| { |
| char m2search[4096]; |
| char m2path[4096]; |
| char name[4096]; |
| char exist[4096]; |
| int s, p; |
| |
| if (getenv ("M2PATH") == NULL) |
| strcpy (m2path, "."); |
| else |
| strcpy (m2path, getenv ("M2PATH")); |
| |
| snprintf (name, sizeof (name), "%s.o", Name); |
| p = 0; |
| while (m2path[p] != (char)0) |
| { |
| s = 0; |
| while ((m2path[p] != (char)0) && (m2path[p] != ' ')) |
| { |
| m2search[s] = m2path[p]; |
| s++; |
| p++; |
| } |
| if (m2path[p] == ' ') |
| p++; |
| m2search[s] = (char)0; |
| snprintf (exist, sizeof (exist), "%s/%s", m2search, name); |
| if (IsExists (exist)) |
| { |
| printf (" %s", exist); |
| return; |
| } |
| } |
| fprintf (stderr, "cannot find %s\n", name); |
| } |
| |
| /* IsExists - returns true if a file, Name, exists. It returns false |
| otherwise. */ |
| |
| static int |
| IsExists (char *Name) |
| { |
| struct stat buf; |
| |
| return (stat (Name, &buf) == 0); |
| } |
| |
| /* add_function - adds a name to the list of functions, in order. */ |
| |
| void |
| add_function (char *name) |
| { |
| functList *p = (functList *)malloc (sizeof (functList)); |
| p->functname = (char *)malloc (strlen (name) + 1); |
| strcpy (p->functname, name); |
| |
| if (head == NULL) |
| { |
| head = p; |
| tail = p; |
| p->next = NULL; |
| } |
| else |
| { |
| tail->next = p; |
| tail = p; |
| tail->next = NULL; |
| } |
| } |
| |
| static void |
| GenerateInitCalls (functList *p) |
| { |
| while (p != NULL) |
| { |
| printf (" _M2_%s_init (argc, argv, envp);\n", p->functname); |
| p = p->next; |
| } |
| } |
| |
| static void |
| GenerateFinishCalls (functList *p) |
| { |
| if (p->next != NULL) |
| GenerateFinishCalls (p->next); |
| printf (" _M2_%s_fini (argc, argv, envp);\n", p->functname); |
| } |
| |
| static void |
| GeneratePrototypes (functList *p) |
| { |
| while (p != NULL) |
| { |
| if (langC) |
| { |
| printf ("extern void _M2_%s_init (int argc, char *argv[], char *envp[]);\n", |
| p->functname); |
| printf ("extern void _M2_%s_fini (int argc, char *argv[], char *envp[]);\n", |
| p->functname); |
| } |
| else |
| { |
| printf ("extern \"C\" void _M2_%s_init (int argc, char *argv[], char *envp[]);\n", |
| p->functname); |
| printf ("extern \"C\" void _M2_%s_fini (int argc, char *argv[], char *envp[]);\n", |
| p->functname); |
| } |
| p = p->next; |
| } |
| } |
| |
| /* ParseFileStartup - generates the startup code. */ |
| |
| static void |
| ParseFileStartup (void) |
| { |
| char name[MAX_FILE_NAME]; |
| functList *p; |
| |
| while (PutChar (GetChar ()) != (char)EOF) |
| { |
| CopyUntilEolInto (name); |
| if ((strlen (name) > 0) && (strcmp (name, "mod_init") != 0) |
| && (name[0] != '#')) |
| add_function (name); |
| } |
| GeneratePrototypes (head); |
| printf ("extern"); |
| if (!langC) |
| printf (" \"C\""); |
| printf (" void _exit(int);\n"); |
| |
| printf ("\n\nint %s(int argc, char *argv[], char *envp[])\n", NameOfMain); |
| printf ("{\n"); |
| GenerateInitCalls (head); |
| GenerateFinishCalls (head); |
| if (ExitNeeded) |
| printf (" _exit(0);\n"); |
| printf (" return(0);\n"); |
| printf ("}\n"); |
| } |
| |
| /* OpenOutputFile - shut down stdout and open the new mod_init.c */ |
| |
| static void |
| OpenOutputFile (void) |
| { |
| if (strcmp (NameOfFile, "-") != 0) |
| { |
| if (close (STDOUT) != 0) |
| { |
| ERROR ("Unable to close stdout"); |
| exit (1); |
| } |
| OutputFile = creat (NameOfFile, 0666); |
| if (OutputFile != STDOUT) |
| { |
| ERROR ("Expected that the file descriptor should be 1"); |
| } |
| } |
| } |
| |
| /* CloseFile - flush and close the file. */ |
| |
| static void |
| CloseFile (void) |
| { |
| #if 0 |
| fflush(stdout); |
| if (close(STDOUT) != 0) { |
| ERROR("Unable to close our output file"); exit(1); |
| } |
| #endif |
| } |
| |
| /* CopyUntilEof - copies from the current input marker until ENDOFILE |
| is reached. */ |
| |
| static void |
| CopyUntilEof (void) |
| { |
| char ch; |
| |
| while ((ch = GetChar ()) != ENDOFILE) |
| putchar (ch); |
| } |
| |
| /* CopyUntilEol - copies from the current input marker until '\n' is |
| reached. */ |
| |
| static void |
| CopyUntilEol (void) |
| { |
| char ch; |
| |
| while (((ch = GetChar ()) != '\n') && (ch != (char)EOF)) |
| putchar (ch); |
| if (ch == '\n') |
| putchar (ch); |
| } |
| |
| /* CopyUntilEolInto - copies from the current input marker until '\n' |
| is reached into a Buffer. */ |
| |
| static void |
| CopyUntilEolInto (char *Buffer) |
| { |
| char ch; |
| int i = 0; |
| |
| while (((ch = GetChar ()) != '\n') && (ch != (char)EOF)) |
| { |
| Buffer[i] = ch; |
| i++; |
| } |
| if ((ch == '\n') || (ch == (char)EOF)) |
| Buffer[i] = (char)0; |
| } |
| |
| /* IsSym - returns true if string, s, was found in the input stream. |
| The input stream is uneffected. */ |
| |
| static int |
| IsSym (char *s) |
| { |
| int i = 0; |
| |
| while ((s[i] != (char)0) && (s[i] == PutChar (GetChar ()))) |
| { |
| GetChar (); |
| i++; |
| } |
| if (s[i] == (char)0) |
| { |
| PushBack (s); |
| /* found s in input string. */ |
| return (TRUE); |
| } |
| else |
| { |
| /* push back the characters we have scanned. */ |
| if (i > 0) |
| { |
| do |
| { |
| i--; |
| PutChar (s[i]); |
| } |
| while (i > 0); |
| } |
| return (FALSE); |
| } |
| } |
| |
| /* SymIs - returns true if string, s, was found in the input stream. |
| The token s is consumed from the input stream. */ |
| |
| static int |
| SymIs (char *s) |
| { |
| int i = 0; |
| |
| while ((s[i] != (char)0) && (s[i] == PutChar (GetChar ()))) |
| { |
| GetChar (); |
| i++; |
| } |
| if (s[i] == (char)0) |
| { |
| /* found s in input string. */ |
| return (TRUE); |
| } |
| else |
| { |
| /* push back the characters we have scanned. */ |
| if (i > 0) |
| { |
| do |
| { |
| i--; |
| PutChar (s[i]); |
| } |
| while (i > 0); |
| } |
| return (FALSE); |
| } |
| } |
| |
| /* FindString - keeps on reading input until a string, String, is |
| matched. If end of file is reached then FALSE is returned, otherwise |
| TRUE is returned. */ |
| |
| static int |
| FindString (char *String) |
| { |
| int StringIndex = 0; |
| int Found = FALSE; |
| int eof = FALSE; |
| char ch; |
| |
| while ((!Found) && (!eof)) |
| { |
| if (String[StringIndex] == (char)0) |
| /* must have found string. */ |
| Found = TRUE; |
| else |
| { |
| ch = GetChar (); |
| eof = (ch == ENDOFILE); |
| if (ch == String[StringIndex]) |
| StringIndex++; |
| else |
| StringIndex = 0; |
| } |
| } |
| return (Found); |
| } |
| |
| /* GetNL - keeps on reading input from until a new line is found. */ |
| |
| static void |
| GetNL (void) |
| { |
| char ch; |
| |
| while ((ch = GetChar ()) != '\n') |
| putchar (ch); |
| putchar ('\n'); |
| } |
| |
| /* GetChar - returns the current character in input. */ |
| |
| static char |
| GetChar (void) |
| { |
| char ch; |
| |
| if (StackPtr > 0) |
| { |
| StackPtr--; |
| return (Stack[StackPtr]); |
| } |
| else |
| { |
| if (GetSingleChar (&ch)) |
| return (ch); |
| else |
| return (ENDOFILE); |
| } |
| } |
| |
| #define MAXBUF 0x1000 |
| static int Pointer = 0; |
| static int AmountRead = 0; |
| static char Buffer[MAXBUF]; |
| |
| /* ResetBuffer - resets the buffer information to an initial state. */ |
| |
| static void |
| ResetBuffer (void) |
| { |
| StackPtr = 0; |
| Pointer = 0; |
| AmountRead = 0; |
| } |
| |
| /* GetSingleChar - gets a single character from input. TRUE is |
| returned upon success. */ |
| |
| static int |
| GetSingleChar (char *ch) |
| { |
| if (Pointer == AmountRead) |
| { |
| AmountRead = read (CurrentFile, &Buffer, MAXBUF); |
| if (AmountRead < 0) |
| AmountRead = 0; |
| Pointer = 0; |
| } |
| if (Pointer == AmountRead) |
| { |
| *ch = ENDOFILE; |
| return (FALSE); |
| } |
| else |
| { |
| *ch = Buffer[Pointer]; |
| Pointer++; |
| return (TRUE); |
| } |
| } |
| |
| /* InRange - returns true if Element is within the range Min..Max. */ |
| |
| static int |
| InRange (int Element, unsigned int Min, unsigned int Max) |
| { |
| return ((Element >= Min) && (Element <= Max)); |
| } |
| |
| /* PutChar - pushes a character back onto input. This character is |
| also returned. */ |
| |
| static char |
| PutChar (char ch) |
| { |
| if (StackPtr < MAXSTACK) |
| { |
| Stack[StackPtr] = ch; |
| StackPtr++; |
| } |
| else |
| { |
| ERROR ("Stack overflow in PutChar"); |
| } |
| return (ch); |
| } |
| |
| /* IsSpace - returns true if character, ch, is a space. */ |
| |
| static int |
| IsSpace (char ch) |
| { |
| return ((ch == ' ') || (ch == '\t')); |
| } |
| |
| /* SkipSpaces - eats up spaces in input. */ |
| |
| static void |
| SkipSpaces (void) |
| { |
| while (IsSpace (PutChar (GetChar ()))) |
| putchar (GetChar ()); |
| } |
| |
| /* SilentSkipSpaces - eats up spaces in input. */ |
| |
| static void |
| SilentSkipSpaces (void) |
| { |
| char ch; |
| |
| while (IsSpace (PutChar (GetChar ()))) |
| ch = GetChar (); /* throw away character. */ |
| } |
| |
| /* SkipText - skips ascii text, it does not skip white spaces. */ |
| |
| static void |
| SkipText (void) |
| { |
| while (!IsSpace (PutChar (GetChar ()))) |
| putchar (GetChar ()); |
| } |
| |
| /* SilentSkipText - skips ascii text, it does not skip white spaces. */ |
| |
| static void |
| SilentSkipText (void) |
| { |
| char ch; |
| |
| while (!IsSpace (PutChar (GetChar ()))) |
| ch = GetChar (); /* throw away character. */ |
| } |
| |
| /* PushBack - pushes a string, backwards onto the input stack. */ |
| |
| static void |
| PushBack (char *s) |
| { |
| int i; |
| |
| i = strlen (s); |
| while (i > 0) |
| { |
| i--; |
| PutChar (s[i]); |
| } |
| } |
| |
| /* IsDigit - returns true if a character, ch, is a decimal digit. */ |
| |
| static int |
| IsDigit (char ch) |
| { |
| return (((ch >= '0') && (ch <= '9'))); |
| } |
| |
| /* GetName - returns the next name found. */ |
| |
| static void |
| GetName (char *Name) |
| { |
| int i; |
| char ch; |
| |
| SkipSpaces (); |
| ch = GetChar (); |
| i = 0; |
| while (!IsSpace (ch)) |
| { |
| Name[i] = ch; |
| i++; |
| ch = GetChar (); |
| } |
| Name[i] = '\0'; |
| } |
| |
| /* FindSource - open source file on StdIn. */ |
| |
| static void |
| FindSource (char *Name) |
| { |
| if (close (STDIN) != 0) |
| { |
| ERROR ("close on STDIN failed"); |
| } |
| CurrentFile = open (Name, O_RDONLY); |
| if (CurrentFile < 0) |
| { |
| perror ("failed to open file"); |
| exit (1); |
| } |
| if (CurrentFile != STDIN) |
| { |
| ERROR ("Expecting file descriptor value of 1"); |
| } |
| } |