joeylib2/tools/winIni.py
2026-10-13 14:01:46 -05:00

88 lines
2.8 KiB
Python

#!/usr/bin/env python3
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
"""winIni.py - edit a Windows 3.x .INI file in place, CRLF line ends kept.
winIni.py FILE OP SECTION KEY VALUE [OP SECTION KEY VALUE ...]
OP is one of:
set KEY=VALUE in SECTION: the first KEY line there is replaced (keys and
sections match without regard to case), else the line is added at
the end of the section, the section at the end of the file if it is
not there.
add another KEY=VALUE line in SECTION unless that exact line is there
already: for the keys Windows reads every line of (device= in
[386Enh]).
Used by the installer's win311 component (installer/lib/win16.sh) to set up
Windows the way its Setup and Control Panel would, and by the win16 gates and
joey-run to name the program Windows runs as its shell.
"""
import sys
ENCODING = 'latin-1'
NEWLINE = '\r\n'
OPS = ('set', 'add')
GROUP = 4
def sectionBounds(lines, section):
# The index of SECTION's header and of the line after its last, or None.
header = '[' + section.lower() + ']'
for start, line in enumerate(lines):
if line.strip().lower() == header:
end = start + 1
while end < len(lines) and not lines[end].lstrip().startswith('['):
end += 1
return start, end
return None
def insertAtEnd(lines, start, end, line):
# Before the blank lines that close the section.
at = end
while at > start + 1 and lines[at - 1].strip() == '':
at -= 1
lines.insert(at, line)
def keyOf(line):
return line.split('=', 1)[0].strip().lower() if '=' in line else None
def apply(lines, op, section, key, value):
line = key + '=' + value
bounds = sectionBounds(lines, section)
if bounds is None:
while lines and lines[-1].strip() == '':
lines.pop()
lines.extend(['', '[' + section + ']', line, ''])
return
start, end = bounds
for i in range(start + 1, end):
if op == 'set' and keyOf(lines[i]) == key.lower():
lines[i] = line
return
if op == 'add' and lines[i].strip().lower() == line.lower():
return
insertAtEnd(lines, start, end, line)
def main(argv):
if len(argv) < 2 + GROUP or (len(argv) - 2) % GROUP != 0:
sys.exit(__doc__)
path = argv[1]
lines = open(path, 'rb').read().decode(ENCODING).split(NEWLINE)
for at in range(2, len(argv), GROUP):
op, section, key, value = argv[at:at + GROUP]
if op not in OPS:
sys.exit('winIni.py: unknown op ' + op)
apply(lines, op, section, key, value)
open(path, 'wb').write(NEWLINE.join(lines).encode(ENCODING))
if __name__ == '__main__':
main(sys.argv)