1require "table"
2require "io"
3require "string"
4
5--
6local function auto( tab, key )
7	return setmetatable( {}, {
8		__index = auto,
9		__newindex = assign,
10		parent = tab,
11		key = key
12	})
13end
14
15local meta = { __index = auto }
16
17--	The if statement below prevents the table from being created if the value assigned is nil.
18--	This is, I think, technically correct but it might be desirable to use assignment to nil to force a table into existence.
19function assign( tab, key, val )
20	-- if val ~= nil then
21	local oldmt = getmetatable( tab )
22	oldmt.parent[ oldmt.key ] = tab
23	setmetatable( tab, meta )
24	tab[ key ] = val
25	-- end
26end
27
28--
29function AutoTable( _tab )
30	return setmetatable( _tab, meta )
31end
32
33
34
35
36--	exportstring( string )
37--	returns a "Lua" portable version of the string
38local function exportstring( s )
39  s = string.format( "%q",s )
40  -- to replace
41  s = string.gsub( s,"\\\n","\\n" )
42  s = string.gsub( s,"\r","\\r" )
43  s = string.gsub( s,string.char(26),"\"..string.char(26)..\"" )
44  return s
45end
46
47--	The Save Function
48function table.save(  tbl,filename )
49
50   local charS,charE = "   ","\n"
51   local file,err
52   -- create a pseudo file that writes to a string and return the string
53   if not filename then
54      file =  { write = function( self,newstr ) self.str = self.str..newstr end, str = "" }
55      charS,charE = "",""
56   -- write table to tmpfile
57   elseif filename == true or filename == 1 then
58      charS,charE,file = "","",io.tmpfile()
59   -- write table to file
60   -- use io.open here rather than io.output, since in windows when clicking on a file opened with io.output will create an error
61   else
62      file,err = io.open( filename, "w" )
63      if err then return _,err end
64   end
65   -- initiate variables for save procedure
66   local tables,lookup = { tbl },{ [tbl] = 1 }
67   file:write( "return {"..charE )
68   for idx,t in ipairs( tables ) do
69      if filename and filename ~= true and filename ~= 1 then
70         file:write( "-- Table: {"..idx.."}"..charE )
71      end
72      file:write( "{"..charE )
73      local thandled = {}
74      for i,v in ipairs( t ) do
75         thandled[i] = true
76         -- escape functions and userdata
77         if type( v ) ~= "userdata" then
78            -- only handle value
79            if type( v ) == "table" then
80               if not lookup[v] then
81                  table.insert( tables, v )
82                  lookup[v] = #tables
83               end
84               file:write( charS.."{"..lookup[v].."},"..charE )
85            elseif type( v ) == "function" then
86               file:write( charS.."loadstring("..exportstring(string.dump( v )).."),"..charE )
87            else
88               local value =  ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
89               file:write(  charS..value..","..charE )
90            end
91         end
92      end
93      for i,v in pairs( t ) do
94         -- escape functions and userdata
95         if (not thandled[i]) and type( v ) ~= "userdata" then
96            -- handle index
97            if type( i ) == "table" then
98               if not lookup[i] then
99                  table.insert( tables,i )
100                  lookup[i] = #tables
101               end
102               file:write( charS.."[{"..lookup[i].."}]=" )
103            else
104               local index = ( type( i ) == "string" and "["..exportstring( i ).."]" ) or string.format( "[%d]",i )
105               file:write( charS..index.."=" )
106            end
107            -- handle value
108            if type( v ) == "table" then
109               if not lookup[v] then
110                  table.insert( tables,v )
111                  lookup[v] = #tables
112               end
113               file:write( "{"..lookup[v].."},"..charE )
114            elseif type( v ) == "function" then
115               file:write( "loadstring("..exportstring(string.dump( v )).."),"..charE )
116            else
117               local value =  ( type( v ) == "string" and exportstring( v ) ) or tostring( v )
118               file:write( value..","..charE )
119            end
120         end
121      end
122      file:write( "},"..charE )
123   end
124   file:write( "}" )
125   -- Return Values
126   -- return stringtable from string
127   if not filename then
128      -- set marker for stringtable
129      return file.str.."--|"
130   -- return stringttable from file
131   elseif filename == true or filename == 1 then
132      file:seek ( "set" )
133      -- no need to close file, it gets closed and removed automatically
134      -- set marker for stringtable
135      return file:read( "*a" ).."--|"
136   -- close file and return 1
137   else
138      file:close()
139      return 1
140   end
141end
142
143--// The Load Function
144function table.load( sfile )
145   -- catch marker for stringtable
146   if string.sub( sfile,-3,-1 ) == "--|" then
147      tables,err = loadstring( sfile )
148   else
149      tables,err = loadfile( sfile )
150   end
151   if err then return _,err
152   end
153   tables = tables()
154
155   if tables == nil then
156      return _, "Config file is corrupted"
157   end
158
159   for idx = 1,#tables do
160      local tolinkv,tolinki = {},{}
161      for i,v in pairs( tables[idx] ) do
162         if type( v ) == "table" and tables[v[1]] then
163            table.insert( tolinkv, { i,tables[v[1]] } )
164         end
165         if type( i ) == "table" and tables[i[1]] then
166            table.insert( tolinki, { i,tables[i[1]] } )
167         end
168      end
169
170      -- we need to set AutoTable on every level, otherwise we get asserts for uknown settings
171      tables[idx] = AutoTable(tables[idx])
172
173      -- link values, first due to possible changes of indices
174      for _,v in ipairs( tolinkv ) do
175         tables[idx][v[1]] = v[2]
176      end
177      -- link indices
178      for _,v in ipairs( tolinki ) do
179         tables[idx][v[2]],tables[idx][v[1]] =  tables[idx][v[1]],nil
180      end
181   end
182   return AutoTable(tables[1])
183end