99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
import os
|
|
import re
|
|
import sys
|
|
from dataclasses import dataclass, field
|
|
from typing import List
|
|
|
|
|
|
def get_tagtime_command() -> List[str]:
|
|
"""
|
|
The original Perl implementation spawns another Perl script in a terminal
|
|
to ask the user for their current tags. To replicate this behavior we find
|
|
out the python executable and the location of the tagtime.py script.
|
|
"""
|
|
python_exe = sys.executable
|
|
tagtimerc_py = os.path.abspath(__file__)
|
|
tagtime_py = tagtimerc_py.replace("tagtimerc.py", "tagtime.py")
|
|
return [python_exe, tagtime_py]
|
|
|
|
|
|
@dataclass
|
|
class TagTimeRc:
|
|
log_file: str
|
|
ed: str
|
|
xt: str
|
|
retrothresh: int
|
|
gap: int
|
|
urping: int
|
|
seed: int
|
|
linelen: int
|
|
catchup: int
|
|
tags_off: List[str] = field(default_factory=lambda:
|
|
["afk", "off", "RETRO"])
|
|
tags_afk: List[str] = field(default_factory=lambda: ["afk", "RETRO"])
|
|
tags_err: List[str] = field(default_factory=lambda: ["err"])
|
|
tagtimecmd: List[str] = field(default_factory=get_tagtime_command)
|
|
|
|
|
|
def value_to_int(value: str) -> int:
|
|
"""
|
|
The tagtimerc file is itself a Perl script. That means the assignments to
|
|
the configuration parameters can be Perl expressions. We could replicate
|
|
that behavior via `eval`, but we don't want to do that because it allows
|
|
arbitrary code execution. Instead we only support multiplication for now
|
|
(`45*60`).
|
|
"""
|
|
try:
|
|
return int(value)
|
|
except ValueError:
|
|
pass
|
|
result = 1
|
|
for v in value.split("*"):
|
|
result *= int(v)
|
|
return result
|
|
|
|
|
|
def parse_tagtimerc(tagtimerc_path: str) -> TagTimeRc:
|
|
"""
|
|
Parses the configuration attributes from a tagtimerc file into Python.
|
|
|
|
All lines that start with a dollar sign are configuration lines. These
|
|
lines are than split into key and value. This function only considers
|
|
attributes that are part of TagTimeRc.
|
|
"""
|
|
s = ("^\s*" # potential leading whitespace
|
|
"\$(\w+)" # key as group (variables in Perl start with $)
|
|
"\s*=\s*" # equal sign including potential whitespaces around it
|
|
"\"?" # potential opening quote
|
|
"([^\";]+)" # value (everything that is not a semicolon or quote)
|
|
"\"?" # potential closing quote
|
|
";") # semicolon to terminate key value pair
|
|
re_config_line = re.compile(s)
|
|
tagtimerc_path = os.path.expanduser(tagtimerc_path)
|
|
with open(tagtimerc_path, 'r') as f:
|
|
key_value_pairs = {m.groups()[0].lower(): m.groups()[1]
|
|
for line in f.readlines()
|
|
if (m := re_config_line.match(line))}
|
|
|
|
# Get dictionary of the TagTimeRc attribute types.
|
|
tagtimerc_types = TagTimeRc.__annotations__
|
|
|
|
# Process the key-value-pairs. Keep expected pairs and store them into
|
|
# kwarsg.
|
|
kwargs = {}
|
|
for key, value in key_value_pairs.items():
|
|
if key in tagtimerc_types and tagtimerc_types[key] is int:
|
|
kwargs[key] = value_to_int(value)
|
|
elif key == "logf":
|
|
# Give special treatman to logf, because it may contain other
|
|
# variables (`$logf = "$path$usr.log";`) Iterate over those
|
|
# variables and replace them with their value.
|
|
for variable in re.findall("\$\w+", value):
|
|
var = variable.replace("$", "")
|
|
value = value.replace(variable, key_value_pairs[var])
|
|
kwargs["log_file"] = value
|
|
elif key in tagtimerc_types:
|
|
kwargs[key] = value
|
|
|
|
return TagTimeRc(**kwargs)
|