/* mklink.c creates startup code and the link command line. Copyright (C) 2000-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley . 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 . */ #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 \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 " "\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 " "\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"); } }