summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Laboissière <rafael@debian.org>2024-02-13 11:57:55 -0300
committerRafael Laboissière <rafael@debian.org>2024-02-13 11:57:55 -0300
commit2e408fde1c6995a65abcb4dc4bdd37ef1359c63b (patch)
tree7e8d83f5fdd07f509372759ec44f704bc67f86a8
parent569a17f20295024fa11d76a7a6a9f4070eac851a (diff)
Add lacking upstream file doc/mkfuncdocs.py
Origin: upstream, https://github.com/gnu-octave/octave-arduino/blob/main/doc/mkfuncdocs.py Forwarded: not-needed Last-Update: 2024-01-28 Gbp-Pq: Name add-mkfuncdocs-py.patch
-rw-r--r--doc/mkfuncdocs.py382
1 files changed, 382 insertions, 0 deletions
diff --git a/doc/mkfuncdocs.py b/doc/mkfuncdocs.py
new file mode 100644
index 0000000..2e851ba
--- /dev/null
+++ b/doc/mkfuncdocs.py
@@ -0,0 +1,382 @@
+#!/usr/bin/python3
+
+## Copyright 2018-2023 John Donoghue
+##
+## This program 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 of the License, or
+## (at your option) any later version.
+##
+## This program 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 this program. If not, see
+## <https://www.gnu.org/licenses/>.
+
+## mkfuncdocs v1.0.7
+## mkfuncdocs.py will attempt to extract the help texts from functions in src
+## dirs, extracting only those that are in the specifed INDEX file and output them
+## to stdout in texi format
+##
+## It will extract from both .m and the help text for DEFUN_DLD help in .cc/.cpp
+## files.
+##
+## It attempts to find the help text for each function in a file within the src search
+## folders that match in order: [ functionname.m functionname.cc functionname.cpp
+## functionname_withoutprefix.cc functionname_withoutprefix.cpp ]
+##
+## Usage:
+## mkfundocs.py options INDEXfile
+## Options can be 0 or more of:
+## --verbose : Turn on verbose mode
+## --src-dir=xxxxx : Add dir xxxxx to the dirs searched for the function file.
+## If no directories are provided, it will default to looking in the
+## 'inst' directory.
+## --ignore=xxxxx : dont attempt to generate help for function xxxxx.
+## --funcprefix=xxxxx : remove xxxxx from the function name when searching for matching
+## source file.
+## --allowscan : if can not find function, attemp to scan .cc,cpp,cxx files for match
+##
+## --standalone : generate a texinfo file expected to be used with being included in
+## another document file.
+
+import sys
+import os
+import re
+import tempfile
+import shutil
+import fnmatch
+import subprocess
+import glob
+import calendar;
+import time;
+
+class Group:
+ name = "Functions"
+ functions = []
+
+ def __init__ (self, name=""):
+ if name:
+ self.name = name
+ self.functions = []
+
+class Index:
+ name = ""
+ groups = []
+
+def texify_line(line):
+ # convert any special chars in a line to texinfo format
+ # currently just used for group formatting ?
+ line = line.replace("@", "@@")
+ line = line.replace("{", "@{")
+ line = line.replace("}", "@}")
+ line = line.replace(",", "@comma{}")
+ return line
+
+def find_defun_line_in_file(filename, fnname):
+ linecnt = 0
+
+ defun_line=re.compile(r"^DEFUN_DLD\s*\(\s*{}".format(fnname))
+ with open(filename, 'rt') as f:
+ for line in f:
+ if re.match(defun_line, line):
+ return linecnt
+
+ linecnt = linecnt + 1
+
+ return -1
+
+def read_m_file(filename, skip=0):
+ help = []
+ inhelp = False
+ havehelp = False;
+ with open(filename, 'rt') as f:
+ for line in f:
+ line = line.lstrip()
+ if skip > 0:
+ skip = skip - 1
+ elif not havehelp:
+ if havehelp == False and inhelp == False and line.startswith('##'):
+ if "texinfo" in line:
+ inhelp = True
+ elif inhelp == True:
+ if not line.startswith('##'):
+ inhelp = False
+ havehelp = True
+ else:
+ if line.startswith("## @"):
+ line = line[3:]
+ else:
+ line = line[2:]
+ help.append (line.rstrip());
+
+ return help
+
+def read_cc_file(filename, skip=0):
+ help = []
+ inhelp = False
+ havehelp = False;
+ with open(filename, 'rt') as f:
+ for line in f:
+ line = line.lstrip()
+ if skip > 0:
+ skip = skip - 1
+ elif not havehelp:
+ if havehelp == False and inhelp == False:
+ if "texinfo" in line:
+ inhelp = True
+ elif inhelp == True:
+ line = line.rstrip()
+ if len(line) > 0 and line[-1] == '\\':
+ line = line[:-1]
+ line = line.rstrip()
+
+ line = line.replace("\\n", "\n")
+ line = line.replace("\\\"", "\"")
+
+ if len(line) > 0 and line[-1] == '\n':
+ line = line[:-1]
+ # endif a texinfo line
+ elif line.endswith('")'):
+ line = line[:-2]
+
+ if line.startswith('{'):
+ inhelp = False
+ havehelp = True
+ else:
+ help.append (line);
+
+ return help
+
+def read_help (filename, skip=0):
+ help = []
+
+ if filename[-2:] == ".m":
+ help = read_m_file(filename, skip)
+ else:
+ help = read_cc_file(filename, skip)
+
+ return help
+
+def read_index (filename, ignore):
+ index = Index ()
+
+ with open(filename, 'rt') as f:
+ lines = f.read().splitlines()
+
+ #print ("read", lines)
+ first = True
+ category = Group()
+ for l in lines:
+ if l.startswith("#"):
+ pass
+ elif first:
+ index.name = l;
+ first = False
+ elif l.startswith(" "):
+ l = l.strip()
+ # may be multiple functions here
+ funcs = l.split()
+ for f in funcs:
+ if f not in ignore:
+ category.functions.append(f);
+ else:
+ # new category name
+ if len(category.functions) > 0:
+ index.groups.append(category)
+ category = Group(l.strip())
+
+ # left over category ?
+ if len(category.functions) > 0:
+ index.groups.append(category)
+
+ return index;
+
+def find_func_file(fname, paths, prefix, scanfiles=False):
+ for f in paths:
+ name = f + "/" + fname + ".m"
+ if os.path.isfile(name):
+ return name, 0
+ # class constructor ?
+ name = f + "/@" + fname + "/" + fname + ".m"
+ if os.path.isfile(name):
+ return name, 0
+ name = f + "/" + fname + ".cc"
+ name = f + "/" + fname + ".cc"
+ if os.path.isfile(name):
+ return name, 0
+ name = f + "/" + fname + ".cpp"
+ if os.path.isfile(name):
+ return name, 0
+ # if have a prefix, remove and try
+ if prefix and fname.startswith(prefix):
+ fname = fname[len(prefix):]
+ name = f + "/" + fname + ".cc"
+ if os.path.isfile(name):
+ return name, 0
+ name = f + "/" + fname + ".cpp"
+ if os.path.isfile(name):
+ return name, 0
+
+ # if here, we still dont have a file match
+ # if allowed to scan files, do that
+ if scanfiles:
+ #sys.stderr.write("Warning: Scaning for {}\n".format(fname))
+ for f in paths:
+ files = list(f + "/" + a for a in os.listdir(f))
+ cc_files = fnmatch.filter(files, "*.cc")
+ cpp_files = fnmatch.filter(files, "*.cpp")
+ cxx_files = fnmatch.filter(files, "*.cxx")
+
+ for fn in cc_files + cpp_files + cxx_files:
+ line = find_defun_line_in_file(fn, fname)
+ if line >= 0:
+ #sys.stderr.write("Warning: Found function for {} in {} at {}\n".format(fname, fn, line))
+ return fn, line
+
+ return None, -1
+
+def display_standalone_header():
+ # make a file that doesnt need to be included in a texinfo file to
+ # be valid
+ print("@c mkfuncdocs output for a standalone function list")
+ print("@include macros.texi")
+ print("@ifnottex")
+ print("@node Top")
+ print("@top Function Documentation")
+ print("Function documentation extracted from texinfo source in octave source files.")
+ print("@contents")
+ print("@end ifnottex")
+ print("@node Function Reference")
+ print("@chapter Function Reference")
+ print("@cindex Function Reference")
+
+def display_standalone_footer():
+ print("@bye")
+
+def display_func(name, ref, help):
+ print ("@c -----------------------------------------")
+ print ("@subsection {}".format(name))
+ print ("@cindex {}".format(ref))
+ for l in help:
+ print ("{}".format(l))
+
+def process (args):
+ options = {
+ "verbose": False,
+ "srcdir": [],
+ "funcprefix": "",
+ "ignore": [],
+ "standalone": False,
+ "allowscan": False
+ }
+ indexfile = ""
+
+ for a in args:
+ #print ("{}".format(a))
+ c=a.split("=")
+ key=c[0]
+
+ if len(c) > 1:
+ val=c[1]
+ else:
+ val=""
+
+ if key == "--verbose":
+ options["verbose"] = True;
+ if key == "--standalone":
+ options["standalone"] = True;
+ elif key == "--allowscan":
+ options["allowscan"] = True;
+ elif key == "--src-dir":
+ if val:
+ options["srcdir"].append(val);
+ elif key == "--ignore":
+ if val:
+ options["ignore"].append(val);
+ elif key == "--func-prefix":
+ if val:
+ options["funcprefix"] = val;
+ elif val == "":
+ if indexfile == "":
+ indexfile = key
+
+ if indexfile == "":
+ raise Exception("No index filename")
+
+ if len(options["srcdir"]) == 0:
+ options["srcdir"].append("inst")
+
+ #print "options=", options
+ if options['standalone']:
+ display_standalone_header()
+
+ idx = read_index(indexfile, options["ignore"])
+ for g in idx.groups:
+ #print ("************ {}".format(g.name))
+ g_name = texify_line(g.name)
+ print ("@c ---------------------------------------------------")
+ print ("@node {}".format(g_name))
+ print ("@section {}".format(g_name))
+ print ("@cindex {}".format(g_name))
+
+ for f in sorted(g.functions):
+ print ("@c {} {}".format(g_name, f))
+ h = ""
+ filename = ""
+ path = ""
+ if "@" in f:
+ #print ("class func")
+ path = f
+ name = "@" + f
+ ref = f.split("/")[-1]
+ filename, lineno = find_func_file(path, options["srcdir"], options["funcprefix"])
+ elif "." in f:
+ parts = f.split('.')
+ cnt = 0
+ path = ""
+ for p in parts:
+ if cnt < len(parts)-1:
+ path = path + "/+"
+ else:
+ path = path + "/"
+ path = path + p
+ cnt = cnt + 1
+ name = f;
+ ref = parts[-1]
+ filename, lineno = find_func_file(path, options["srcdir"], options["funcprefix"])
+ elif "/" in f:
+ path = f
+ name = f
+ ref = f.split("/")[-1]
+ filename, lineno = find_func_file(path, options["srcdir"], options["funcprefix"])
+ else:
+ path = f
+ name = f
+ ref = f
+ filename, lineno = find_func_file(path, options["srcdir"], options["funcprefix"], options['allowscan'])
+
+ if not filename:
+ sys.stderr.write("Warning: Cant find source file for {}\n".format(path))
+ else:
+ h = read_help (filename, lineno)
+
+ if h:
+ display_func (name, ref, h)
+
+ if options['standalone']:
+ display_standalone_footer()
+
+
+def show_usage():
+ print (sys.argv[0], "[options] indexfile")
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ status = process(sys.argv[1:])
+ sys.exit(status)
+ else:
+ show_usage()