88 lines
2.8 KiB
Python
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)
|