From 60cf749e9cb8f751346db0d378ac9ed93534db4d Mon Sep 17 00:00:00 2001 From: Louie Shprung Date: Thu, 24 Nov 2022 16:32:35 -0800 Subject: First commit as mandocsets; added ncurses --- .gitignore | 7 +++ Makefile | 30 ++++++++++++ README.md | 28 +++++++++++ getters/ncurses.sh | 12 +++++ mandocset.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100755 getters/ncurses.sh create mode 100644 mandocset.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82f3668 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +* +!.gitignore +!getters +!getters/* +!Makefile +!mandocset.py +!README.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a6fd474 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +all: begin $(DOCSET) $(DOCSET).docset + +# Check that DOCSET is set +.PHONY: begin +begin: +ifndef DOCSET + $(error Error: DOCSET is not defined) +endif + +# Get DOCSET from system files +$(DOCSET): getters/$(DOCSET).sh + rm -rf $(DOCSET) + getters/$(DOCSET).sh + +# Convert to dash docset using python script +$(DOCSET).docset: $(DOCSET) mandocset.py + python3 mandocset.py -o $(DOCSET) -p $(DOCSET)/ + +# Remove generated files/directories +.PHONY: clean +clean: begin + rm -rf $(DOCSET) + rm -rf $(DOCSET).docset + rm -rf $(DOCSET).tgz + +# Create .tgz archive of generated docset directory +.PHONY: tgz +tgz: begin $(DOCSET).tgz +$(DOCSET).tgz: $(DOCSET).docset + tar --exclude='.DS_Store' -cvzf $(DOCSET).tgz $(DOCSET).docset diff --git a/README.md b/README.md new file mode 100644 index 0000000..74cc38a --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# mandocset + +This is python script (mandocset.py) that generates Dash docset from man pages. It takes folders with man pages as it's arguments. Then in each folder it finds all folders, containing digit in their name, runs `man2html -r` for each file inside them. + +By default script uses man2html utility. If you prefer pandoc just use `-e "pandoc -f man -t html"`. + +--- + +This is a loose fork of [Yanpas' repository](https://github.com/Yanpas/mandocset), forked to help package additional man page collections. Currently, this fork only supports Debian and Debian-based systems. + +### Prerequisites + +- `apt-file` +- `coreutils` +- `make` +- `man2html` +- `python3` + +``` +# apt install apt-file coreutils make man2html python3 +``` + +### Usage + +Available docsets to build are listed under `getters/`. To build a docset, run `make DOCSET=[DOCSET]`, where `[DOCSET]` is the docset you wish to generate (the name of the script in `getters/` minus the extension). Additional make directives include: + +- `make clean` - remove the directories created when building the docset +- `make tgz` - Create a .tgz archive for easily submitting to [Dash-User-Contributions](https://github.com/Kapeli/Dash-User-Contributions) diff --git a/getters/ncurses.sh b/getters/ncurses.sh new file mode 100755 index 0000000..e933896 --- /dev/null +++ b/getters/ncurses.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env sh + +SOURCE="$(apt-file list ncurses-doc | grep -Eo "/usr/share/man/.*")" +DESTINATION="$(dirname "$0")/../ncurses/" + +# Copy files to source +echo "$SOURCE" | while read -r line; do + unset CATEGORY + CATEGORY="$(basename "$(dirname "$line")")" + mkdir -p "$DESTINATION/$CATEGORY" + cp "$line" "$DESTINATION/$CATEGORY" +done diff --git a/mandocset.py b/mandocset.py new file mode 100644 index 0000000..277eff6 --- /dev/null +++ b/mandocset.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +''' +Created on 19 feb 2017 + +DocsetMaker + +@author: yanpas +''' + +import sqlite3, argparse, os, re, subprocess, sys +import shutil +from typing import List + +def getPlist(name: str) -> str: + return ''' + + + + CFBundleIdentifier + {} + CFBundleName + {} + DocSetPlatformFamily + {} + isDashDocset + + + +'''.format(name.split('_')[0], + name.replace('_', ' '), + name.split('_')[0].lower()) + +def toHtml(executable: List[str], inf: str, outdir: str, basedir: str): + name = os.path.basename(inf) + inf_f = open(inf) + for suff, decoder in [('.gz', 'gzip'), ('.bz2', 'bzip2')]: + if inf.endswith(suff): + name = ''.join(name.rsplit(suff, 1)) + inf_f = subprocess.Popen([decoder, '-d'], stdin=inf_f, stdout=subprocess.PIPE).stdout + break + subp = subprocess.Popen(executable, stdout=subprocess.PIPE, stdin=inf_f) + subp.stdout.readline() # skip Content-Type http header + outpath = os.path.join(basedir, name) + '.html' + with open(os.path.join(outdir, outpath), 'wb') as f: + f.write(subp.stdout.read()) + if subp.wait() != 0: + print(executable[0], "error:", subp.returncode, file=sys.stderr) + return outpath + +def getType(n): + if n == 1: + return 'Command' + elif n == 2: + return 'Service' # Command and Callback have too similar icons + elif n == 3: + return 'Function' + else: + return 'Object' + +class DocsetMaker: + fldre = re.compile(r'\w*(\d)\w*') + manfre = re.compile(r'(.+)\..*?\d.*') + + def __init__(self, outname, executable: str): + self.outname = outname + self.dups = set() + self.db = None + self.executable = executable.split() + + def __enter__(self): + os.makedirs(self.outname + '.docset/Contents/Resources/Documents') + self.db = sqlite3.connect(self.outname + '.docset/Contents/Resources/docSet.dsidx') + self.db.execute('CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);') + self.db.execute('CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path);') + with open(self.outname + '.docset/Contents/Info.plist', 'w') as plist: + plist.write(getPlist(self.outname)) + return self + + def __exit__(self, *oth): + self.db.close() + + def scanDirectory(self, path1, it, mannum): + outbase = self.outname + '.docset/Contents/Resources/Documents' + os.makedirs(os.path.join(outbase, it), exist_ok=True) + for jt in os.listdir(path1): + manf = os.path.join(path1, jt) + if os.path.isfile(manf) and re.match(DocsetMaker.manfre, jt): + print('\tman', jt) + fname = os.path.join(it, os.path.basename(manf)) + '.html' + name_for_db = re.match(DocsetMaker.manfre, jt).group(1) + dashtype = getType(mannum) + new_el = (mannum, name_for_db) + if not (new_el in self.dups): + outpath = toHtml(self.executable, manf, outbase, it) + self.db.execute('INSERT OR IGNORE INTO searchIndex(name, type, path) VALUES (?,?,?);', + [name_for_db, dashtype, outpath]) + self.dups.add(new_el) + else: + print('\tduplicate skipped',fname) + + def addToDocset(self, indir): + self.db.execute("BEGIN") + for it in os.listdir(indir): + path1 = os.path.join(indir, it) + if os.path.isdir(path1): + mo = re.match(DocsetMaker.fldre, it) + if mo: + print('dir', it) + self.scanDirectory(path1, it, int(mo.group(1))) + self.db.commit() + +def main(): + argp = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + argp.add_argument('-p', '--paths', help='paths with unpacked archive, order matters', nargs='+', required=True) + argp.add_argument('-o', '--out', help='new docset name', required=True) + argp.add_argument('-f', help='force outdir', action='store_true') + argp.add_argument('-i', help='x1 icon (16x16)', metavar='icon.png') + argp.add_argument('-I', help='x2 icon (32x32)', metavar='icon@2x.png') + argp.add_argument('-e', default='man2html -r', + help=('Executable with arguments which reads from stdin and writes to stdout.' + ' Alterntaively "pandoc -f man -t html" may be used')) + args = argp.parse_args() + if ' ' in args.out: + exit('spaces are forbidden in outname') + outpath = args.out + '.docset' + if os.path.exists(outpath): + if args.f: + shutil.rmtree(outpath) + else: + exit('path already exists, exiting (use "-f" to ignore this)') + with DocsetMaker(args.out, args.e) as dsm: + for path in args.paths: + dsm.addToDocset(path) + for name,path in [('icon.png', args.i), ('icon@2x.png', args.I)]: + if path: + shutil.copyfile(path, os.path.join(outpath, name)) + +if __name__ == '__main__': + main() -- cgit