1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
mnv9script
# Script to fix preprocessor indentation in MNV's C source code.
#
# Usage: MNV -S <this-file>
#
# Specifications:
# - If there is no indentation on the line containing the preprocessor
# directive (`#`) following the first `#if~`, the indentation amount is
# `nesting level - 1` spaces. Otherwise, the indentation amount is `nesting
# level` spaces.
# - However, if a preprocessor directive line is detected after the
# corresponding `#endif` of the above `#if~`, the indentation amount is
# fixed at `nesting level` and the line is reprocessed from the first line.
# - If the preprocessor directive line ends with a line continuation (`\`) and
# the next line is blank, the line continuation (`\`) and the next line are
# deleted.
#
# Author: Hirohito Higashi (@h-east)
# Last Update: 2026 Jan 12
def Get_C_source_files(): list<string>
var list_of_c_files: list<string> = []
if empty(list_of_c_files)
var fpath = '../../src'
var list = glob(fpath .. '/*.[ch]', 0, 1) + [fpath .. '/xxd/xxd.c']
# Some files are auto-generated, so skip those
list_of_c_files = filter(list, (i, v) => v !~ 'dlldata.c\|if_ole.h\|iid_ole.c')
endif
return list_of_c_files
enddef
def FixPreprocessorIndent(fname: string)
execute 'edit! ' .. fname
var nest: number = 0
var indent_offset: number = 0 # -1 if whole-file guard detected
var first_if_seen: bool = false
var offset_determined: bool = false
var whole_file_guard_ended = false
# First pass: remove trailing backslash + empty next line
var lnum = 1
while lnum <= line('$')
var line: string = getline(lnum)
if line =~# '^\s*#.*\\$'
var next_line: string = getline(lnum + 1)
if next_line =~# '^\s*$'
# Remove backslash from current line and delete next line
setline(lnum, substitute(line, '\s*\\$', '', ''))
deletebufline('%', lnum + 1)
continue # Don't increment, check same line again
endif
endif
lnum += 1
endwhile
# Second pass: fix preprocessor indent
while true
var is_reprocess: bool = false
for l in range(1, line('$'))
var line: string = getline(l)
# Skip if not a preprocessor directive
if line !~# '^\s*#'
continue
endif
# Extract directive and current indent
var match_li: list<string> = matchlist(line, '^\(\s*\)#\(\s*\)\(\w\+\)')
if empty(match_li)
continue
endif
var cur_spaces: string = !empty(match_li[1]) ? match_li[1] : match_li[2]
var directive: string = match_li[3]
# If indent_offset != 0 but we encounter indented #, it's not whole-file
# guard. Reprocess from line 1 with indent_offset=0
if whole_file_guard_ended && offset_determined && indent_offset != 0
indent_offset = 0
nest = 0
is_reprocess = true
break
endif
# After first #if, determine offset from first nested directive
# Only check if # is at column 1 (no leading spaces)
if first_if_seen && !offset_determined
offset_determined = true
if empty(cur_spaces)
# No indent after first `#if` --> whole-file guard style
indent_offset = -1
endif
endif
# Determine expected indent based on directive type
var expected_indent: number
if directive ==# 'if' || directive ==# 'ifdef' || directive ==# 'ifndef'
if !first_if_seen
first_if_seen = true
endif
expected_indent = nest + indent_offset
nest += 1
elseif directive ==# 'elif' || directive ==# 'else'
expected_indent = nest - 1 + indent_offset
elseif directive ==# 'endif'
nest -= 1
if nest <= 0
# Reset for next top-level block (but keep offset_determined)
nest = 0
whole_file_guard_ended = true
endif
expected_indent = nest + indent_offset
else
# Other directives (#define, #include, #error, #pragma, etc.)
expected_indent = nest + indent_offset
endif
if expected_indent < 0
expected_indent = 0
endif
# Build expected line
var rest = substitute(line, '^\s*#\s*', '', '')
var expected_line: string
expected_line = '#' .. repeat(' ', expected_indent) .. rest
# Update line if different
if line !=# expected_line
setline(l, expected_line)
endif
endfor
if !is_reprocess
break
endif
endwhile
update
enddef
# Main
for fname in Get_C_source_files()
FixPreprocessorIndent(fname)
endfor
qall!
# mnv: et ts=2 sw=0
|