summaryrefslogtreecommitdiffstats
path: root/lib/getopts.lua
blob: 68271b968acacbf21faabc17b3fa160c4eb386c4 (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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
module (..., package.seeall)
require("fs")

-- Create a new config file entry
-- see function description for setoptsinfile
-- line is the current line for this name:value if it exists
local create_entry = function(search_name, value, to_table, optionvalue, line)
	local oldvalue, comment = "", ""
	if line then
		-- find the old value
		oldvalue = string.match ( line, '^%s*%S+%s*%=%s*(.*)$' ) or ""
		-- split out comment
		if string.find ( oldvalue, '#' ) then
			oldvalue, comment = string.match ( oldvalue, '^(.*)(#.*)$' )
			if comment then
				comment = " " .. string.match ( comment, '^(.*%S)%s*$' )
			else
				comment = ""
			end
		end
		-- remove spaces
		oldvalue = string.match ( oldvalue, '^%s*(.*%S)%s*$' ) or ""
	end

	if to_table == true then
		local table = opts_to_table(oldvalue) or {}
		table[value] = optionvalue
		return search_name .. "=" .. table_to_opts(table) .. comment
	else
		if value then
			return search_name .. "=" .. value .. comment
		else
			return nil
		end
	end
end

-- Search the option string for separate options (-x or --xyz) and put them in a table
opts_to_table = function ( optstring, filter )
	local optsparams
	if optstring then
		local optstr = optstring .. " "
		for o in string.gmatch(optstr, "%-%-?%a+%s+[^-%s]*") do
			local option = string.match(o, "%-%-?%a+")
			if not filter or filter == option then
				if not optsparams then optsparams = {} end
				optsparams[option] = string.match(o, "%S*$")
			end
		end
	end
	return optsparams
end

-- Go through an options table and create the option string
table_to_opts = function ( optsparams )
	local optstring = {}
	for opt,val in pairs(optsparams) do
		if val ~= "" then
			optstring[#optstring + 1] = opt .. " " .. val
		else
			optstring[#optstring + 1] = opt
		end
	end
	return table.concat(optstring, " ")
end

-- Set a name=value pair
-- If search_section is undefined or "", goes in the default section
-- If to_table is false or undefined
-- 	if value is defined we put "search_name=value" into search_section
-- 	if value is undefined, we clear search_name out of search section
-- If to_table is true (and value is defined)
-- 	if optionvalue defined, we add "search_value optionvalue" to the value for search_name in search_section
-- 	if optionvalue undefined, we remove search_value from the value of search_name in search_section
-- Try not to touch anything but the value we're interested in (although will combine multi-line into one)
-- If the search_section is not found, we'll add it at the end of the file
-- If the search_name is not found, we'll add it at the end of the section
function setoptsinfile (file, search_section, search_name, value, to_table, optionvalue)
	if not file or not search_name or search_name == "" or (to_table == true and not value) then
		return false, nil, "Invalid input for getopts.setoptsinfile()", file
	end
	search_section = search_section or ""
	local conf_file = {}
	if (fs.is_file(file)) then
		conf_file = fs.read_file_as_array ( file )
	else
		for line in string.gmatch(file, "([^\n]*)\n") do
			conf_file[#conf_file + 1] = line
		end
		local extra = string.match(file,"([^\n]*)$")
		if extra ~= "" then
			conf_file[#conf_file + 1] = extra
		end
	end
	local new_conf_file = {}
	local section = ""
	local done = false
	local skip_lines = 0
	for i,l in ipairs(conf_file) do
		if skip_lines>0 then
			skip_lines = skip_lines-1
		else
			-- check if comment line
			if done == false and not string.find ( l, "^%s*#" ) then
				-- first, concat lines
				local j = 1
				while string.find ( l, "\\%s*$" ) and conf_file[i+j] do
					l = string.match ( l, "^(.*)\\%s*$" )  .. " " .. conf_file[i+j]
					j = j+1
				end
				if j>1 then skip_lines = j-1 end
				-- find section name
				local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" )
				if a then
					-- we reached a new section, if we were in the one we wanted
					-- we have to add in the name:value pair now
					if (search_section == section) then
						new_conf_file[#new_conf_file + 1] = create_entry(search_name, value, to_table, optionvalue, nil)
						done = true
					end
					section = a
				elseif (search_section == section) then
					-- find name
					a = string.match ( l, "^%s*(%S+)%s*=" )
					if a and (search_name == a) then
						-- We found the name, change the value
						l = create_entry(search_name, value, to_table, optionvalue, l)
						done = true
					end
				end
			end
			new_conf_file[#new_conf_file + 1] = l
		end
	end

	if done == false then
		-- we didn't find the section:name, add it now
		if section ~= search_section then
			new_conf_file[#new_conf_file + 1] = '[' .. search_section .. ']'
		end
		new_conf_file[#new_conf_file + 1] = create_entry(search_name, value, to_table, optionvalue, nil)
	end

	if (fs.is_file(file)) then
		fs.write_file(file, table.concat(new_conf_file, '\n'))
	else
		file = table.concat(new_conf_file, '\n')
	end

	return true, "File has been modified!", nil, file
end

-- Parse file for name=value pairs, returned in a table
-- If search_section is defined, only report values in matching section
-- If search_name is defined, only report matching name (possibly in multiple sections)
-- If to_table is true, attempt to convert value string to array of options
-- If filter is defined (and table is true), only list option matching filter
function getoptsfromfile (file, search_section, search_name, to_table, filter)
	if not file or file == "" then
		return nil
	end
	local opts = nil
	local conf_file = {}
	if (fs.is_file(file)) then
		conf_file = fs.read_file_as_array ( file )
	else
		for line in string.gmatch(file, "([^\n]*)\n") do
			conf_file[#conf_file + 1] = line
		end
		local extra = string.match(file,"([^\n]*)$")
		if extra ~= "" then
			conf_file[#conf_file + 1] = extra
		end
	end
	local section = ""
	local skip_lines = 0
	for i,l in ipairs(conf_file) do
		if skip_lines>0 then
			skip_lines = skip_lines-1
		-- check if comment line
		elseif not string.find ( l, "^%s*#" ) then
			-- first, concat lines
			local j = 1
			while string.find ( l, "\\%s*$" ) and conf_file[i+j] do
				l = string.match ( l, "^(.*)\\%s*$" )  .. " " .. conf_file[i+j]
				j = j+1
			end
			if j>1 then skip_lines = j-1 end
			-- find section name
			local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" )
			if a then
				section = a
			elseif not (search_section) or (search_section == section) then
				-- find name
				a = string.match ( l, "^%s*(%S+)%s*=" )
				if a and (not (search_name) or (search_name == a)) then
					-- Figure out the value
					local b = string.match ( l, '^%s*%S+%s*%=%s*(.*)$' ) or ""
					-- remove comments from end of line
					if string.find ( b, '#' ) then
						b = string.match ( b, '^(.*)#.*$' ) or ""
					end
					-- remove spaces from front and back
					b = string.match ( b, '^%s*(.*%S)%s*$' ) or ""
					-- finally, remove quotes
					if #b > 1 and string.sub(b,1,1) == '"' and string.sub(b,-1) == '"' then
						b = string.sub(b,2,-2) or ""
					end
					if to_table == true then
						local optstable = opts_to_table(b,filter)
						if (optstable) then
							if not (opts) then opts = {} end
							if not (opts[section]) then opts[section] = {} end
							opts[section][a] = optstable
							---[[ Next line is DEBUG info. Should be commented out!
							--opts[a]["debug"] = b
							-- End debug info. --]] 
						end
					else
						if not (opts) then opts = {} end
						if not (opts[section]) then opts[section] = {} end
						opts[section][a] = b
					end
				end
			end
		end
	end

	if opts and search_section and search_name then
		return opts[search_section][search_name]
	elseif opts and search_section then
		return opts[search_section]
	end	
	return opts
end

function getsection (file, search_section)
	if not file or file == "" then
		return nil
	end
	search_section = search_section or ""
	local conf_file = {}
	if (fs.is_file(file)) then
		conf_file = fs.read_file_as_array ( file )
	else
		for line in string.gmatch(file, "([^\n]*)\n") do
			conf_file[#conf_file + 1] = line
		end
		local extra = string.match(file,"([^\n]*)$")
		if extra ~= "" then
			conf_file[#conf_file + 1] = extra
		end
	end
	local sectionlines = {}
	local section = ""
	for i,l in ipairs(conf_file) do
		-- find section name
		local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" )
		if a then
			if (search_section == section) then break end
			section = a
		elseif (search_section == section) then
			sectionlines[#sectionlines + 1] = l
		end
	end

	return table.concat(sectionlines, "\n")
end

function setsection (file, search_section, section_content)
	if not file then
		return false, nil, "Invalid input for getopts.setoptsinfile()", file
	end
	search_section = search_section or ""
	local conf_file = {}
	if (fs.is_file(file)) then
		conf_file = fs.read_file_as_array ( file )
	else
		for line in string.gmatch(file, "([^\n]*)\n") do
			conf_file[#conf_file + 1] = line
		end
		local extra = string.match(file,"([^\n]*)$")
		if extra ~= "" then
			conf_file[#conf_file + 1] = extra
		end
	end
	local new_conf_file = {}
	local done = false
	local section = ""
	for i,l in ipairs(conf_file) do
		-- find section name
		if not done then
			local a = string.match ( l, "^%s*%[%s*(%S+)%s*%]" )
			if a then
				if (search_section == section) then
					done = true
				else
					section = a
					if (search_section == section) then
						l = l .. "\n" .. (section_content or "")
					end
				end 
			elseif (search_section == section) then
				l = nil
			end
		end
		new_conf_file[#new_conf_file + 1] = l
	end

	if not done then
		-- we didn't find the section, add it now
		new_conf_file[#new_conf_file + 1] = '[' .. search_section .. ']\n' .. (section_content or "")
	end

	if (fs.is_file(file)) then
		fs.write_file(file, table.concat(new_conf_file, '\n'))
	else
		file = table.concat(new_conf_file, '\n')
	end

	return true, "File has been modified!", nil, file
end