16/19 modula2 front end: bootstrap and documentation tools

Message ID E1ohukX-00Bm41-MC@lancelot
State New
Headers
Series 16/19 modula2 front end: bootstrap and documentation tools |

Commit Message

Gaius Mulley Oct. 10, 2022, 3:31 p.m. UTC
  This patch set contains the bootstrap linking tool as well as python3
scripts to automatically generate texi libraries section of the gm2
documentation.  In the fullness of time this will be changed to emit
sphinx.

 
------8<----------8<----------8<----------8<----------8<----------8<----
  

Comments

Martin Liška Oct. 13, 2022, 9:12 a.m. UTC | #1
On 10/10/22 17:31, Gaius Mulley via Gcc-patches wrote:
>  
> 

Hi!

> This patch set contains the bootstrap linking tool as well as python3
> scripts to automatically generate texi libraries section of the gm2
> documentation.  In the fullness of time this will be changed to emit
> sphinx.

Yep, looking forward to it. I'm going to write an email with Sphinx transition
schedule once Sphinx 5.3 gets released (should happen during the upcoming weekend).

I have general comments about the Python scripts:

1) please follow the Python coding style and not the GCC one (I'm going to document
it in https://gcc.gnu.org/codingconventions.html under a new Python section).
The easiest approach is using flake8 and the following plugins:

python3-flake8, python3-flake8-builtins, python3-flake8-bugbear, python3-flake8-import-order, python3-flake8-quotes

plus, you might want to come up with a setup.cfg like we have in:
./maintainer-scripts/setup.cfg

> 
>  
> ------8<----------8<----------8<----------8<----------8<----------8<---- 
> 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-10-07 20:21:18.682097332 +0100
> @@ -0,0 +1,184 @@
> +#!/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, sys
> +
> +maxLineLength = 60
> +
> +
> +#
> +#  visitDir - call func for each file below, dir, matching extension, ext.
> +#
> +
> +def visitDir (dir, ext, func):
> +    listOfFiles = os.listdir(dir)
> +    listOfFiles.sort()
> +    for file in listOfFiles:
> +        if os.path.isfile(os.path.join(dir, file)):
> +            l = len(ext)
> +            if (len(file)>l) and (file[-l:] == ext):
> +                func(os.path.join(dir, file))

please use pathlib.Path(...).stem

> +        elif os.path.isdir(os.path.join(dir, file)):
> +            visitDir(os.path.join(dir, file), ext, func)
> +
> +#
> +#  isYear - returns True if, year, is legal.
> +#
> +
> +def isYear (year):
> +    if len(year)==5:
> +        year = year[:-1]
> +    for c in year:
> +        if not c.isdigit():
> +            return False
> +    return True
> +
> +
> +#
> +#  handleCopyright -
> +#
> +
> +def handleCopyright (outfile, lines, n, leader1, leader2):
> +    global maxLineLength
> +    i = lines[n]
> +    c = i.find('Copyright (C) ')+len('Copyright (C)')
> +    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 = ""

Please unify "" and '', you only apostrophes.

> +            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
> +
> +#
> +#  handleHeader - reads in the header of a file and inserts
> +#                 a line break around the Copyright dates.
> +#
> +
> +def handleHeader (file, leader1, leader2):
> +    print("------------------------------")
> +    l = open(file, 'r').readlines()
> +    if len(l)>20:
> +        outfile = open('tmptidy', 'w')

use 'with open(...) as outfile:'
https://docs.python.org/3/reference/compound_stmts.html#the-with-statement
   

> +        n = 0
> +        for i in l:
> +            if i.find('Copyright (C)')>=0:
> +                outfile, n = handleCopyright(outfile, l, n, leader1, leader2)
> +                outfile.writelines(l[n:])
> +                outfile.close()
> +                print("-> mv tmptidy", file)
> +                command = "mv tmptidy %s" % file
> +                os.system(command)

shutil.move

> +                return
> +            else:
> +                outfile.write(l[n])
> +                n += 1
> +        outfile.close()

... will be closed automatically by 'with' statement.

> +        sys.stdout.write("%s:1:1 needs a Copyright notice..\n" % file)
> +
> +
> +#
> +#  bashTidy - tidy up dates using '#' comment
> +#
> +
> +def bashTidy (file):

Better putting comments here in function body.

> +    handleHeader(file, '#', ' ')
> +
> +#
> +#  cTidy - tidy up dates using '/* */' comments
> +#
> +
> +def cTidy (file):
> +    handleHeader(file, ' ', '*')
> +
> +#
> +#  m2Tidy - tidy up dates using '(* *)' comments
> +#
> +
> +def m2Tidy (file):
> +    handleHeader(file, ' ', ' ')
> +
> +#
> +#  main - for each file extension call the appropriate tidy
> +#         routine.
> +#
> +
> +def main ():
> +    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-10-07 20:21:18.682097332 +0100
> @@ -0,0 +1,599 @@
> +#!/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 sys
> +import os
> +import glob
> +import sys, getopt, string
> +import datetime
> +
> +forceGPL3x, forceGPL3 = False, False
> +doModify, verbose = True, False,
> +multiFilemode, updateAll, forceCheck = False, False, False
> +
> +summaryGiven, contributedBy, outputName = "", "", "-"
> +errorCount = 0
> +startDir = "."
> +seenFiles = []
> +
> +
> +#
> +#  printf - keeps C programmers happy :-)
> +#
> +
> +def printf (fmt, *args):
> +    print(str (fmt) % args, end=' ')
> +
> +#
> +#  error - issue an error message.
> +#
> +
> +def error (fmt, *args):
> +    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]
> +
> +
> +#
> +#  analyseComment -
> +#
> +
> +def analyseComment (text, f):
> +    start_date, end_date, contribution, summary, lic = None, None, None, None, None
> +    if text.find ("Copyright ISO/IEC") > 0:
> +        lic = "BSISO"
> +        now = datetime.datetime.now ()
> +        for d in range (1984, now.year+1):
> +            if text.find (str (d)) > 0:
> +                if start_date == None:
> +                    start_date = str (d)
> +                end_date = str (d)
> +        return start_date, end_date, "", "", lic
> +    elif text.find ("Copyright (C)") > 0:

better 'Copyright (C)' in text, similarly at other places ..

> +        if text.find ("GNU General Public License") > 0:
> +            lic = "GPL"
> +        elif text.find ("GNU Lesser General") > 0:
> +            lic = "LGPL"
> +        if text.find ("version 2.1") > 0:
> +            lic += "v2.1"
> +        elif text.find ("version 2") > 0:
> +            lic += "v2"
> +        elif text.find ("version 3") > 0:
> +            lic += "v3"
> +        if text.find ("GCC Runtime Library Exception") > 0:
> +            lic += "x"
> +        now = datetime.datetime.now ()
> +        for d in range (1984, now.year+1):
> +            if text.find (str (d)) > 0:
> +                if start_date == None:
> +                    start_date = str (d)
> +                end_date = str (d)
> +        if text.find ("ontributed by") > 0:
> +            i = text.find ("ontributed by")
> +            i += len ("ontributed 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
> +
> +
> +#
> +#  analyseHeader -
> +#
> +
> +def analyseHeader (f, start, end):
> +    text = ""
> +    if end == None:
> +        for count, l in enumerate (open (f, "r").readlines ()):
> +            parts = l.split (start)
> +            if len (parts) > 1:
> +                line = start.join (parts[1:])
> +                line = line.rstrip ()
> +                line = line.lstrip ()

line = line.strip()

> +                text += " "
> +                text += line
> +            elif (l.rstrip () != "") and (len (parts[0]) > 0):
> +                return analyseComment (text, f), count
> +    else:
> +        inComment = False
> +        for count, l in enumerate (open (f, "r").readlines ()):

'r' is default

> +            while l != "":
> +                l = l.strip ()
> +                l = l.rstrip ()
> +                if inComment:
> +                    text += " "
> +                    pos = l.find (end)

better use https://docs.python.org/3/library/stdtypes.html?highlight=partition#str.partition

> +                    if pos >= 0:
> +                        text += l[:pos]
> +                        l = l[pos:]
> +                        inComment = False
> +                    else:
> +                        text += l
> +                        l = ""
> +                else:
> +                    pos = l.find (start)
> +                    if (pos >= 0) and (len (l) > len (start)):
> +                        before = l[:pos]
> +                        before = before.rstrip ()
> +                        before = before.lstrip ()
> +                        if before != "":
> +                            return analyseComment (text, f), count
> +                        l = l[pos + len (start):]
> +                        inComment = True
> +                    elif (l != "") and (l == end):
> +                        l = ""
> +                    else:
> +                        return analyseComment (text, f), count
> +    return [None, None, None, None, None], 0
> +
> +
> +#
> +#  addStop - add a full stop to a sentance.
> +#
> +
> +def addStop (sentence):
> +    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 = { "GPLv3":GPLv3,
> +              "GPLv3x":GPLv3x,
> +              "LGPLv3":LGPLv3,
> +              "LGPLv2.1":LGPLv3,
> +              "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 != None:
> +                fo.write (magic)
> +                fo.write ("\n")
> +            text = templates[lic] % (summary, dates, contribution)
> +            text = text.rstrip ()
> +        if end == 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 == 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):
> +    l = open (f, "r").readlines ()[lines:]
> +    text = "".join (l)
> +    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 ()
> +
> +
> +#
> +#  handleHeader - keep reading lines of file, f, looking for start, end
> +#                 sequences and comments inside.  The comments are checked
> +#                 for:  date, contribution, summary
> +#
> +
> +def handleHeader (f, magic, start, end):
> +    global date, contribution, summary, doModify, forceCheck, errorCount
> +
> +    errorCount = 0
> +    [start_date, end_date, contribution, summary, lic], lines =  analyseHeader (f, start, end)
> +    if lic == None:
> +        error ("%s:1:no GPL found at the top of the file\n", f)
> +    else:
> +        if verbose:
> +            printf ("copyright: %s\n", lic)

f-string format might be better, but that's just a hint:
https://docs.python.org/3/reference/lexical_analysis.html#f-strings

> +            if (start_date != None) and (end_date != None):
> +                if start_date == end_date:
> +                    printf ("dates = %s\n", start_date)
> +                else:
> +                    printf ("dates = %s-%s\n", start_date, end_date)
> +            if summary != None:
> +                printf ("summary: %s\n", summary)
> +            if contribution != None:
> +                printf ("contribution: %s\n", contribution)
> +        if start_date == None:

I prefer 'if not start_date' (simiarly at other places).

> +            error ("%s:1:no date found in the GPL at the top of the file\n", f)
> +        if contribution == None:
> +            if contributedBy == "":
> +                error ("%s:1:no contribution found in the GPL at the top of the file\n", f)
> +            else:
> +                contribution = contributedBy
> +        if summary == None:
> +            if summaryGiven == "":
> +                error ("%s:1:no single line summary found in the GPL at the top of the file\n", f)
> +            else:
> +                summary = summaryGiven
> +    if errorCount == 0:
> +        now = datetime.datetime.now ()
> +        if doModify:
> +            if lic == "BSISO":
> +                # don't change the BS ISO license!
> +                pass
> +            elif forceGPL3x:
> +                lic = "GPLv3x"
> +            elif forceGPL3:
> +                lic = "GPLv3"
> +            rewriteFile (f, magic, start, end, start_date, str (now.year), contribution, summary, lic, lines)
> +        elif forceCheck:
> +            print(f, "suppressing change as requested", start_date, end_date, lic)
> +    else:
> +        printf ("too many errors, no modifications will occur\n")
> +
> +
> +#
> +#  bashTidy - tidy up dates using '#' comment
> +#
> +
> +def bashTidy (f):
> +    handleHeader (f, "#!/bin/bash", "#", None)
> +
> +
> +#
> +#  pythonTidy - tidy up dates using '#' comment
> +#
> +
> +def pythonTidy (f):
> +    handleHeader (f, "#!/usr/bin/env python3", '#', None)
> +
> +
> +#
> +#  bnfTidy - tidy up dates using '--' comment
> +#
> +
> +def bnfTidy (f):
> +    handleHeader (f, None, '--', None)
> +
> +
> +#
> +#  cTidy - tidy up dates using '/* */' comments
> +#
> +
> +def cTidy (f):
> +    handleHeader (f, None, '/*', '*/')
> +
> +#
> +#  m2Tidy - tidy up dates using '(* *)' comments
> +#
> +
> +def m2Tidy (f):
> +    handleHeader (f, None, '(*', '*)')
> +
> +#
> +#  inTidy - tidy up dates using '#' as a comment and check the first line for magic number.
> +#
> +
> +def inTidy (f):
> +    first = open (f, "r").readlines ()[0]
> +    if (len (first) > 0) and (first[:2] == "#!"):
> +        # magic number found, use this
> +        handleHeader (f, first, "#", None)
> +    else:
> +        handleHeader (f, None, "#", None)
> +
> +
> +#
> +#  doVisit -
> +#
> +
> +def doVisit (args, dirname, names):
> +    global outputName
> +    func, extension = args
> +    for f in names:
> +        if len (f) > len (extension) and f[-len (extension):] == extension:
> +            # print os.path.join (dirname, f)
> +            outputName = f
> +            func (os.path.join (dirname, f))
> +
> +
> +#
> +#  visitDir - visit
> +#
> +
> +def visitDir (startDir, extension, func):
> +    global outputName, seenFiles
> +    # os.walk (startDir, doVisit, [func, extension])
> +    for dirName, subdirList, fileList in os.walk(startDir):
> +        for fname in fileList:
> +            if (len (fname) > len (extension)) and (fname[-len(extension):] == extension):

Path(...).stem again would be better.

> +                fullpath = os.path.join (dirName, fname)
> +                outputName = fullpath
> +                # printf ("outputName = %s\n", outputName)
> +                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]
> +
> +#
> +#  findFiles - for each file extension call the appropriate tidy
> +#              routine.
> +#
> +
> +def findFiles ():
> +    visitDir (startDir, '.h.in', cTidy)
> +    visitDir (startDir, '.in', inTidy)
> +    visitDir (startDir, '.sh', inTidy)
> +    visitDir (startDir, '.py', pythonTidy)
> +    visitDir (startDir, '.c', cTidy)
> +    visitDir (startDir, '.h', cTidy)
> +    visitDir (startDir, '.cc', cTidy)
> +    visitDir (startDir, '.def', m2Tidy)
> +    visitDir (startDir, '.mod', m2Tidy)
> +    visitDir (startDir, '.bnf', bnfTidy)
> +
> +
> +#
> +#  usage - output very brief usage instructions.
> +#
> +
> +def usage (code = 0):
> +    print("boilerplate [-c contributionstring] [ -s summarystring ] [-d] [-v] [-g] [-x] [-o outputfile] inputfile.c")
> +    print("  -o outputfile   (this must be before the final inputfile on the command line).")
> +    print("  -c              a string which will be used as the contribution line.")
> +    print("  -s              a string which will be used as the summary line.")
> +    print("  -f              force a check to insist that the contribution, summary and GPL exists.")
> +    print("  -g              change to GPLv3.")
> +    print("  -x              change to GPLv3 with GCC runtime extension.")
> +    print("  -r directory    recusively scan directory for known file extensions (.def, .mod, .c, .h, .py, .in, .sh).")
> +    print("  -u              update all dates.")
> +    print("  -v              verbose.")
> +    print("  -N              do not modify any file")
> +    os.sys.exit (code)

https://docs.python.org/3/library/argparse.html would be much better, you get arguments parsing for free.

> +
> +
> +#
> +#  handleArguments - check the legal arguments.
> +#
> +
> +def handleArguments ():
> +    global multiFilemode, contributedBy, updateAll, forceCheck, outputName, verbose, startDir, doModify, forceGPL3, forceGPL3x, summaryGiven
> +    try:
> +        optlist, l = getopt.getopt (sys.argv[1:],':c:dfgho:r:s:uvxN')
> +    except getopt.GetoptError:
> +        usage (1)
> +    for opt in optlist:
> +        if opt[0] == '-c':
> +            contributedBy = opt[1]
> +        if opt[0] == '-s':
> +            summaryGiven = opt[1]
> +        if opt[0] == '-d':
> +            debugging = True
> +        if opt[0] == '-f':
> +            forceCheck = True
> +        if opt[0] == '-g':
> +            forceGPL3 = True
> +        if opt[0] == '-x':
> +            forceGPL3x = True
> +        if opt[0] == '-h':
> +            usage ()
> +        if opt[0] == '-r':
> +            multiFilemode = True
> +            startDir = opt[1]
> +        if opt[0] == '-o':
> +            outputName = opt[1]
> +        if opt[0] == '-u':
> +            updateAll = True
> +        if opt[0] == '-v':
> +            verbose = True
> +        if opt[0] == '-N':
> +            doModify = False
> +    if l == []:
> +        return None
> +    return l[0]

^^^ this will be done automatically.

Hope it's usefull.

Thanks,
Martin

> +
> +
> +#
> +#  hasExt - return True if, name, ends with, ext.
> +#
> +
> +def hasExt (name, ext):
> +    if len (name) > len (ext):
> +        return name[-len (ext):] == ext
> +    return False
> +
> +
> +#
> +#  singleFile - scan the single file for a GPL boilerplate which
> +#               has a GPL, contribution field and a summary heading.
> +#
> +
> +def singleFile (i):
> +    if hasExt (i, ".def") or hasExt (i, ".mod"):
> +        m2Tidy (i)
> +    elif hasExt (i, ".h") or hasExt (i, ".c") or hasExt (i, ".cc"):
> +        cTidy (i)
> +    elif hasExt (i, ".in"):
> +        inTidy (i)
> +    elif hasExt (i, ".sh"):
> +        inTidy (i)  # uses magic number for actual sh/bash
> +    elif hasExt (i, ".py"):
> +        pythonTidy (i)
> +
> +
> +#
> +#  main - handleArguments and then find source files.
> +#
> +
> +def main ():
> +    i = handleArguments ()
> +    if multiFilemode:
> +        findFiles ()
> +    elif i == None:
> +        print("an input file must be specified on the command line")
> +        usage (1)
> +    else:
> +        singleFile (i)
> +    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-10-07 20:21:18.682097332 +0100
> @@ -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-10-07 20:21:18.682097332 +0100
> @@ -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-10-07 20:21:18.682097332 +0100
> @@ -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-10-07 20:21:18.682097332 +0100
> @@ -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/mklink.c
> --- /dev/null	2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/mklink.c	2022-10-07 20:21:18.682097332 +0100
> @@ -0,0 +1,810 @@
> +/* 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 defined(XENIX)
> +      name[10] = (char)0; /* truncate object file name.  */
> +#endif
> +      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");
> +    }
> +}
> diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/def2texi.py
> --- /dev/null	2022-08-24 16:22:16.888000070 +0100
> +++ gcc-git-devel-modula2/gcc/m2/tools-src/def2texi.py	2022-10-07 20:21:18.682097332 +0100
> @@ -0,0 +1,423 @@
> +#!/usr/bin/env python3
> +
> +# def2texi.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 sys
> +import os
> +import glob
> +import getopt
> +
> +libraryClassifications = [['gm2-libs','Base libraries',
> +                           'Basic M2F compatible libraries'],
> +                          ['gm2-libs-pim','PIM and Logitech 3.0 Compatible',
> +                           'PIM and Logitech 3.0 compatible libraries'],
> +                          ['gm2-libs-coroutines','PIM coroutine support',
> +                           'PIM compatible process support'],
> +                          ['gm2-libs-iso','M2 ISO Libraries',
> +                           'ISO defined libraries']]
> +
> +def initState ():
> +    global inVar, inType, inConst
> +    inVar, inType, inConst = False, False, False
> +
> +
> +#
> +#  displayLibraryClass - displays a node for a library directory and invokes
> +#                        a routine to summarize each module
> +#
> +
> +def displayLibraryClass():
> +    global buildDir, up
> +    previous = ""
> +
> +    next=libraryClassifications[1][1]
> +    i = 0
> +    l = libraryClassifications[i]
> +
> +    while True:
> +        print("@node " + l[1] + ", " + next + ", " + previous + ", " + up)
> +        print("@section " + l[1])
> +        print("")
> +        displayModules(l[1], l[0], buildDir, sourceDir)
> +        print("")
> +        print("@c ---------------------------------------------------------------------")
> +        previous = l[1]
> +        i += 1
> +        if i == len(libraryClassifications):
> +            break
> +        l = libraryClassifications[i]
> +        if i+1 == len(libraryClassifications):
> +            next = ""
> +        else:
> +            next = libraryClassifications[i+1][1]
> +
> +#
> +#  displayMenu - displays the top level menu for library documentation
> +#
> +
> +def displayMenu():
> +    print("@menu")
> +    for l in libraryClassifications:
> +        print("* " + l[1] + "::" + l[2])
> +    print("@end menu")
> +
> +    print("\n")
> +    print("@c =====================================================================")
> +    print("\n")
> +
> +
> +#
> +#  removeInitialComments - removes any (* *) at the top of the definition module
> +#
> +
> +def removeInitialComments (file, line):
> +    while (str.find(line, "*)") == -1):
> +        line = file.readline()
> +
> +#
> +#  removeFields - removes Author/Date/Last edit/SYSTEM/Revision fields from a comment within the start
> +#                 of a definition module
> +#
> +
> +def removeFields (file, line):
> +    while (str.find(line, "*)") == -1):
> +        if (str.find(line, "Author") != -1) and (str.find(line, ":") != -1):
> +            line = file.readline()
> +        elif (str.find(line, "Last edit") != -1) and (str.find(line, ":") != -1):
> +            line = file.readline()
> +        elif (str.find(line, "LastEdit") != -1) and (str.find(line, ":") != -1):
> +            line = file.readline()
> +        elif (str.find(line, "Last update") != -1) and (str.find(line, ":") != -1):
> +            line = file.readline()
> +        elif (str.find(line, "Date") != -1) and (str.find(line, ":") != -1):
> +            line = file.readline()
> +        elif (str.find(line, "Title") != -1) and (str.find(line, ":") != -1):
> +            line = file.readline()
> +        elif (str.find(line, "Revision") != -1) and (str.find(line, ":") != -1):
> +            line = file.readline()
> +        elif (str.find(line, "System") != -1) and (str.find(line, ":") != -1) and (str.find(line, "Description:") == -1):
> +            line = file.readline()
> +        elif (str.find(line, "SYSTEM") != -1) and (str.find(line, ":") != -1) and (str.find(line, "Description:") == -1):
> +            line = file.readline()
> +        else:
> +           print(str.replace(str.replace(str.rstrip(line),
> +                                            "{", "@{"), "}", "@}"))
> +           line = file.readline()
> +    print(str.rstrip(line))
> +
> +
> +#
> +#  checkIndex
> +#
> +
> +def checkIndex (line):
> +    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:
> +                    print("@findex " + word + " (var)")
> +                elif len(word)>0:
> +                    var = str.split(word, ':')
> +                    if len(var)>0:
> +                        print("@findex " + var[0] + " (var)")
> +
> +    if inType:
> +        words = str.lstrip(line)
> +        if str.find(words, '=') != -1:
> +            word = str.split(words, "=")
> +            if (len(word[0])>0) and (word[0][0] != '_'):
> +                print("@findex " + str.rstrip(word[0]) + " (type)")
> +        else:
> +            word = str.split(words)
> +            if (len(word)>1) and (word[1] == ';'):
> +                # hidden type
> +                if (len(word[0])>0) and (word[0][0] != '_'):
> +                    print("@findex " + str.rstrip(word[0]) + " (type)")
> +
> +    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:
> +                        print("@findex " + var[0] + " (const)")
> +
> +    if procedure != "":
> +        name = str.split(procedure, "(")
> +        if name[0] != "":
> +            proc = name[0]
> +            if proc[-1] == ";":
> +                proc = proc[:-1]
> +            if proc != "":
> +                print("@findex " + proc)
> +
> +
> +#
> +#  parseDefinition
> +#
> +
> +def parseDefinition (dir, source, build, file, needPage):
> +    print("")
> +    f = open(findFile(dir, build, source, file), 'r')
> +    initState()
> +    line = f.readline()
> +#   while (str.find(line, "(*") != -1):
> +    while (str.find(line, "(*") != -1):
> +        removeInitialComments(f, line)
> +        line = f.readline()
> +
> +    while (str.find(line, "DEFINITION") == -1):
> +        line = f.readline()
> +
> +    print("@example")
> +    print(str.rstrip(line))
> +    line = f.readline()
> +    if len(str.rstrip(line)) == 0:
> +        print(str.replace(str.replace(str.rstrip(line),
> +                                            "{", "@{"), "}", "@}"))
> +        line = f.readline()
> +        if (str.find(line, "(*") != -1):
> +            removeFields(f, line)
> +        else:
> +            print(str.rstrip(line))
> +    else:
> +        print(str.rstrip(line))
> +
> +    line = f.readline()
> +    while line:
> +        line = str.rstrip(line)
> +        checkIndex(line)
> +        print(str.replace(str.replace(line, "{", "@{"), "}", "@}"))
> +        line = f.readline()
> +    print("@end example")
> +    if needPage:
> +        print("@page")
> +    f.close()
> +
> +def parseModules (up, dir, build, source, listOfModules):
> +    previous = ""
> +    i = 0
> +    if len(listOfModules)>1:
> +        next = dir + "/" + listOfModules[1][:-4]
> +    else:
> +        next = ""
> +
> +    while i<len(listOfModules):
> +       print("@node " + dir + "/" + listOfModules[i][:-4] + ", " + next + ", " + previous + ", " + up)
> +       print("@subsection " + dir + "/" + listOfModules[i][:-4])
> +       parseDefinition(dir, source, build, listOfModules[i], True)
> +       print("\n")
> +       previous = dir + "/" + listOfModules[i][:-4]
> +       i = i + 1
> +       if i+1<len(listOfModules):
> +           next = dir + "/" + listOfModules[i+1][:-4]
> +       else:
> +           next = ""
> +
> +
> +#
> +#  doCat - displays the contents of file, name, to stdout
> +#
> +
> +def doCat (name):
> +    file = open(name, 'r')
> +    line = file.readline()
> +    while line:
> +        print(str.rstrip(line))
> +        line = file.readline()
> +    file.close()
> +
> +
> +#
> +#  moduleMenu - generates a simple menu for all definition modules
> +#               in dir
> +#
> +
> +def moduleMenu (dir, build, source):
> +    print("@menu")
> +    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'):
> +                print("* " + dir + "/" + file[:-4] + "::" + file)
> +    print("@end menu")
> +    print("\n")
> +
> +
> +#
> +#  checkDirectory - returns True if dir exists in either build or source.
> +#
> +
> +def checkDirectory (dir, build, 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
> +
> +
> +#
> +#  foundFile - return True if file is found in build/dir/file or source/dir/file.
> +#
> +
> +def foundFile (dir, build, source, 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
> +
> +
> +#
> +#  findFile - return the path to file searching in build/dir/file first then source/dir/file.
> +#
> +
> +def findFile (dir, build, source, 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
> +    print("file cannot be found in either " + name1 + " or " + name2)
> +    os.sys.exit(1)
> +
> +
> +#
> +#  displayModules - walks though the files in dir and parses
> +#                   definition modules and includes README.texi
> +#
> +
> +def displayModules(up, dir, build, source):
> +    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:
> +        print("directory " + dir + " not found in either " + build + " or " + source)
> +
> +
> +def displayCopyright ():
> +    print("@c Copyright (C) 2000-2022 Free Software Foundation, Inc.")
> +    print("@c This file is part of GNU Modula-2.")
> +    print("""
> +@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 Usage():
> +    print("def2texi.py [-h][-bbuilddir][-uupnode][-ffilename]")
> +
> +def collectArgs():
> +    buildDir="."
> +    sourceDir="."
> +    filename=""
> +    up=""
> +    try:
> +        optlist, list = getopt.getopt(sys.argv[1:],':hb:f:s:u:')
> +    except getopt.GetoptError:
> +        Usage()
> +        os.sys.exit(1)
> +    for opt in optlist:
> +        if opt[0] == '-h':
> +            Usage()
> +        if opt[0] == '-b':
> +            buildDir = opt[1]
> +        if opt[0] == '-f':
> +            filename = opt[1]
> +        if opt[0] == '-s':
> +            sourceDir = opt[1]
> +        if opt[0] == '-u':
> +            up = opt[1]
> +    return buildDir, sourceDir, filename, up
> +
> +
> +buildDir, sourceDir, filename, up = collectArgs()
> +
> +if filename == "":
> +    displayCopyright()
> +    displayMenu()
> +    displayLibraryClass()
> +else:
> +    parseDefinition('.', sourceDir, buildDir, filename, False)
  
Martin Liška Oct. 20, 2022, 12:42 p.m. UTC | #2
Hello.

I noticed the devel/modula-2 branch contains the following dead links:

- http://www.gccsummit.org/2006
- http://www.gccsummit.org/2010/speakers.php?types=LIGHTNING
- http://floppsie.comp.glam.ac.uk/Papers/paper23/gaius-mulley-gnu-m2.pdf
- http://floppsie.comp.glam.ac.uk/Papers/paper15/mulley-proc.pdf
- http://floppsie.comp.glam.ac.uk/Papers/paper22/gaius-gcc-cauldron-2016.pdf

Thanks,
Martin
  
Gaius Mulley Oct. 28, 2022, 1:05 p.m. UTC | #3
Martin Liška <mliska@suse.cz> writes:

> Hello.
>
> I noticed the devel/modula-2 branch contains the following dead links:
>
> - http://www.gccsummit.org/2006
> - http://www.gccsummit.org/2010/speakers.php?types=LIGHTNING
> - http://floppsie.comp.glam.ac.uk/Papers/paper23/gaius-mulley-gnu-m2.pdf
> - http://floppsie.comp.glam.ac.uk/Papers/paper15/mulley-proc.pdf
> - http://floppsie.comp.glam.ac.uk/Papers/paper22/gaius-gcc-cauldron-2016.pdf
>
> Thanks,
> Martin

many thanks - I will remove these,

regards,
Gaius
  

Patch

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-10-07 20:21:18.682097332 +0100
@@ -0,0 +1,184 @@ 
+#!/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, sys
+
+maxLineLength = 60
+
+
+#
+#  visitDir - call func for each file below, dir, matching extension, ext.
+#
+
+def visitDir (dir, ext, func):
+    listOfFiles = os.listdir(dir)
+    listOfFiles.sort()
+    for file in listOfFiles:
+        if os.path.isfile(os.path.join(dir, file)):
+            l = len(ext)
+            if (len(file)>l) and (file[-l:] == ext):
+                func(os.path.join(dir, file))
+        elif os.path.isdir(os.path.join(dir, file)):
+            visitDir(os.path.join(dir, file), ext, func)
+
+#
+#  isYear - returns True if, year, is legal.
+#
+
+def isYear (year):
+    if len(year)==5:
+        year = year[:-1]
+    for c in year:
+        if not c.isdigit():
+            return False
+    return True
+
+
+#
+#  handleCopyright -
+#
+
+def handleCopyright (outfile, lines, n, leader1, leader2):
+    global maxLineLength
+    i = lines[n]
+    c = i.find('Copyright (C) ')+len('Copyright (C)')
+    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
+
+#
+#  handleHeader - reads in the header of a file and inserts
+#                 a line break around the Copyright dates.
+#
+
+def handleHeader (file, leader1, leader2):
+    print("------------------------------")
+    l = open(file, 'r').readlines()
+    if len(l)>20:
+        outfile = open('tmptidy', 'w')
+        n = 0
+        for i in l:
+            if i.find('Copyright (C)')>=0:
+                outfile, n = handleCopyright(outfile, l, n, leader1, leader2)
+                outfile.writelines(l[n:])
+                outfile.close()
+                print("-> mv tmptidy", file)
+                command = "mv tmptidy %s" % file
+                os.system(command)
+                return
+            else:
+                outfile.write(l[n])
+                n += 1
+        outfile.close()
+        sys.stdout.write("%s:1:1 needs a Copyright notice..\n" % file)
+
+
+#
+#  bashTidy - tidy up dates using '#' comment
+#
+
+def bashTidy (file):
+    handleHeader(file, '#', ' ')
+
+#
+#  cTidy - tidy up dates using '/* */' comments
+#
+
+def cTidy (file):
+    handleHeader(file, ' ', '*')
+
+#
+#  m2Tidy - tidy up dates using '(* *)' comments
+#
+
+def m2Tidy (file):
+    handleHeader(file, ' ', ' ')
+
+#
+#  main - for each file extension call the appropriate tidy
+#         routine.
+#
+
+def main ():
+    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-10-07 20:21:18.682097332 +0100
@@ -0,0 +1,599 @@ 
+#!/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 sys
+import os
+import glob
+import sys, getopt, string
+import datetime
+
+forceGPL3x, forceGPL3 = False, False
+doModify, verbose = True, False,
+multiFilemode, updateAll, forceCheck = False, False, False
+
+summaryGiven, contributedBy, outputName = "", "", "-"
+errorCount = 0
+startDir = "."
+seenFiles = []
+
+
+#
+#  printf - keeps C programmers happy :-)
+#
+
+def printf (fmt, *args):
+    print(str (fmt) % args, end=' ')
+
+#
+#  error - issue an error message.
+#
+
+def error (fmt, *args):
+    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]
+
+
+#
+#  analyseComment -
+#
+
+def analyseComment (text, f):
+    start_date, end_date, contribution, summary, lic = None, None, None, None, None
+    if text.find ("Copyright ISO/IEC") > 0:
+        lic = "BSISO"
+        now = datetime.datetime.now ()
+        for d in range (1984, now.year+1):
+            if text.find (str (d)) > 0:
+                if start_date == None:
+                    start_date = str (d)
+                end_date = str (d)
+        return start_date, end_date, "", "", lic
+    elif text.find ("Copyright (C)") > 0:
+        if text.find ("GNU General Public License") > 0:
+            lic = "GPL"
+        elif text.find ("GNU Lesser General") > 0:
+            lic = "LGPL"
+        if text.find ("version 2.1") > 0:
+            lic += "v2.1"
+        elif text.find ("version 2") > 0:
+            lic += "v2"
+        elif text.find ("version 3") > 0:
+            lic += "v3"
+        if text.find ("GCC Runtime Library Exception") > 0:
+            lic += "x"
+        now = datetime.datetime.now ()
+        for d in range (1984, now.year+1):
+            if text.find (str (d)) > 0:
+                if start_date == None:
+                    start_date = str (d)
+                end_date = str (d)
+        if text.find ("ontributed by") > 0:
+            i = text.find ("ontributed by")
+            i += len ("ontributed 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
+
+
+#
+#  analyseHeader -
+#
+
+def analyseHeader (f, start, end):
+    text = ""
+    if end == None:
+        for count, l in enumerate (open (f, "r").readlines ()):
+            parts = l.split (start)
+            if len (parts) > 1:
+                line = start.join (parts[1:])
+                line = line.rstrip ()
+                line = line.lstrip ()
+                text += " "
+                text += line
+            elif (l.rstrip () != "") and (len (parts[0]) > 0):
+                return analyseComment (text, f), count
+    else:
+        inComment = False
+        for count, l in enumerate (open (f, "r").readlines ()):
+            while l != "":
+                l = l.strip ()
+                l = l.rstrip ()
+                if inComment:
+                    text += " "
+                    pos = l.find (end)
+                    if pos >= 0:
+                        text += l[:pos]
+                        l = l[pos:]
+                        inComment = False
+                    else:
+                        text += l
+                        l = ""
+                else:
+                    pos = l.find (start)
+                    if (pos >= 0) and (len (l) > len (start)):
+                        before = l[:pos]
+                        before = before.rstrip ()
+                        before = before.lstrip ()
+                        if before != "":
+                            return analyseComment (text, f), count
+                        l = l[pos + len (start):]
+                        inComment = True
+                    elif (l != "") and (l == end):
+                        l = ""
+                    else:
+                        return analyseComment (text, f), count
+    return [None, None, None, None, None], 0
+
+
+#
+#  addStop - add a full stop to a sentance.
+#
+
+def addStop (sentence):
+    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 = { "GPLv3":GPLv3,
+              "GPLv3x":GPLv3x,
+              "LGPLv3":LGPLv3,
+              "LGPLv2.1":LGPLv3,
+              "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 != None:
+                fo.write (magic)
+                fo.write ("\n")
+            text = templates[lic] % (summary, dates, contribution)
+            text = text.rstrip ()
+        if end == 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 == 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):
+    l = open (f, "r").readlines ()[lines:]
+    text = "".join (l)
+    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 ()
+
+
+#
+#  handleHeader - keep reading lines of file, f, looking for start, end
+#                 sequences and comments inside.  The comments are checked
+#                 for:  date, contribution, summary
+#
+
+def handleHeader (f, magic, start, end):
+    global date, contribution, summary, doModify, forceCheck, errorCount
+
+    errorCount = 0
+    [start_date, end_date, contribution, summary, lic], lines =  analyseHeader (f, start, end)
+    if lic == None:
+        error ("%s:1:no GPL found at the top of the file\n", f)
+    else:
+        if verbose:
+            printf ("copyright: %s\n", lic)
+            if (start_date != None) and (end_date != None):
+                if start_date == end_date:
+                    printf ("dates = %s\n", start_date)
+                else:
+                    printf ("dates = %s-%s\n", start_date, end_date)
+            if summary != None:
+                printf ("summary: %s\n", summary)
+            if contribution != None:
+                printf ("contribution: %s\n", contribution)
+        if start_date == None:
+            error ("%s:1:no date found in the GPL at the top of the file\n", f)
+        if contribution == None:
+            if contributedBy == "":
+                error ("%s:1:no contribution found in the GPL at the top of the file\n", f)
+            else:
+                contribution = contributedBy
+        if summary == None:
+            if summaryGiven == "":
+                error ("%s:1:no single line summary found in the GPL at the top of the file\n", f)
+            else:
+                summary = summaryGiven
+    if errorCount == 0:
+        now = datetime.datetime.now ()
+        if doModify:
+            if lic == "BSISO":
+                # don't change the BS ISO license!
+                pass
+            elif forceGPL3x:
+                lic = "GPLv3x"
+            elif forceGPL3:
+                lic = "GPLv3"
+            rewriteFile (f, magic, start, end, start_date, str (now.year), contribution, summary, lic, lines)
+        elif forceCheck:
+            print(f, "suppressing change as requested", start_date, end_date, lic)
+    else:
+        printf ("too many errors, no modifications will occur\n")
+
+
+#
+#  bashTidy - tidy up dates using '#' comment
+#
+
+def bashTidy (f):
+    handleHeader (f, "#!/bin/bash", "#", None)
+
+
+#
+#  pythonTidy - tidy up dates using '#' comment
+#
+
+def pythonTidy (f):
+    handleHeader (f, "#!/usr/bin/env python3", '#', None)
+
+
+#
+#  bnfTidy - tidy up dates using '--' comment
+#
+
+def bnfTidy (f):
+    handleHeader (f, None, '--', None)
+
+
+#
+#  cTidy - tidy up dates using '/* */' comments
+#
+
+def cTidy (f):
+    handleHeader (f, None, '/*', '*/')
+
+#
+#  m2Tidy - tidy up dates using '(* *)' comments
+#
+
+def m2Tidy (f):
+    handleHeader (f, None, '(*', '*)')
+
+#
+#  inTidy - tidy up dates using '#' as a comment and check the first line for magic number.
+#
+
+def inTidy (f):
+    first = open (f, "r").readlines ()[0]
+    if (len (first) > 0) and (first[:2] == "#!"):
+        # magic number found, use this
+        handleHeader (f, first, "#", None)
+    else:
+        handleHeader (f, None, "#", None)
+
+
+#
+#  doVisit -
+#
+
+def doVisit (args, dirname, names):
+    global outputName
+    func, extension = args
+    for f in names:
+        if len (f) > len (extension) and f[-len (extension):] == extension:
+            # print os.path.join (dirname, f)
+            outputName = f
+            func (os.path.join (dirname, f))
+
+
+#
+#  visitDir - visit
+#
+
+def visitDir (startDir, extension, func):
+    global outputName, seenFiles
+    # os.walk (startDir, doVisit, [func, extension])
+    for dirName, subdirList, fileList in os.walk(startDir):
+        for fname in fileList:
+            if (len (fname) > len (extension)) and (fname[-len(extension):] == extension):
+                fullpath = os.path.join (dirName, fname)
+                outputName = fullpath
+                # printf ("outputName = %s\n", outputName)
+                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]
+
+#
+#  findFiles - for each file extension call the appropriate tidy
+#              routine.
+#
+
+def findFiles ():
+    visitDir (startDir, '.h.in', cTidy)
+    visitDir (startDir, '.in', inTidy)
+    visitDir (startDir, '.sh', inTidy)
+    visitDir (startDir, '.py', pythonTidy)
+    visitDir (startDir, '.c', cTidy)
+    visitDir (startDir, '.h', cTidy)
+    visitDir (startDir, '.cc', cTidy)
+    visitDir (startDir, '.def', m2Tidy)
+    visitDir (startDir, '.mod', m2Tidy)
+    visitDir (startDir, '.bnf', bnfTidy)
+
+
+#
+#  usage - output very brief usage instructions.
+#
+
+def usage (code = 0):
+    print("boilerplate [-c contributionstring] [ -s summarystring ] [-d] [-v] [-g] [-x] [-o outputfile] inputfile.c")
+    print("  -o outputfile   (this must be before the final inputfile on the command line).")
+    print("  -c              a string which will be used as the contribution line.")
+    print("  -s              a string which will be used as the summary line.")
+    print("  -f              force a check to insist that the contribution, summary and GPL exists.")
+    print("  -g              change to GPLv3.")
+    print("  -x              change to GPLv3 with GCC runtime extension.")
+    print("  -r directory    recusively scan directory for known file extensions (.def, .mod, .c, .h, .py, .in, .sh).")
+    print("  -u              update all dates.")
+    print("  -v              verbose.")
+    print("  -N              do not modify any file")
+    os.sys.exit (code)
+
+
+#
+#  handleArguments - check the legal arguments.
+#
+
+def handleArguments ():
+    global multiFilemode, contributedBy, updateAll, forceCheck, outputName, verbose, startDir, doModify, forceGPL3, forceGPL3x, summaryGiven
+    try:
+        optlist, l = getopt.getopt (sys.argv[1:],':c:dfgho:r:s:uvxN')
+    except getopt.GetoptError:
+        usage (1)
+    for opt in optlist:
+        if opt[0] == '-c':
+            contributedBy = opt[1]
+        if opt[0] == '-s':
+            summaryGiven = opt[1]
+        if opt[0] == '-d':
+            debugging = True
+        if opt[0] == '-f':
+            forceCheck = True
+        if opt[0] == '-g':
+            forceGPL3 = True
+        if opt[0] == '-x':
+            forceGPL3x = True
+        if opt[0] == '-h':
+            usage ()
+        if opt[0] == '-r':
+            multiFilemode = True
+            startDir = opt[1]
+        if opt[0] == '-o':
+            outputName = opt[1]
+        if opt[0] == '-u':
+            updateAll = True
+        if opt[0] == '-v':
+            verbose = True
+        if opt[0] == '-N':
+            doModify = False
+    if l == []:
+        return None
+    return l[0]
+
+
+#
+#  hasExt - return True if, name, ends with, ext.
+#
+
+def hasExt (name, ext):
+    if len (name) > len (ext):
+        return name[-len (ext):] == ext
+    return False
+
+
+#
+#  singleFile - scan the single file for a GPL boilerplate which
+#               has a GPL, contribution field and a summary heading.
+#
+
+def singleFile (i):
+    if hasExt (i, ".def") or hasExt (i, ".mod"):
+        m2Tidy (i)
+    elif hasExt (i, ".h") or hasExt (i, ".c") or hasExt (i, ".cc"):
+        cTidy (i)
+    elif hasExt (i, ".in"):
+        inTidy (i)
+    elif hasExt (i, ".sh"):
+        inTidy (i)  # uses magic number for actual sh/bash
+    elif hasExt (i, ".py"):
+        pythonTidy (i)
+
+
+#
+#  main - handleArguments and then find source files.
+#
+
+def main ():
+    i = handleArguments ()
+    if multiFilemode:
+        findFiles ()
+    elif i == None:
+        print("an input file must be specified on the command line")
+        usage (1)
+    else:
+        singleFile (i)
+    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-10-07 20:21:18.682097332 +0100
@@ -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-10-07 20:21:18.682097332 +0100
@@ -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-10-07 20:21:18.682097332 +0100
@@ -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-10-07 20:21:18.682097332 +0100
@@ -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/mklink.c
--- /dev/null	2022-08-24 16:22:16.888000070 +0100
+++ gcc-git-devel-modula2/gcc/m2/tools-src/mklink.c	2022-10-07 20:21:18.682097332 +0100
@@ -0,0 +1,810 @@ 
+/* 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 defined(XENIX)
+      name[10] = (char)0; /* truncate object file name.  */
+#endif
+      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");
+    }
+}
diff -ruw /dev/null gcc-git-devel-modula2/gcc/m2/tools-src/def2texi.py
--- /dev/null	2022-08-24 16:22:16.888000070 +0100
+++ gcc-git-devel-modula2/gcc/m2/tools-src/def2texi.py	2022-10-07 20:21:18.682097332 +0100
@@ -0,0 +1,423 @@ 
+#!/usr/bin/env python3
+
+# def2texi.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 sys
+import os
+import glob
+import getopt
+
+libraryClassifications = [['gm2-libs','Base libraries',
+                           'Basic M2F compatible libraries'],
+                          ['gm2-libs-pim','PIM and Logitech 3.0 Compatible',
+                           'PIM and Logitech 3.0 compatible libraries'],
+                          ['gm2-libs-coroutines','PIM coroutine support',
+                           'PIM compatible process support'],
+                          ['gm2-libs-iso','M2 ISO Libraries',
+                           'ISO defined libraries']]
+
+def initState ():
+    global inVar, inType, inConst
+    inVar, inType, inConst = False, False, False
+
+
+#
+#  displayLibraryClass - displays a node for a library directory and invokes
+#                        a routine to summarize each module
+#
+
+def displayLibraryClass():
+    global buildDir, up
+    previous = ""
+
+    next=libraryClassifications[1][1]
+    i = 0
+    l = libraryClassifications[i]
+
+    while True:
+        print("@node " + l[1] + ", " + next + ", " + previous + ", " + up)
+        print("@section " + l[1])
+        print("")
+        displayModules(l[1], l[0], buildDir, sourceDir)
+        print("")
+        print("@c ---------------------------------------------------------------------")
+        previous = l[1]
+        i += 1
+        if i == len(libraryClassifications):
+            break
+        l = libraryClassifications[i]
+        if i+1 == len(libraryClassifications):
+            next = ""
+        else:
+            next = libraryClassifications[i+1][1]
+
+#
+#  displayMenu - displays the top level menu for library documentation
+#
+
+def displayMenu():
+    print("@menu")
+    for l in libraryClassifications:
+        print("* " + l[1] + "::" + l[2])
+    print("@end menu")
+
+    print("\n")
+    print("@c =====================================================================")
+    print("\n")
+
+
+#
+#  removeInitialComments - removes any (* *) at the top of the definition module
+#
+
+def removeInitialComments (file, line):
+    while (str.find(line, "*)") == -1):
+        line = file.readline()
+
+#
+#  removeFields - removes Author/Date/Last edit/SYSTEM/Revision fields from a comment within the start
+#                 of a definition module
+#
+
+def removeFields (file, line):
+    while (str.find(line, "*)") == -1):
+        if (str.find(line, "Author") != -1) and (str.find(line, ":") != -1):
+            line = file.readline()
+        elif (str.find(line, "Last edit") != -1) and (str.find(line, ":") != -1):
+            line = file.readline()
+        elif (str.find(line, "LastEdit") != -1) and (str.find(line, ":") != -1):
+            line = file.readline()
+        elif (str.find(line, "Last update") != -1) and (str.find(line, ":") != -1):
+            line = file.readline()
+        elif (str.find(line, "Date") != -1) and (str.find(line, ":") != -1):
+            line = file.readline()
+        elif (str.find(line, "Title") != -1) and (str.find(line, ":") != -1):
+            line = file.readline()
+        elif (str.find(line, "Revision") != -1) and (str.find(line, ":") != -1):
+            line = file.readline()
+        elif (str.find(line, "System") != -1) and (str.find(line, ":") != -1) and (str.find(line, "Description:") == -1):
+            line = file.readline()
+        elif (str.find(line, "SYSTEM") != -1) and (str.find(line, ":") != -1) and (str.find(line, "Description:") == -1):
+            line = file.readline()
+        else:
+           print(str.replace(str.replace(str.rstrip(line),
+                                            "{", "@{"), "}", "@}"))
+           line = file.readline()
+    print(str.rstrip(line))
+
+
+#
+#  checkIndex
+#
+
+def checkIndex (line):
+    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:
+                    print("@findex " + word + " (var)")
+                elif len(word)>0:
+                    var = str.split(word, ':')
+                    if len(var)>0:
+                        print("@findex " + var[0] + " (var)")
+
+    if inType:
+        words = str.lstrip(line)
+        if str.find(words, '=') != -1:
+            word = str.split(words, "=")
+            if (len(word[0])>0) and (word[0][0] != '_'):
+                print("@findex " + str.rstrip(word[0]) + " (type)")
+        else:
+            word = str.split(words)
+            if (len(word)>1) and (word[1] == ';'):
+                # hidden type
+                if (len(word[0])>0) and (word[0][0] != '_'):
+                    print("@findex " + str.rstrip(word[0]) + " (type)")
+
+    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:
+                        print("@findex " + var[0] + " (const)")
+
+    if procedure != "":
+        name = str.split(procedure, "(")
+        if name[0] != "":
+            proc = name[0]
+            if proc[-1] == ";":
+                proc = proc[:-1]
+            if proc != "":
+                print("@findex " + proc)
+
+
+#
+#  parseDefinition
+#
+
+def parseDefinition (dir, source, build, file, needPage):
+    print("")
+    f = open(findFile(dir, build, source, file), 'r')
+    initState()
+    line = f.readline()
+#   while (str.find(line, "(*") != -1):
+    while (str.find(line, "(*") != -1):
+        removeInitialComments(f, line)
+        line = f.readline()
+
+    while (str.find(line, "DEFINITION") == -1):
+        line = f.readline()
+
+    print("@example")
+    print(str.rstrip(line))
+    line = f.readline()
+    if len(str.rstrip(line)) == 0:
+        print(str.replace(str.replace(str.rstrip(line),
+                                            "{", "@{"), "}", "@}"))
+        line = f.readline()
+        if (str.find(line, "(*") != -1):
+            removeFields(f, line)
+        else:
+            print(str.rstrip(line))
+    else:
+        print(str.rstrip(line))
+
+    line = f.readline()
+    while line:
+        line = str.rstrip(line)
+        checkIndex(line)
+        print(str.replace(str.replace(line, "{", "@{"), "}", "@}"))
+        line = f.readline()
+    print("@end example")
+    if needPage:
+        print("@page")
+    f.close()
+
+def parseModules (up, dir, build, source, listOfModules):
+    previous = ""
+    i = 0
+    if len(listOfModules)>1:
+        next = dir + "/" + listOfModules[1][:-4]
+    else:
+        next = ""
+
+    while i<len(listOfModules):
+       print("@node " + dir + "/" + listOfModules[i][:-4] + ", " + next + ", " + previous + ", " + up)
+       print("@subsection " + dir + "/" + listOfModules[i][:-4])
+       parseDefinition(dir, source, build, listOfModules[i], True)
+       print("\n")
+       previous = dir + "/" + listOfModules[i][:-4]
+       i = i + 1
+       if i+1<len(listOfModules):
+           next = dir + "/" + listOfModules[i+1][:-4]
+       else:
+           next = ""
+
+
+#
+#  doCat - displays the contents of file, name, to stdout
+#
+
+def doCat (name):
+    file = open(name, 'r')
+    line = file.readline()
+    while line:
+        print(str.rstrip(line))
+        line = file.readline()
+    file.close()
+
+
+#
+#  moduleMenu - generates a simple menu for all definition modules
+#               in dir
+#
+
+def moduleMenu (dir, build, source):
+    print("@menu")
+    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'):
+                print("* " + dir + "/" + file[:-4] + "::" + file)
+    print("@end menu")
+    print("\n")
+
+
+#
+#  checkDirectory - returns True if dir exists in either build or source.
+#
+
+def checkDirectory (dir, build, 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
+
+
+#
+#  foundFile - return True if file is found in build/dir/file or source/dir/file.
+#
+
+def foundFile (dir, build, source, 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
+
+
+#
+#  findFile - return the path to file searching in build/dir/file first then source/dir/file.
+#
+
+def findFile (dir, build, source, 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
+    print("file cannot be found in either " + name1 + " or " + name2)
+    os.sys.exit(1)
+
+
+#
+#  displayModules - walks though the files in dir and parses
+#                   definition modules and includes README.texi
+#
+
+def displayModules(up, dir, build, source):
+    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:
+        print("directory " + dir + " not found in either " + build + " or " + source)
+
+
+def displayCopyright ():
+    print("@c Copyright (C) 2000-2022 Free Software Foundation, Inc.")
+    print("@c This file is part of GNU Modula-2.")
+    print("""
+@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 Usage():
+    print("def2texi.py [-h][-bbuilddir][-uupnode][-ffilename]")
+
+def collectArgs():
+    buildDir="."
+    sourceDir="."
+    filename=""
+    up=""
+    try:
+        optlist, list = getopt.getopt(sys.argv[1:],':hb:f:s:u:')
+    except getopt.GetoptError:
+        Usage()
+        os.sys.exit(1)
+    for opt in optlist:
+        if opt[0] == '-h':
+            Usage()
+        if opt[0] == '-b':
+            buildDir = opt[1]
+        if opt[0] == '-f':
+            filename = opt[1]
+        if opt[0] == '-s':
+            sourceDir = opt[1]
+        if opt[0] == '-u':
+            up = opt[1]
+    return buildDir, sourceDir, filename, up
+
+
+buildDir, sourceDir, filename, up = collectArgs()
+
+if filename == "":
+    displayCopyright()
+    displayMenu()
+    displayLibraryClass()
+else:
+    parseDefinition('.', sourceDir, buildDir, filename, False)