aboutsummaryrefslogtreecommitdiff
path: root/sdn-view
blob: b53db49499efcf56d0ec11f250304d4ef31bc6bb (plain)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/bin/sh -e
# sdn-view: a viewer for sdn that makes use of Midnight Commander configuration
# to make more kinds of files directly viewable

if [ "$#" -ne 1 ]
then
	echo "Usage: $0 FILE" >&2
	exit 2
fi

# This handles both MC_DATADIR and odd installation locations.
datadir=
if command -v mc >/dev/null
then datadir=$(mc --datadir | sed 's/ (.*)$//')
fi

export SDN_VIEW_CONFIG=
for dir in "$HOME"/.config/mc "$datadir" /etc/mc
do
	if [ -n "$dir" -a -f "$dir/mc.ext.ini" ]
	then
		SDN_VIEW_CONFIG=$dir/mc.ext.ini
		break
	fi
done

export PAGER=${PAGER:-less}
export MC_EXT_FILENAME=$(realpath "$1")
export MC_EXT_BASENAME=$(basename "$1")
export MC_EXT_CURRENTDIR=$(dirname "$1")
export SDN_VIEW_TYPE=$(file -bz "$1")
process() (awk -f - <<'EOF'
BEGIN {
	if (!(Config = ENVIRON["SDN_VIEW_CONFIG"]))
		exit

	Verb = "View"
	Section = ""
	while ((getline < Config) > 0) {
		if (/^\s*(#.*)?$/) {
			# Skip.
		} else if (/^\[[^]]+\]$/) {
			Sections[++SectionsLen] = Section = substr($0, 2, length($0) - 2)
		} else if (/^[^=]+=[^=]*$/) {
			split($0, kv, "=")
			Keys[Section, kv[1]] = kv[2]
		}
	}

	Type     = ENVIRON["SDN_VIEW_TYPE"]
	Path     = ENVIRON["MC_EXT_FILENAME"]
	Basename = ENVIRON["MC_EXT_BASENAME"]
	Dirname  = ENVIRON["MC_EXT_CURRENTDIR"]

	for (i = 1; i <= SectionsLen; i++) {
		if (Sections[i] == "mc.ext.ini" ||
			Sections[i] == "Default" ||
			Sections[i] ~ /^Include[\/]/)
			continue
		try(Sections[i])
	}

	# Not attempting any inclusions here.
	print expand_command(Keys["Default", Verb])
}

function try(section,    pair, a, key, full, include) {
	for (pair in Keys) {
		split(pair, a, SUBSEP)
		if (a[1] == section)
			full[a[2]] = Keys[pair]
	}
	if ("Include" in full) {
		delete full["Open"]
		delete full["View"]
		delete full["Edit"]
		include = "Include/" full["Include"]
		for (pair in Keys) {
			split(pair, a, SUBSEP)
			if (a[1] == include)
				full[a[2]] = Keys[pair]
		}
	}
	if (ENVIRON["SDN_VIEW_DEBUG"]) {
		print "[" section "]" > "/dev/stderr"
		for (key in full)
			print "  " key ": " full[key] > "/dev/stderr"
	}
	if (Verb in full && section_matches(full, Type, Basename)) {
		print expand_command(full[Verb])
		exit
	}
}

function shell_escape(string) {
	gsub(/'/, "'\\''", string)
	return "'" string "'"
}

function expand_command(cmd,    toview, out, seq, argument, value, a, pipe) {
	out = ""
	while (match(cmd, /%[a-zA-Z]*\{[^}]*\}|%[a-zA-Z]+|%%/)) {
		out = out substr(cmd, 1, RSTART - 1)
		seq = substr(cmd, RSTART + 1, RLENGTH - 1)
		cmd = substr(cmd, RSTART + RLENGTH)

		argument = ""
		if (match(seq, /\{.*\}$/)) {
			argument = substr(seq, RSTART + 1, RLENGTH - 2)
			seq = substr(seq, 1, RSTART - 1)
		}

		if (seq == "%") {
			out = out "%"
		} else if (seq == "p") {
			out = out shell_escape(Basename)
		} else if (seq == "f") {
			out = out shell_escape(Path)
		} else if (seq == "d") {
			out = out shell_escape(Dirname)
		} else if (seq == "view") {
			toview = 1

			sub(/^ +/, "", cmd)
			split(argument, a, /,/)
			for (value in a) {
				if (a[value] == "hex")
					pipe = pipe " | od -t x1"

				# more(1) and less(1) either ignore or display this:
				#if (a[value] == "nroff")
				#	pipe = pipe " | col -b"
			}
		} else if (seq == "var") {
			value = ""
			if (!match(argument, /:.*/)) {
				if (argument in ENVIRON)
					value = ENVIRON[argument]
			} else {
				value = substr(argument, RSTART + 1)
				argument = substr(argument, 1, RSTART - 1)
				if (argument in ENVIRON)
					value = ENVIRON[argument]
			}
			out = out shell_escape(value)
		} else if (seq == "") {
			print Config ": prompting not supported" > "/dev/stderr"
			return
		} else {
			print Config ": unsupported: %" seq > "/dev/stderr"
			return
		}
	}
	out = out cmd pipe

	# While the processing is mostly generic for all verbs,
	# we'd have to distinguish non-view commands in this AWK script's output.
	if (!toview)
		return

	# In the case of out == "", we should just explicitly pass it to the pager,
	# however it currently mixes with the case of "we can't use this View=".
	return out
}

function section_matches(section, type, basename,    value) {
	if ("Directory" in section)
		return 0

	if ("Type" in section) {
		value = section["Type"]
		if ("TypeIgnoreCase" in section &&
			section["TypeIgnoreCase"] == "true") {
			type = tolower(type)
			value = tolower(value)
		}
		gsub(/\\\\/, "\\", value)
		gsub(/\\ /, " ", value)
		if (type !~ value)
			return 0
	}
	if ("Regex" in section) {
		value = section["Regex"]
		if ("RegexIgnoreCase" in section &&
			section["RegexIgnoreCase"] == "true") {
			basename = tolower(basename)
			value = tolower(value)
		}
		gsub(/\\\\/, "\\", value)
		return basename ~ value
	} else if ("Shell" in section) {
		value = section["Shell"]
		if ("RegexIgnoreCase" in section &&
			section["ShellIgnoreCase"] == "true") {
			basename = tolower(basename)
			value = tolower(value)
		}
		if (value !~ /^[.]/)
			return value == basename
		return length(basename) >= length(value) &&
			substr(basename, length(basename) - length(value) + 1) == value
	}
	return type != ""
}
EOF
)
command=$(process)
if [ -z "$command" ]
then "$PAGER" -- "$MC_EXT_FILENAME"
else eval "$command" | "$PAGER"
fi