public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: "Martin Liška" <mliska@suse.cz>
To: Gaius Mulley <gaiusmod2@gmail.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] 16/19 modula2 front end: bootstrap and documentation tools
Date: Thu, 13 Oct 2022 11:12:16 +0200	[thread overview]
Message-ID: <11f42175-8e23-5da3-6a13-6172039bfca2@suse.cz> (raw)
In-Reply-To: <E1ohukX-00Bm41-MC@lancelot>

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)


  reply	other threads:[~2022-10-13  9:12 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-10 15:31 Gaius Mulley
2022-10-13  9:12 ` Martin Liška [this message]
2022-10-14 12:10   ` Gaius Mulley
2022-11-07 13:09     ` [PATCH v2 16/19] " Gaius Mulley
2022-11-07 13:47       ` Martin Liška
2022-11-08 13:22         ` Gaius Mulley
2022-11-24 11:53           ` Martin Liška
2022-11-24 14:30             ` Gaius Mulley
2022-11-25 16:25               ` Martin Liška
2022-11-25 17:10               ` David Malcolm
2022-11-27 16:51                 ` Gaius Mulley
2022-10-20 12:42 ` [PATCH] 16/19 " Martin Liška
2022-10-28 13:05   ` Gaius Mulley

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=11f42175-8e23-5da3-6a13-6172039bfca2@suse.cz \
    --to=mliska@suse.cz \
    --cc=gaiusmod2@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).