| ... | ... |
@@ -153,9 +153,9 @@ Environment variables: |
| 153 | 153 |
`enums-tree`, `params`, `params-tree`, `audit,` and `audit-tree` commands. |
| 154 | 154 |
It uses the same format (and defaults) as GREP_COLORS, i.e. a |
| 155 | 155 |
colon-separated list of capabilties: `ms` (matching selected), `fn` (file |
| 156 |
- name), `ln` (line number), `se` (separators). Added custom capabilities |
|
| 157 |
- are: `ve` (version), `ex` (extension), `un` (unsupported). Defaults to |
|
| 158 |
- `ms=01;31:fn=35:ln=32:se=36:ve=01;34:ex=34:un=01;33`. |
|
| 156 |
+ name), `ln` (line and column number), `se` (separators). Added custom |
|
| 157 |
+ capabilities are: `ve` (version), `ex` (extension), `un` (unsupported). |
|
| 158 |
+ Defaults to `ms=01;31:fn=35:ln=32:se=36:ve=01;34:ex=34:un=01;33`. |
|
| 159 | 159 |
``` |
| 160 | 160 |
|
| 161 | 161 |
## References |
| ... | ... |
@@ -110,9 +110,9 @@ Environment variables: |
| 110 | 110 |
`enums-tree`, `params`, `params-tree`, `audit,` and `audit-tree` commands. |
| 111 | 111 |
It uses the same format (and defaults) as GREP_COLORS, i.e. a |
| 112 | 112 |
colon-separated list of capabilties: `ms` (matching selected), `fn` (file |
| 113 |
- name), `ln` (line number), `se` (separators). Added custom capabilities |
|
| 114 |
- are: `ve` (version), `ex` (extension), `un` (unsupported). Defaults to |
|
| 115 |
- `ms=01;31:fn=35:ln=32:se=36:ve=01;34:ex=34:un=01;33`. |
|
| 113 |
+ name), `ln` (line and column number), `se` (separators). Added custom |
|
| 114 |
+ capabilities are: `ve` (version), `ex` (extension), `un` (unsupported). |
|
| 115 |
+ Defaults to `ms=01;31:fn=35:ln=32:se=36:ve=01;34:ex=34:un=01;33`. |
|
| 116 | 116 |
""" |
| 117 | 117 |
|
| 118 | 118 |
|
| ... | ... |
@@ -125,6 +125,7 @@ import functools |
| 125 | 125 |
import subprocess |
| 126 | 126 |
import shutil |
| 127 | 127 |
import shlex |
| 128 |
+import fnmatch |
|
| 128 | 129 |
import urllib.request |
| 129 | 130 |
import docopt |
| 130 | 131 |
from lxml import etree |
| ... | ... |
@@ -139,6 +140,7 @@ USER_AGENT = 'Mozilla/5.0' |
| 139 | 140 |
REGEX = r'\b(gl|GL_)[0-9A-Z][0-9A-Za-z_]+\b' |
| 140 | 141 |
EXCLUDE_DIRS = ['.?*', '_*'] |
| 141 | 142 |
EXCLUDE_FILES = ['README*', 'TODO*'] |
| 143 |
+BINARY_PEEK = 1024 |
|
| 142 | 144 |
INDENT = 2 |
| 143 | 145 |
ENV_XDG = lambda var, default: ( |
| 144 | 146 |
os.environ.get(f'GLREGISTRY_{var}') or
|
| ... | ... |
@@ -297,21 +299,45 @@ def grep( |
| 297 | 299 |
exclude_files=EXCLUDE_FILES, |
| 298 | 300 |
silent=False, |
| 299 | 301 |
): |
| 300 |
- path = path if path else '.' |
|
| 301 |
- cmd = ['grep', '-EIrno'] |
|
| 302 |
- exclude_dirs = [f'--exclude-dir={exclude}' for exclude in exclude_dirs]
|
|
| 303 |
- exclude_files = [f'--exclude={exclude}' for exclude in exclude_files]
|
|
| 304 |
- process = subprocess.run( |
|
| 305 |
- [*cmd, *exclude_dirs, *exclude_files, regex, path], |
|
| 306 |
- stdout=subprocess.PIPE, |
|
| 307 |
- stderr=subprocess.DEVNULL if silent else None, |
|
| 308 |
- text=True, |
|
| 309 |
- ) |
|
| 310 |
- for string in process.stdout.splitlines(): |
|
| 311 |
- string = removeprefix(f'.{os.path.sep}', string)
|
|
| 312 |
- file, line, name = string.split(':', 2)
|
|
| 313 |
- line = int(line) |
|
| 314 |
- yield file, line, name |
|
| 302 |
+ path = path if path else '.' |
|
| 303 |
+ def onerror(error, file=None): |
|
| 304 |
+ file = removeprefix(f'.{os.path.sep}', file or error.filename)
|
|
| 305 |
+ if silent: |
|
| 306 |
+ pass |
|
| 307 |
+ elif isinstance(error, OSError): |
|
| 308 |
+ log(f"{file}: {error.strerror}")
|
|
| 309 |
+ elif isinstance(error, UnicodeDecodeError): |
|
| 310 |
+ log(f"{file}: {error.reason}")
|
|
| 311 |
+ else: |
|
| 312 |
+ log(f"{file}: {error}")
|
|
| 313 |
+ def exclude(excludes, names): |
|
| 314 |
+ names = set(names) |
|
| 315 |
+ for exclude in excludes: |
|
| 316 |
+ names -= set(fnmatch.filter(names, exclude)) |
|
| 317 |
+ return sorted(names) |
|
| 318 |
+ def grep_file(file): |
|
| 319 |
+ try: |
|
| 320 |
+ with open(file, 'rb') as f: |
|
| 321 |
+ if 0 in f.read(BINARY_PEEK): |
|
| 322 |
+ return |
|
| 323 |
+ with open(file, errors='ignore') as f: |
|
| 324 |
+ file = removeprefix(f'.{os.path.sep}', file)
|
|
| 325 |
+ for line, string in enumerate(f): |
|
| 326 |
+ for match in re.finditer(regex, string): |
|
| 327 |
+ column, name = match.start(), match.group() |
|
| 328 |
+ yield file, line+1, column+1, name |
|
| 329 |
+ except Exception as error: |
|
| 330 |
+ onerror(error, file) |
|
| 331 |
+ if os.path.isfile(path): |
|
| 332 |
+ for match in grep_file(path): |
|
| 333 |
+ yield match |
|
| 334 |
+ else: |
|
| 335 |
+ for root, dirs, files in os.walk(path, onerror=onerror): |
|
| 336 |
+ dirs [:] = exclude(exclude_dirs, dirs) |
|
| 337 |
+ files[:] = exclude(exclude_files, files) |
|
| 338 |
+ for file in files: |
|
| 339 |
+ for match in grep_file(os.path.join(root, file)): |
|
| 340 |
+ yield match |
|
| 315 | 341 |
|
| 316 | 342 |
|
| 317 | 343 |
## Commands |
| ... | ... |
@@ -551,25 +577,26 @@ def params_tree(xml, group=None): |
| 551 | 577 |
### `audit_` |
| 552 | 578 |
def audit_(xml, path=None): |
| 553 | 579 |
audit_ = collections.defaultdict(lambda: collections.defaultdict(list)) |
| 554 |
- for file, line, name in grep(path): |
|
| 580 |
+ for file, line, column, name in grep(path): |
|
| 555 | 581 |
supports_ = supports(xml, name) |
| 556 | 582 |
if not supports_: |
| 557 | 583 |
supports_ = ['UNSUPPORTED'] |
| 558 |
- audit_[tuple(supports_)][name].append([file, line]) |
|
| 584 |
+ audit_[tuple(supports_)][name].append([file, line, column]) |
|
| 559 | 585 |
return audit_ |
| 560 | 586 |
|
| 561 | 587 |
|
| 562 | 588 |
### `audit` |
| 563 | 589 |
def audit(xml, path=None): |
| 564 |
- for file, line, supports, name in sorted( |
|
| 565 |
- [file, line, supports, name] |
|
| 566 |
- for supports, names in audit_(xml, path).items() |
|
| 567 |
- for name, locations in names.items() |
|
| 568 |
- for file, line in locations |
|
| 590 |
+ for file, line, column, supports, name in sorted( |
|
| 591 |
+ [file, line, column, supports, name] |
|
| 592 |
+ for supports, names in audit_(xml, path).items() |
|
| 593 |
+ for name, locations in names.items() |
|
| 594 |
+ for file, line, column in locations |
|
| 569 | 595 |
): |
| 570 | 596 |
yield indentjoin(0, ':', [ |
| 571 | 597 |
color('fn', file),
|
| 572 | 598 |
color('ln', line),
|
| 599 |
+ color('ln', column),
|
|
| 573 | 600 |
indentjoin(0, ',', color_supports(supports)), |
| 574 | 601 |
color('ms', name),
|
| 575 | 602 |
]) |
| ... | ... |
@@ -581,10 +608,11 @@ def audit_tree(xml, path=None): |
| 581 | 608 |
yield indentjoin(0, ',', color_supports(supports)) |
| 582 | 609 |
for name, locations in sorted(names.items()): |
| 583 | 610 |
yield indentjoin(1, '', [color('ms', name)])
|
| 584 |
- for file, line in sorted(locations): |
|
| 611 |
+ for file, line, column in sorted(locations): |
|
| 585 | 612 |
yield indentjoin(2, ':', [ |
| 586 | 613 |
color('fn', file),
|
| 587 | 614 |
color('ln', line),
|
| 615 |
+ color('ln', column),
|
|
| 588 | 616 |
]) |
| 589 | 617 |
|
| 590 | 618 |
|