blob: e489e2ffcb4b55032a2d87a8aa5537a64fa69532 [file] [log] [blame]
/* 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");
}
}