[v2,16/19] modula2 front end: bootstrap and documentation tools
Checks
Commit Message
Hi Martin,
here is the revised patch having applied all previous recommendations:
https://gcc.gnu.org/pipermail/gcc-patches/2022-October/603436.html
Is this ok now? Thanks for the improvement suggestions (argparse
results in fewer lines of code :-)
regards,
Gaius
------8<----------8<----------8<----------8<----------8<----------8<----
Comments
On 11/7/22 14:09, Gaius Mulley wrote:
>
> Hi Martin,
>
> here is the revised patch having applied all previous recommendations:
>
> https://gcc.gnu.org/pipermail/gcc-patches/2022-October/603436.html
Hi.
>
> Is this ok now? Thanks for the improvement suggestions (argparse
> results in fewer lines of code :-)
Thanks, much better, however some comments are still not addressed:
1) I would prefer using ' instead of ":
$ flake8 ./gcc/m2/tools-src/tidydates.py
...
./gcc/m2/tools-src/tidydates.py:124:30: Q000 Double quotes found but single quotes preferred
./gcc/m2/tools-src/tidydates.py:127:27: Q000 Double quotes found but single quotes preferred
./gcc/m2/tools-src/tidydates.py:132:27: Q000 Double quotes found but single quotes preferred
./gcc/m2/tools-src/tidydates.py:133:33: Q000 Double quotes found but single quotes preferred
./gcc/m2/tools-src/tidydates.py:138:26: Q000 Double quotes found but single quotes preferred
./gcc/m2/tools-src/tidydates.py:143:28: Q000 Double quotes found but single quotes preferred
2) Python-names would be nicer:
def writeTemplate(fo, magic, start, end, dates, contribution, summary, lic):
def write_template(...)
3) def hasExt(name, ext) - please use Path from pathlib
4) while (str.find(line, "(*") != -1):
'(*' in line
? Similarly elsewhere.
5) str.find(line, ...)
Use rather directly: line.find(...)
6) please use flake8:
https://gcc.gnu.org/codingconventions.html#python
Thanks,
Martin
P.S. I'm going to merge Sphinx branch this Wednesday, so then we should port your
conversion scripts to emit .rst instead of .texi.
>
> regards,
> Gaius
>
>
> ------8<----------8<----------8<----------8<----------8<----------8<----
> diff -ruw /dev/null gcc-git-devel-modula2/mliska@suse.cz
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/tidydates.py
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/tidydates.py 2022-11-05 17:33:53.685584357 +0000
> @@ -0,0 +1,166 @@
> +#!/usr/bin/env python3
> +
> +# utility to tidy dates and detect lack of copyright.
> +
> +# Copyright (C) 2016-2022 Free Software Foundation, Inc.
> +#
> +# 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 COPYING. If not, write to the
> +# Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
> +# 02110-1301, USA.
> +
> +import os
> +import sys
> +import pathlib
> +import shutil
> +
> +maxLineLength = 60
> +
> +COPYRIGHT = "Copyright (C)"
> +
> +
> +def visitDir(directory, ext, func):
> + # visitDir - call func for each file below, dir, matching extension, ext.
> + listOfFiles = os.listdir(directory)
> + listOfFiles.sort()
> + for filename in listOfFiles:
> + path = pathlib.PurePath(filename)
> + full = os.path.join(directory, filename)
> + if path.is_file(full):
> + if path.suffix == ext:
> + func(full)
> + elif path.is_dir(full):
> + visitDir(full, ext, func)
> +
> +
> +def isYear(year):
> + # isYear - returns True if, year, is legal.
> + if len(year) == 5:
> + year = year[:-1]
> + for c in year:
> + if not c.isdigit():
> + return False
> + return True
> +
> +
> +def handleCopyright(outfile, lines, n, leader1, leader2):
> + # handleCopyright look for Copyright in the comment.
> + global maxLineLength
> + i = lines[n]
> + c = i.find(COPYRIGHT)+len(COPYRIGHT)
> + outfile.write(i[:c])
> + d = i[c:].split()
> + start = c
> + seenDate = True
> + years = []
> + while seenDate:
> + if d == []:
> + n += 1
> + i = lines[n]
> + d = i[2:].split()
> + else:
> + e = d[0]
> + punctuation = ""
> + if len(d) == 1:
> + d = []
> + else:
> + d = d[1:]
> + if c > maxLineLength:
> + outfile.write("\n")
> + outfile.write(leader1)
> + outfile.write(leader2)
> + outfile.write(" "*(start-2))
> + c = start
> + if isYear(e):
> + if (e[-1] == ".") or (e[-1] == ","):
> + punctuation = e[-1]
> + e = e[:-1]
> + else:
> + punctuation = ""
> + else:
> + seenDate = False
> + if seenDate:
> + if not (e in years):
> + c += len(e) + len(punctuation)
> + outfile.write(" ")
> + outfile.write(e)
> + outfile.write(punctuation)
> + years += [e]
> + else:
> + if start < c:
> + outfile.write("\n")
> + outfile.write(leader1)
> + outfile.write(leader2)
> + outfile.write(" "*(start-2))
> +
> + outfile.write(" ")
> + outfile.write(e)
> + outfile.write(punctuation)
> + for w in d:
> + outfile.write(" ")
> + outfile.write(w)
> + outfile.write("\n")
> + return outfile, n+1
> +
> +
> +def handleHeader(filename, leader1, leader2):
> + # handleHeader reads in the header of a file and inserts
> + # a line break around the Copyright dates.
> + print("------------------------------")
> + lines = open(filename, "r").readlines()
> + if len(lines) > 20:
> + with open("tmptidy", "w") as outfile:
> + n = 0
> + for i in lines:
> + if i.find("Copyright (C)") >= 0:
> + outfile, n = handleCopyright(outfile, lines,
> + n, leader1, leader2)
> + outfile.writelines(lines[n:])
> + outfile.close()
> + print("-> mv tmptidy", filename)
> + shutil.move("tmptidy", filename)
> + return
> + else:
> + outfile.write(lines[n])
> + n += 1
> + sys.stdout.write("%s:1:1 needs a Copyright notice..\n" % filename)
> +
> +
> +def bashTidy(filename):
> + # bashTidy - tidy up dates using "#" comment
> + handleHeader(filename, "#", " ")
> +
> +
> +def cTidy(filename):
> + # cTidy - tidy up dates using "/* */" comments
> + handleHeader(filename, " ", "*")
> +
> +
> +def m2Tidy(filename):
> + # m2Tidy - tidy up dates using "(* *)" comments
> + handleHeader(filename, " ", " ")
> +
> +
> +def main():
> + # main - for each file extension call the appropriate tidy routine.
> + visitDir(".", ".in", bashTidy)
> + visitDir(".", ".py", bashTidy)
> + visitDir(".", ".c", cTidy)
> + visitDir(".", ".h", cTidy)
> + visitDir(".", ".def", m2Tidy)
> + visitDir(".", ".mod", m2Tidy)
> +
> +
> +main()
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/boilerplate.py
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/boilerplate.py 2022-11-05 17:33:53.685584357 +0000
> @@ -0,0 +1,548 @@
> +#!/usr/bin/env python3
> +#
> +# boilerplate.py utility to rewrite the boilerplate with new dates.
> +#
> +# Copyright (C) 2018-2022 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/>.
> +#
> +
> +import argparse
> +import datetime
> +import os
> +import sys
> +
> +
> +errorCount = 0
> +seenFiles = []
> +outputName = None
> +
> +ISO_COPYRIGHT = "Copyright ISO/IEC"
> +COPYRIGHT = "Copyright (C)"
> +GNU_PUBLIC_LICENSE = "GNU General Public License"
> +GNU_LESSER_GENERAL = "GNU Lesser General"
> +GCC_RUNTIME_LIB_EXC = "GCC Runtime Library Exception"
> +VERSION_2_1 = "version 2.1"
> +VERSION_2 = "version 2"
> +VERSION_3 = "version 3"
> +Licenses = {VERSION_2_1: "v2.1", VERSION_2: "v2", VERSION_3: "v3"}
> +CONTRIBUTED_BY = "ontributed by"
> +
> +
> +def printf(fmt, *args):
> + # printf - keeps C programmers happy :-)
> + print(str(fmt) % args, end=' ')
> +
> +
> +def error(fmt, *args):
> + # error - issue an error message.
> + global errorCount
> +
> + print(str(fmt) % args, end=' ')
> + errorCount += 1
> +
> +
> +def haltOnError():
> + if errorCount > 0:
> + os.sys.exit(1)
> +
> +
> +def basename(f):
> + b = f.split("/")
> + return b[-1]
> +
> +
> +def analyseComment(text, f):
> + # analyseComment determine the license from the top comment.
> + start_date, end_date = None, None
> + contribution, summary, lic = None, None, None
> + if text.find(ISO_COPYRIGHT) > 0:
> + lic = "BSISO"
> + now = datetime.datetime.now()
> + for d in range(1984, now.year+1):
> + if text.find(str(d)) > 0:
> + if start_date is None:
> + start_date = str(d)
> + end_date = str(d)
> + return start_date, end_date, "", "", lic
> + elif text.find(COPYRIGHT) > 0:
> + if text.find(GNU_PUBLIC_LICENSE) > 0:
> + lic = "GPL"
> + elif text.find(GNU_LESSER_GENERAL) > 0:
> + lic = "LGPL"
> + for license in Licenses.keys():
> + if text.find(license) > 0:
> + lic += Licenses[license]
> + if text.find(GCC_RUNTIME_LIB_EXC) > 0:
> + lic += "x"
> + now = datetime.datetime.now()
> + for d in range(1984, now.year+1):
> + if text.find(str(d)) > 0:
> + if start_date is None:
> + start_date = str(d)
> + end_date = str(d)
> + if text.find(CONTRIBUTED_BY) > 0:
> + i = text.find(CONTRIBUTED_BY)
> + i += len(CONTRIBUTED_BY)
> + j = text.index(". ", i)
> + contribution = text[i:j]
> + if text.find(basename(f)) > 0:
> + i = text.find(basename(f))
> + j = text.find(". ", i)
> + if j < 0:
> + error('summary of the file does not finish with a "."')
> + summary = text[i:]
> + else:
> + summary = text[i:j]
> + return start_date, end_date, contribution, summary, lic
> +
> +
> +def analyseHeaderWithoutTerminator(f, start):
> + text = ""
> + for count, l in enumerate(open(f).readlines()):
> + parts = l.split(start)
> + if len(parts) > 1:
> + line = start.join(parts[1:])
> + line = line.strip()
> + text += " "
> + text += line
> + elif (l.rstrip() != "") and (len(parts[0]) > 0):
> + return analyseComment(text, f), count
> + return [None, None, None, None, None], 0
> +
> +
> +def analyseHeaderWithTerminator(f, start, end):
> + inComment = False
> + text = ""
> + for count, line in enumerate(open(f).readlines()):
> + while line != "":
> + line = line.strip()
> + if inComment:
> + text += " "
> + pos = line.find(end)
> + if pos >= 0:
> + text += line[:pos]
> + line = line[pos:]
> + inComment = False
> + else:
> + text += line
> + line = ""
> + else:
> + pos = line.find(start)
> + if (pos >= 0) and (len(line) > len(start)):
> + before = line[:pos].strip()
> + if before != "":
> + return analyseComment(text, f), count
> + line = line[pos + len(start):]
> + inComment = True
> + elif (line != "") and (line == end):
> + line = ""
> + else:
> + return analyseComment(text, f), count
> + return [None, None, None, None, None], 0
> +
> +
> +def analyseHeader(f, start, end):
> + # analyseHeader -
> + if end is None:
> + return analyseHeaderWithoutTerminator(f, start)
> + else:
> + return analyseHeaderWithTerminator(f, start, end)
> +
> +
> +def addStop(sentence):
> + # addStop - add a full stop to a sentance.
> + if sentence is None:
> + return None
> + sentence = sentence.rstrip()
> + if (len(sentence) > 0) and (sentence[-1] != "."):
> + return sentence + "."
> + return sentence
> +
> +
> +GPLv3 = """
> +%s
> +
> +Copyright (C) %s Free Software Foundation, Inc.
> +Contributed by %s
> +
> +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/>.
> +"""
> +
> +GPLv3x = """
> +%s
> +
> +Copyright (C) %s Free Software Foundation, Inc.
> +Contributed by %s
> +
> +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.
> +
> +Under Section 7 of GPL version 3, you are granted additional
> +permissions described in the GCC Runtime Library Exception, version
> +3.1, as published by the Free Software Foundation.
> +
> +You should have received a copy of the GNU General Public License and
> +a copy of the GCC Runtime Library Exception along with this program;
> +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +<http://www.gnu.org/licenses/>.
> +"""
> +
> +LGPLv3 = """
> +%s
> +
> +Copyright (C) %s Free Software Foundation, Inc.
> +Contributed by %s
> +
> +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 Lesser General Public License as
> +published by the Free Software Foundation, either version 3 of the
> +License, 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
> +Lesser General Public License for more details.
> +
> +You should have received a copy of the GNU Lesser General Public License
> +along with GNU Modula-2. If not, see <https://www.gnu.org/licenses/>.
> +"""
> +
> +BSISO = """
> +Library module defined by the International Standard
> + Information technology - programming languages
> + BS ISO/IEC 10514-1:1996E Part 1: Modula-2, Base Language.
> +
> + Copyright ISO/IEC (International Organization for Standardization
> + and International Electrotechnical Commission) %s.
> +
> + It may be freely copied for the purpose of implementation (see page
> + 707 of the Information technology - Programming languages Part 1:
> + Modula-2, Base Language. BS ISO/IEC 10514-1:1996).
> +"""
> +
> +templates = {}
> +templates["GPLv3"] = GPLv3
> +templates["GPLv3x"] = GPLv3x
> +templates["LGPLv3"] = LGPLv3
> +templates["LGPLv2.1"] = LGPLv3
> +templates["BSISO"] = BSISO
> +
> +
> +def writeTemplate(fo, magic, start, end, dates, contribution, summary, lic):
> + if lic in templates:
> + if lic == "BSISO":
> + # non gpl but freely distributed for the implementation of a
> + # compiler
> + text = templates[lic] % (dates)
> + text = text.rstrip()
> + else:
> + summary = summary.lstrip()
> + contribution = contribution.lstrip()
> + summary = addStop(summary)
> + contribution = addStop(contribution)
> + if magic is not None:
> + fo.write(magic)
> + fo.write("\n")
> + text = templates[lic] % (summary, dates, contribution)
> + text = text.rstrip()
> + if end is None:
> + text = text.split("\n")
> + for line in text:
> + fo.write(start)
> + fo.write(" ")
> + fo.write(line)
> + fo.write("\n")
> + else:
> + text = text.lstrip()
> + fo.write(start)
> + fo.write(" ")
> + fo.write(text)
> + fo.write(" ")
> + fo.write(end)
> + fo.write("\n")
> + # add a blank comment line for a script for eye candy.
> + if start == "#" and end is None:
> + fo.write(start)
> + fo.write("\n")
> + else:
> + error("no template found for: %s\n", lic)
> + os.sys.exit(1)
> + return fo
> +
> +
> +def writeBoilerPlate(fo, magic, start, end,
> + start_date, end_date, contribution, summary, gpl):
> + if start_date == end_date:
> + dates = start_date
> + else:
> + dates = "%s-%s" % (start_date, end_date)
> + return writeTemplate(fo, magic, start, end,
> + dates, contribution, summary, gpl)
> +
> +
> +def rewriteFile(f, magic, start, end, start_date, end_date,
> + contribution, summary, gpl, lines):
> + text = "".join(open(f).readlines()[lines:])
> + if outputName == "-":
> + fo = sys.stdout
> + else:
> + fo = open(f, "w")
> + fo = writeBoilerPlate(fo, magic, start, end,
> + start_date, end_date, contribution, summary, gpl)
> + fo.write(text)
> + fo.flush()
> + if outputName != "-":
> + fo.close()
> +
> +
> +def handleHeader(f, magic, start, end):
> + # handleHeader keep reading lines of file, f, looking for start, end
> + # sequences and comments inside. The comments are checked for:
> + # date, contribution, summary
> + global errorCount
> +
> + errorCount = 0
> + [start_date, end_date,
> + contribution, summary, lic], lines = analyseHeader(f, start, end)
> + if lic is None:
> + error("%s:1:no GPL found at the top of the file\n", f)
> + else:
> + if args.verbose:
> + printf("copyright: %s\n", lic)
> + if (start_date is not None) and (end_date is not None):
> + if start_date == end_date:
> + printf("dates = %s\n", start_date)
> + else:
> + printf("dates = %s-%s\n", start_date, end_date)
> + if summary is not None:
> + printf("summary: %s\n", summary)
> + if contribution is not None:
> + printf("contribution: %s\n", contribution)
> + if start_date is None:
> + error("%s:1:no date found in the GPL at the top of the file\n", f)
> + if args.contribution is None:
> + if contribution == "":
> + error("%s:1:no contribution found in the " +
> + "GPL at the top of the file\n", f)
> + else:
> + contribution = args.contribution
> + if summary is None:
> + if args.summary == "":
> + error("%s:1:no single line summary found in the " +
> + "GPL at the top of the file\n", f)
> + else:
> + summary = args.summary
> + if errorCount == 0:
> + now = datetime.datetime.now()
> + if args.no:
> + print(f, "suppressing change as requested: %s-%s %s"
> + % (start_date, end_date, lic))
> + else:
> + if lic == "BSISO":
> + # don't change the BS ISO license!
> + pass
> + elif args.extensions:
> + lic = "GPLv3x"
> + elif args.gpl3:
> + lic = "GPLv3"
> + rewriteFile(f, magic, start, end, start_date,
> + str(now.year), contribution, summary, lic, lines)
> + else:
> + printf("too many errors, no modifications will occur\n")
> +
> +
> +def bashTidy(f):
> + # bashTidy tidy up dates using '#' comment
> + handleHeader(f, "#!/bin/bash", "#", None)
> +
> +
> +def pythonTidy(f):
> + # pythonTidy tidy up dates using '#' comment
> + handleHeader(f, "#!/usr/bin/env python3", '#', None)
> +
> +
> +def bnfTidy(f):
> + # bnfTidy tidy up dates using '--' comment
> + handleHeader(f, None, '--', None)
> +
> +
> +def cTidy(f):
> + # cTidy tidy up dates using '/* */' comments
> + handleHeader(f, None, '/*', '*/')
> +
> +
> +def m2Tidy(f):
> + # m2Tidy tidy up dates using '(* *)' comments
> + handleHeader(f, None, '(*', '*)')
> +
> +
> +def inTidy(f):
> + # inTidy tidy up dates using '#' as a comment and check
> + # the first line for magic number.
> + first = open(f).readlines()[0]
> + if (len(first) > 0) and (first[:2] == "#!"):
> + # magic number found, use this
> + handleHeader(f, first, "#", None)
> + else:
> + handleHeader(f, None, "#", None)
> +
> +
> +def doVisit(args, dirname, names):
> + # doVisit helper function to call func on every extension file.
> + global outputName
> + func, extension = args
> + for f in names:
> + if len(f) > len(extension) and f[-len(extension):] == extension:
> + outputName = f
> + func(os.path.join(dirname, f))
> +
> +
> +def visitDir(startDir, ext, func):
> + # visitDir call func for each file in startDir which has ext.
> + global outputName, seenFiles
> + for dirName, subdirList, fileList in os.walk(startDir):
> + for fname in fileList:
> + if (len(fname) > len(ext)) and (fname[-len(ext):] == ext):
> + fullpath = os.path.join(dirName, fname)
> + outputName = fullpath
> + if not (fullpath in seenFiles):
> + seenFiles += [fullpath]
> + func(fullpath)
> + # Remove the first entry in the list of sub-directories
> + # if there are any sub-directories present
> + if len(subdirList) > 0:
> + del subdirList[0]
> +
> +
> +def findFiles():
> + # findFiles for each file extension call the appropriate tidy routine.
> + visitDir(args.recursive, '.h.in', cTidy)
> + visitDir(args.recursive, '.in', inTidy)
> + visitDir(args.recursive, '.sh', inTidy)
> + visitDir(args.recursive, '.py', pythonTidy)
> + visitDir(args.recursive, '.c', cTidy)
> + visitDir(args.recursive, '.h', cTidy)
> + visitDir(args.recursive, '.cc', cTidy)
> + visitDir(args.recursive, '.def', m2Tidy)
> + visitDir(args.recursive, '.mod', m2Tidy)
> + visitDir(args.recursive, '.bnf', bnfTidy)
> +
> +
> +def handleArguments():
> + # handleArguments create and return the args object.
> + parser = argparse.ArgumentParser()
> + parser.add_argument("-c", "--contribution",
> + help="set the contribution string " +
> + "at the top of the file.",
> + default="", action="store")
> + parser.add_argument("-d", "--debug", help="turn on internal debugging.",
> + default=False, action="store_true")
> + parser.add_argument("-f", "--force",
> + help="force a check to insist that the " +
> + "contribution, summary and GPL exist.",
> + default=False, action="store_true")
> + parser.add_argument("-g", "--gplv3", help="change to GPLv3",
> + default=False, action="store_true")
> + parser.add_argument("-o", "--outputfile", help="set the output file",
> + default="-", action="store")
> + parser.add_argument("-r", "--recursive",
> + help="recusively scan directory for known file " +
> + "extensions (.def, .mod, .c, .h, .py, .in, .sh).",
> + default=".", action="store")
> + parser.add_argument("-s", "--summary",
> + help="set the summary line for the file.",
> + default=None, action="store")
> + parser.add_argument("-u", "--update", help="update all dates.",
> + default=False, action="store_true")
> + parser.add_argument("-v", "--verbose",
> + help="display copyright, " +
> + "date and contribution messages",
> + action="store_true")
> + parser.add_argument("-x", "--extensions",
> + help="change to GPLv3 with GCC runtime extensions.",
> + default=False, action="store_true")
> + parser.add_argument("-N", "--no",
> + help="do not modify any file.",
> + action="store_true")
> + args = parser.parse_args()
> + return args
> +
> +
> +def hasExt(name, ext):
> + # hasExt return True if, name, ends with, ext.
> + if len(name) > len(ext):
> + return name[-len(ext):] == ext
> + return False
> +
> +
> +def singleFile(name):
> + # singleFile scan the single file for a GPL boilerplate which
> + # has a GPL, contribution field and a summary heading.
> + if hasExt(name, ".def") or hasExt(name, ".mod"):
> + m2Tidy(name)
> + elif hasExt(name, ".h") or hasExt(name, ".c") or hasExt(name, ".cc"):
> + cTidy(name)
> + elif hasExt(name, ".in"):
> + inTidy(name)
> + elif hasExt(name, ".sh"):
> + inTidy(name) # uses magic number for actual sh/bash
> + elif hasExt(name, ".py"):
> + pythonTidy(name)
> +
> +
> +def main():
> + # main - handleArguments and then find source files.
> + global args, outputName
> + args = handleArguments()
> + outputName = args.outputfile
> + if args.recursive:
> + findFiles()
> + elif args.inputfile is None:
> + print("an input file must be specified on the command line")
> + else:
> + singleFile(args.inputfile)
> + haltOnError()
> +
> +
> +main()
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/buildpg
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/buildpg 2022-11-05 17:33:53.685584357 +0000
> @@ -0,0 +1,289 @@
> +#!/bin/sh
> +
> +# Copyright (C) 2000-2022 Free Software Foundation, Inc.
> +# 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 COPYING. If not, write to the
> +# Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
> +# 02110-1301, USA.
> +#
> +
> +# builds the pg.bnf from ppg.mod
> +# usage buildpg ppg.mod destination [-e]
> +# -e build without error recovery
> +#
> +PPGSRC=$1
> +PPGDST=$2
> +
> +includeNonErrorChecking () {
> + sed -e "1,/StartNonErrorChecking/d" < $PPGSRC |\
> + sed -e "1,/EndNonErrorChecking/!d"
> +}
> +
> +includeErrorChecking () {
> + sed -e "1,/StartErrorChecking/d" < $PPGSRC |\
> + sed -e "1,/EndErrorChecking/!d"
> +}
> +
> +
> +echo "% module" $PPGDST "begin"
> +sed -e "1,/% declaration/!d" < $PPGSRC | sed -e "s/ppg/${PPGDST}/g"
> +
> +echo "% declaration" $PPGDST "begin"
> +
> +sed -e "1,/% declaration/d" < $PPGSRC | sed -e "1,/% rules/!d" | sed -e "s/ppg/${PPGDST}/g"
> +
> +if [ "$3" = "-e" ] ; then
> + includeNonErrorChecking
> + echo "% module" $PPGDST "end"
> + sed -e "1,/% module pg end/d" < $PPGSRC | sed -e "s/ppg/${PPGDST}/g"
> +else
> + includeErrorChecking
> + echo "% module" $PPGDST "end"
> + sed -e "1,/% module pg end/d" < $PPGSRC | sed -e "s/ppg/${PPGDST}/g" |\
> + sed -e "s/WasNoError := Main() ;/Main({eoftok}) ;/"
> +fi
> +
> +echo "% rules"
> +
> +cat << EOFEOF | sed -e "s/ppg/${PPGDST}/g"
> +error 'WarnError' 'WarnString'
> +tokenfunc 'GetCurrentTokenType()'
> +
> +token 'identifier' identtok -- internal token
> +token 'literal' literaltok
> +token '%' codetok
> +token ':=' lbecomestok
> +token '=:' rbecomestok
> +token '|' bartok
> +token '[' lsparatok
> +token ']' rsparatok
> +token '{' lcparatok -- left curly para
> +token '}' rcparatok -- right curly para
> +token '(' lparatok
> +token ')' rparatok
> +token "error" errortok
> +token "tokenfunc" tfunctok
> +token "symfunc" symfunctok
> +token '"' dquotetok
> +token "'" squotetok
> +token "module" moduletok
> +token "begin" begintok
> +token "rules" rulestok
> +token "end" endtok
> +token '<' lesstok
> +token '>' gretok
> +token "token" tokentok
> +token "special" specialtok
> +token "first" firsttok
> +token "follow" followtok
> +token "BNF" BNFtok
> +token "FNB" FNBtok
> +token "declaration" declarationtok
> +token "epsilon" epsilontok
> +token '' eoftok -- internal token
> +
> +special Ident first { < identtok > } follow { }
> +special Modula2Code first { } follow { '%' }
> +special StartModName first { < identtok > } follow { }
> +special EndModName first { < identtok > } follow { }
> +special DoDeclaration first { < identtok > } follow { }
> +special CollectLiteral first { < literaltok > } follow { }
> +special CollectTok first { < identtok > } follow { }
> +special DefineToken first { < identtok > } follow { }
> +
> +BNF
> +
> +Rules := "%" "rules" { Defs } ExtBNF =:
> +
> +Special := Ident
> + % VAR p: ProductionDesc ; %
> + % p := NewProduction() ;
> + p^.statement := NewStatement() ;
> + p^.statement^.followinfo^.calcfollow := TRUE ;
> + p^.statement^.followinfo^.epsilon := false ;
> + p^.statement^.followinfo^.reachend := false ;
> + p^.statement^.ident := CurrentIdent ;
> + p^.statement^.expr := NIL ;
> + p^.firstsolved := TRUE ;
> + p^.followinfo^.calcfollow := TRUE ;
> + p^.followinfo^.epsilon := false ;
> + p^.followinfo^.reachend := false %
> + First Follow [ "epsilon" % p^.statement^.followinfo^.epsilon := true ; (* these are not used - but they are displayed when debugging *)
> + p^.statement^.followinfo^.reachend := true ;
> + p^.followinfo^.epsilon := true ;
> + p^.followinfo^.reachend := true
> + % ]
> + [ Literal % p^.description := LastLiteral % ]
> + =:
> +
> +Factor := "%" Modula2Code "%" |
> + Ident % WITH CurrentFactor^ DO
> + type := id ;
> + ident := CurrentIdent
> + END ; % |
> + Literal % WITH CurrentFactor^ DO
> + type := lit ;
> + string := LastLiteral ;
> + IF GetSymKey(Aliases, LastLiteral)=NulName
> + THEN
> + WarnError1('no token defined for literal %s', LastLiteral)
> + END
> + END ; % |
> + "{" % WITH CurrentFactor^ DO
> + type := mult ;
> + expr := NewExpression() ;
> + CurrentExpression := expr ;
> + END ; %
> + Expression "}" |
> + "[" % WITH CurrentFactor^ DO
> + type := opt ;
> + expr := NewExpression() ;
> + CurrentExpression := expr ;
> + END ; %
> + Expression "]" |
> + "(" % WITH CurrentFactor^ DO
> + type := sub ;
> + expr := NewExpression() ;
> + CurrentExpression := expr ;
> + END ; %
> + Expression ")" =:
> +
> +Statement := % VAR i: IdentDesc ; %
> + Ident
> + % VAR p: ProductionDesc ; %
> + % p := FindDefinition(CurrentIdent^.name) ;
> + IF p=NIL
> + THEN
> + p := NewProduction()
> + ELSE
> + IF NOT ((p^.statement=NIL) OR (p^.statement^.expr=NIL))
> + THEN
> + WarnError1('already declared rule %s', CurrentIdent^.name)
> + END
> + END ;
> + i := CurrentIdent ; %
> + ":="
> + % VAR e: ExpressionDesc ; %
> + % e := NewExpression() ;
> + CurrentExpression := e ; %
> + % VAR s: StatementDesc ; %
> + % s := NewStatement() ;
> + WITH s^ DO
> + ident := i ;
> + expr := e
> + END ; %
> + Expression
> + % p^.statement := s ; %
> + "=:" =:
> +
> +Defs := "special" Special | "token" Token | "error" ErrorProcedures |
> + "tokenfunc" TokenProcedure | "symfunc" SymProcedure =:
> +ExtBNF := "BNF" { Production } "FNB" =:
> +Main := Header Decls Footer Rules =:
> +Header := "%" "module" StartModName =:
> +Decls := "%" "declaration" DoDeclaration =:
> +Footer := "%" "module" EndModName =:
> +
> +First := "first" "{" { LitOrTokenOrIdent
> + % WITH CurrentSetDesc^ DO
> + next := TailProduction^.first ;
> + END ;
> + TailProduction^.first := CurrentSetDesc
> + %
> + } "}" =:
> +Follow := "follow" "{" { LitOrTokenOrIdent
> + % WITH CurrentSetDesc^ DO
> + next := TailProduction^.followinfo^.follow ;
> + END ;
> + TailProduction^.followinfo^.follow := CurrentSetDesc
> + %
> + } "}" =:
> +LitOrTokenOrIdent := Literal % CurrentSetDesc := NewSetDesc() ;
> + WITH CurrentSetDesc^ DO
> + type := litel ;
> + string := LastLiteral ;
> + END ;
> + % |
> + '<' CollectTok '>' |
> + Ident % CurrentSetDesc := NewSetDesc() ;
> + WITH CurrentSetDesc^ DO
> + type := idel ;
> + ident := CurrentIdent ;
> + END ;
> + % =:
> +
> +Literal := '"' CollectLiteral '"' |
> + "'" CollectLiteral "'" =:
> +
> +CollectTok := % CurrentSetDesc := NewSetDesc() ;
> + WITH CurrentSetDesc^ DO
> + type := tokel ;
> + string := GetCurrentToken() ;
> + END ;
> + IF NOT ContainsSymKey(Values, GetCurrentToken())
> + THEN
> + AddEntry(Values, GetCurrentToken(), LargestValue) ;
> + AddEntry(ReverseValues, Name(LargestValue), GetCurrentToken()) ;
> + AddEntry(Aliases, GetCurrentToken(), GetCurrentToken()) ;
> + AddEntry(ReverseAliases, GetCurrentToken(), GetCurrentToken()) ;
> + INC(LargestValue)
> + END ;
> + AdvanceToken() ; % =:
> +
> +CollectLiteral := % LastLiteral := GetCurrentToken() ;
> + AdvanceToken ; % =:
> +
> +DefineToken := % AddEntry(Aliases, LastLiteral, GetCurrentToken()) ;
> + AddEntry(ReverseAliases, GetCurrentToken(), LastLiteral) ;
> + AddEntry(Values, GetCurrentToken(), LargestValue) ;
> + AddEntry(ReverseValues, Name(LargestValue), GetCurrentToken()) ;
> + INC(LargestValue) ;
> + AdvanceToken ; % =:
> +
> +Token := Literal DefineToken =:
> +
> +ErrorProcedures := Literal % ErrorProcArray := LastLiteral %
> + Literal % ErrorProcString := LastLiteral % =:
> +TokenProcedure := Literal % TokenTypeProc := LastLiteral % =:
> +SymProcedure := Literal % SymIsProc := LastLiteral % =:
> +
> +Production := Statement =:
> +Expression := % VAR t1, t2: TermDesc ;
> + e : ExpressionDesc ; %
> + % e := CurrentExpression ;
> + t1 := NewTerm() ;
> + CurrentTerm := t1 ; %
> + Term % e^.term := t1 ; %
> + { "|" % t2 := NewTerm() ;
> + CurrentTerm := t2 %
> + Term % t1^.next := t2 ;
> + t1 := t2 % } =:
> +
> +Term := % VAR t1: TermDesc ; f1, f2: FactorDesc ; %
> + % CurrentFactor := NewFactor() ;
> + f1 := CurrentFactor ;
> + t1 := CurrentTerm ; %
> + Factor % t1^.factor := f1 ;
> + f2 := NewFactor() ;
> + CurrentFactor := f2 %
> + { Factor % f1^.next := f2 ;
> + f1 := f2 ;
> + f2 := NewFactor() ;
> + CurrentFactor := f2 ; % }
> + =:
> +
> +FNB
> +
> +EOFEOF
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/calcpath
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/calcpath 2022-11-05 17:33:53.685584357 +0000
> @@ -0,0 +1,51 @@
> +#!/bin/sh
> +
> +# calcpath return a path which is $1/$2/$3 when $2 is relative and $2/$3 if absolute.
> +
> +# Copyright (C) 2021-2022 Free Software Foundation, Inc.
> +# Contributed by Gaius Mulley <gaius.mulley@southwales.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 gm2; see the file COPYING. If not, write to the Free Software
> +# Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *)
> +
> +
> +Usage () {
> + echo "Usage: calcpath pathcomponent1 pathcomponent2 subdir"
> + echo -n " if pathcomponent1 is relative then pathcomponent1/pathcomponet2/subdir is"
> + echo " returned"
> + echo " otherwise pathcomponet2/subdir is returned"
> + echo " the path is checked for legality in subdir."
> +}
> +
> +
> +if [ $# -eq 3 ]; then
> + if [ "$(echo $2 | cut -b 1)" = "." ] ; then
> + # relative path
> + the_path=$1/$2/$3
> + else
> + the_path=$2/$3
> + fi
> + cd $3
> + if realpath ${the_path} > /dev/null ; then
> + echo ${the_path}
> + else
> + echo "calcpath: error ${the_path} is not a valid path in subdirectory $3" 1>&2
> + exit 1
> + fi
> +else
> + Usage
> + exit 1
> +fi
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/makeSystem
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/makeSystem 2022-11-05 17:33:53.685584357 +0000
> @@ -0,0 +1,108 @@
> +#!/bin/sh
> +
> +# makeSystem creates a target SYSTEM.def using the appropriate dialect template.
> +
> +# Copyright (C) 2008-2022 Free Software Foundation, Inc.
> +# Contributed by Gaius Mulley <gaius.mulley@southwales.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 gm2; see the file COPYING. If not, write to the Free Software
> +# Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *)
> +
> +
> +Usage () {
> + echo "Usage: makesystem dialectflag SYSTEM.def SYSTEM.mod librarypath compiler"
> +}
> +
> +if [ $# -lt 6 ] ; then
> + Usage
> + exit 1
> +fi
> +
> +DIALECT=$1
> +SYSTEMDEF=$2
> +SYSTEMMOD=$3
> +LIBRARY=$4
> +COMPILER=$5
> +OUTPUTFILE=$6
> +
> +if [ "$COMPILER" = "" ] ; then
> + echo "parameter 5 of makeSystem is incorrect, GM2_FOR_TARGET was unset"
> + exit 1
> +fi
> +
> +if [ "$DIALECT" != "-fiso" -a "$DIALECT" != "-fpim" ] ; then
> + Usage
> + echo "dialect must be -fiso or -fpim"
> + exit 1
> +fi
> +
> +displayExportedTypes () {
> + n=1
> + c=0
> + for i in ${types} ; do
> + if [ $n -eq 1 ] ; then
> + n=0
> + echo -n " " >> ${OUTPUTFILE}
> + fi
> + echo -n "$i, " >> ${OUTPUTFILE}
> + if [ $c -eq 4 ] ; then
> + echo " " >> ${OUTPUTFILE}
> + n=1
> + c=0
> + fi
> + c=`expr $c + 1`
> + done
> + echo " " >> ${OUTPUTFILE}
> +}
> +
> +displayBuiltinTypes () {
> + for i in ${types} ; do
> + echo " $i ; " >> ${OUTPUTFILE}
> + done
> +}
> +
> +displayStart () {
> + sed -e "1,/@SYSTEM_DATATYPES@/!d" < ${SYSTEMDEF} | \
> + sed -e "/@SYSTEM_DATATYPES@/d" >> ${OUTPUTFILE}
> +}
> +
> +displayMiddle () {
> + sed -e "1,/@SYSTEM_DATATYPES@/d" < ${SYSTEMDEF} | \
> + sed -e "1,/@SYSTEM_TYPES@/!d" | \
> + sed -e "/@SYSTEM_TYPES@/d" >> ${OUTPUTFILE}
> +}
> +
> +displayEnd () {
> + sed -e "1,/@SYSTEM_TYPES@/d" < ${SYSTEMDEF} >> ${OUTPUTFILE}
> +}
> +
> +MINIMAL="-fno-scaffold-main -fno-scaffold-dynamic -fno-scaffold-static -fno-m2-plugin"
> +
> +rm -f ${OUTPUTFILE}
> +if ${COMPILER} ${DIALECT} ${LIBRARY} ${MINIMAL} \
> + -c -fdump-system-exports ${SYSTEMMOD} -o /dev/null 2>&1 > /dev/null ; then
> + types=`${COMPILER} ${DIALECT} ${LIBRARY} ${MINIMAL} -fno-m2-plugin -c -fdump-system-exports ${SYSTEMMOD} -o /dev/null | cut -f5 -d' '`
> + touch ${OUTPUTFILE}
> + displayStart
> + displayExportedTypes
> + displayMiddle
> + displayBuiltinTypes
> + displayEnd
> +else
> + ${COMPILER} ${DIALECT} ${LIBRARY} ${MINIMAL} \
> + -c -fdump-system-exports ${SYSTEMMOD} -o /dev/null
> + exit $?
> +fi
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/README
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/README 2022-11-05 17:33:53.685584357 +0000
> @@ -0,0 +1,3 @@
> +This directory contains miscellaneous scripts and programs (mklink.c)
> +to allow for bootstrap linking and creating library documentation from
> +sources.
> \ No newline at end of file
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/def2doc.py
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/def2doc.py 2022-11-05 17:33:53.685584357 +0000
> @@ -0,0 +1,419 @@
> +#!/usr/bin/env python3
> +
> +# def2doc.py creates texi library documentation for all exported procedures.
> +# Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
> +
> +# Copyright (C) 2000-2022 Free Software Foundation, Inc.
> +# 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 COPYING. If not, write to the
> +# Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
> +# 02110-1301, USA.
> +#
> +
> +import argparse
> +import os
> +import sys
> +
> +BaseLibs = ["gm2-libs", "Base libraries", "Basic M2F compatible libraries"]
> +
> +PIMLogDesc = "PIM and Logitech 3.0 compatible libraries"
> +PIMLog = ["gm2-libs-pim", "PIM and Logitech 3.0 Compatible", PIMLogDesc]
> +PIMCorDesc = "PIM compatible process support"
> +PIMCor = ["gm2-libs-coroutines", "PIM coroutine support", PIMCorDesc]
> +ISOLibs = ["gm2-libs-iso", "M2 ISO Libraries", "ISO defined libraries"]
> +
> +libraryClassifications = [BaseLibs, PIMLog, PIMCor, ISOLibs]
> +
> +
> +def initState():
> + global inVar, inType, inConst
> + inVar, inType, inConst = False, False, False
> +
> +
> +def emitNode(name, nxt, previous, up):
> + if args.texinfo:
> + output.write("@node " + name + ", " + nxt + ", ")
> + output.write(previous + ", " + up + "\n")
> + elif args.sphinx:
> + output.write("@c @node " + name + ", " + nxt + ", ")
> + output.write(previous + ", " + up + "\n")
> +
> +
> +def emitSection(name):
> + if args.texinfo:
> + output.write("@section " + name + "\n")
> + elif args.sphinx:
> + output.write(name + "\n")
> + output.write("=" * len(name) + "\n")
> +
> +
> +def emitSubSection(name):
> + if args.texinfo:
> + output.write("@subsection " + name + "\n")
> + elif args.sphinx:
> + output.write(name + "\n")
> + output.write("-" * len(name) + "\n")
> +
> +
> +def displayLibraryClass():
> + # displayLibraryClass displays a node for a library directory and invokes
> + # a routine to summarize each module.
> + global args
> + previous = ""
> + nxt = libraryClassifications[1][1]
> + i = 0
> + lib = libraryClassifications[i]
> + while True:
> + emitNode(lib[1], nxt, previous, args.up)
> + emitSection(lib[1])
> + output.write("\n")
> + displayModules(lib[1], lib[0], args.builddir, args.sourcedir)
> + output.write("\n")
> + output.write("@c " + "-" * 60 + "\n")
> + previous = lib[1]
> + i += 1
> + if i == len(libraryClassifications):
> + break
> + lib = libraryClassifications[i]
> + if i+1 == len(libraryClassifications):
> + nxt = ""
> + else:
> + nxt = libraryClassifications[i+1][1]
> +
> +
> +def displayMenu():
> + # displayMenu displays the top level menu for library documentation.
> + output.write("@menu\n")
> + for lib in libraryClassifications:
> + output.write("* " + lib[1] + "::" + lib[2] + "\n")
> + output.write("@end menu\n")
> + output.write("\n")
> + output.write("@c " + "=" * 60 + "\n")
> + output.write("\n")
> +
> +
> +def removeInitialComments(file, line):
> + # removeInitialComments removes any (* *) at the top
> + # of the definition module.
> + while (str.find(line, "*)") == -1):
> + line = file.readline()
> +
> +
> +def removeableField(line):
> + # removeableField - returns True if a comment field should be removed
> + # from the definition module.
> + field_list = ["Author", "Last edit", "LastEdit", "Last update",
> + "Date", "Title", "Revision"]
> + for field in field_list:
> + if (str.find(line, field) != -1) and (str.find(line, ":") != -1):
> + return True
> + ignore_list = ["System", "SYSTEM"]
> + for ignore_field in ignore_list:
> + if str.find(line, ignore_field) != -1:
> + if str.find(line, ":") != -1:
> + if str.find(line, "Description:") == -1:
> + return True
> + return False
> +
> +
> +def removeFields(file, line):
> + # removeFields removes Author/Date/Last edit/SYSTEM/Revision
> + # fields from a comment within the start of a definition module.
> + while (str.find(line, "*)") == -1):
> + if not removeableField(line):
> + output.write(str.replace(str.replace(str.rstrip(line),
> + "{", "@{"), "}", "@}") + "\n")
> + line = file.readline()
> + output.write(str.rstrip(line) + "\n")
> +
> +
> +def checkIndex(line):
> + # checkIndex - create an index entry for a PROCEDURE, TYPE, CONST or VAR.
> + global inVar, inType, inConst
> +
> + words = str.split(line)
> + procedure = ""
> + if (len(words) > 1) and (words[0] == "PROCEDURE"):
> + inConst = False
> + inType = False
> + inVar = False
> + if (words[1] == "__BUILTIN__") and (len(words) > 2):
> + procedure = words[2]
> + else:
> + procedure = words[1]
> + if (len(line) > 1) and (line[0:2] == "(*"):
> + inConst = False
> + inType = False
> + inVar = False
> + elif line == "VAR":
> + inConst = False
> + inVar = True
> + inType = False
> + return
> + elif line == "TYPE":
> + inConst = False
> + inType = True
> + inVar = False
> + return
> + elif line == "CONST":
> + inConst = True
> + inType = False
> + inVar = False
> + if inVar:
> + words = str.split(line, ",")
> + for word in words:
> + word = str.lstrip(word)
> + if word != "":
> + if str.find(word, ":") == -1:
> + output.write("@findex " + word + " (var)\n")
> + elif len(word) > 0:
> + var = str.split(word, ":")
> + if len(var) > 0:
> + output.write("@findex " + var[0] + " (var)\n")
> +
> + if inType:
> + words = str.lstrip(line)
> + if str.find(words, "=") != -1:
> + word = str.split(words, "=")
> + if (len(word[0]) > 0) and (word[0][0] != "_"):
> + output.write("@findex " + str.rstrip(word[0]) + " (type)\n")
> + else:
> + word = str.split(words)
> + if (len(word) > 1) and (word[1] == ";"):
> + # hidden type
> + if (len(word[0]) > 0) and (word[0][0] != "_"):
> + output.write("@findex " + str.rstrip(word[0]))
> + output.write(" (type)\n")
> + if inConst:
> + words = str.split(line, ";")
> + for word in words:
> + word = str.lstrip(word)
> + if word != "":
> + if str.find(word, "=") != -1:
> + var = str.split(word, "=")
> + if len(var) > 0:
> + output.write("@findex " + var[0] + " (const)\n")
> + if procedure != "":
> + name = str.split(procedure, "(")
> + if name[0] != "":
> + proc = name[0]
> + if proc[-1] == ";":
> + proc = proc[:-1]
> + if proc != "":
> + output.write("@findex " + proc + "\n")
> +
> +
> +def parseDefinition(dir, source, build, file, needPage):
> + # parseDefinition reads a definition module and creates
> + # indices for procedures, constants, variables and types.
> + output.write("\n")
> + with open(findFile(dir, build, source, file), "r") as f:
> + initState()
> + line = f.readline()
> + while (str.find(line, "(*") != -1):
> + removeInitialComments(f, line)
> + line = f.readline()
> + while (str.find(line, "DEFINITION") == -1):
> + line = f.readline()
> + output.write("@example\n")
> + output.write(str.rstrip(line) + "\n")
> + line = f.readline()
> + if len(str.rstrip(line)) == 0:
> + output.write("\n")
> + line = f.readline()
> + if (str.find(line, "(*") != -1):
> + removeFields(f, line)
> + else:
> + output.write(str.rstrip(line) + "\n")
> + else:
> + output.write(str.rstrip(line) + "\n")
> + line = f.readline()
> + while line:
> + line = str.rstrip(line)
> + checkIndex(line)
> + output.write(str.replace(str.replace(line, "{", "@{"), "}", "@}"))
> + output.write("\n")
> + line = f.readline()
> + output.write("@end example\n")
> + if needPage:
> + output.write("@page\n")
> +
> +
> +def parseModules(up, dir, build, source, listOfModules):
> + previous = ""
> + i = 0
> + if len(listOfModules) > 1:
> + nxt = dir + "/" + listOfModules[1][:-4]
> + else:
> + nxt = ""
> + while i < len(listOfModules):
> + emitNode(dir + "/" + listOfModules[i][:-4], nxt, previous, up)
> + emitSubSection(dir + "/" + listOfModules[i][:-4])
> + parseDefinition(dir, source, build, listOfModules[i], True)
> + output.write("\n")
> + previous = dir + "/" + listOfModules[i][:-4]
> + i = i + 1
> + if i+1 < len(listOfModules):
> + nxt = dir + "/" + listOfModules[i+1][:-4]
> + else:
> + nxt = ""
> +
> +
> +def doCat(name):
> + # doCat displays the contents of file, name, to stdout
> + with open(name, "r") as file:
> + line = file.readline()
> + while line:
> + output.write(str.rstrip(line) + "\n")
> + line = file.readline()
> +
> +
> +def moduleMenu(dir, build, source):
> + # moduleMenu generates a simple menu for all definition modules
> + # in dir
> + output.write("@menu\n")
> + listOfFiles = []
> + if os.path.exists(os.path.join(source, dir)):
> + listOfFiles += os.listdir(os.path.join(source, dir))
> + if os.path.exists(os.path.join(source, dir)):
> + listOfFiles += os.listdir(os.path.join(build, dir))
> + listOfFiles = list(dict.fromkeys(listOfFiles).keys())
> + listOfFiles.sort()
> + for file in listOfFiles:
> + if foundFile(dir, build, source, file):
> + if (len(file) > 4) and (file[-4:] == ".def"):
> + output.write("* " + dir + "/" + file[:-4] + "::" + file + "\n")
> + output.write("@end menu\n")
> + output.write("\n")
> +
> +
> +def checkDirectory(dir, build, source):
> + # checkDirectory - returns True if dir exists in either build or source.
> + if os.path.isdir(build) and os.path.exists(os.path.join(build, dir)):
> + return True
> + elif os.path.isdir(source) and os.path.exists(os.path.join(source, dir)):
> + return True
> + else:
> + return False
> +
> +
> +def foundFile(dir, build, source, file):
> + # foundFile return True if file is found in build/dir/file or
> + # source/dir/file.
> + name = os.path.join(os.path.join(build, dir), file)
> + if os.path.exists(name):
> + return True
> + name = os.path.join(os.path.join(source, dir), file)
> + if os.path.exists(name):
> + return True
> + return False
> +
> +
> +def findFile(dir, build, source, file):
> + # findFile return the path to file searching in build/dir/file
> + # first then source/dir/file.
> + name1 = os.path.join(os.path.join(build, dir), file)
> + if os.path.exists(name1):
> + return name1
> + name2 = os.path.join(os.path.join(source, dir), file)
> + if os.path.exists(name2):
> + return name2
> + sys.stderr.write("file cannot be found in either " + name1)
> + sys.stderr.write(" or " + name2 + "\n")
> + os.sys.exit(1)
> +
> +
> +def displayModules(up, dir, build, source):
> + # displayModules walks though the files in dir and parses
> + # definition modules and includes README.texi
> + if checkDirectory(dir, build, source):
> + if foundFile(dir, build, source, "README.texi"):
> + doCat(findFile(dir, build, source, "README.texi"))
> + moduleMenu(dir, build, source)
> + listOfFiles = []
> + if os.path.exists(os.path.join(source, dir)):
> + listOfFiles += os.listdir(os.path.join(source, dir))
> + if os.path.exists(os.path.join(source, dir)):
> + listOfFiles += os.listdir(os.path.join(build, dir))
> + listOfFiles = list(dict.fromkeys(listOfFiles).keys())
> + listOfFiles.sort()
> + listOfModules = []
> + for file in listOfFiles:
> + if foundFile(dir, build, source, file):
> + if (len(file) > 4) and (file[-4:] == ".def"):
> + listOfModules += [file]
> + listOfModules.sort()
> + parseModules(up, dir, build, source, listOfModules)
> + else:
> + line = "directory " + dir + " not found in either "
> + line += build + " or " + source
> + sys.stderr.write(line + "\n")
> +
> +
> +def displayCopyright():
> + output.write("@c Copyright (C) 2000-2022 Free Software Foundation, Inc.\n")
> + output.write("@c This file is part of GNU Modula-2.\n")
> + output.write("""
> +@c Permission is granted to copy, distribute and/or modify this document
> +@c under the terms of the GNU Free Documentation License, Version 1.2 or
> +@c any later version published by the Free Software Foundation.
> +""")
> +
> +
> +def collectArgs():
> + parser = argparse.ArgumentParser()
> + parser.add_argument("-v", "--verbose", help="generate progress messages",
> + action="store_true")
> + parser.add_argument("-b", "--builddir", help="set the build directory",
> + default=".", action="store")
> + parser.add_argument("-f", "--inputfile", help="set the input file",
> + default=None, action="store")
> + parser.add_argument("-o", "--outputfile", help="set the output file",
> + default=None, action="store")
> + parser.add_argument("-s", "--sourcedir", help="set the source directory",
> + default=".", action="store")
> + parser.add_argument("-t", "--texinfo",
> + help="generate texinfo documentation",
> + default=False, action="store_true")
> + parser.add_argument("-u", "--up", help="set the up node",
> + default="", action="store")
> + parser.add_argument("-x", "--sphinx", help="generate sphinx documentation",
> + default=False, action="store_true")
> + args = parser.parse_args()
> + return args
> +
> +
> +def handleFile():
> + if args.inputfile is None:
> + displayCopyright()
> + displayMenu()
> + displayLibraryClass()
> + else:
> + parseDefinition(".", args.sourcedir, args.builddir,
> + args.inputfile, False)
> +
> +
> +def main():
> + global args, output
> + args = collectArgs()
> + if args.outputfile is None:
> + output = sys.stdout
> + handleFile()
> + else:
> + with open(args.outputfile, "w") as output:
> + handleFile()
> +
> +
> +main()
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/mklink.c
> --- /dev/null 2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/mklink.c 2022-11-07 13:01:28.329424600 +0000
> @@ -0,0 +1,807 @@
> +/* mklink.c creates startup code and the link command line.
> +
> +Copyright (C) 2000-2022 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_finish (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_finish (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_finish (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");
> + }
> +}
Martin Liška <mliska@suse.cz> writes:
> 1) I would prefer using ' instead of ":
>
> $ flake8 ./gcc/m2/tools-src/tidydates.py
> ...
> ./gcc/m2/tools-src/tidydates.py:124:30: Q000 Double quotes found but single quotes preferred
> ./gcc/m2/tools-src/tidydates.py:127:27: Q000 Double quotes found but single quotes preferred
> ./gcc/m2/tools-src/tidydates.py:132:27: Q000 Double quotes found but single quotes preferred
> ./gcc/m2/tools-src/tidydates.py:133:33: Q000 Double quotes found but single quotes preferred
> ./gcc/m2/tools-src/tidydates.py:138:26: Q000 Double quotes found but single quotes preferred
> ./gcc/m2/tools-src/tidydates.py:143:28: Q000 Double quotes found but
> single quotes preferred
ah yes will switch the quotes character.
> 2) Python-names would be nicer:
>
> def writeTemplate(fo, magic, start, end, dates, contribution, summary,
> lic):
>
> def write_template(...)
agreed, will change
> 3) def hasExt(name, ext) - please use Path from pathlib
>
> 4) while (str.find(line, "(*") != -1):
>
> '(*' in line
> ? Similarly elsewhere.
>
> 5) str.find(line, ...)
>
> Use rather directly: line.find(...)
>
> 6) please use flake8:
> https://gcc.gnu.org/codingconventions.html#python
sure will do all above - I used flake8 but maybe the plugins weren't
enabled. I'll try flake8 on tumbleweed.
> Thanks,
> Martin
>
> P.S. I'm going to merge Sphinx branch this Wednesday, so then we should port your
> conversion scripts to emit .rst instead of .texi.
should be good - I'll complete the rst output in the scripts,
regards,
Gaius
On 11/8/22 14:22, Gaius Mulley wrote:
> Martin Liška <mliska@suse.cz> writes:
>
>> 1) I would prefer using ' instead of ":
>>
>> $ flake8 ./gcc/m2/tools-src/tidydates.py
>> ...
>> ./gcc/m2/tools-src/tidydates.py:124:30: Q000 Double quotes found but single quotes preferred
>> ./gcc/m2/tools-src/tidydates.py:127:27: Q000 Double quotes found but single quotes preferred
>> ./gcc/m2/tools-src/tidydates.py:132:27: Q000 Double quotes found but single quotes preferred
>> ./gcc/m2/tools-src/tidydates.py:133:33: Q000 Double quotes found but single quotes preferred
>> ./gcc/m2/tools-src/tidydates.py:138:26: Q000 Double quotes found but single quotes preferred
>> ./gcc/m2/tools-src/tidydates.py:143:28: Q000 Double quotes found but
>> single quotes preferred
>
> ah yes will switch the quotes character.
>
>> 2) Python-names would be nicer:
>>
>> def writeTemplate(fo, magic, start, end, dates, contribution, summary,
>> lic):
>>
>> def write_template(...)
>
> agreed, will change
>
>> 3) def hasExt(name, ext) - please use Path from pathlib
>>
>> 4) while (str.find(line, "(*") != -1):
>>
>> '(*' in line
>> ? Similarly elsewhere.
>>
>> 5) str.find(line, ...)
>>
>> Use rather directly: line.find(...)
>>
>> 6) please use flake8:
>> https://gcc.gnu.org/codingconventions.html#python
>
> sure will do all above - I used flake8 but maybe the plugins weren't
> enabled. I'll try flake8 on tumbleweed.
>
>> Thanks,
>> Martin
>>
>> P.S. I'm going to merge Sphinx branch this Wednesday, so then we should port your
>> conversion scripts to emit .rst instead of .texi.
>
> should be good - I'll complete the rst output in the scripts,
>
> regards,
> Gaius
Hi.
As you probably noticed, the Sphinx migration didn't go well. However, it's still up to you
if you want to use it or not for Modula 2. We have manuals like libgccjit, or Ada manuals
that use RST natively and provide exported .texi files.
Cheers and sorry for the troubles I caused.
Martin
Martin Liška <mliska@suse.cz> writes:
> On 11/8/22 14:22, Gaius Mulley wrote:
>> Martin Liška <mliska@suse.cz> writes:
>>
>> should be good - I'll complete the rst output in the scripts,
>
> Hi.
>
Hi Martin,
> As you probably noticed, the Sphinx migration didn't go well.
Yes, sorry to see this didn't happen. Thank you for your hard work and
I hope it can occur in the future.
> However, it's still up to you if you want to use it or not for Modula
> 2.
Once modula-2 is in master I'd like to revisit rst in devel/modula-2
along with analyzer patches and m2 generics. If successful then submit
patches in early stage 1.
> We have manuals like libgccjit, or Ada manuals
> that use RST natively and provide exported .texi files.
Ok thanks for the pointers, I will experiment with these build rhunes.
> Cheers and sorry for the troubles I caused.
No problem at all - the modula-2 scripts are now improved and cleaner
due to the port. Hopefully rst will happen sometime in the future,
regards,
Gaius
On 11/24/22 15:30, Gaius Mulley wrote:
> Martin Liška <mliska@suse.cz> writes:
>
>> On 11/8/22 14:22, Gaius Mulley wrote:
>>> Martin Liška <mliska@suse.cz> writes:
>>>
>>> should be good - I'll complete the rst output in the scripts,
>>
>> Hi.
>>
>
> Hi Martin,
>
>> As you probably noticed, the Sphinx migration didn't go well.
>
> Yes, sorry to see this didn't happen. Thank you for your hard work and
> I hope it can occur in the future.
Hi.
We'll see, it's not in my plans for near future.
>
>> However, it's still up to you if you want to use it or not for Modula
>> 2.
>
> Once modula-2 is in master I'd like to revisit rst in devel/modula-2
> along with analyzer patches and m2 generics. If successful then submit
> patches in early stage 1.
Sounds good.
>
>> We have manuals like libgccjit, or Ada manuals
>> that use RST natively and provide exported .texi files.
>
> Ok thanks for the pointers, I will experiment with these build rhunes.
>
>> Cheers and sorry for the troubles I caused.
>
> No problem at all - the modula-2 scripts are now improved and cleaner
> due to the port. Hopefully rst will happen sometime in the future,
Regards,
Martin
>
> regards,
> Gaius
On Thu, 2022-11-24 at 14:30 +0000, Gaius Mulley via Gcc-patches wrote:
> Martin Liška <mliska@suse.cz> writes:
>
> > On 11/8/22 14:22, Gaius Mulley wrote:
> > > Martin Liška <mliska@suse.cz> writes:
> > >
> > > should be good - I'll complete the rst output in the scripts,
> >
> > Hi.
> >
>
> Hi Martin,
>
> > As you probably noticed, the Sphinx migration didn't go well.
>
> Yes, sorry to see this didn't happen. Thank you for your hard work
> and
> I hope it can occur in the future.
Likewise, thanks for all your work on this.
>
> > However, it's still up to you if you want to use it or not for
> > Modula
> > 2.
>
> Once modula-2 is in master I'd like to revisit rst in devel/modula-2
> along with analyzer patches and m2 generics. If successful then
> submit
> patches in early stage 1.
Am I right in thinking the analyzer stuff would be an updated version
of the work you posted here:
https://gcc.gnu.org/pipermail/gcc-patches/2021-April/567953.html
?
Thanks
Dave
David Malcolm <dmalcolm@redhat.com> writes:
>> Once modula-2 is in master I'd like to revisit rst in devel/modula-2
>> along with analyzer patches and m2 generics. If successful then
>> submit
>> patches in early stage 1.
>
> Am I right in thinking the analyzer stuff would be an updated version
> of the work you posted here:
> https://gcc.gnu.org/pipermail/gcc-patches/2021-April/567953.html ?
Sure, yes.
regards,
Gaius
@@ -0,0 +1,166 @@
+#!/usr/bin/env python3
+
+# utility to tidy dates and detect lack of copyright.
+
+# Copyright (C) 2016-2022 Free Software Foundation, Inc.
+#
+# 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 COPYING. If not, write to the
+# Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+import os
+import sys
+import pathlib
+import shutil
+
+maxLineLength = 60
+
+COPYRIGHT = "Copyright (C)"
+
+
+def visitDir(directory, ext, func):
+ # visitDir - call func for each file below, dir, matching extension, ext.
+ listOfFiles = os.listdir(directory)
+ listOfFiles.sort()
+ for filename in listOfFiles:
+ path = pathlib.PurePath(filename)
+ full = os.path.join(directory, filename)
+ if path.is_file(full):
+ if path.suffix == ext:
+ func(full)
+ elif path.is_dir(full):
+ visitDir(full, ext, func)
+
+
+def isYear(year):
+ # isYear - returns True if, year, is legal.
+ if len(year) == 5:
+ year = year[:-1]
+ for c in year:
+ if not c.isdigit():
+ return False
+ return True
+
+
+def handleCopyright(outfile, lines, n, leader1, leader2):
+ # handleCopyright look for Copyright in the comment.
+ global maxLineLength
+ i = lines[n]
+ c = i.find(COPYRIGHT)+len(COPYRIGHT)
+ outfile.write(i[:c])
+ d = i[c:].split()
+ start = c
+ seenDate = True
+ years = []
+ while seenDate:
+ if d == []:
+ n += 1
+ i = lines[n]
+ d = i[2:].split()
+ else:
+ e = d[0]
+ punctuation = ""
+ if len(d) == 1:
+ d = []
+ else:
+ d = d[1:]
+ if c > maxLineLength:
+ outfile.write("\n")
+ outfile.write(leader1)
+ outfile.write(leader2)
+ outfile.write(" "*(start-2))
+ c = start
+ if isYear(e):
+ if (e[-1] == ".") or (e[-1] == ","):
+ punctuation = e[-1]
+ e = e[:-1]
+ else:
+ punctuation = ""
+ else:
+ seenDate = False
+ if seenDate:
+ if not (e in years):
+ c += len(e) + len(punctuation)
+ outfile.write(" ")
+ outfile.write(e)
+ outfile.write(punctuation)
+ years += [e]
+ else:
+ if start < c:
+ outfile.write("\n")
+ outfile.write(leader1)
+ outfile.write(leader2)
+ outfile.write(" "*(start-2))
+
+ outfile.write(" ")
+ outfile.write(e)
+ outfile.write(punctuation)
+ for w in d:
+ outfile.write(" ")
+ outfile.write(w)
+ outfile.write("\n")
+ return outfile, n+1
+
+
+def handleHeader(filename, leader1, leader2):
+ # handleHeader reads in the header of a file and inserts
+ # a line break around the Copyright dates.
+ print("------------------------------")
+ lines = open(filename, "r").readlines()
+ if len(lines) > 20:
+ with open("tmptidy", "w") as outfile:
+ n = 0
+ for i in lines:
+ if i.find("Copyright (C)") >= 0:
+ outfile, n = handleCopyright(outfile, lines,
+ n, leader1, leader2)
+ outfile.writelines(lines[n:])
+ outfile.close()
+ print("-> mv tmptidy", filename)
+ shutil.move("tmptidy", filename)
+ return
+ else:
+ outfile.write(lines[n])
+ n += 1
+ sys.stdout.write("%s:1:1 needs a Copyright notice..\n" % filename)
+
+
+def bashTidy(filename):
+ # bashTidy - tidy up dates using "#" comment
+ handleHeader(filename, "#", " ")
+
+
+def cTidy(filename):
+ # cTidy - tidy up dates using "/* */" comments
+ handleHeader(filename, " ", "*")
+
+
+def m2Tidy(filename):
+ # m2Tidy - tidy up dates using "(* *)" comments
+ handleHeader(filename, " ", " ")
+
+
+def main():
+ # main - for each file extension call the appropriate tidy routine.
+ visitDir(".", ".in", bashTidy)
+ visitDir(".", ".py", bashTidy)
+ visitDir(".", ".c", cTidy)
+ visitDir(".", ".h", cTidy)
+ visitDir(".", ".def", m2Tidy)
+ visitDir(".", ".mod", m2Tidy)
+
+
+main()
@@ -0,0 +1,548 @@
+#!/usr/bin/env python3
+#
+# boilerplate.py utility to rewrite the boilerplate with new dates.
+#
+# Copyright (C) 2018-2022 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/>.
+#
+
+import argparse
+import datetime
+import os
+import sys
+
+
+errorCount = 0
+seenFiles = []
+outputName = None
+
+ISO_COPYRIGHT = "Copyright ISO/IEC"
+COPYRIGHT = "Copyright (C)"
+GNU_PUBLIC_LICENSE = "GNU General Public License"
+GNU_LESSER_GENERAL = "GNU Lesser General"
+GCC_RUNTIME_LIB_EXC = "GCC Runtime Library Exception"
+VERSION_2_1 = "version 2.1"
+VERSION_2 = "version 2"
+VERSION_3 = "version 3"
+Licenses = {VERSION_2_1: "v2.1", VERSION_2: "v2", VERSION_3: "v3"}
+CONTRIBUTED_BY = "ontributed by"
+
+
+def printf(fmt, *args):
+ # printf - keeps C programmers happy :-)
+ print(str(fmt) % args, end=' ')
+
+
+def error(fmt, *args):
+ # error - issue an error message.
+ global errorCount
+
+ print(str(fmt) % args, end=' ')
+ errorCount += 1
+
+
+def haltOnError():
+ if errorCount > 0:
+ os.sys.exit(1)
+
+
+def basename(f):
+ b = f.split("/")
+ return b[-1]
+
+
+def analyseComment(text, f):
+ # analyseComment determine the license from the top comment.
+ start_date, end_date = None, None
+ contribution, summary, lic = None, None, None
+ if text.find(ISO_COPYRIGHT) > 0:
+ lic = "BSISO"
+ now = datetime.datetime.now()
+ for d in range(1984, now.year+1):
+ if text.find(str(d)) > 0:
+ if start_date is None:
+ start_date = str(d)
+ end_date = str(d)
+ return start_date, end_date, "", "", lic
+ elif text.find(COPYRIGHT) > 0:
+ if text.find(GNU_PUBLIC_LICENSE) > 0:
+ lic = "GPL"
+ elif text.find(GNU_LESSER_GENERAL) > 0:
+ lic = "LGPL"
+ for license in Licenses.keys():
+ if text.find(license) > 0:
+ lic += Licenses[license]
+ if text.find(GCC_RUNTIME_LIB_EXC) > 0:
+ lic += "x"
+ now = datetime.datetime.now()
+ for d in range(1984, now.year+1):
+ if text.find(str(d)) > 0:
+ if start_date is None:
+ start_date = str(d)
+ end_date = str(d)
+ if text.find(CONTRIBUTED_BY) > 0:
+ i = text.find(CONTRIBUTED_BY)
+ i += len(CONTRIBUTED_BY)
+ j = text.index(". ", i)
+ contribution = text[i:j]
+ if text.find(basename(f)) > 0:
+ i = text.find(basename(f))
+ j = text.find(". ", i)
+ if j < 0:
+ error('summary of the file does not finish with a "."')
+ summary = text[i:]
+ else:
+ summary = text[i:j]
+ return start_date, end_date, contribution, summary, lic
+
+
+def analyseHeaderWithoutTerminator(f, start):
+ text = ""
+ for count, l in enumerate(open(f).readlines()):
+ parts = l.split(start)
+ if len(parts) > 1:
+ line = start.join(parts[1:])
+ line = line.strip()
+ text += " "
+ text += line
+ elif (l.rstrip() != "") and (len(parts[0]) > 0):
+ return analyseComment(text, f), count
+ return [None, None, None, None, None], 0
+
+
+def analyseHeaderWithTerminator(f, start, end):
+ inComment = False
+ text = ""
+ for count, line in enumerate(open(f).readlines()):
+ while line != "":
+ line = line.strip()
+ if inComment:
+ text += " "
+ pos = line.find(end)
+ if pos >= 0:
+ text += line[:pos]
+ line = line[pos:]
+ inComment = False
+ else:
+ text += line
+ line = ""
+ else:
+ pos = line.find(start)
+ if (pos >= 0) and (len(line) > len(start)):
+ before = line[:pos].strip()
+ if before != "":
+ return analyseComment(text, f), count
+ line = line[pos + len(start):]
+ inComment = True
+ elif (line != "") and (line == end):
+ line = ""
+ else:
+ return analyseComment(text, f), count
+ return [None, None, None, None, None], 0
+
+
+def analyseHeader(f, start, end):
+ # analyseHeader -
+ if end is None:
+ return analyseHeaderWithoutTerminator(f, start)
+ else:
+ return analyseHeaderWithTerminator(f, start, end)
+
+
+def addStop(sentence):
+ # addStop - add a full stop to a sentance.
+ if sentence is None:
+ return None
+ sentence = sentence.rstrip()
+ if (len(sentence) > 0) and (sentence[-1] != "."):
+ return sentence + "."
+ return sentence
+
+
+GPLv3 = """
+%s
+
+Copyright (C) %s Free Software Foundation, Inc.
+Contributed by %s
+
+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/>.
+"""
+
+GPLv3x = """
+%s
+
+Copyright (C) %s Free Software Foundation, Inc.
+Contributed by %s
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+<http://www.gnu.org/licenses/>.
+"""
+
+LGPLv3 = """
+%s
+
+Copyright (C) %s Free Software Foundation, Inc.
+Contributed by %s
+
+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 Lesser General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with GNU Modula-2. If not, see <https://www.gnu.org/licenses/>.
+"""
+
+BSISO = """
+Library module defined by the International Standard
+ Information technology - programming languages
+ BS ISO/IEC 10514-1:1996E Part 1: Modula-2, Base Language.
+
+ Copyright ISO/IEC (International Organization for Standardization
+ and International Electrotechnical Commission) %s.
+
+ It may be freely copied for the purpose of implementation (see page
+ 707 of the Information technology - Programming languages Part 1:
+ Modula-2, Base Language. BS ISO/IEC 10514-1:1996).
+"""
+
+templates = {}
+templates["GPLv3"] = GPLv3
+templates["GPLv3x"] = GPLv3x
+templates["LGPLv3"] = LGPLv3
+templates["LGPLv2.1"] = LGPLv3
+templates["BSISO"] = BSISO
+
+
+def writeTemplate(fo, magic, start, end, dates, contribution, summary, lic):
+ if lic in templates:
+ if lic == "BSISO":
+ # non gpl but freely distributed for the implementation of a
+ # compiler
+ text = templates[lic] % (dates)
+ text = text.rstrip()
+ else:
+ summary = summary.lstrip()
+ contribution = contribution.lstrip()
+ summary = addStop(summary)
+ contribution = addStop(contribution)
+ if magic is not None:
+ fo.write(magic)
+ fo.write("\n")
+ text = templates[lic] % (summary, dates, contribution)
+ text = text.rstrip()
+ if end is None:
+ text = text.split("\n")
+ for line in text:
+ fo.write(start)
+ fo.write(" ")
+ fo.write(line)
+ fo.write("\n")
+ else:
+ text = text.lstrip()
+ fo.write(start)
+ fo.write(" ")
+ fo.write(text)
+ fo.write(" ")
+ fo.write(end)
+ fo.write("\n")
+ # add a blank comment line for a script for eye candy.
+ if start == "#" and end is None:
+ fo.write(start)
+ fo.write("\n")
+ else:
+ error("no template found for: %s\n", lic)
+ os.sys.exit(1)
+ return fo
+
+
+def writeBoilerPlate(fo, magic, start, end,
+ start_date, end_date, contribution, summary, gpl):
+ if start_date == end_date:
+ dates = start_date
+ else:
+ dates = "%s-%s" % (start_date, end_date)
+ return writeTemplate(fo, magic, start, end,
+ dates, contribution, summary, gpl)
+
+
+def rewriteFile(f, magic, start, end, start_date, end_date,
+ contribution, summary, gpl, lines):
+ text = "".join(open(f).readlines()[lines:])
+ if outputName == "-":
+ fo = sys.stdout
+ else:
+ fo = open(f, "w")
+ fo = writeBoilerPlate(fo, magic, start, end,
+ start_date, end_date, contribution, summary, gpl)
+ fo.write(text)
+ fo.flush()
+ if outputName != "-":
+ fo.close()
+
+
+def handleHeader(f, magic, start, end):
+ # handleHeader keep reading lines of file, f, looking for start, end
+ # sequences and comments inside. The comments are checked for:
+ # date, contribution, summary
+ global errorCount
+
+ errorCount = 0
+ [start_date, end_date,
+ contribution, summary, lic], lines = analyseHeader(f, start, end)
+ if lic is None:
+ error("%s:1:no GPL found at the top of the file\n", f)
+ else:
+ if args.verbose:
+ printf("copyright: %s\n", lic)
+ if (start_date is not None) and (end_date is not None):
+ if start_date == end_date:
+ printf("dates = %s\n", start_date)
+ else:
+ printf("dates = %s-%s\n", start_date, end_date)
+ if summary is not None:
+ printf("summary: %s\n", summary)
+ if contribution is not None:
+ printf("contribution: %s\n", contribution)
+ if start_date is None:
+ error("%s:1:no date found in the GPL at the top of the file\n", f)
+ if args.contribution is None:
+ if contribution == "":
+ error("%s:1:no contribution found in the " +
+ "GPL at the top of the file\n", f)
+ else:
+ contribution = args.contribution
+ if summary is None:
+ if args.summary == "":
+ error("%s:1:no single line summary found in the " +
+ "GPL at the top of the file\n", f)
+ else:
+ summary = args.summary
+ if errorCount == 0:
+ now = datetime.datetime.now()
+ if args.no:
+ print(f, "suppressing change as requested: %s-%s %s"
+ % (start_date, end_date, lic))
+ else:
+ if lic == "BSISO":
+ # don't change the BS ISO license!
+ pass
+ elif args.extensions:
+ lic = "GPLv3x"
+ elif args.gpl3:
+ lic = "GPLv3"
+ rewriteFile(f, magic, start, end, start_date,
+ str(now.year), contribution, summary, lic, lines)
+ else:
+ printf("too many errors, no modifications will occur\n")
+
+
+def bashTidy(f):
+ # bashTidy tidy up dates using '#' comment
+ handleHeader(f, "#!/bin/bash", "#", None)
+
+
+def pythonTidy(f):
+ # pythonTidy tidy up dates using '#' comment
+ handleHeader(f, "#!/usr/bin/env python3", '#', None)
+
+
+def bnfTidy(f):
+ # bnfTidy tidy up dates using '--' comment
+ handleHeader(f, None, '--', None)
+
+
+def cTidy(f):
+ # cTidy tidy up dates using '/* */' comments
+ handleHeader(f, None, '/*', '*/')
+
+
+def m2Tidy(f):
+ # m2Tidy tidy up dates using '(* *)' comments
+ handleHeader(f, None, '(*', '*)')
+
+
+def inTidy(f):
+ # inTidy tidy up dates using '#' as a comment and check
+ # the first line for magic number.
+ first = open(f).readlines()[0]
+ if (len(first) > 0) and (first[:2] == "#!"):
+ # magic number found, use this
+ handleHeader(f, first, "#", None)
+ else:
+ handleHeader(f, None, "#", None)
+
+
+def doVisit(args, dirname, names):
+ # doVisit helper function to call func on every extension file.
+ global outputName
+ func, extension = args
+ for f in names:
+ if len(f) > len(extension) and f[-len(extension):] == extension:
+ outputName = f
+ func(os.path.join(dirname, f))
+
+
+def visitDir(startDir, ext, func):
+ # visitDir call func for each file in startDir which has ext.
+ global outputName, seenFiles
+ for dirName, subdirList, fileList in os.walk(startDir):
+ for fname in fileList:
+ if (len(fname) > len(ext)) and (fname[-len(ext):] == ext):
+ fullpath = os.path.join(dirName, fname)
+ outputName = fullpath
+ if not (fullpath in seenFiles):
+ seenFiles += [fullpath]
+ func(fullpath)
+ # Remove the first entry in the list of sub-directories
+ # if there are any sub-directories present
+ if len(subdirList) > 0:
+ del subdirList[0]
+
+
+def findFiles():
+ # findFiles for each file extension call the appropriate tidy routine.
+ visitDir(args.recursive, '.h.in', cTidy)
+ visitDir(args.recursive, '.in', inTidy)
+ visitDir(args.recursive, '.sh', inTidy)
+ visitDir(args.recursive, '.py', pythonTidy)
+ visitDir(args.recursive, '.c', cTidy)
+ visitDir(args.recursive, '.h', cTidy)
+ visitDir(args.recursive, '.cc', cTidy)
+ visitDir(args.recursive, '.def', m2Tidy)
+ visitDir(args.recursive, '.mod', m2Tidy)
+ visitDir(args.recursive, '.bnf', bnfTidy)
+
+
+def handleArguments():
+ # handleArguments create and return the args object.
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-c", "--contribution",
+ help="set the contribution string " +
+ "at the top of the file.",
+ default="", action="store")
+ parser.add_argument("-d", "--debug", help="turn on internal debugging.",
+ default=False, action="store_true")
+ parser.add_argument("-f", "--force",
+ help="force a check to insist that the " +
+ "contribution, summary and GPL exist.",
+ default=False, action="store_true")
+ parser.add_argument("-g", "--gplv3", help="change to GPLv3",
+ default=False, action="store_true")
+ parser.add_argument("-o", "--outputfile", help="set the output file",
+ default="-", action="store")
+ parser.add_argument("-r", "--recursive",
+ help="recusively scan directory for known file " +
+ "extensions (.def, .mod, .c, .h, .py, .in, .sh).",
+ default=".", action="store")
+ parser.add_argument("-s", "--summary",
+ help="set the summary line for the file.",
+ default=None, action="store")
+ parser.add_argument("-u", "--update", help="update all dates.",
+ default=False, action="store_true")
+ parser.add_argument("-v", "--verbose",
+ help="display copyright, " +
+ "date and contribution messages",
+ action="store_true")
+ parser.add_argument("-x", "--extensions",
+ help="change to GPLv3 with GCC runtime extensions.",
+ default=False, action="store_true")
+ parser.add_argument("-N", "--no",
+ help="do not modify any file.",
+ action="store_true")
+ args = parser.parse_args()
+ return args
+
+
+def hasExt(name, ext):
+ # hasExt return True if, name, ends with, ext.
+ if len(name) > len(ext):
+ return name[-len(ext):] == ext
+ return False
+
+
+def singleFile(name):
+ # singleFile scan the single file for a GPL boilerplate which
+ # has a GPL, contribution field and a summary heading.
+ if hasExt(name, ".def") or hasExt(name, ".mod"):
+ m2Tidy(name)
+ elif hasExt(name, ".h") or hasExt(name, ".c") or hasExt(name, ".cc"):
+ cTidy(name)
+ elif hasExt(name, ".in"):
+ inTidy(name)
+ elif hasExt(name, ".sh"):
+ inTidy(name) # uses magic number for actual sh/bash
+ elif hasExt(name, ".py"):
+ pythonTidy(name)
+
+
+def main():
+ # main - handleArguments and then find source files.
+ global args, outputName
+ args = handleArguments()
+ outputName = args.outputfile
+ if args.recursive:
+ findFiles()
+ elif args.inputfile is None:
+ print("an input file must be specified on the command line")
+ else:
+ singleFile(args.inputfile)
+ haltOnError()
+
+
+main()
@@ -0,0 +1,289 @@
+#!/bin/sh
+
+# Copyright (C) 2000-2022 Free Software Foundation, Inc.
+# 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 COPYING. If not, write to the
+# Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+# builds the pg.bnf from ppg.mod
+# usage buildpg ppg.mod destination [-e]
+# -e build without error recovery
+#
+PPGSRC=$1
+PPGDST=$2
+
+includeNonErrorChecking () {
+ sed -e "1,/StartNonErrorChecking/d" < $PPGSRC |\
+ sed -e "1,/EndNonErrorChecking/!d"
+}
+
+includeErrorChecking () {
+ sed -e "1,/StartErrorChecking/d" < $PPGSRC |\
+ sed -e "1,/EndErrorChecking/!d"
+}
+
+
+echo "% module" $PPGDST "begin"
+sed -e "1,/% declaration/!d" < $PPGSRC | sed -e "s/ppg/${PPGDST}/g"
+
+echo "% declaration" $PPGDST "begin"
+
+sed -e "1,/% declaration/d" < $PPGSRC | sed -e "1,/% rules/!d" | sed -e "s/ppg/${PPGDST}/g"
+
+if [ "$3" = "-e" ] ; then
+ includeNonErrorChecking
+ echo "% module" $PPGDST "end"
+ sed -e "1,/% module pg end/d" < $PPGSRC | sed -e "s/ppg/${PPGDST}/g"
+else
+ includeErrorChecking
+ echo "% module" $PPGDST "end"
+ sed -e "1,/% module pg end/d" < $PPGSRC | sed -e "s/ppg/${PPGDST}/g" |\
+ sed -e "s/WasNoError := Main() ;/Main({eoftok}) ;/"
+fi
+
+echo "% rules"
+
+cat << EOFEOF | sed -e "s/ppg/${PPGDST}/g"
+error 'WarnError' 'WarnString'
+tokenfunc 'GetCurrentTokenType()'
+
+token 'identifier' identtok -- internal token
+token 'literal' literaltok
+token '%' codetok
+token ':=' lbecomestok
+token '=:' rbecomestok
+token '|' bartok
+token '[' lsparatok
+token ']' rsparatok
+token '{' lcparatok -- left curly para
+token '}' rcparatok -- right curly para
+token '(' lparatok
+token ')' rparatok
+token "error" errortok
+token "tokenfunc" tfunctok
+token "symfunc" symfunctok
+token '"' dquotetok
+token "'" squotetok
+token "module" moduletok
+token "begin" begintok
+token "rules" rulestok
+token "end" endtok
+token '<' lesstok
+token '>' gretok
+token "token" tokentok
+token "special" specialtok
+token "first" firsttok
+token "follow" followtok
+token "BNF" BNFtok
+token "FNB" FNBtok
+token "declaration" declarationtok
+token "epsilon" epsilontok
+token '' eoftok -- internal token
+
+special Ident first { < identtok > } follow { }
+special Modula2Code first { } follow { '%' }
+special StartModName first { < identtok > } follow { }
+special EndModName first { < identtok > } follow { }
+special DoDeclaration first { < identtok > } follow { }
+special CollectLiteral first { < literaltok > } follow { }
+special CollectTok first { < identtok > } follow { }
+special DefineToken first { < identtok > } follow { }
+
+BNF
+
+Rules := "%" "rules" { Defs } ExtBNF =:
+
+Special := Ident
+ % VAR p: ProductionDesc ; %
+ % p := NewProduction() ;
+ p^.statement := NewStatement() ;
+ p^.statement^.followinfo^.calcfollow := TRUE ;
+ p^.statement^.followinfo^.epsilon := false ;
+ p^.statement^.followinfo^.reachend := false ;
+ p^.statement^.ident := CurrentIdent ;
+ p^.statement^.expr := NIL ;
+ p^.firstsolved := TRUE ;
+ p^.followinfo^.calcfollow := TRUE ;
+ p^.followinfo^.epsilon := false ;
+ p^.followinfo^.reachend := false %
+ First Follow [ "epsilon" % p^.statement^.followinfo^.epsilon := true ; (* these are not used - but they are displayed when debugging *)
+ p^.statement^.followinfo^.reachend := true ;
+ p^.followinfo^.epsilon := true ;
+ p^.followinfo^.reachend := true
+ % ]
+ [ Literal % p^.description := LastLiteral % ]
+ =:
+
+Factor := "%" Modula2Code "%" |
+ Ident % WITH CurrentFactor^ DO
+ type := id ;
+ ident := CurrentIdent
+ END ; % |
+ Literal % WITH CurrentFactor^ DO
+ type := lit ;
+ string := LastLiteral ;
+ IF GetSymKey(Aliases, LastLiteral)=NulName
+ THEN
+ WarnError1('no token defined for literal %s', LastLiteral)
+ END
+ END ; % |
+ "{" % WITH CurrentFactor^ DO
+ type := mult ;
+ expr := NewExpression() ;
+ CurrentExpression := expr ;
+ END ; %
+ Expression "}" |
+ "[" % WITH CurrentFactor^ DO
+ type := opt ;
+ expr := NewExpression() ;
+ CurrentExpression := expr ;
+ END ; %
+ Expression "]" |
+ "(" % WITH CurrentFactor^ DO
+ type := sub ;
+ expr := NewExpression() ;
+ CurrentExpression := expr ;
+ END ; %
+ Expression ")" =:
+
+Statement := % VAR i: IdentDesc ; %
+ Ident
+ % VAR p: ProductionDesc ; %
+ % p := FindDefinition(CurrentIdent^.name) ;
+ IF p=NIL
+ THEN
+ p := NewProduction()
+ ELSE
+ IF NOT ((p^.statement=NIL) OR (p^.statement^.expr=NIL))
+ THEN
+ WarnError1('already declared rule %s', CurrentIdent^.name)
+ END
+ END ;
+ i := CurrentIdent ; %
+ ":="
+ % VAR e: ExpressionDesc ; %
+ % e := NewExpression() ;
+ CurrentExpression := e ; %
+ % VAR s: StatementDesc ; %
+ % s := NewStatement() ;
+ WITH s^ DO
+ ident := i ;
+ expr := e
+ END ; %
+ Expression
+ % p^.statement := s ; %
+ "=:" =:
+
+Defs := "special" Special | "token" Token | "error" ErrorProcedures |
+ "tokenfunc" TokenProcedure | "symfunc" SymProcedure =:
+ExtBNF := "BNF" { Production } "FNB" =:
+Main := Header Decls Footer Rules =:
+Header := "%" "module" StartModName =:
+Decls := "%" "declaration" DoDeclaration =:
+Footer := "%" "module" EndModName =:
+
+First := "first" "{" { LitOrTokenOrIdent
+ % WITH CurrentSetDesc^ DO
+ next := TailProduction^.first ;
+ END ;
+ TailProduction^.first := CurrentSetDesc
+ %
+ } "}" =:
+Follow := "follow" "{" { LitOrTokenOrIdent
+ % WITH CurrentSetDesc^ DO
+ next := TailProduction^.followinfo^.follow ;
+ END ;
+ TailProduction^.followinfo^.follow := CurrentSetDesc
+ %
+ } "}" =:
+LitOrTokenOrIdent := Literal % CurrentSetDesc := NewSetDesc() ;
+ WITH CurrentSetDesc^ DO
+ type := litel ;
+ string := LastLiteral ;
+ END ;
+ % |
+ '<' CollectTok '>' |
+ Ident % CurrentSetDesc := NewSetDesc() ;
+ WITH CurrentSetDesc^ DO
+ type := idel ;
+ ident := CurrentIdent ;
+ END ;
+ % =:
+
+Literal := '"' CollectLiteral '"' |
+ "'" CollectLiteral "'" =:
+
+CollectTok := % CurrentSetDesc := NewSetDesc() ;
+ WITH CurrentSetDesc^ DO
+ type := tokel ;
+ string := GetCurrentToken() ;
+ END ;
+ IF NOT ContainsSymKey(Values, GetCurrentToken())
+ THEN
+ AddEntry(Values, GetCurrentToken(), LargestValue) ;
+ AddEntry(ReverseValues, Name(LargestValue), GetCurrentToken()) ;
+ AddEntry(Aliases, GetCurrentToken(), GetCurrentToken()) ;
+ AddEntry(ReverseAliases, GetCurrentToken(), GetCurrentToken()) ;
+ INC(LargestValue)
+ END ;
+ AdvanceToken() ; % =:
+
+CollectLiteral := % LastLiteral := GetCurrentToken() ;
+ AdvanceToken ; % =:
+
+DefineToken := % AddEntry(Aliases, LastLiteral, GetCurrentToken()) ;
+ AddEntry(ReverseAliases, GetCurrentToken(), LastLiteral) ;
+ AddEntry(Values, GetCurrentToken(), LargestValue) ;
+ AddEntry(ReverseValues, Name(LargestValue), GetCurrentToken()) ;
+ INC(LargestValue) ;
+ AdvanceToken ; % =:
+
+Token := Literal DefineToken =:
+
+ErrorProcedures := Literal % ErrorProcArray := LastLiteral %
+ Literal % ErrorProcString := LastLiteral % =:
+TokenProcedure := Literal % TokenTypeProc := LastLiteral % =:
+SymProcedure := Literal % SymIsProc := LastLiteral % =:
+
+Production := Statement =:
+Expression := % VAR t1, t2: TermDesc ;
+ e : ExpressionDesc ; %
+ % e := CurrentExpression ;
+ t1 := NewTerm() ;
+ CurrentTerm := t1 ; %
+ Term % e^.term := t1 ; %
+ { "|" % t2 := NewTerm() ;
+ CurrentTerm := t2 %
+ Term % t1^.next := t2 ;
+ t1 := t2 % } =:
+
+Term := % VAR t1: TermDesc ; f1, f2: FactorDesc ; %
+ % CurrentFactor := NewFactor() ;
+ f1 := CurrentFactor ;
+ t1 := CurrentTerm ; %
+ Factor % t1^.factor := f1 ;
+ f2 := NewFactor() ;
+ CurrentFactor := f2 %
+ { Factor % f1^.next := f2 ;
+ f1 := f2 ;
+ f2 := NewFactor() ;
+ CurrentFactor := f2 ; % }
+ =:
+
+FNB
+
+EOFEOF
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+# calcpath return a path which is $1/$2/$3 when $2 is relative and $2/$3 if absolute.
+
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+# Contributed by Gaius Mulley <gaius.mulley@southwales.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 gm2; see the file COPYING. If not, write to the Free Software
+# Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *)
+
+
+Usage () {
+ echo "Usage: calcpath pathcomponent1 pathcomponent2 subdir"
+ echo -n " if pathcomponent1 is relative then pathcomponent1/pathcomponet2/subdir is"
+ echo " returned"
+ echo " otherwise pathcomponet2/subdir is returned"
+ echo " the path is checked for legality in subdir."
+}
+
+
+if [ $# -eq 3 ]; then
+ if [ "$(echo $2 | cut -b 1)" = "." ] ; then
+ # relative path
+ the_path=$1/$2/$3
+ else
+ the_path=$2/$3
+ fi
+ cd $3
+ if realpath ${the_path} > /dev/null ; then
+ echo ${the_path}
+ else
+ echo "calcpath: error ${the_path} is not a valid path in subdirectory $3" 1>&2
+ exit 1
+ fi
+else
+ Usage
+ exit 1
+fi
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# makeSystem creates a target SYSTEM.def using the appropriate dialect template.
+
+# Copyright (C) 2008-2022 Free Software Foundation, Inc.
+# Contributed by Gaius Mulley <gaius.mulley@southwales.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 gm2; see the file COPYING. If not, write to the Free Software
+# Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *)
+
+
+Usage () {
+ echo "Usage: makesystem dialectflag SYSTEM.def SYSTEM.mod librarypath compiler"
+}
+
+if [ $# -lt 6 ] ; then
+ Usage
+ exit 1
+fi
+
+DIALECT=$1
+SYSTEMDEF=$2
+SYSTEMMOD=$3
+LIBRARY=$4
+COMPILER=$5
+OUTPUTFILE=$6
+
+if [ "$COMPILER" = "" ] ; then
+ echo "parameter 5 of makeSystem is incorrect, GM2_FOR_TARGET was unset"
+ exit 1
+fi
+
+if [ "$DIALECT" != "-fiso" -a "$DIALECT" != "-fpim" ] ; then
+ Usage
+ echo "dialect must be -fiso or -fpim"
+ exit 1
+fi
+
+displayExportedTypes () {
+ n=1
+ c=0
+ for i in ${types} ; do
+ if [ $n -eq 1 ] ; then
+ n=0
+ echo -n " " >> ${OUTPUTFILE}
+ fi
+ echo -n "$i, " >> ${OUTPUTFILE}
+ if [ $c -eq 4 ] ; then
+ echo " " >> ${OUTPUTFILE}
+ n=1
+ c=0
+ fi
+ c=`expr $c + 1`
+ done
+ echo " " >> ${OUTPUTFILE}
+}
+
+displayBuiltinTypes () {
+ for i in ${types} ; do
+ echo " $i ; " >> ${OUTPUTFILE}
+ done
+}
+
+displayStart () {
+ sed -e "1,/@SYSTEM_DATATYPES@/!d" < ${SYSTEMDEF} | \
+ sed -e "/@SYSTEM_DATATYPES@/d" >> ${OUTPUTFILE}
+}
+
+displayMiddle () {
+ sed -e "1,/@SYSTEM_DATATYPES@/d" < ${SYSTEMDEF} | \
+ sed -e "1,/@SYSTEM_TYPES@/!d" | \
+ sed -e "/@SYSTEM_TYPES@/d" >> ${OUTPUTFILE}
+}
+
+displayEnd () {
+ sed -e "1,/@SYSTEM_TYPES@/d" < ${SYSTEMDEF} >> ${OUTPUTFILE}
+}
+
+MINIMAL="-fno-scaffold-main -fno-scaffold-dynamic -fno-scaffold-static -fno-m2-plugin"
+
+rm -f ${OUTPUTFILE}
+if ${COMPILER} ${DIALECT} ${LIBRARY} ${MINIMAL} \
+ -c -fdump-system-exports ${SYSTEMMOD} -o /dev/null 2>&1 > /dev/null ; then
+ types=`${COMPILER} ${DIALECT} ${LIBRARY} ${MINIMAL} -fno-m2-plugin -c -fdump-system-exports ${SYSTEMMOD} -o /dev/null | cut -f5 -d' '`
+ touch ${OUTPUTFILE}
+ displayStart
+ displayExportedTypes
+ displayMiddle
+ displayBuiltinTypes
+ displayEnd
+else
+ ${COMPILER} ${DIALECT} ${LIBRARY} ${MINIMAL} \
+ -c -fdump-system-exports ${SYSTEMMOD} -o /dev/null
+ exit $?
+fi
@@ -0,0 +1,3 @@
+This directory contains miscellaneous scripts and programs (mklink.c)
+to allow for bootstrap linking and creating library documentation from
+sources.
\ No newline at end of file
@@ -0,0 +1,419 @@
+#!/usr/bin/env python3
+
+# def2doc.py creates texi library documentation for all exported procedures.
+# Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
+
+# Copyright (C) 2000-2022 Free Software Foundation, Inc.
+# 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 COPYING. If not, write to the
+# Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+import argparse
+import os
+import sys
+
+BaseLibs = ["gm2-libs", "Base libraries", "Basic M2F compatible libraries"]
+
+PIMLogDesc = "PIM and Logitech 3.0 compatible libraries"
+PIMLog = ["gm2-libs-pim", "PIM and Logitech 3.0 Compatible", PIMLogDesc]
+PIMCorDesc = "PIM compatible process support"
+PIMCor = ["gm2-libs-coroutines", "PIM coroutine support", PIMCorDesc]
+ISOLibs = ["gm2-libs-iso", "M2 ISO Libraries", "ISO defined libraries"]
+
+libraryClassifications = [BaseLibs, PIMLog, PIMCor, ISOLibs]
+
+
+def initState():
+ global inVar, inType, inConst
+ inVar, inType, inConst = False, False, False
+
+
+def emitNode(name, nxt, previous, up):
+ if args.texinfo:
+ output.write("@node " + name + ", " + nxt + ", ")
+ output.write(previous + ", " + up + "\n")
+ elif args.sphinx:
+ output.write("@c @node " + name + ", " + nxt + ", ")
+ output.write(previous + ", " + up + "\n")
+
+
+def emitSection(name):
+ if args.texinfo:
+ output.write("@section " + name + "\n")
+ elif args.sphinx:
+ output.write(name + "\n")
+ output.write("=" * len(name) + "\n")
+
+
+def emitSubSection(name):
+ if args.texinfo:
+ output.write("@subsection " + name + "\n")
+ elif args.sphinx:
+ output.write(name + "\n")
+ output.write("-" * len(name) + "\n")
+
+
+def displayLibraryClass():
+ # displayLibraryClass displays a node for a library directory and invokes
+ # a routine to summarize each module.
+ global args
+ previous = ""
+ nxt = libraryClassifications[1][1]
+ i = 0
+ lib = libraryClassifications[i]
+ while True:
+ emitNode(lib[1], nxt, previous, args.up)
+ emitSection(lib[1])
+ output.write("\n")
+ displayModules(lib[1], lib[0], args.builddir, args.sourcedir)
+ output.write("\n")
+ output.write("@c " + "-" * 60 + "\n")
+ previous = lib[1]
+ i += 1
+ if i == len(libraryClassifications):
+ break
+ lib = libraryClassifications[i]
+ if i+1 == len(libraryClassifications):
+ nxt = ""
+ else:
+ nxt = libraryClassifications[i+1][1]
+
+
+def displayMenu():
+ # displayMenu displays the top level menu for library documentation.
+ output.write("@menu\n")
+ for lib in libraryClassifications:
+ output.write("* " + lib[1] + "::" + lib[2] + "\n")
+ output.write("@end menu\n")
+ output.write("\n")
+ output.write("@c " + "=" * 60 + "\n")
+ output.write("\n")
+
+
+def removeInitialComments(file, line):
+ # removeInitialComments removes any (* *) at the top
+ # of the definition module.
+ while (str.find(line, "*)") == -1):
+ line = file.readline()
+
+
+def removeableField(line):
+ # removeableField - returns True if a comment field should be removed
+ # from the definition module.
+ field_list = ["Author", "Last edit", "LastEdit", "Last update",
+ "Date", "Title", "Revision"]
+ for field in field_list:
+ if (str.find(line, field) != -1) and (str.find(line, ":") != -1):
+ return True
+ ignore_list = ["System", "SYSTEM"]
+ for ignore_field in ignore_list:
+ if str.find(line, ignore_field) != -1:
+ if str.find(line, ":") != -1:
+ if str.find(line, "Description:") == -1:
+ return True
+ return False
+
+
+def removeFields(file, line):
+ # removeFields removes Author/Date/Last edit/SYSTEM/Revision
+ # fields from a comment within the start of a definition module.
+ while (str.find(line, "*)") == -1):
+ if not removeableField(line):
+ output.write(str.replace(str.replace(str.rstrip(line),
+ "{", "@{"), "}", "@}") + "\n")
+ line = file.readline()
+ output.write(str.rstrip(line) + "\n")
+
+
+def checkIndex(line):
+ # checkIndex - create an index entry for a PROCEDURE, TYPE, CONST or VAR.
+ global inVar, inType, inConst
+
+ words = str.split(line)
+ procedure = ""
+ if (len(words) > 1) and (words[0] == "PROCEDURE"):
+ inConst = False
+ inType = False
+ inVar = False
+ if (words[1] == "__BUILTIN__") and (len(words) > 2):
+ procedure = words[2]
+ else:
+ procedure = words[1]
+ if (len(line) > 1) and (line[0:2] == "(*"):
+ inConst = False
+ inType = False
+ inVar = False
+ elif line == "VAR":
+ inConst = False
+ inVar = True
+ inType = False
+ return
+ elif line == "TYPE":
+ inConst = False
+ inType = True
+ inVar = False
+ return
+ elif line == "CONST":
+ inConst = True
+ inType = False
+ inVar = False
+ if inVar:
+ words = str.split(line, ",")
+ for word in words:
+ word = str.lstrip(word)
+ if word != "":
+ if str.find(word, ":") == -1:
+ output.write("@findex " + word + " (var)\n")
+ elif len(word) > 0:
+ var = str.split(word, ":")
+ if len(var) > 0:
+ output.write("@findex " + var[0] + " (var)\n")
+
+ if inType:
+ words = str.lstrip(line)
+ if str.find(words, "=") != -1:
+ word = str.split(words, "=")
+ if (len(word[0]) > 0) and (word[0][0] != "_"):
+ output.write("@findex " + str.rstrip(word[0]) + " (type)\n")
+ else:
+ word = str.split(words)
+ if (len(word) > 1) and (word[1] == ";"):
+ # hidden type
+ if (len(word[0]) > 0) and (word[0][0] != "_"):
+ output.write("@findex " + str.rstrip(word[0]))
+ output.write(" (type)\n")
+ if inConst:
+ words = str.split(line, ";")
+ for word in words:
+ word = str.lstrip(word)
+ if word != "":
+ if str.find(word, "=") != -1:
+ var = str.split(word, "=")
+ if len(var) > 0:
+ output.write("@findex " + var[0] + " (const)\n")
+ if procedure != "":
+ name = str.split(procedure, "(")
+ if name[0] != "":
+ proc = name[0]
+ if proc[-1] == ";":
+ proc = proc[:-1]
+ if proc != "":
+ output.write("@findex " + proc + "\n")
+
+
+def parseDefinition(dir, source, build, file, needPage):
+ # parseDefinition reads a definition module and creates
+ # indices for procedures, constants, variables and types.
+ output.write("\n")
+ with open(findFile(dir, build, source, file), "r") as f:
+ initState()
+ line = f.readline()
+ while (str.find(line, "(*") != -1):
+ removeInitialComments(f, line)
+ line = f.readline()
+ while (str.find(line, "DEFINITION") == -1):
+ line = f.readline()
+ output.write("@example\n")
+ output.write(str.rstrip(line) + "\n")
+ line = f.readline()
+ if len(str.rstrip(line)) == 0:
+ output.write("\n")
+ line = f.readline()
+ if (str.find(line, "(*") != -1):
+ removeFields(f, line)
+ else:
+ output.write(str.rstrip(line) + "\n")
+ else:
+ output.write(str.rstrip(line) + "\n")
+ line = f.readline()
+ while line:
+ line = str.rstrip(line)
+ checkIndex(line)
+ output.write(str.replace(str.replace(line, "{", "@{"), "}", "@}"))
+ output.write("\n")
+ line = f.readline()
+ output.write("@end example\n")
+ if needPage:
+ output.write("@page\n")
+
+
+def parseModules(up, dir, build, source, listOfModules):
+ previous = ""
+ i = 0
+ if len(listOfModules) > 1:
+ nxt = dir + "/" + listOfModules[1][:-4]
+ else:
+ nxt = ""
+ while i < len(listOfModules):
+ emitNode(dir + "/" + listOfModules[i][:-4], nxt, previous, up)
+ emitSubSection(dir + "/" + listOfModules[i][:-4])
+ parseDefinition(dir, source, build, listOfModules[i], True)
+ output.write("\n")
+ previous = dir + "/" + listOfModules[i][:-4]
+ i = i + 1
+ if i+1 < len(listOfModules):
+ nxt = dir + "/" + listOfModules[i+1][:-4]
+ else:
+ nxt = ""
+
+
+def doCat(name):
+ # doCat displays the contents of file, name, to stdout
+ with open(name, "r") as file:
+ line = file.readline()
+ while line:
+ output.write(str.rstrip(line) + "\n")
+ line = file.readline()
+
+
+def moduleMenu(dir, build, source):
+ # moduleMenu generates a simple menu for all definition modules
+ # in dir
+ output.write("@menu\n")
+ listOfFiles = []
+ if os.path.exists(os.path.join(source, dir)):
+ listOfFiles += os.listdir(os.path.join(source, dir))
+ if os.path.exists(os.path.join(source, dir)):
+ listOfFiles += os.listdir(os.path.join(build, dir))
+ listOfFiles = list(dict.fromkeys(listOfFiles).keys())
+ listOfFiles.sort()
+ for file in listOfFiles:
+ if foundFile(dir, build, source, file):
+ if (len(file) > 4) and (file[-4:] == ".def"):
+ output.write("* " + dir + "/" + file[:-4] + "::" + file + "\n")
+ output.write("@end menu\n")
+ output.write("\n")
+
+
+def checkDirectory(dir, build, source):
+ # checkDirectory - returns True if dir exists in either build or source.
+ if os.path.isdir(build) and os.path.exists(os.path.join(build, dir)):
+ return True
+ elif os.path.isdir(source) and os.path.exists(os.path.join(source, dir)):
+ return True
+ else:
+ return False
+
+
+def foundFile(dir, build, source, file):
+ # foundFile return True if file is found in build/dir/file or
+ # source/dir/file.
+ name = os.path.join(os.path.join(build, dir), file)
+ if os.path.exists(name):
+ return True
+ name = os.path.join(os.path.join(source, dir), file)
+ if os.path.exists(name):
+ return True
+ return False
+
+
+def findFile(dir, build, source, file):
+ # findFile return the path to file searching in build/dir/file
+ # first then source/dir/file.
+ name1 = os.path.join(os.path.join(build, dir), file)
+ if os.path.exists(name1):
+ return name1
+ name2 = os.path.join(os.path.join(source, dir), file)
+ if os.path.exists(name2):
+ return name2
+ sys.stderr.write("file cannot be found in either " + name1)
+ sys.stderr.write(" or " + name2 + "\n")
+ os.sys.exit(1)
+
+
+def displayModules(up, dir, build, source):
+ # displayModules walks though the files in dir and parses
+ # definition modules and includes README.texi
+ if checkDirectory(dir, build, source):
+ if foundFile(dir, build, source, "README.texi"):
+ doCat(findFile(dir, build, source, "README.texi"))
+ moduleMenu(dir, build, source)
+ listOfFiles = []
+ if os.path.exists(os.path.join(source, dir)):
+ listOfFiles += os.listdir(os.path.join(source, dir))
+ if os.path.exists(os.path.join(source, dir)):
+ listOfFiles += os.listdir(os.path.join(build, dir))
+ listOfFiles = list(dict.fromkeys(listOfFiles).keys())
+ listOfFiles.sort()
+ listOfModules = []
+ for file in listOfFiles:
+ if foundFile(dir, build, source, file):
+ if (len(file) > 4) and (file[-4:] == ".def"):
+ listOfModules += [file]
+ listOfModules.sort()
+ parseModules(up, dir, build, source, listOfModules)
+ else:
+ line = "directory " + dir + " not found in either "
+ line += build + " or " + source
+ sys.stderr.write(line + "\n")
+
+
+def displayCopyright():
+ output.write("@c Copyright (C) 2000-2022 Free Software Foundation, Inc.\n")
+ output.write("@c This file is part of GNU Modula-2.\n")
+ output.write("""
+@c Permission is granted to copy, distribute and/or modify this document
+@c under the terms of the GNU Free Documentation License, Version 1.2 or
+@c any later version published by the Free Software Foundation.
+""")
+
+
+def collectArgs():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-v", "--verbose", help="generate progress messages",
+ action="store_true")
+ parser.add_argument("-b", "--builddir", help="set the build directory",
+ default=".", action="store")
+ parser.add_argument("-f", "--inputfile", help="set the input file",
+ default=None, action="store")
+ parser.add_argument("-o", "--outputfile", help="set the output file",
+ default=None, action="store")
+ parser.add_argument("-s", "--sourcedir", help="set the source directory",
+ default=".", action="store")
+ parser.add_argument("-t", "--texinfo",
+ help="generate texinfo documentation",
+ default=False, action="store_true")
+ parser.add_argument("-u", "--up", help="set the up node",
+ default="", action="store")
+ parser.add_argument("-x", "--sphinx", help="generate sphinx documentation",
+ default=False, action="store_true")
+ args = parser.parse_args()
+ return args
+
+
+def handleFile():
+ if args.inputfile is None:
+ displayCopyright()
+ displayMenu()
+ displayLibraryClass()
+ else:
+ parseDefinition(".", args.sourcedir, args.builddir,
+ args.inputfile, False)
+
+
+def main():
+ global args, output
+ args = collectArgs()
+ if args.outputfile is None:
+ output = sys.stdout
+ handleFile()
+ else:
+ with open(args.outputfile, "w") as output:
+ handleFile()
+
+
+main()
@@ -0,0 +1,807 @@
+/* mklink.c creates startup code and the link command line.
+
+Copyright (C) 2000-2022 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_finish (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_finish (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_finish (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");
+ }
+}