#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} ] }