summaryrefslogtreecommitdiff
path: root/lib/lib/settings.tcl
blob: 374934ddaecafeb243f34e86316e3148aef521be (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
#!/usr/bin/tclsh
# Part of MCU 8051 IDE ( http://https://sourceforge.net/projects/mcu8051ide/ )

############################################################################
#    Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 by Martin Ošmera     #
#    martin.osmera@gmail.com                                               #
#                                                                          #
#    Copyright (C) 2014 by Moravia Microsystems, s.r.o.                    #
#    martin.osmera@gmail.com                                #
#                                                                          #
#    This program is free software; you can redistribute it and#or modify  #
#    it under the terms of the GNU General Public License as published by  #
#    the Free Software Foundation; either version 2 of the License, or     #
#    (at your option) any later version.                                   #
#                                                                          #
#    This program is distributed in the hope that it will be useful,       #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#    GNU General Public License for more details.                          #
#                                                                          #
#    You should have received a copy of the GNU General Public License     #
#    along with this program; if not, write to the                         #
#    Free Software Foundation, Inc.,                                       #
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
############################################################################

# >>> File inclusion guard
if { ! [ info exists _SETTINGS_TCL ] } {
set _SETTINGS_TCL _
# <<< File inclusion guard

# --------------------------------------------------------------------------
# DESCRIPTION
# Implements interface to program settings (which are stored in a file)
# --------------------------------------------------------------------------

class Settings {
	public common dir_sep	[file separator]	;# Directory separator (eg. '/')
	public common settings_count		0	;# Counter of instances

	private variable isEmpty	1	;# Is settings array empty
	private variable isReady	0	;# Is interface ready

	private variable directory		;# Path to directory with settings file
	private variable filename		;# Name of file with settings related to this instance
	private variable fileFullPath		;# Full name of settings file (including directory)
	private variable configArray	{}	;# Content of settings maneged by this interface

	## Object contructor
	 # @parm String configDir	- Path to directory with settings file
	 # @parm String configFileName	- Name of file with settings
	constructor {configDir configFileName} {
		incr settings_count	;# increment instance conter

		# Incalize object variables
		set configArray	"::Settings::S${settings_count}"	;# Array of settings
		set directory	[string trimright $configDir "/\/"]	;# Path to directory with settings file
		set filename	[string trimleft $configFileName "/\/"]	;# Name of file with settings
		set fileFullPath "${directory}${dir_sep}${filename}"	;# Full name of settings file

		# If specified file does not exist -> create it
		if {![file exists $fileFullPath]} {
			if {[catch {
				file mkdir $directory
				close [open $fileFullPath w 0640]
			}]} then {
				return
			} else {
				set isReady 1
			}

		# Else check if the file is readable and writable
		} else {
			if {$::MICROSOFT_WINDOWS || ([file readable $fileFullPath] && [file writable $fileFullPath])} {
				set isReady 1
			} else {
				return
			}
		}

		# Load settings from the file
		reLoadConfig
	}

	## Object destructor
	destructor {
	}

	## (Re)load settings from config file
	 # @return result
	public method reLoadConfig {} {

		# Check if file is readable
		if {!$::MICROSOFT_WINDOWS && ![file readable $fileFullPath]} {
			return 0
		}

		# Read content of config file and store it as list of lines into fileData
		set configFile [open $fileFullPath r]
		set fileData [read $configFile]
		set fileData [regsub -all {\r\n} $fileData "\n"]
		set fileData [regsub -all {\r} $fileData "\n"]
		set fileData [split $fileData "\n"]
		close $configFile

		# Parse content of the file
		set category {general}
		foreach line $fileData {
			# Local variables
			set line	[string trim $line]	;# Line of config file
			set key		{}	;# Key
			set value	{}	;# Value for the key

			# Skip empty lines
			if {$line == {}} {continue}

			# Handle category declaration
			if {[regexp {^\[\s*[\w \t]+\s*\]$} $line]} {
				set category [string trim $line "\[\] \t"]

			# Handle key and its value
			} elseif {[regexp {^\s*[\w \t:]+\s*\=\s*\".*\"\s*$} $line]} {
				# Determinate key
				regexp {^\s*[\w \t:]+\s*\=} $line key
				set key [string trim $key "=\t "]
				# Determinate value
				regexp {\s*\".*\"\s*$} $line value
				set value [string trim $value]
				regsub {^\"} $value {} value
				regsub {\"$} $value {} value
				regsub -all "\a" $value "\n" value
				# Set key and value to array
				set "$configArray\($category/$key\)" $value
			}
		}

		# Set variable isEmpty
		if {[array size $configArray] != 0} {
			set isEmpty 0
		} else {
			set isEmpty 1
		}

		# return result
		return 1
	}

	## Save current content of $configArray to config file
	 # @return result
	public method saveConfig {} {

		# Check if file is writable
		if {![file writable $fileFullPath]} {
			return 0
		}

		# Local variables
		set configFile	[open $fileFullPath w 0640]	;# ID of config file chanel
		set categories	{general}			;# Name of current category

		# Determinate list of categories
		foreach key [array names $configArray] {
			# Determinate category
			regexp {^.+/} $key category
			set category [string trimright $category {/}]
			# Append category to the list
			if {[lsearch $categories $category] == -1} {
				lappend categories $category
			}
		}

		# Iterate over categories and save them to the file
		foreach category $categories {
			# Get names of keys in current category
			set keys [array names $configArray -regexp "$category/"]
			# Save category declaration
			puts $configFile "\n\[$category\]"
			# Iterate over keys in current category
			foreach fullKey $keys {
				# Determinate key
				regsub {^[^/]*/} $fullKey {} key
				# Determinate value
				set value [subst -nocommands "\$$configArray\(\$fullKey\)"]
				regsub -all "\n" $value "\a" value
				# Save key and value
				puts $configFile "$key=\"$value\""
			}
		}

		# Done ...
		close $configFile
		return 1
	}

	## Return True if config array is empty
	 # @return Bool - result
	public method isEmpty {} {
		return $isEmpty
	}

	## Return True if interface is ready
	 # @return Bool - result
	public method isReady {} {
		return $isReady
	}

	## Clear all settings
	 # @return void
	public method clear {} {
		array unset $configArray
	}

	## Remove specified key from settings
	 # @parm String key - name of key to remove
	 # @return Bool - result
	public method remove {key} {
		regsub -all {_} $key {__} key
		regsub -all {\s} $key {_} key

		if {[i_contains $key]} {
			unset "$configArray\($key\)"
			return 1
		} else {
			return 0
		}
	}

	## Return True if the specified key is defined
	 # @parm String key - key to search for
	 # @return Bool - result
	public method contains {key} {
		regsub -all {_} $key {__} key
		regsub -all {\s} $key {_} key

		return [i_contains $key]
	}

	## Internal key search (Does not peform key name adjusment)
	 # @parm String key - name of key to search for
	 # @return Bool - result
	private method i_contains {key} {
		if {[array names $configArray -exact $key] == {}} {
			return 0
		} else {
			return 1
		}
	}

	## Get value for the given key
	 # @parm String key	- Key
	 # @parm Mixed default	- Default value
	 # @return Mixed - value for the given key
	public method getValue {key default} {

		# Adjust key name
		if {![regexp {^.+/} $key]} {
			set key "general/$key"
		}
		regsub -all {_} $key {__} key
		regsub -all {\s} $key {_} key

		# Check for valid key format
		if {![regexp {^[\w:]+/[\w:]+$} $key]} {
			return $default
		}

		# Check if the given key is defined
		if {[i_contains $key]} {
			return [subst -nocommands "\$$configArray\(\$key\)"]
		} else {
			return $default
		}
	}

	## Set value for the given key
	 # @parm String key	- Key
	 # @parm Mixed value	- Value
	 # @return Bool - result
	public method setValue {key value} {

		## Check for key validity
		if {[regexp {[\!\=\$\^\*\+\?\.\[\]\{\}\(\)]} $key]} {
			return 0
		}

		regsub -all {_} $key {__} key
		regsub -all {\s} $key {_} key

		if {![regexp {^.+/} $key]} {
			set key "general/$key"
		}

		if {![regexp {^[\w:]+/[\w:]+$} $key]} {
			return 0
		}

		## Set value
		set "$configArray\($key\)" $value
		return 1
	}
}

# >>> File inclusion guard
}
# <<< File inclusion guard