292 lines
11 KiB
Racket
292 lines
11 KiB
Racket
#lang scribble/manual
|
|
|
|
@(require
|
|
scribble/example
|
|
scribble/core
|
|
racket/runtime-path
|
|
(for-label racket/base
|
|
racket/string
|
|
racket/class
|
|
racket/file
|
|
racket/serialize))
|
|
|
|
@(define-runtime-path main-rkt "../main.rkt")
|
|
|
|
@(define myeval
|
|
(make-base-eval `(require (file ,(path->string main-rkt)) roos)))
|
|
|
|
@title[#:tag "ini-parser"]{INI File Parser and Writer}
|
|
|
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
|
|
|
@defmodule[simple-ini]{This module provides simple facilities for reading and
|
|
writing INI-style configuration files. It supports strings, numbers, booleans,
|
|
paths, and serializable Racket values, and allows the parsed configuration to
|
|
be queried and modified.}
|
|
|
|
@section{Creating and Parsing INI Files}
|
|
|
|
@defproc[(make-ini) is-ini?]{
|
|
Creates a new, empty INI value. The representation of an INI value is
|
|
private; use @racket[is-ini?] to recognize it and the procedures in this
|
|
module to inspect or modify it.
|
|
}
|
|
|
|
@defproc[(file->ini [file (or/c path-string? symbol?)]) is-ini?]{
|
|
Reads an INI file from disk and returns an INI value.
|
|
If the file does not exist, an empty INI value is returned.
|
|
If @racket[file] is a symbol, the path consists of the user's preferences
|
|
directory and the symbol's name with an @filepath{.ini} suffix.
|
|
|
|
The parser supports:
|
|
|
|
@itemlist[
|
|
@item{Sections (e.g., @tt{[section-name]})}
|
|
@item{Key-value pairs (e.g., @tt{key=value})}
|
|
@item{Comments (lines starting with @tt{;})}
|
|
@item{Empty lines}
|
|
]
|
|
|
|
An unrecognized non-empty line raises an exception that includes its line
|
|
number and contents.
|
|
|
|
The keys are stored as symbols, and values are automatically interpreted as:
|
|
@itemlist[
|
|
@item{Numbers, if the value matches a number pattern}
|
|
@item{Booleans, if the value is @tt{#t}, @tt{true}, @tt{#f}, or @tt{false} (case-insensitive)}
|
|
@item{Paths written with the @tt{PATH:} prefix}
|
|
@item{Explicit Racket-readable strings written with the @tt{VALUE:} prefix,
|
|
including strings containing newlines}
|
|
@item{Serializable Racket values written with the @tt{SER:} prefix}
|
|
@item{Otherwise, as strings}
|
|
]
|
|
}
|
|
|
|
@defproc[(ini->file [ini is-ini?] [file path-string?] [#:private? private? boolean? #f]) void?]{
|
|
Writes an INI structure (as produced by @racket[file->ini] or @racket[make-ini]) to the specified file.
|
|
Missing parent directories are created automatically.
|
|
If @racket[private?] is @racket[#t], Unix-like systems restrict the file to mode @tt{0600} before the INI contents are written.
|
|
|
|
The output preserves:
|
|
@itemlist[
|
|
@item{Section headers}
|
|
@item{Key-value pairs}
|
|
@item{Comments (prefixed with @tt{;})}
|
|
@item{Empty lines}
|
|
]
|
|
|
|
Strings, paths, numbers, and booleans have dedicated encodings. Other values
|
|
are written using @racket[serialize] and must therefore be serializable.
|
|
}
|
|
|
|
@defproc[(get-ini-file [file (or/c path-string? symbol?)]) path?]{
|
|
Resolves the file argument used by the API. A symbol is resolved in the
|
|
user's preferences directory and receives an @filepath{.ini} suffix; a path
|
|
string is converted to a path without otherwise changing its location.
|
|
}
|
|
|
|
@defproc[(is-ini? [value any/c]) boolean?]{
|
|
Returns @racket[#t] when @racket[value] is an INI value created by
|
|
@racket[make-ini] or @racket[file->ini], and @racket[#f] otherwise.
|
|
}
|
|
|
|
@section{Accessing and Modifying Values}
|
|
|
|
@defproc[(ini-get [ini is-ini?]
|
|
[section (or/c symbol? string?)]
|
|
[key (or/c symbol? string?)]
|
|
[def-val any/c])
|
|
any/c]{
|
|
Retrieves the value associated with the given @racket[key] in the specified
|
|
@racket[section]. Section and key matching is case-insensitive. If the key is
|
|
not found, returns @racket[def-val].
|
|
}
|
|
|
|
@defproc[(ini-set! [ini is-ini?]
|
|
[section (or/c symbol? string?)]
|
|
[key (or/c symbol? string?)]
|
|
[val any/c])
|
|
is-ini?]{
|
|
Sets the value of @racket[key] in the specified @racket[section] of the INI structure. If the section or key does not exist, it is created. Returns the modified INI structure.}
|
|
|
|
@section{Inspecting and Iterating over INI Values}
|
|
|
|
@defproc[(ini-sections [ini is-ini?]) (listof symbol?)]{
|
|
Returns the named sections in source order. Key-value pairs before the first
|
|
section header belong to the internal top-level section and are not included
|
|
in this result.
|
|
}
|
|
|
|
@defproc[(ini-keys [ini is-ini?] [section symbol?]) (listof symbol?)]{
|
|
Returns the keys in @racket[section] in source order. If the section does not
|
|
exist, returns the empty list. Use @racket['nil] to retrieve keys that occur
|
|
before the first section header.
|
|
}
|
|
|
|
@defproc[(ini-for-each [proc procedure?] [ini is-ini?]) void?]{
|
|
Calls @racket[proc] with three arguments---the section, key, and value---for
|
|
every key-value pair, in source order. Key-value pairs before the first
|
|
section header use @racket['nil] as their section.
|
|
|
|
@examples[#:eval myeval
|
|
(define settings (make-ini))
|
|
(ini-set! settings 'server 'port 8080)
|
|
(ini-set! settings 'server 'tls #t)
|
|
(ini-sections settings)
|
|
(ini-keys settings 'server)
|
|
(ini-for-each
|
|
(lambda (section key value)
|
|
(printf "~a.~a = ~v~n" section key value))
|
|
settings)]
|
|
}
|
|
|
|
|
|
@section{The @racket[ini%] Racket Class}
|
|
|
|
@defmodule[simple-ini/class]
|
|
|
|
Require this module for the OO implementation of this Simple INI implementation
|
|
|
|
|
|
@defclass[ini% object% ()]{
|
|
|
|
An OO wrapper around the ini functions.
|
|
|
|
@defconstructor[([file (or/c symbol? string? path? boolean?) #f]
|
|
[fail (or/c boolean?) #f]
|
|
[private? (or/c boolean?) #f]
|
|
)]{
|
|
Creates the ini from the given file.
|
|
* If (eq? file #f), an empty ini will be made.
|
|
* if (symbol? file), an ini will be made or read in the users preferences folder with the given (format "~a.ini" file) as name.
|
|
* Otherwise, the file will be made or read at the given location.
|
|
|
|
The fail flag determines if methods of the class will fail when some value in the ini file is written while there is no file to write to
|
|
or if some non existing key is read.
|
|
}
|
|
|
|
@defmethod*[([(get-file) (or/c path? #f)])]{
|
|
Gets the current ini file. See constructor for more information.
|
|
}
|
|
|
|
@defmethod*[([(set-file! [file (or/c symbol? string? path?)]) this])]{
|
|
Sets the ini file to be used. See constructor for more information.
|
|
}
|
|
|
|
@defmethod*[([(get-fail) boolean?])]{
|
|
Gets the value of the 'fail' flag. See constructor for more information.
|
|
}
|
|
|
|
@defmethod*[([(set-fail! [fail boolean?]) this])]{
|
|
Sets the value of the 'fail' flag. See constructor for more information.
|
|
}
|
|
|
|
|
|
@defmethod*[([(get-private?) boolean?])]{
|
|
Returns whether private file permissions are enabled.
|
|
}
|
|
|
|
@defmethod*[([(set-private! [private? boolean?]) this])]{
|
|
Enables or disables private file permissions for subsequent writes.
|
|
}
|
|
|
|
@defmethod*[([(sections) (listof symbol?)])]{
|
|
Returns the named sections in source order, as with @racket[ini-sections].
|
|
}
|
|
|
|
@defmethod*[([(keys [section symbol?]) (listof symbol?)])]{
|
|
Returns the keys in @racket[section], as with @racket[ini-keys].
|
|
}
|
|
|
|
@defmethod*[([(for-each [proc procedure?]) void?])]{
|
|
Calls @racket[proc] with the section, key, and value of each key-value pair,
|
|
as with @racket[ini-for-each].
|
|
}
|
|
|
|
@defmethod*[([(reload) this])]{
|
|
Reloads the ini file in memory, or empties the ini structure (eq? file #f).
|
|
}
|
|
|
|
@defmethod*[([(set! [section (or/c symbol? string?)] [key (or/c symbol? string?)] [value any/c]) this])]{
|
|
Sets the value of the key in the given section. After the set! operation, the ini structure will be written to file.
|
|
Note. Although ini files can be read from standard .ini formats, the simple-ini format will be enhanced. It wil store values in racket format, so that 'read' can be used to read in the racket value.
|
|
}
|
|
|
|
@defmethod*[([(get [section (or/c symbol? string?)] [key (or/c symbol? string?)] [default-value any/c]) any/c]
|
|
[(get [section (or/c symbol? string?)] [key (or/c symbol? string?)]) any/c]
|
|
)]{
|
|
Returns the value for the given section and key combination. If this combination does not exist in the ini structure, it will return the default-value. However, if default-value is not given, it will return #f.
|
|
}
|
|
|
|
} ; end class
|
|
|
|
|
|
|
|
@section{The @racket[ini] Roos Class}
|
|
|
|
@defmodule[simple-ini/roos]{Require this module for the OO implementation of this Simple INI implementation in the ROOS OO framework
|
|
|
|
Provides a @seclink["top" #:doc '(lib "roos/scribblings/roos.scrbl")]{Roos class} that gives object-oriented access to INI files using the underlying @racket[file->ini] parser system. The class offers methods to load, query, and update INI files using familiar object-style interactions.}
|
|
|
|
|
|
@defproc[(%-! [ini roos-class*] [or/c path-string?]) roos-object*]{
|
|
Creates an @racket[ini] object. If a @racket[file] path is provided and the file exists, it is loaded immediately. Otherwise, an empty INI structure is created.
|
|
|
|
If no file is provided, the object operates in-memory only. Subsequent @racket[set!] operations will raise an error unless a file is later specified with @racket[(file!)].
|
|
}
|
|
|
|
|
|
@defproc[(file) (or/c path-string? #f)]{
|
|
Returns the current filename associated with this INI object.
|
|
}
|
|
|
|
|
|
@defproc[(file! [f path-string?]) void?]{
|
|
Sets the file to use as backing storage for the INI structure. Triggers a reload from disk.
|
|
}
|
|
|
|
@defproc[(fail) boolean?]{
|
|
Returns if an error will be thrown when a set! is done and no file has been set to write the contents to}
|
|
|
|
@defproc[(fail! [yn boolean?]) boolean?]{
|
|
Sets fail to the given value.}
|
|
|
|
|
|
@defproc[(reload) void?]{
|
|
Reloads the INI content from disk, using the current file path.
|
|
If the file does not exist, the content is reset to an empty INI structure.
|
|
}
|
|
|
|
@defproc[(sections) (listof symbol?)]{
|
|
Returns the named sections in source order, as with
|
|
@racket[ini-sections].
|
|
}
|
|
|
|
@defproc[(keys [section symbol?]) (listof symbol?)]{
|
|
Returns the keys in @racket[section], as with @racket[ini-keys].
|
|
}
|
|
|
|
@defproc[(for-each [proc procedure?]) void?]{
|
|
Calls @racket[proc] with the section, key, and value of each key-value
|
|
pair, as with @racket[ini-for-each].
|
|
}
|
|
|
|
@defproc[(set! [section (or/c symbol? string?)] [key (or/c symbol? string?)] [val any/c]) ini]{
|
|
Sets the value in the INI structure for the given @racket[section] and @racket[key] to @racket[val].
|
|
|
|
If a file is associated with the object, the structure is saved to disk immediately.
|
|
If no file is set and @racket[fail] is enabled, an error is raised.
|
|
Returns the INI object itself.
|
|
}
|
|
|
|
@defproc[(get [section (or/c symbol? string?)] [key (or/c symbol? string?)] [def-val any/c] ...) any/c]{
|
|
Retrieves the value associated with the given @racket[section] and @racket[key].
|
|
|
|
If not found:
|
|
@itemlist[
|
|
@item{Returns @racket[#f] if no default is given and @racket[fail] is disabled}
|
|
@item{Returns @racket[def-val] if one is provided}
|
|
@item{Raises an error if @racket[fail] is enabled and no default is given}
|
|
]
|
|
}
|