1e5c31af7Sopenharmony_ci#!/usr/bin/python3
2e5c31af7Sopenharmony_ci#
3e5c31af7Sopenharmony_ci# Copyright 2015-2024 The Khronos Group Inc.
4e5c31af7Sopenharmony_ci#
5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0
6e5c31af7Sopenharmony_ci
7e5c31af7Sopenharmony_ci# checkLinks.py - validate link/reference API constructs in files
8e5c31af7Sopenharmony_ci#
9e5c31af7Sopenharmony_ci# Usage: checkLinks.py [options] files > logfile
10e5c31af7Sopenharmony_ci#
11e5c31af7Sopenharmony_ci# Options:
12e5c31af7Sopenharmony_ci# -follow attempt to follow include:: directives. This script is not an
13e5c31af7Sopenharmony_ci#  Asciidoctor processor, so only literal relative paths can be followed.
14e5c31af7Sopenharmony_ci# -info print some internal diagnostics.
15e5c31af7Sopenharmony_ci# -paramcheck attempt to validate param: names against the surrounding
16e5c31af7Sopenharmony_ci#  context (the current structure/function being validated, for example).
17e5c31af7Sopenharmony_ci#  This generates many false positives, so is not enabled by default.
18e5c31af7Sopenharmony_ci# -fatal unvalidatable links cause immediate error exit from the script.
19e5c31af7Sopenharmony_ci#  Otherwise, errors are accumulated and summarized at the end.
20e5c31af7Sopenharmony_ci#
21e5c31af7Sopenharmony_ci# Depends on vkapi.py, which is a Python representation of relevant parts
22e5c31af7Sopenharmony_ci# of the Vulkan API. Only works when vkapi.py is generated for the full
23e5c31af7Sopenharmony_ci# API, e.g. 'makeAllExts checklinks'; otherwise many false-flagged errors
24e5c31af7Sopenharmony_ci# will occur.
25e5c31af7Sopenharmony_ci
26e5c31af7Sopenharmony_ciimport copy, os, pdb, re, string, sys
27e5c31af7Sopenharmony_cifrom vkapi import *
28e5c31af7Sopenharmony_ci
29e5c31af7Sopenharmony_ciglobal curFile, curLine, sectionDepth
30e5c31af7Sopenharmony_ciglobal errCount, warnCount, emittedPrefix, printInfo
31e5c31af7Sopenharmony_ci
32e5c31af7Sopenharmony_cicurFile = '???'
33e5c31af7Sopenharmony_cicurLine = -1
34e5c31af7Sopenharmony_cisectionDepth = 0
35e5c31af7Sopenharmony_ciemittedPrefix = {}
36e5c31af7Sopenharmony_ciprintInfo = False
37e5c31af7Sopenharmony_ci
38e5c31af7Sopenharmony_ci# Called before printing a warning or error. Only prints once prior
39e5c31af7Sopenharmony_ci# to output for a given file.
40e5c31af7Sopenharmony_cidef emitPrefix():
41e5c31af7Sopenharmony_ci    global curFile, curLine, emittedPrefix
42e5c31af7Sopenharmony_ci    if (curFile not in emittedPrefix.keys()):
43e5c31af7Sopenharmony_ci        emittedPrefix[curFile] = None
44e5c31af7Sopenharmony_ci        print('Checking file:', curFile)
45e5c31af7Sopenharmony_ci        print('-------------------------------')
46e5c31af7Sopenharmony_ci
47e5c31af7Sopenharmony_cidef info(*args, **kwargs):
48e5c31af7Sopenharmony_ci    global curFile, curLine, printInfo
49e5c31af7Sopenharmony_ci    if (printInfo):
50e5c31af7Sopenharmony_ci
51e5c31af7Sopenharmony_ci        emitPrefix()
52e5c31af7Sopenharmony_ci        print('INFO: %s line %d:' % (curFile, curLine),
53e5c31af7Sopenharmony_ci            ' '.join([str(arg) for arg in args]))
54e5c31af7Sopenharmony_ci
55e5c31af7Sopenharmony_ci# Print a validation warning found in a file
56e5c31af7Sopenharmony_cidef warning(*args, **kwargs):
57e5c31af7Sopenharmony_ci    global curFile, curLine, warnCount
58e5c31af7Sopenharmony_ci
59e5c31af7Sopenharmony_ci    warnCount = warnCount + 1
60e5c31af7Sopenharmony_ci    emitPrefix()
61e5c31af7Sopenharmony_ci    print('WARNING: %s line %d:' % (curFile, curLine),
62e5c31af7Sopenharmony_ci        ' '.join([str(arg) for arg in args]))
63e5c31af7Sopenharmony_ci
64e5c31af7Sopenharmony_ci# Print a validation error found in a file
65e5c31af7Sopenharmony_cidef error(*args, **kwargs):
66e5c31af7Sopenharmony_ci    global curFile, curLine, errCount
67e5c31af7Sopenharmony_ci
68e5c31af7Sopenharmony_ci    errCount = errCount + 1
69e5c31af7Sopenharmony_ci    emitPrefix()
70e5c31af7Sopenharmony_ci    print('ERROR: %s line %d:' % (curFile, curLine),
71e5c31af7Sopenharmony_ci        ' '.join([str(arg) for arg in args]))
72e5c31af7Sopenharmony_ci
73e5c31af7Sopenharmony_ci# See if a tag value exists in the specified dictionary and
74e5c31af7Sopenharmony_ci# suggest it as an alternative if so.
75e5c31af7Sopenharmony_cidef checkTag(tag, value, dict, dictName, tagName):
76e5c31af7Sopenharmony_ci    if (value in dict.keys()):
77e5c31af7Sopenharmony_ci        warning(value, 'exists in the API but not as a',
78e5c31af7Sopenharmony_ci            tag + ': .', 'Try using the', tagName + ': tag.')
79e5c31af7Sopenharmony_ci
80e5c31af7Sopenharmony_ci# Report an error due to an asciidoc tag which does not match
81e5c31af7Sopenharmony_ci# a corresponding API entity.
82e5c31af7Sopenharmony_cidef foundError(errType, tag, value, fatal):
83e5c31af7Sopenharmony_ci    global curFile, curLine
84e5c31af7Sopenharmony_ci    error('no such', errType, tag + ':' + value)
85e5c31af7Sopenharmony_ci    # Try some heuristics to detect likely problems such as missing vk
86e5c31af7Sopenharmony_ci    # prefixes or the wrong tag.
87e5c31af7Sopenharmony_ci
88e5c31af7Sopenharmony_ci    # Look in all the dictionaries in vkapi.py to see if the tag
89e5c31af7Sopenharmony_ci    # is just wrong but the API entity actually exists.
90e5c31af7Sopenharmony_ci    checkTag(tag, value, flags,   'flags', 'tlink/tname')
91e5c31af7Sopenharmony_ci    checkTag(tag, value, enums,   'enums', 'elink')
92e5c31af7Sopenharmony_ci    checkTag(tag, value, structs, 'structs', 'slink/sname')
93e5c31af7Sopenharmony_ci    checkTag(tag, value, handles, 'handles', 'slink/sname')
94e5c31af7Sopenharmony_ci    checkTag(tag, value, defines, 'defines', 'slink/sname')
95e5c31af7Sopenharmony_ci    checkTag(tag, value, consts,  'consts', 'ename')
96e5c31af7Sopenharmony_ci    checkTag(tag, value, protos,  'protos', 'flink/fname')
97e5c31af7Sopenharmony_ci    checkTag(tag, value, funcpointers, 'funcpointers', 'tlink/tname')
98e5c31af7Sopenharmony_ci
99e5c31af7Sopenharmony_ci    # Look for missing vk prefixes (quirky since it is case-dependent)
100e5c31af7Sopenharmony_ci    # NOT DONE YET
101e5c31af7Sopenharmony_ci
102e5c31af7Sopenharmony_ci    if fatal:
103e5c31af7Sopenharmony_ci        print('ERROR: %s line %d:' % (curFile, curLine),
104e5c31af7Sopenharmony_ci            ' '.join(['no such', errType, tag + ':' + value]), file=sys.stderr)
105e5c31af7Sopenharmony_ci        sys.exit(1)
106e5c31af7Sopenharmony_ci
107e5c31af7Sopenharmony_ci# Look for param in the list of all parameters of the specified functions
108e5c31af7Sopenharmony_ci# Returns True if found, False otherwise
109e5c31af7Sopenharmony_cidef findParam(param, funclist):
110e5c31af7Sopenharmony_ci    for f in funclist:
111e5c31af7Sopenharmony_ci        if (param in protos[f]):
112e5c31af7Sopenharmony_ci            info('parameter:', param, 'found in function:', f)
113e5c31af7Sopenharmony_ci            return True
114e5c31af7Sopenharmony_ci    return False
115e5c31af7Sopenharmony_ci
116e5c31af7Sopenharmony_ci# Initialize tracking state for checking links/includes
117e5c31af7Sopenharmony_cidef initChecks():
118e5c31af7Sopenharmony_ci    global curFile, curLine, curFuncs, curStruct, accumFunc, sectionDepth
119e5c31af7Sopenharmony_ci    global errCount, warnCount
120e5c31af7Sopenharmony_ci    global incPat, linkPat, pathPat, sectionPat
121e5c31af7Sopenharmony_ci
122e5c31af7Sopenharmony_ci    # Matches asciidoc single-line section tags
123e5c31af7Sopenharmony_ci    sectionPat = re.compile('^(=+) ')
124e5c31af7Sopenharmony_ci
125e5c31af7Sopenharmony_ci    # Matches any asciidoc include:: directive
126e5c31af7Sopenharmony_ci    pathPat = re.compile('^include::([\w./_]+)\[\]')
127e5c31af7Sopenharmony_ci
128e5c31af7Sopenharmony_ci    # Matches asciidoc include:: directives used in spec/ref pages (and also
129e5c31af7Sopenharmony_ci    # others such as validity). This is specific to the layout of the api/
130e5c31af7Sopenharmony_ci    # includes and allows any path preceding 'api/' followed by the category
131e5c31af7Sopenharmony_ci    # (protos, structs, enums, etc.) followed by the name of the proto,
132e5c31af7Sopenharmony_ci    # struct, etc. file.
133e5c31af7Sopenharmony_ci    incPat = re.compile('^.*api/(\w+)/(\w+)\.adoc')
134e5c31af7Sopenharmony_ci
135e5c31af7Sopenharmony_ci    # Lists of current /protos/ (functions) and /structs/ includes. There
136e5c31af7Sopenharmony_ci    # can be several protos contiguously for different forms of a command
137e5c31af7Sopenharmony_ci    curFuncs = []
138e5c31af7Sopenharmony_ci    curStruct = None
139e5c31af7Sopenharmony_ci
140e5c31af7Sopenharmony_ci    # Tag if we should accumulate funcs or start a new list. Any intervening
141e5c31af7Sopenharmony_ci    # pname: tags or struct includes will restart the list.
142e5c31af7Sopenharmony_ci    accumFunc = False
143e5c31af7Sopenharmony_ci
144e5c31af7Sopenharmony_ci    # Matches all link names in the current spec/man pages. Assumes these
145e5c31af7Sopenharmony_ci    # macro names are not trailing subsets of other macros. Used to
146e5c31af7Sopenharmony_ci    # precede the regexp with [^A-Za-z], but this did not catch macros
147e5c31af7Sopenharmony_ci    # at start of line.
148e5c31af7Sopenharmony_ci    linkPat = re.compile('([efpst](name|link)):(\w*)')
149e5c31af7Sopenharmony_ci
150e5c31af7Sopenharmony_ci    # Total error/warning counters
151e5c31af7Sopenharmony_ci    errCount = 0
152e5c31af7Sopenharmony_ci    warnCount = 0
153e5c31af7Sopenharmony_ci
154e5c31af7Sopenharmony_ci# Validate asciidoc internal links in specified file.
155e5c31af7Sopenharmony_ci#   infile - filename to validate
156e5c31af7Sopenharmony_ci#   follow - if True, recursively follow include:: directives
157e5c31af7Sopenharmony_ci#   paramCheck - if True, try to verify pname: refers to valid
158e5c31af7Sopenharmony_ci#   parameter/member names. This generates many false flags currently
159e5c31af7Sopenharmony_ci#   included - if True, function was called recursively
160e5c31af7Sopenharmony_ci#   fatalExit - if True, validation errors cause an error exit immediately
161e5c31af7Sopenharmony_ci# Links checked are:
162e5c31af7Sopenharmony_ci#   fname:vkBlah     - Vulkan command name (generates internal link)
163e5c31af7Sopenharmony_ci#   flink:vkBlah     - Vulkan command name
164e5c31af7Sopenharmony_ci#   sname:VkBlah     - Vulkan struct name (generates internal link)
165e5c31af7Sopenharmony_ci#   slink:VkBlah     - Vulkan struct name
166e5c31af7Sopenharmony_ci#   elink:VkEnumName - Vulkan enumeration ('enum') type name (generates internal link)
167e5c31af7Sopenharmony_ci#   ename:VK_BLAH    - Vulkan enumerant token name
168e5c31af7Sopenharmony_ci#   pname:name       - parameter name to a command or a struct member
169e5c31af7Sopenharmony_ci#   tlink:name       - Other Vulkan type name (generates internal link)
170e5c31af7Sopenharmony_ci#   tname:name       - Other Vulkan type name
171e5c31af7Sopenharmony_cidef checkLinks(infile, follow = False, paramCheck = True, included = False, fatalExit = False):
172e5c31af7Sopenharmony_ci    global curFile, curLine, curFuncs, curStruct, accumFunc, sectionDepth
173e5c31af7Sopenharmony_ci    global errCount, warnCount
174e5c31af7Sopenharmony_ci    global incPat, linkPat, pathPat, sectionPat
175e5c31af7Sopenharmony_ci
176e5c31af7Sopenharmony_ci    # Global state which gets saved and restored by this function
177e5c31af7Sopenharmony_ci    oldCurFile = curFile
178e5c31af7Sopenharmony_ci    oldCurLine = curLine
179e5c31af7Sopenharmony_ci    curFile = infile
180e5c31af7Sopenharmony_ci    curLine = 0
181e5c31af7Sopenharmony_ci
182e5c31af7Sopenharmony_ci    # N.b. dirname() returns an empty string for a path with no directories,
183e5c31af7Sopenharmony_ci    # unlike the shell dirname(1).
184e5c31af7Sopenharmony_ci    if (not os.path.exists(curFile)):
185e5c31af7Sopenharmony_ci        error('No such file', curFile, '- skipping check')
186e5c31af7Sopenharmony_ci        # Restore global state before exiting the function
187e5c31af7Sopenharmony_ci        curFile = oldCurFile
188e5c31af7Sopenharmony_ci        curLine = oldCurLine
189e5c31af7Sopenharmony_ci        return
190e5c31af7Sopenharmony_ci
191e5c31af7Sopenharmony_ci    inPath = os.path.dirname(curFile)
192e5c31af7Sopenharmony_ci    fp = open(curFile, 'r', encoding='utf-8')
193e5c31af7Sopenharmony_ci
194e5c31af7Sopenharmony_ci    for line in fp:
195e5c31af7Sopenharmony_ci        curLine = curLine + 1
196e5c31af7Sopenharmony_ci
197e5c31af7Sopenharmony_ci        # Track changes up and down section headers, and forget
198e5c31af7Sopenharmony_ci        # the current functions/structure when popping up a level
199e5c31af7Sopenharmony_ci        match = sectionPat.search(line)
200e5c31af7Sopenharmony_ci        if (match):
201e5c31af7Sopenharmony_ci            info('Match sectionPat for line:', line)
202e5c31af7Sopenharmony_ci            depth = len(match.group(1))
203e5c31af7Sopenharmony_ci            if (depth < sectionDepth):
204e5c31af7Sopenharmony_ci                info('Resetting current function/structure for section:', line)
205e5c31af7Sopenharmony_ci                curFuncs = []
206e5c31af7Sopenharmony_ci                curStruct = None
207e5c31af7Sopenharmony_ci            sectionDepth = depth
208e5c31af7Sopenharmony_ci
209e5c31af7Sopenharmony_ci        match = pathPat.search(line)
210e5c31af7Sopenharmony_ci        if (match):
211e5c31af7Sopenharmony_ci            incpath = match.group(1)
212e5c31af7Sopenharmony_ci            info('Match pathPat for line:', line)
213e5c31af7Sopenharmony_ci            info('  incpath =', incpath)
214e5c31af7Sopenharmony_ci            # An include:: directive. First check if it looks like a
215e5c31af7Sopenharmony_ci            # function or struct include file, and modify the corresponding
216e5c31af7Sopenharmony_ci            # current function or struct state accordingly.
217e5c31af7Sopenharmony_ci            match = incPat.search(incpath)
218e5c31af7Sopenharmony_ci            if (match):
219e5c31af7Sopenharmony_ci                info('Match incPat for line:', line)
220e5c31af7Sopenharmony_ci                # For prototypes, if it is preceded by
221e5c31af7Sopenharmony_ci                # another include:: directive with no intervening link: tags,
222e5c31af7Sopenharmony_ci                # add to the current function list. Otherwise start a new list.
223e5c31af7Sopenharmony_ci                # There is only one current structure.
224e5c31af7Sopenharmony_ci                category = match.group(1)
225e5c31af7Sopenharmony_ci                tag = match.group(2)
226e5c31af7Sopenharmony_ci                # @ Validate tag!
227e5c31af7Sopenharmony_ci                # @ Arguably, any intervening text should shift to accumFuncs = False,
228e5c31af7Sopenharmony_ci                # e.g. only back-to-back includes separated by blank lines would be
229e5c31af7Sopenharmony_ci                # accumulated.
230e5c31af7Sopenharmony_ci                if (category == 'protos'):
231e5c31af7Sopenharmony_ci                    if (tag in protos.keys()):
232e5c31af7Sopenharmony_ci                        if (accumFunc):
233e5c31af7Sopenharmony_ci                            curFuncs.append(tag)
234e5c31af7Sopenharmony_ci                        else:
235e5c31af7Sopenharmony_ci                            curFuncs = [ tag ]
236e5c31af7Sopenharmony_ci                            # Restart accumulating functions
237e5c31af7Sopenharmony_ci                            accumFunc = True
238e5c31af7Sopenharmony_ci                        info('curFuncs =', curFuncs, 'accumFunc =', accumFunc)
239e5c31af7Sopenharmony_ci                    else:
240e5c31af7Sopenharmony_ci                        error('include of nonexistent function', tag)
241e5c31af7Sopenharmony_ci                elif (category == 'structs'):
242e5c31af7Sopenharmony_ci                    if (tag in structs.keys()):
243e5c31af7Sopenharmony_ci                        curStruct = tag
244e5c31af7Sopenharmony_ci                        # Any /structs/ include means to stop accumulating /protos/
245e5c31af7Sopenharmony_ci                        accumFunc = False
246e5c31af7Sopenharmony_ci                        info('curStruct =', curStruct)
247e5c31af7Sopenharmony_ci                    else:
248e5c31af7Sopenharmony_ci                        error('include of nonexistent struct', tag)
249e5c31af7Sopenharmony_ci            if (follow):
250e5c31af7Sopenharmony_ci                # Actually process the included file now, recursively
251e5c31af7Sopenharmony_ci                newpath = os.path.normpath(os.path.join(inPath, incpath))
252e5c31af7Sopenharmony_ci                info(curFile, ': including file:', newpath)
253e5c31af7Sopenharmony_ci                checkLinks(newpath, follow, paramCheck, included = True, fatalExit = fatalExit)
254e5c31af7Sopenharmony_ci
255e5c31af7Sopenharmony_ci        matches = linkPat.findall(line)
256e5c31af7Sopenharmony_ci        for match in matches:
257e5c31af7Sopenharmony_ci            # Start actual validation work. Depending on what the
258e5c31af7Sopenharmony_ci            # asciidoc tag name is, look up the value in the corresponding
259e5c31af7Sopenharmony_ci            # dictionary.
260e5c31af7Sopenharmony_ci            tag = match[0]
261e5c31af7Sopenharmony_ci            value = match[2]
262e5c31af7Sopenharmony_ci            if (tag == 'fname' or tag == 'flink'):
263e5c31af7Sopenharmony_ci                if (value not in protos.keys()):
264e5c31af7Sopenharmony_ci                    foundError('function', tag, value, False)
265e5c31af7Sopenharmony_ci            elif (tag == 'sname' or tag == 'slink'):
266e5c31af7Sopenharmony_ci                if (value not in structs.keys() and
267e5c31af7Sopenharmony_ci                    value not in handles.keys()):
268e5c31af7Sopenharmony_ci                    foundError('aggregate/scalar/handle/define type', tag, value, False)
269e5c31af7Sopenharmony_ci            elif (tag == 'ename'):
270e5c31af7Sopenharmony_ci                if (value not in consts.keys() and value not in defines.keys()):
271e5c31af7Sopenharmony_ci                    foundError('enumerant/constant', tag, value, False)
272e5c31af7Sopenharmony_ci            elif (tag == 'elink'):
273e5c31af7Sopenharmony_ci                if (value not in enums.keys() and value not in flags.keys()):
274e5c31af7Sopenharmony_ci                    foundError('enum/bitflag type', tag, value, fatalExit)
275e5c31af7Sopenharmony_ci            # tname and tlink are the same except if the errors are treated as fatal
276e5c31af7Sopenharmony_ci            # They can be recombined once both are error-clean
277e5c31af7Sopenharmony_ci            elif (tag == 'tname'):
278e5c31af7Sopenharmony_ci                if (value not in funcpointers.keys() and value not in flags.keys()):
279e5c31af7Sopenharmony_ci                    foundError('function pointer/other type', tag, value, fatalExit)
280e5c31af7Sopenharmony_ci            elif (tag == 'tlink'):
281e5c31af7Sopenharmony_ci                if (value not in funcpointers.keys() and value not in flags.keys()):
282e5c31af7Sopenharmony_ci                    foundError('function pointer/other type', tag, value, False)
283e5c31af7Sopenharmony_ci            elif (tag == 'pname'):
284e5c31af7Sopenharmony_ci                # Any pname: tag means to stop accumulating /protos/
285e5c31af7Sopenharmony_ci                accumFunc = False
286e5c31af7Sopenharmony_ci                # See if this parameter is in the current proto(s) and struct
287e5c31af7Sopenharmony_ci                foundParam = False
288e5c31af7Sopenharmony_ci                if (curStruct and value in structs[curStruct]):
289e5c31af7Sopenharmony_ci                    info('parameter', value, 'found in struct', curStruct)
290e5c31af7Sopenharmony_ci                elif (curFuncs and findParam(value, curFuncs)):
291e5c31af7Sopenharmony_ci                    True
292e5c31af7Sopenharmony_ci                else:
293e5c31af7Sopenharmony_ci                    if paramCheck:
294e5c31af7Sopenharmony_ci                        warning('parameter', value, 'not found. curStruct =',
295e5c31af7Sopenharmony_ci                                curStruct, 'curFuncs =', curFuncs)
296e5c31af7Sopenharmony_ci            else:
297e5c31af7Sopenharmony_ci                # This is a logic error
298e5c31af7Sopenharmony_ci                error('unknown tag', tag + ':' + value)
299e5c31af7Sopenharmony_ci    fp.close()
300e5c31af7Sopenharmony_ci
301e5c31af7Sopenharmony_ci    if (errCount > 0 or warnCount > 0):
302e5c31af7Sopenharmony_ci        if (not included):
303e5c31af7Sopenharmony_ci            print('Errors found:', errCount, 'Warnings found:', warnCount)
304e5c31af7Sopenharmony_ci            print('')
305e5c31af7Sopenharmony_ci
306e5c31af7Sopenharmony_ci    if (included):
307e5c31af7Sopenharmony_ci        info('----- returning from:', infile, 'to parent file', '-----')
308e5c31af7Sopenharmony_ci
309e5c31af7Sopenharmony_ci    # Do not generate any output for files without errors
310e5c31af7Sopenharmony_ci    # else:
311e5c31af7Sopenharmony_ci    #     print(curFile + ': No errors found')
312e5c31af7Sopenharmony_ci
313e5c31af7Sopenharmony_ci    # Restore global state before exiting the function
314e5c31af7Sopenharmony_ci    curFile = oldCurFile
315e5c31af7Sopenharmony_ci    curLine = oldCurLine
316e5c31af7Sopenharmony_ci
317e5c31af7Sopenharmony_ciif __name__ == '__main__':
318e5c31af7Sopenharmony_ci    follow = False
319e5c31af7Sopenharmony_ci    paramCheck = False
320e5c31af7Sopenharmony_ci    included = False
321e5c31af7Sopenharmony_ci    fatalExit = False
322e5c31af7Sopenharmony_ci
323e5c31af7Sopenharmony_ci    totalErrCount = 0
324e5c31af7Sopenharmony_ci    totalWarnCount = 0
325e5c31af7Sopenharmony_ci
326e5c31af7Sopenharmony_ci    if (len(sys.argv) > 1):
327e5c31af7Sopenharmony_ci        for file in sys.argv[1:]:
328e5c31af7Sopenharmony_ci            if (file == '-follow'):
329e5c31af7Sopenharmony_ci                follow = True
330e5c31af7Sopenharmony_ci            elif (file == '-info'):
331e5c31af7Sopenharmony_ci                printInfo = True
332e5c31af7Sopenharmony_ci            elif file == '-paramcheck':
333e5c31af7Sopenharmony_ci                paramCheck = True
334e5c31af7Sopenharmony_ci            elif (file == '-fatal'):
335e5c31af7Sopenharmony_ci                fatalExit = True
336e5c31af7Sopenharmony_ci            else:
337e5c31af7Sopenharmony_ci                initChecks()
338e5c31af7Sopenharmony_ci                checkLinks(file,
339e5c31af7Sopenharmony_ci                           follow,
340e5c31af7Sopenharmony_ci                           paramCheck = paramCheck,
341e5c31af7Sopenharmony_ci                           included = included,
342e5c31af7Sopenharmony_ci                           fatalExit = fatalExit)
343e5c31af7Sopenharmony_ci                totalErrCount = totalErrCount + errCount
344e5c31af7Sopenharmony_ci                totalWarnCount = totalWarnCount + warnCount
345e5c31af7Sopenharmony_ci    else:
346e5c31af7Sopenharmony_ci        print('Need arguments: [-follow] [-info] [-paramcheck] [-fatal] infile [infile...]', file=sys.stderr)
347e5c31af7Sopenharmony_ci
348e5c31af7Sopenharmony_ci    if (totalErrCount > 0 or totalWarnCount > 0):
349e5c31af7Sopenharmony_ci        if (not included):
350e5c31af7Sopenharmony_ci            print('TOTAL Errors found:', totalErrCount, 'Warnings found:',
351e5c31af7Sopenharmony_ci                  totalWarnCount)
352e5c31af7Sopenharmony_ci            if totalErrCount > 0:
353e5c31af7Sopenharmony_ci                sys.exit(1)
354