# -*- coding: utf-8 -*- # $Id$ """ Makes id-only reference in the given file into dita-compliant file#id references, using the mapping database generated by build_id_to_file_mapping.py. """ __copyright__ = \ """ Copyright (C) 2023 Oracle and/or its affiliates. This file is part of VirtualBox base platform packages, as available from https://www.virtualbox.org. 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, in version 3 of the License. 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 . SPDX-License-Identifier: GPL-3.0-only """ __version__ = "$Revision$" # Standard python imports. import glob; import os; import re; import sys; g_oReHref = re.compile(r'\bhref=("[^">#./]+"|\'[^\'>#./]+\')'); def modifyDitaFile(dIdToFile, sContent): """ Modifies the href attributes in this file. """ sModified = ''; offPrev = 0; for oMatch in g_oReHref.finditer(sContent): sId = oMatch.group(1)[1:-1]; if sId in dIdToFile: sModified += sContent[offPrev : oMatch.start(1)] + '"' + dIdToFile[sId] + '#' + sId + '"'; offPrev = oMatch.end(1); if offPrev < len(sContent): sModified += sContent[offPrev:]; return sModified; def info(sMessage): """ Info message. """ print('add_file_to_id_only_references.py: info: %s' % sMessage); return 1; def error(sMessage): """ Reports an error. """ print('add_file_to_id_only_references.py: error: %s' % sMessage, file = sys.stderr); return 1; def syntax(sMessage): """ Reports a syntax error. """ print('add_file_to_id_only_references.py: syntax error: %s' % sMessage, file = sys.stderr); return 2; def usage(): """ Reports usage. """ print('usage: add_file_to_id_only_references.py [--verbose|--quiet] --mapping-file file1.dita [file2.dita [...]]'); return 0; def main(asArgs): """ C-like main function. """ # # Process arguments. # dIdToFile = None; fEndOfArgs = False; fVerbose = False; iArg = 1; while iArg < len(asArgs): sArg = asArgs[iArg]; if sArg[0] == '-' and not fEndOfArgs: # Options. if sArg == '--': fEndOfArgs = True; elif sArg in ('--help', '-h', '-?'): return usage(); elif sArg in ('--version', '-V' ): print(__version__[__version__.find(':') + 2:-2]); elif sArg in ('--quiet', '-q' ): fVerbose = False; elif sArg in ('--verbose', '-v' ): fVerbose = True; elif sArg in ('--mapping-file', '-m'): iArg += 1; if iArg >= len(asArgs): return syntax('Expected filename following "--mapping-file"!'); # Load the database file. sArg = asArgs[iArg]; try: with open(sArg, 'r', encoding = 'utf-8') as oFile: dIdToFile = {}; for sLine in oFile: sId, sFile = sLine.split('='); dIdToFile[sId.strip()] = sFile.strip(); except Exception as oXcpt: # pylint: disable=broad-exception-caught return error('Failed to open and parse "%s": %s' % (sArg, oXcpt,)); if fVerbose: info('Loaded %s IDs from "%s"' % (len(dIdToFile), sArg)); else: return syntax('Unknown option: %s' % (sArg,)); else: # File to modify. if dIdToFile is None: return syntax('A mapping database must be given before any other files!'); try: with open(sArg, 'r', encoding = 'utf-8') as oFile: sContent = oFile.read(); except Exception as oXcpt: # pylint: disable=broad-exception-caught return error('Failed to open and read "%s": %s' % (sArg, oXcpt,)); sModified = modifyDitaFile(dIdToFile, sContent); if sModified != sContent: if fVerbose: info('Writing out modified "%s"...' % (sArg,)); try: with open(sArg, 'w', encoding = 'utf-8') as oFile: oFile.write(sModified); except Exception as oXcpt: # pylint: disable=broad-exception-caught return error('Failed to open and write back "%s": %s' % (sArg, oXcpt,)); elif fVerbose: info('No changes to "%s"...' % (sArg,)); iArg += 1; return 0; if __name__ == "__main__": sys.exit(main(sys.argv));