; $Id: ini_file.pro,v 1.3 2005/09/21 13:21:29 Harald Exp $ ; ;+ ; Read INI-file and determine number of sections and tags. ; ; @file_comments ; ; An INI-file is an ASCII text file containing one or more sections ; (indicated by '[section_name]'. Each section contains one or more tags ; of the form 'tag_name=tag_value.' Lines containing only) whitespace ; characters are ignored. ; ; @param file {in}{required}{type=string} filepath to INI-file ; ; @keyword start {out}{optional}{type=lonarr} section start indices ; @keyword ntags {out}{optional}{type=lonarr} list of section tag counts ; ; @returns string array contaning INI-file content (empty lines removed) ;- FUNCTION ini_data, file, START=start, NTAGS=ntags COMPILE_OPT HIDDEN line = '' data = '[]' IF (FILE_TEST(file)) THEN BEGIN OPENR, unit, file, /GET_LUN WHILE NOT EOF(unit) DO BEGIN READF, unit, line line = STRTRIM(line, 2) IF (line EQ '') THEN $ CONTINUE data = [TEMPORARY(data), line] ENDWHILE FREE_LUN, unit ENDIF idx = [WHERE(STRMATCH(data, '\[*\]'), cnt), N_ELEMENTS(data)] start = idx[0:cnt-1] ntags = idx[1:cnt] - idx[0:cnt-1] - 1 RETURN, data END ; ini_data PRO ini_save, file, data, section COMPILE_OPT HIDDEN ; create section block hook = data list = TAG_NAMES(data) blck = STRARR(N_TAGS(data)) FOR i = 0L, N_TAGS(data) - 1 DO BEGIN type = SIZE(data.(i), /TNAME) nval = STRTRIM(N_ELEMENTS(data.(i)), 2) SWITCH (type) OF 'BYTE': 'INT': 'LONG': 'LONG64': 'UINT': 'ULONG': 'ULONG64': BEGIN form = 'I0' BREAK END 'FLOAT': 'DOUBLE': BEGIN form = 'F0' BREAK END 'STRING' : BEGIN ; escape charcters ',' and '\' data.(i) = Escape(data.(i), '\,', '\') form = 'A' BREAK END ELSE : MESSAGE, 'Type ' + type + ' not handled.', /CONTINUE ENDSWITCH form = '(A,"=",' + nval +'(' + form + ',:,","))' blck[i] = STRING(list[i], data.(i), FORMAT=form) ENDFOR blck = ['[' + STRUPCASE(section) + ']', blck] ; read INI file from disk and merge section block info = ini_data(file, START=start, NTAGS=ntags) expr = '\[' + section + '\]' n = N_ELEMENTS(start) idx = (WHERE(STRMATCH(info[start], expr, /FOLD_CASE)))[0] CASE (idx) OF -1 : info = [info, blck] 0 : info = (n GT 1 ? [blck, info[start[idx+1]:*]] : blck) n-1 : info = [info[0:start[idx]-1], blck] ELSE : info = [info[0:start[idx]-1], blck, info[start[idx+1]:*]] ENDCASE ; write INI file to disk (insert space between sections) idx = [WHERE(STRMATCH(info, '\[*\]'), cnt), N_ELEMENTS(info)] data = info[0:idx[1]-1] FOR i = 1, cnt - 1 DO $ data = [TEMPORARY(data), ' ', info[idx[i]:idx[i+1]-1]] OPENW, unit, file, /GET_LUN PRINTF, unit, data[1 + (idx[1] EQ 1):*], FORMAT='(A)' FREE_LUN, unit data = hook END ; ini_save PRO ini_setval, data, index, value CATCH, error IF (error NE 0) THEN BEGIN MESSAGE, !ERROR_STATE.MSG, /CONTINUE RETURN ENDIF list = TAG_NAMES(data) ndat = N_ELEMENTS(data.(index)) nval = N_ELEMENTS(value) IF (nval NE ndat) THEN BEGIN ; MESSAGE, 'Tag ' + list[index] + ': Dimension mismatch.', /INFO FOR i = 0, N_TAGS(data) - 1 DO BEGIN dval = (i EQ index ? REPLICATE((data.(i))[0], nval) : data.(i)) IF (N_ELEMENTS(dnew) EQ 0) THEN $ dnew = CREATE_STRUCT(list[i], dval) $ ELSE $ dnew = CREATE_STRUCT(dnew, list[i], dval) ENDFOR data = dnew ENDIF data.(index) = value END ; ini_setval PRO ini_load, file, data, section COMPILE_OPT HIDDEN ; read INI file from disk and search for section block info = ini_data(file, START=start, NTAGS=ntags) expr = '\[' + section + '\]' n = N_ELEMENTS(start) idx = (WHERE(STRMATCH(info[start], expr, /FOLD_CASE)))[0] IF (idx EQ -1) THEN $ MESSAGE, 'Section ' + section + ' not found.' IF (ntags[idx] EQ 0) THEN $ MESSAGE, 'Section ' + section + ' contains no tags.' info = info[start[idx]+1:start[idx]+ntags[idx]] ;PRINT, info, FORMAT='(A)' ; read into data structure list = TAG_NAMES(data) FOR i = 0L, N_TAGS(data) - 1 DO BEGIN idx = (WHERE(STRMATCH(info, list[i]+'=*')))[0] IF (idx EQ -1) THEN BEGIN MESSAGE, 'Tag ' + list[i] + ' not found.', /INFO CONTINUE ENDIF type = SIZE(data.(i), /TNAME) vals = STRMID(info[idx], STRLEN(list[i])+1) vals = STRSPLIT(vals, ',', ESCAPE='\', /EXTRACT) ; escape character already removed by STRSPLIT ; vals = Escape(vals, '\,', '\', /UNDO) CASE (type) OF 'BYTE' : ini_setval, data, i, BYTE(FIX(vals)) 'INT' : ini_setval, data, i, FIX(vals) 'LONG' : ini_setval, data, i, LONG(vals) 'LONG64' : ini_setval, data, i, LONG64(vals) 'UINT' : ini_setval, data, i, UINT(vals) 'ULONG' : ini_setval, data, i, ULONG(vals) 'ULONG64' : ini_setval, data, i, ULONG64(vals) 'FLOAT' : ini_setval, data, i, FLOAT(vals) 'DOUBLE' : ini_setval, data, i, DOUBLE(vals) 'STRING' : ini_setval, data, i, vals ELSE : MESSAGE, 'Type ' + type + ' not handled.', /INFO ENDCASE ENDFOR END ; ini_load PRO ini_file, data, section, SAVE=isSave, LOAD=isLoad, ERROR=error, FILE=file CATCH, error IF (error NE 0) THEN BEGIN MESSAGE, !ERROR_STATE.MSG, /CONTINUE RETURN ENDIF IF (N_PARAMS() LT 1) OR (N_PARAMS() GT 2) THEN $ MESSAGE, 'Usage: ini_file, data [, section] /LOAD|SAVE.', /NONAME IF (SIZE(data, /TNAME) NE 'STRUCT') THEN $ MESSAGE, 'Data parameter requires structure.', /NONAME IF (N_ELEMENTS(section) EQ 0) THEN $ section = '' isSave = KEYWORD_SET(isSave) isLoad = KEYWORD_SET(isLoad) IF ((isSave + isLoad) NE 1) THEN $ MESSAGE, 'Use either /SAVE or /LOAD.', /NONAME IF N_ELEMENTS(file) EQ 0 THEN BEGIN path = GETENV('HOME') IF (path EQ '') THEN $ path = GETENV('USERPROFILE') IF (path EQ '') THEN BEGIN MESSAGE, 'Environment variable HOME|USERPROFILE not found, ' + $ 'using current directory.', /INFORMATIONAL CD, CURRENT=path ENDIF file = FILEPATH('idl_user.ini', ROOT_DIR=path) ENDIF IF (isLoad) THEN $ ini_load, file, data, STRUPCASE(section) IF (isSave) THEN $ ini_save, file, data, STRUPCASE(section) END ; ini_file