; _______________________________________________________________________________________
;| |
;| ..::FreshLib::.. Free, open source. Licensed under "Fresh artistic license." |
;|_______________________________________________________________________________________|
;
; Description: OS independent micro configuration files library.
;
; Target OS: Any
;
; Dependencies: strlib.asm;
;
; Notes:
;
;_________________________________________________________________________________________
module "uConfig library"
; Reads a record from a config file as an integer value.
; Returns:
; CF=1 if the record was not found or when the record contains no number.
; eax = -1; the record was not found.
; eax = 0; the record exists, but contains no valid number.
; CF=0; eax=number if the record was found and contains a number.
proc GetConfigInt, .hConfig, .dir, .key
begin
mov eax, -1
stdcall GetConfigRecord, [.hConfig], [.dir], [.key]
jc .finish
push eax
stdcall StrToNumEx, eax
stdcall StrDel ; from the stack
clc
.finish:
return
endp
; Writes a record with the given 32bit integer number
; Returns:
; Nothing
proc SetConfigInt, .hConfig, .dir, .key, .int
begin
stdcall NumToStr, [.int], ntsDec
push eax
stdcall SetConfigRecord, [.hConfig], [.dir], [.key], eax
stdcall StrDel ;from the stack
return
endp
; Search for the given parameter in the database and return its value.
;
; arguments:
; .hConfig - handle of a string with the config database.
; .path - pointer or handle to the string with full path to the database element.
;
; returns:
; CF=0; eax - string handle with the value of the record. if eax==0 the value is NULL.
; CF=1; the record does not exists.
;
; GetConfigRecord.__info.codesize
proc GetConfigRecord, .hConfig, .dir, .key
.path rb 1024
begin
pushad
lea edi, [.path]
mov ebx, [.dir]
mov ecx, [.key]
call __prepare_path
stdcall StrPtr, [.hConfig]
mov esi, eax
lea edx, [.path]
call __SearchData
jc .exit
mov dword [esp+4*regEAX], 0
mov ecx, esi
.end_loop:
lodsb
cmp al, ' '
jae .end_loop
dec esi
cmp esi, ecx
je .exit_ok ; NULL
sub esi, ecx
stdcall StrExtract, ecx, 0, esi
mov [esp+4*regEAX], eax
.exit_ok:
clc
.exit:
popad
return
endp
; SetConfigRecord.__info.codesize
; Set the value of the given record. If the record does not exists, creates it
; with all parent directories if needed.
; Arguments:
; .hConfig - handle of the string with the database.
; .path - full path to the record.
; .hData - pointer or handle to the string with the value for the record.
; if == 0, cdtNULL will be written.
; Returns:
; Nothing
proc SetConfigRecord, .hConfig, .dir, .key, .hData
.path rb 1024
begin
pushad
lea edi, [.path]
mov ebx, [.dir]
mov ecx, [.key]
call __prepare_path
stdcall StrPtr, [.hConfig]
mov esi, eax
push eax
lea edx, [.path]
call __SearchData
pop edi
jnc .record_found
; The record with this path is not found, so it need to be created.
; search back the new line.
.back_loop:
cmp esi, edi
je .create_here
dec esi
mov cl, [esi]
test cl, cl
jz .create_here
cmp cl, ' '
ja .back_loop
inc esi
.create_here:
sub esi, edi ; position inside the string.
push esi ; store the insert position in the stack.
stdcall StrNew
mov edi, eax
dec edx
test cl, cl
jnz .scan_path
stdcall StrCharCat, edi, $0a0d
.scan_path:
inc edx
inc ebx ; the next level of indent
jz .next_indent
inc ebx
.next_indent:
mov esi, edx ; the remainder of the path
.key_loop:
lodsb
cmp al, ':'
je .record
test al, al
jnz .key_loop
; record found
.record:
dec esi
sub esi, edx
mov ecx, ebx
jecxz .indent_ok
.indent_loop:
stdcall StrCharCat, edi, ' '
dec ecx
jnz .indent_loop
.indent_ok: ; add the subdirectory/record name. ECX==0 here
xchg cl, [edx+esi]
stdcall StrCat, edi, edx
mov [edx+esi], cl
lea edx, [edx+esi]
mov eax, '='
jecxz .suffix_ok ; cl contains the separator char
mov eax, ':'+$0a0d00
.suffix_ok:
stdcall StrCharCat, edi, eax
test ecx, ecx
jnz .scan_path
cmp [.hData], 0
je .ins_ready
stdcall StrCat, edi, [.hData]
.ins_ready:
stdcall StrCharCat, edi, $0a0d
mov [.hData], edi
pop edx ; position where to insert the content.
xor esi, esi ; there is no old record.
mov [.dir], esi ; the [.hData] must be freed after use.
jmp .insert_data
; The record with this path was found, so, the old value have to be deleted and
; new one to be recorded.
.record_found:
; search the bounds of the record...
mov edx, esi
sub edx, edi ; position of the record start.
.end_loop:
lodsb
cmp al, ' '
jae .end_loop
dec esi
sub esi, edi
sub esi, edx ; length of the record
; here, edx == position, where the value should be inserted.
; esi == length of the old record value, that have to be deleted.
.insert_data:
stdcall StrSplit,[.hConfig], edx
test esi, esi
jz .record_ok ; no need to delete the old record.
push eax
stdcall StrSplit, eax, esi
stdcall StrDel ; from the stack
.record_ok:
mov edx, eax ; the part of the string after the record.
cmp [.hData], 0
je .restore
stdcall StrCat, [.hConfig], [.hData]
.restore:
stdcall StrCat, [.hConfig], edx
stdcall StrDel, eax
stdcall StrDel, edx
cmp [.dir], 0
jne .exit_ok
stdcall StrDel, [.hData]
.exit_ok:
popad
return
endp
; __SearchData.__info.codesize
;
; Arguments:
; esi - pointer to the text of the database
; edx - pointer to the path string
; Returns:
; CF=0 if the path is found.
; esi - pointer to the value.
; ah=":" if the path is directory.
; ah="=" if the path is data field.
;
; CF=1 the path was not found.
; esi - points to the first row after the end of the block where the path
; is supposed to be (but is not).
; edx - points to the remainder of the path that was not found.
; ebx - the indent of the block where the record should be placed.
proc __SearchData
begin
mov edi, edx
xor ebx, ebx ; current block indent starts with -1
dec ebx
.outer_loop:
; compute the line indent
mov ecx, esi
.scan_indent:
lodsb
test al, al
jz .end_of_file
cmp al, ' '
je .scan_indent
jb .outer_loop
cmp al, ';'
jne .indent_ok
; skips to the end of the line.
.SkipIt:
lodsb
cmp al, ' '
jb .outer_loop
test al, al
jnz .SkipIt
.end_of_file:
dec esi
.error:
stc
return
.indent_ok:
dec esi
sub ecx, esi
neg ecx
; Here the indent of the current line is in ECX.
cmp ecx, ebx
jle .error ; if the current indent is less of equal than the block
; indent, then the needed element can not be found.
cmp ecx, edi
ja .SkipIt ; the current indent is above the current upper limit, so
; the whole line have not to be processed.
mov edi, edx ; the start of the current path (it is always greater than the current indent)
.key_loop:
mov al, [edi]
inc edi
mov ah, [esi]
inc esi
test al, al
jz .maybe_end_of_path
cmp al, ':'
je .key_maybe_found
test ah, ah
jz .end_of_file
cmp al, ah
je .key_loop
; not this, so check the current line whether it is a directory.
.not_this:
dec esi
.dir_loop:
lodsb
test al, al
jz .end_of_file
cmp al, ' '
jb .outer_loop
je .SkipIt
cmp al, '='
je .SkipIt
cmp al, ':'
jne .dir_loop
.skip_dir:
mov edi, ecx ; it is a subdirectory, that is not in the path,
; so set the upper limit.
jmp .SkipIt
.maybe_end_of_path:
cmp ah, '='
je .found
.key_maybe_found:
cmp ah, ':'
jne .not_this
test al, al
jz .skip_dir ; search for another key...
mov edx, edi ; the subdirectory was found. Set new path and block indent.
mov ebx, ecx
jmp .SkipIt
.found: ; the whole path was found.
clc
return
endp
; __prepare_path.__info.codesize
; arguments:
; ebx - pointer/handle to directory.
; ecx - pointer/handle to key
; edi - buffer
proc __prepare_path
begin
stdcall StrPtr, ebx
mov esi, eax
.copy1:
lodsb
stosb
test al, al
jnz .copy1
jecxz .path_ok
mov al, ':'
dec edi
cmp [edi-1], al
je .sep_ok
stosb
.sep_ok:
stdcall StrPtr, ecx
mov esi, eax
.copy2:
lodsb
stosb
test al, al
jnz .copy2
.path_ok:
return
endp
endmodule