Size: 8629
Comment: New version
|
Size: 12013
Comment: New version
|
Deletions are marked like this. | Additions are marked like this. |
Line 2: | Line 2: |
{{{IniFiles}}} is part of the CategoryAplTree project. | `IniFiles` is part of the CategoryAplTree project. |
Line 5: | Line 5: |
~-<<SeeSaw(section="table-of-contents", show="true", seesaw="false", toshow="<<(Show>> table-of-contents)", tohide="<<(Hide>> table-of-contents)", speed="Slow")>>-~ |
|
Line 20: | Line 22: |
== The INI specification == INI files are not well-defined. Different implementation come with all sorts of specialties. For details see: http://en.wikipedia.org/wiki/INI_files However, there is some common ground: * In old fashioned INI files values don't have to be enclosed in quotes. The Windows API functions however except quotes; if found they are simply removed. * It is widely agreed that neither section names nor value names should carry a blank, although the Windows API functions accept them. The `IniFiles` class would throw an error if it finds one in any name. == Differences between APL-like INI files and classic ones == * While values within quotes are treated as text those without quotes are treated as numbers. * Numeric values can carry either a single value or a vector of values. * Nested strings are ppossible. * Section names as well as value names must not carry a blank. * Section names as well as value names must be valid APL names if you want convert in instance of `IniFiles` with the `Convert` method. * There is the concept of variables which one can refer to in any part of an INI file once they got defined. * Section names must be valid APL names. * If you want use the `Convert` method then value names must also be valid APL names. |
|
Line 21: | Line 44: |
Line 24: | Line 48: |
{{{HomeFolder='C:/Windows/Appl/'}}} | {{{HomeFolder='C://Appl/'}}} |
Line 35: | Line 59: |
=== References === Furthermore, an entry like: {{{LogFolder='{"HomeFolder}Logsfiles/'}}} is treated in a special way: the name between the curlies is taken as the name of an already defined value. It is then replaced by the value of that entry. Note that of course "!HomeFolder" must be specified upfront. Prior to version 1.5, this must be specified within the same section. As a result the same variable needed to be specified more than once if the same path needed to be available in more than one section. |
=== References (place holders) === If you need to define a number of paths like: {{{ path='C:\MyApp' pathCertificates='C:\MyApp\Certificates' pathRootCertificates='C:\MyApp\RootCert' pathLogfiles='C:\MyApp\Log' }}} you can simplify this by using the "replacement" syntax: {{{ path='C:\MyApp' pathCertificates='{path}\Certificates' pathRootCertificates='{path}\RootCert' pathLogfiles='{path}\Log' }}} Naturally "path" must be specified upfront. Prior to version 1.5, this must be specified within the same section. As a result the same variable needed to be specified more than once if the same path needed to be available in more than one section. |
Line 47: | Line 83: |
Line 51: | Line 88: |
* They can only be used during the instanciation | * They can only be used during the instantiation |
Line 54: | Line 91: |
=== Nested entries: special "Add" syntax === Sometimes one needs to specify potentially long lists on a particular keyword, for example a list of IP addresses a server is supposed to ignore: "DenyIP". Specifying them on one single line is very hard to read and prone to error in case of a change. Instead this can be achieved with a special syntax: {{{ DenyIP='' DenyIP,='2001:0db8:0000:0000:0000:0000:1428:57ab' DenyIP,='2001:0db8:0000:0000:0000::1428:57ab' }}} This results in a nested vector of strings. Note that you '''must''' initialize the vars as an empty vector in the first place. The "=," syntax implies an {enlose} on the data on the right of "=". This syntax is supported for both, characters and numbers: {{{ vector='' vector,=1 2 3 vector,=200 300 }}} leads to: {{{ (1 2 3) (200 300) }}} |
|
Line 81: | Line 146: |
=== Old-style INI files === Since version 2.2 `IniFiles` is able to process old-style INI files. This means that these two values: |
=== Classic INI files === Since version 2.2 `` is able to process classic INI files. This means that these two values: |
Line 92: | Line 157: |
Note that an INI files must be consistent: it cannot mix old stuff with new stuff which is why this INI file: | Note that an INI files must be consistent: it cannot mix classic stuff with new stuff. This INI file; |
Line 99: | Line 164: |
would cause an error. Note that from version 2.2 on there is a new property `OldStyleFlag` which is a Boolean that will be 1 only if the processed INI file is an old-fashioned one. |
would therefore cause an error. This means that in order to be identified as classic an INI file must not enclose any of its values in quotes. From version 2.2 on there is a new property `OldStyleFlag` which is a Boolean that will be 1 only if the processed INI file is an classic one. |
Line 110: | Line 174: |
.... |
|
Line 111: | Line 177: |
you can get all information you are interested in by calling the method "Get". Note that names are '''not''' case sensitive. | ...you can get all information you are interested in by calling the method "Get". Note that names are '''not''' case sensitive. (Note that any instance of the `IniFiles` class can be convert to an ordinary namescpace - see the `Convert` method) |
Line 177: | Line 246: |
=== Nested Entries === Since version 1.4 nested values are supported. Imagine an INI file that sets an "AcceptIP" value to a number of IP addresses to be accepted when a client tries to connect to your application. That's how that might look like: {{{ AcceptID='192.168.68.1,192.168.68.100,195.64.2.2,127.0.0.1,85.86.87.88,156.147.123.1' }}} and maybe even much longer. Horrible, and prone to error when that needs to be changed. By initializing the value as an empty vector and then using the ",=" syntax one can overcome the problem: {{{ AcceptID='' AcceptID,='192.168.68.1' AcceptID,='192.168.68.100' AcceptID,='195.64.2.2' AcceptID,='127.0.0.1' AcceptID,='85.86.87.88' AcceptID,='156.147.123.1' }}} This results in a nexted vector of length 6 were each item holds a single IP addres. This works with numbers as well: {{{ vector='' vector,=1 2 3 vector,=200 300 }}} leads to: {{{ (1 2 3) (200 300) }}} |
|
Line 215: | Line 255: |
== The "Convert" message == Any instance of the `IniFiles` class can be converted into an ordinary namespace. The `myIni` instance created earlier on for example can be converted in two ways: ==== Use namespace rather than instance ==== The `Convert` method can be used to convert an instance into an ordinary namespace. It takes a reference to a namespace as the right argument: {{{ Ini←myIni.Convert ⎕NS'' Ini.List ⍬ DIR AppFolder C:/mainfolder/appls/ DIR DocsFolder C:/mainfolder/docs/ DIR Home C:/mainfolder/ DIR LogFileFolder C:/mainfolder/Logs/ GENERAL FormSize 800 1200 GENERAL LogLevels 1 2 3 GENERAL LogfileFlag 1 GENERAL MaxNoOfErrors 20 Ini.GENERAL.MaxNoOfErrors 20 }}} You can ask `Convert` to convert the instance into a flat namespace, effectively ignoring the sections: {{{ Ini←'flat' myIni.Convert ⎕NS'' Ini.List ⍬ Ini.List ⍬ AppFolder C:/mainfolder/appls/ DocsFolder C:/mainfolder/docs/ FormSize 800 1200 Home C:/mainfolder/ LogFileFolder C:/mainfolder/Logs/ LogLevels 1 2 3 LogfileFlag 1 MaxNoOfErrors 20 Ini.MaxNoOfErrors 20 }}} |
|
Line 216: | Line 299: |
An INI file is by definition not a kind of database and should '''not''' be used as such. | An INI file is by definition not a kind of database and should ''not'' be used as such. Therefore it is recommended to use the `Save` method only for two purposes: * Establish a new INI file with default settings. * Convert an old-fashioned INI file into an APL-like INI file. |
Line 240: | Line 326: |
Home='D:\' | Home='D:\SomeWhereElse' |
Line 254: | Line 340: |
D:\ThePrintFolder | D:\SomeWhereElse |
Line 259: | Line 345: |
For bug reports, future enhancements and a full version history see IniFiles/ProjectPage | For bug reports, future enhancements and a full version history see /ProjectPage |
IniFiles
IniFiles is part of the CategoryAplTree project.
Contents
Overview
INI files are still useful to provide settings to an application. Neither Vista nor Windows 7 are going to change this.
The Windows API methods provided to read a particular value have one advantage: they deliver always up-to-date values. Whether that is an important feature or not is another matter.
They have disadvantages as well:
- They are slow
- They return everything as a string
In case nobody else is changing your INI files while your application is running then the "IniFile" class introduced in this article might attract your attention.
This class allows you to use a kind of APL-Syntax in your INI files. Values not enclosed in quotes will be converted to numbers, everything else gets a string.
The INI specification
INI files are not well-defined. Different implementation come with all sorts of specialties. For details see: http://en.wikipedia.org/wiki/INI_files
However, there is some common ground:
- In old fashioned INI files values don't have to be enclosed in quotes. The Windows API functions however except quotes; if found they are simply removed.
It is widely agreed that neither section names nor value names should carry a blank, although the Windows API functions accept them. The IniFiles class would throw an error if it finds one in any name.
Differences between APL-like INI files and classic ones
- While values within quotes are treated as text those without quotes are treated as numbers.
- Numeric values can carry either a single value or a vector of values.
- Nested strings are ppossible.
- Section names as well as value names must not carry a blank.
Section names as well as value names must be valid APL names if you want convert in instance of IniFiles with the Convert method.
- There is the concept of variables which one can refer to in any part of an INI file once they got defined.
- Section names must be valid APL names.
If you want use the Convert method then value names must also be valid APL names.
Details
Character Values
An entry like:
HomeFolder='C://Appl/'
results in a string holding the path.
Numeric Values
An entry like:
FormSize=300 400
results in a two-element-vector "FormSize" holding two integers.
References (place holders)
If you need to define a number of paths like:
path='C:\MyApp' pathCertificates='C:\MyApp\Certificates' pathRootCertificates='C:\MyApp\RootCert' pathLogfiles='C:\MyApp\Log'
you can simplify this by using the "replacement" syntax:
path='C:\MyApp' pathCertificates='{path}\Certificates' pathRootCertificates='{path}\RootCert' pathLogfiles='{path}\Log'
Naturally "path" must be specified upfront. Prior to version 1.5, this must be specified within the same section. As a result the same variable needed to be specified more than once if the same path needed to be available in more than one section.
Since version 1.5 this restriction was lifted by the introduction of "local" variables, see there.
Local Variables
Local values are those specified above the first section. They have only one purpose: to be used as references in several sections.
There are some restrictions:
- They can only be used during the instantiation
- They must not be nested
- Although it is possible to specify a numeric value this does not make any sense since numeric values cannot be used as references
Nested entries: special "Add" syntax
Sometimes one needs to specify potentially long lists on a particular keyword, for example a list of IP addresses a server is supposed to ignore: "DenyIP". Specifying them on one single line is very hard to read and prone to error in case of a change.
Instead this can be achieved with a special syntax:
DenyIP='' DenyIP,='2001:0db8:0000:0000:0000:0000:1428:57ab' DenyIP,='2001:0db8:0000:0000:0000::1428:57ab'
This results in a nested vector of strings. Note that you must initialize the vars as an empty vector in the first place. The "=," syntax implies an {enlose} on the data on the right of "=".
This syntax is supported for both, characters and numbers:
vector='' vector,=1 2 3 vector,=200 300
leads to:
(1 2 3) (200 300)
Importing another INI file
Above the first section definition one can also import another INI file. This can be used for two different purposes:
- Make an INI file path/drive independent, for example in order to support an application on a notebook as well as a server.
- Import a network based INI file from a local one.
This is how an INI file that is going to be imported elsewhere could look like:
; INI file to be imported [DRIVES] ; This is a comment ArchiveDrive='D:\' DocDrive='M:\'
Note that the section is ignored in the file that will import this INI file. Assuming that the name of this INI file is "local.ini":
!Import local.ini [PATHS] ArchivePath='{ArchiveDrive}this\that\' DocPath='{DocDrive}there\'
Using this technique all values that depend on the current environment can be specified in local.ini while all the other entries can be specified in the second INI file. However, this is suitable for small "local" INI files. With version 2.0.0 the constructor accepts more than one filename: use this to effectively merge INI files.
Classic INI files
Since version 2.2 is able to process classic INI files. This means that these two values:
text=hello digits=1 2 3
are both converted into text.
Note that an INI files must be consistent: it cannot mix classic stuff with new stuff. This INI file;
text1=hello test2='universe'
would therefore cause an error. This means that in order to be identified as classic an INI file must not enclose any of its values in quotes.
From version 2.2 on there is a new property OldStyleFlag which is a Boolean that will be 1 only if the processed INI file is an classic one.
Example
Creating an Instance
After creating an instance from the class:
myIni←⎕New #.IniClass (,⊂'C:/Appl/Example.ini')
....
Accessing Data with the "Get" method
...you can get all information you are interested in by calling the method "Get". Note that names are not case sensitive.
(Note that any instance of the IniFiles class can be convert to an ordinary namescpace - see the Convert method)
Given this file "Example.ini":
[GENERAL] MaxNoOfErrors=20 FormSize=800 1200 LogfileFlag=1 LogLevels=1 2 3 ; from 1 to 9 [DIR] Home='C:/mainfolder/' AppFolder='{Home}appls/' DocsFolder='{Home}docs/' LogFileFolder='{Home}Logs/'
You can get any level of information you are interested in:
- get everything
- get all keys and values of a particular section
- get a particular value from a particular section
Examples with "Get"
myIni.Get ⍬ ⍬ GENERAL MAXNOOFERRORS 20 FORMSIZE 800 1200 LOGFILEFLAG 1 LOGLEVELS 1 2 3 DIR HOME C:/mainfolder/ APPFOLDER C:/mainfolder/appls/ DOCSFOLDER C:/mainfolder/docs/ LOGFILEFOLDER C:/mainfolder/Logs/ myIni.Get'General' ⍬ MAXNOOFERRORS 20 FORMSIZE 800 1200 LOGFILEFLAG 1 LOGLEVELS 1 2 3 myIni.Get'General' 'FormSize' 800 1200 ¯1 myIni.Get'General' 'Unknown' ⍝ with default ¯1 myIni.Get'General' 'Unknown' ⍝ without default Value Error: "Unknown" myDoc.Get'General' 'Unknown'
Indexing
Since version 1.1, the class provides a default property. That means you can access values by indexing.
Examples (with the same INI file listed above):
myIni[⊂'GeneRAL:'] 20 800 1200 1 1 2 3 ⊃myIni[⊂'GeneRAL:FormSize'] 800 1200
Assigning
myIni[⊂'GeneRAL:FormSize']←⊂12 23
The "Put" method
(12 23) myIni.Put 'GeneRAL:FormSize'
The "Save" method
You can also change a particular value but the changed value will persist only if you execute the "Save" method at some point:
myIni[⊂'GeneRAL:FormSize']←⊂'¯1 1000 myIni.Save
You can use the Save method to convert an old-fashioned INI file because every value will be saved within quotes.
The "Convert" message
Any instance of the IniFiles class can be converted into an ordinary namespace. The myIni instance created earlier on for example can be converted in two ways:
Use namespace rather than instance
The Convert method can be used to convert an instance into an ordinary namespace. It takes a reference to a namespace as the right argument:
Ini←myIni.Convert ⎕NS'' Ini.List ⍬ DIR AppFolder C:/mainfolder/appls/ DIR DocsFolder C:/mainfolder/docs/ DIR Home C:/mainfolder/ DIR LogFileFolder C:/mainfolder/Logs/ GENERAL FormSize 800 1200 GENERAL LogLevels 1 2 3 GENERAL LogfileFlag 1 GENERAL MaxNoOfErrors 20 Ini.GENERAL.MaxNoOfErrors 20
You can ask Convert to convert the instance into a flat namespace, effectively ignoring the sections:
Ini←'flat' myIni.Convert ⎕NS'' Ini.List ⍬ Ini.List ⍬ AppFolder C:/mainfolder/appls/ DocsFolder C:/mainfolder/docs/ FormSize 800 1200 Home C:/mainfolder/ LogFileFolder C:/mainfolder/Logs/ LogLevels 1 2 3 LogfileFlag 1 MaxNoOfErrors 20 Ini.MaxNoOfErrors 20
A Warning
An INI file is by definition not a kind of database and should not be used as such. Therefore it is recommended to use the Save method only for two purposes:
- Establish a new INI file with default settings.
- Convert an old-fashioned INI file into an APL-like INI file.
Multiple INI files (Merging)
Note that since version 2.0.0 one can specify more than one filename in the ⎕NEW statement. This effectively merges the INI files together. Note that....
- in case of name clashes the last file wins.
you cannot execute the Save method when more than one INI file was specified.
the IniFilename property always returns a simple string. In case of more than one INI file the filenames are separated by ";".
Note that placeholders are replaced only after all INI files specified have been processed. Imagine this general INI file Foo.INI:
[CONFIG] Home='C:\' [PATHS] PrintFolder='{Home}ThePrintFolder'
and a machine-specific INI file Foo_MyMachine.INI:
[CONFIG] Home='D:\SomeWhereElse'
When only the first INI file is processed and the result of ⎕NEW is assigned to MyIni then you get this:
MyIni.PrintFolder C:\ThePrintFolder
When you process both INI file, first Foo.INI and then Foo_MyMachine.INI then you get this:
MyIni.PrintFolder D:\SomeWhereElse
Project Page
For bug reports, future enhancements and a full version history see /ProjectPage
Version Information
Original author: |
|
Responsible: |
|
Email: |