June 2018 Update

This commit is contained in:
Ingy döt Net 2018-06-22 20:57:24 +00:00
parent ba8067c3b7
commit 22f33d4004
5278 changed files with 84726 additions and 14379 deletions

View file

@ -1,4 +1,4 @@
The task is to read a configuration file in standard configuration file,
The task is to read a configuration file in standard configuration file format,
and set variables accordingly.
For this task, we have a configuration file as follows:

View file

@ -6,60 +6,53 @@ text an, d, k;
an = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
f_affix(f, "tmp/config");
f.affix("tmp/config");
while ((c = f_peek(f)) ^ -1) {
while ((c = f.peek) ^ -1) {
integer removed;
f_side(f, " \t\r");
c = f_peek(f);
f.side(" \t\r");
c = f.peek;
removed = c == ';';
if (removed) {
f_pick(f);
f_side(f, " \t\r");
c = f_peek(f);
f.pick;
f.side(" \t\r");
c = f.peek;
}
c = index(an, c);
c = place(an, c);
if (-1 < c && c < 52) {
f_near(f, an, k);
f.near(an, k);
if (removed) {
r[k] = "false";
} else {
data b;
f_side(f, " \t\r");
if (f_peek(f) == '=') {
f_pick(f);
f_side(f, " \t\r");
f.side(" \t\r");
if (f.peek == '=') {
f.pick;
f.side(" \t\r");
}
f_ever(f, ",#\n", d);
b = d;
bb_drop(b, " \r\t");
d = b_string(b);
if (f_peek(f) != ',') {
r[k] = length(d) ? d : "true";
f.ever(",#\n", d);
d = bb_drop(d, " \r\t");
if (f.peek != ',') {
r[k] = ~d ? d : "true";
} else {
f_news(f, l, 0, 0, ",");
f.news(l, 0, 0, ",");
lf_push(l, d);
for (c, d in l) {
b = d;
bb_drop(b, " \r\t");
bf_drop(b, " \r\t");
l[c] = b_string(b);
l[c] = bb_drop(d, " \r\t").bf_drop(" \r\t").string;
}
r_put(s, k, l);
f_seek(f, -1, SEEK_CURRENT);
s.put(k, l);
f.seek(-1, SEEK_CURRENT);
}
}
}
f_slip(f);
f.slip;
}
r_wcall(r, o_, 0, 2, ": ", "\n");
r.wcall(o_, 0, 2, ": ", "\n");
for (k, l in s) {
o_(k, ": ");
l_ucall(l, o_, 0, ", ");
l.ucall(o_, 0, ", ");
o_("\n");
}

View file

@ -0,0 +1,171 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <confini.h>
#define rosetta_uint8_t unsigned char
#define FALSE 0
#define TRUE 1
#define CONFIGS_TO_READ 5
#define INI_ARRAY_DELIMITER ','
/* Assume that the config file represent a struct containing all the parameters to load */
struct configs {
char *fullname;
char *favouritefruit;
rosetta_uint8_t needspeeling;
rosetta_uint8_t seedsremoved;
char **otherfamily;
size_t otherfamily_len;
size_t _configs_left_;
};
static char ** make_array (size_t * arrlen, const char * src, const size_t buffsize, IniFormat ini_format) {
/* Allocate a new array of strings and populate it from the stringified source */
*arrlen = ini_array_get_length(src, INI_ARRAY_DELIMITER, ini_format);
char ** const dest = *arrlen ? (char **) malloc(*arrlen * sizeof(char *) + buffsize) : NULL;
if (!dest) { return NULL; }
memcpy(dest + *arrlen, src, buffsize);
char * iter = (char *) (dest + *arrlen);
for (size_t idx = 0; idx < *arrlen; idx++) {
dest[idx] = ini_array_release(&iter, INI_ARRAY_DELIMITER, ini_format);
ini_string_parse(dest[idx], ini_format);
}
return dest;
}
static int configs_member_handler (IniDispatch *this, void *v_confs) {
struct configs *confs = (struct configs *) v_confs;
if (this->type != INI_KEY) {
return 0;
}
if (ini_string_match_si("FULLNAME", this->data, this->format)) {
if (confs->fullname) { return 0; }
this->v_len = ini_string_parse(this->value, this->format); /* Remove all quotes, if any */
confs->fullname = strndup(this->value, this->v_len);
confs->_configs_left_--;
} else if (ini_string_match_si("FAVOURITEFRUIT", this->data, this->format)) {
if (confs->favouritefruit) { return 0; }
this->v_len = ini_string_parse(this->value, this->format); /* Remove all quotes, if any */
confs->favouritefruit = strndup(this->value, this->v_len);
confs->_configs_left_--;
} else if (ini_string_match_si("NEEDSPEELING", this->data, this->format)) {
if (~confs->needspeeling & 0x80) { return 0; }
confs->needspeeling = ini_get_bool(this->value, TRUE);
confs->_configs_left_--;
} else if (ini_string_match_si("SEEDSREMOVED", this->data, this->format)) {
if (~confs->seedsremoved & 0x80) { return 0; }
confs->seedsremoved = ini_get_bool(this->value, TRUE);
confs->_configs_left_--;
} else if (!confs->otherfamily && ini_string_match_si("OTHERFAMILY", this->data, this->format)) {
if (confs->otherfamily) { return 0; }
this->v_len = ini_array_collapse(this->value, INI_ARRAY_DELIMITER, this->format); /* Save memory (not strictly needed) */
confs->otherfamily = make_array(&confs->otherfamily_len, this->value, this->v_len + 1, this->format);
confs->_configs_left_--;
}
/* Optimization: stop reading the INI file when we have all we need */
return !confs->_configs_left_;
}
static int populate_configs (struct configs * confs) {
/* Define the format of the configuration file */
IniFormat config_format = {
.delimiter_symbol = INI_ANY_SPACE,
.case_sensitive = FALSE,
.semicolon_marker = INI_IGNORE,
.hash_marker = INI_IGNORE,
.multiline_nodes = INI_NO_MULTILINE,
.section_paths = INI_NO_SECTIONS,
.no_single_quotes = FALSE,
.no_double_quotes = FALSE,
.no_spaces_in_names = TRUE,
.implicit_is_not_empty = TRUE,
.do_not_collapse_values = FALSE,
.preserve_empty_quotes = FALSE,
.no_disabled_after_space = FALSE,
.disabled_can_be_implicit = FALSE
};
*confs = (struct configs) { NULL, NULL, 0x80, 0x80, NULL, 0, CONFIGS_TO_READ };
if (load_ini_path("rosetta.conf", config_format, NULL, configs_member_handler, confs) & CONFINI_ERROR) {
fprintf(stderr, "Sorry, something went wrong :-(\n");
return 1;
}
confs->needspeeling &= 0x7F;
confs->seedsremoved &= 0x7F;
return 0;
}
int main () {
struct configs confs;
ini_global_set_implicit_value("YES", 0);
if (populate_configs(&confs)) {
return 1;
}
/* Print the configurations parsed */
printf(
"Full name: %s\n"
"Favorite fruit: %s\n"
"Need spelling: %s\n"
"Seeds removed: %s\n",
confs.fullname,
confs.favouritefruit,
confs.needspeeling ? "True" : "False",
confs.seedsremoved ? "True" : "False"
);
for (size_t idx = 0; idx < confs.otherfamily_len; idx++) {
printf("Other family[%d]: %s\n", idx, confs.otherfamily[idx]);
}
/* Free the allocated memory */
#define FREE_NON_NULL(PTR) if (PTR) { free(PTR); }
FREE_NON_NULL(confs.fullname);
FREE_NON_NULL(confs.favouritefruit);
FREE_NON_NULL(confs.otherfamily);
return 0;
}

View file

@ -1,2 +1,48 @@
(let [cfg (read-string (slurp "config.edn"))]
(clojure.pprint/pprint cfg))
(ns read-conf-file.core
(:require [clojure.java.io :as io]
[clojure.string :as str])
(:gen-class))
(def conf-keys ["fullname"
"favouritefruit"
"needspeeling"
"seedsremoved"
"otherfamily"])
(defn get-lines
"Read file returning vec of lines."
[file]
(try
(with-open [rdr (io/reader file)]
(into [] (line-seq rdr)))
(catch Exception e (.getMessage e))))
(defn parse-line
"Parse passed line returning vec: token, vec of values."
[line]
(if-let [[_ k v] (re-matches #"(?i)^\s*([a-z]+)(?:\s+|=)?(.+)?$" line)]
(let [k (str/lower-case k)]
(if v
[k (str/split v #",\s*")]
[k [true]]))))
(defn mk-conf
"Build configuration map from lines."
[lines]
(->> (map parse-line lines)
(filter (comp not nil?))
(reduce (fn
[m [k v]]
(assoc m k v)) {})))
(defn output
[conf-keys conf]
(doseq [k conf-keys]
(let [v (get conf k)]
(if v
(println (format "%s = %s" k (str/join ", " v)))
(println (format "%s = %s" k "false"))))))
(defn -main
[filename]
(output conf-keys (mk-conf (get-lines filename))))

View file

@ -1,10 +1,40 @@
;config-file.txt
;lisp comments works normally as it would in lisp
#S(config-file
:fullname "Foo Barber"
:favoritefruit "banana"
:needspeeling t
:seedsremoved nil
:otherfamily '("Rhu Barber" "Harry Barber")
;:will "not be read"
)
(ql:quickload :parser-combinators)
(defpackage :read-config
(:use :cl :parser-combinators))
(in-package :read-config)
(defun trim-space (string)
(string-trim '(#\space #\tab) string))
(defun any-but1? (except)
(named-seq? (<- res (many1? (except? (item) except)))
(coerce res 'string)))
(defun values? ()
(named-seq? (<- values (sepby? (any-but1? #\,) #\,))
(mapcar 'trim-space values)))
(defun key-values? ()
(named-seq? (<- key (word?))
(opt? (many? (whitespace?)))
(opt? #\=)
(<- values (values?))
(cons key (or (if (cdr values) values (car values)) t))))
(defun parse-line (line)
(setf line (trim-space line))
(if (or (string= line "") (member (char line 0) '(#\# #\;)))
:comment
(parse-string* (key-values?) line)))
(defun parse-config (stream)
(let ((hash (make-hash-table :test 'equal)))
(loop for line = (read-line stream nil nil)
while line
do (let ((parsed (parse-line line)))
(cond ((eq parsed :comment))
((eq parsed nil) (error "config parser error: ~a" line))
(t (setf (gethash (car parsed) hash) (cdr parsed))))))
hash))

View file

@ -1,9 +1,12 @@
;config-file.lisp
(defstruct config-file :fullname :favoritefruit :needspeeling :seedsremoved :otherfamily)
(with-open-file (in "config-file.txt")
(defvar contents (read in))
(format t "~a~%" contents)
;reading the config-file into a structure gives us
;some helper functions to access individualy each element
(format t "Fullname: ~a~%" (config-file-fullname contents))
(format t "Contents is a config-file? ~a~%" (config-file-p contents)))
READ-CONFIG> (with-open-file (s "test.cfg") (parse-config s))
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100BD25B43}>
READ-CONFIG> (maphash (lambda (k v) (print (list k v))) *)
("FULLNAME" "Foo Barber")
("FAVOURITEFRUIT" "banana")
("NEEDSPEELING" T)
("OTHERFAMILY" ("Rhu Barber" "Harry Barber"))
NIL
READ-CONFIG> (gethash "SEEDSREMOVED" **)
NIL
NIL

View file

@ -0,0 +1,24 @@
function readconf(file)
vars = Dict()
for line in eachline(file)
line = strip(line)
if !isempty(line) && !startswith(line, '#') && !startswith(line, ';')
fspace = searchindex(line, " ")
if fspace == 0
vars[Symbol(lowercase(line))] = true
else
vname, line = Symbol(lowercase(line[1:fspace-1])), line[fspace+1:end]
value = ',' ∈ line ? strip.(split(line, ',')) : line
vars[vname] = value
end
end
end
for (vname, value) in vars
eval(:($vname = $value))
end
return vars
end
readconf("test.conf")
@show fullname favouritefruit needspeeling otherfamily

View file

@ -17,7 +17,7 @@ grammar ConfFile {
[ \n || { die "Parse failed at line $*linenum" } ]
}
proto token line() {{*}}
proto token line() {*}
token line:misc { {} (\S+) { die "Unrecognized word: $0" } }

View file

@ -0,0 +1,88 @@
Function Read-ConfigurationFile {
[CmdletBinding()]
[OutputType([Collections.Specialized.OrderedDictionary])]
Param (
[Parameter(
Mandatory=$true,
Position=0
)
]
[Alias('LiteralPath')]
[ValidateScript({
Test-Path -LiteralPath $PSItem -PathType 'Leaf'
})
]
[String]
$_LiteralPath
)
Begin {
Function Houdini-Value ([String]$_Text) {
$__Aux = $_Text.Trim()
If ($__Aux.Length -eq 0) {
$__Aux = $true
} ElseIf ($__Aux.Contains(',')) {
$__Aux = $__Aux.Split(',') |
ForEach-Object {
If ($PSItem.Trim().Length -ne 0) {
$PSItem.Trim()
}
}
}
Return $__Aux
}
}
Process {
$__Configuration = [Ordered]@{}
# Config equivalent pattern
# Select-String -Pattern '^\s*[^\s;#=]+.*\s*$' -LiteralPath '.\filename.cfg'
Switch -Regex -File $_LiteralPath {
'^\s*[;#=]|^\s*$' {
Write-Verbose -Message "v$(' '*20)ignored"
Write-Verbose -Message $Matches[0]
Continue
}
'^([^=]+)=(.*)$' {
Write-Verbose -Message '↓← ← ← ← ← ← ← ← ← ← equal pattern'
Write-Verbose -Message $Matches[0]
$__Name,$__Value = $Matches[1..2]
$__Configuration[$__Name.Trim()] = Houdini-Value($__Value)
Continue
}
'^\s*([^\s;#=]+)(.*)(\s*)$' {
Write-Verbose -Message '↓← ← ← ← ← ← ← ← ← ← space or tab pattern or only name'
Write-Verbose -Message $Matches[0]
$__Name,$__Value = $Matches[1..2]
$__Configuration[$__Name.Trim()] = Houdini-Value($__Value)
Continue
}
}
Return $__Configuration
}
}
Function Show-Value ([Collections.Specialized.OrderedDictionary]$_Dictionary, $_Index, $_SubIndex) {
$__Aux = $_Index + ' = '
If ($_Dictionary[$_Index] -eq $null) {
$__Aux += $false
} ElseIf ($_Dictionary[$_Index].Count -gt 1) {
If ($_SubIndex -eq $null) {
$__Aux += $_Dictionary[$_Index] -join ','
} Else {
$__Aux = $_Index + '(' + $_SubIndex + ') = '
If ($_Dictionary[$_Index][$_SubIndex] -eq $null) {
$__Aux += $false
} Else {
$__Aux += $_Dictionary[$_Index][$_SubIndex]
}
}
} Else {
$__Aux += $_Dictionary[$_Index]
}
Return $__Aux
}

View file

@ -0,0 +1 @@
$Configuration = Read-ConfigurationFile -LiteralPath '.\config.cfg'

View file

@ -0,0 +1 @@
$Configuration

View file

@ -0,0 +1,8 @@
Show-Value $Configuration 'fullname'
Show-Value $Configuration 'favouritefruit'
Show-Value $Configuration 'needspeeling'
Show-Value $Configuration 'seedsremoved'
Show-Value $Configuration 'otherfamily'
Show-Value $Configuration 'otherfamily' 0
Show-Value $Configuration 'otherfamily' 1
Show-Value $Configuration 'otherfamily' 2

View file

@ -0,0 +1,45 @@
'$Configuration[''fullname'']'
$Configuration['fullname']
'$Configuration.''fullname'''
$Configuration.'fullname'
'$Configuration.Item(''fullname'')'
$Configuration.Item('fullname')
'$Configuration[0]'
$Configuration[0]
'$Configuration.Item(0)'
$Configuration.Item(0)
' '
'=== $Configuration[''otherfamily''] ==='
$Configuration['otherfamily']
'=== $Configuration[''otherfamily''][0] ==='
$Configuration['otherfamily'][0]
'=== $Configuration[''otherfamily''][1] ==='
$Configuration['otherfamily'][1]
' '
'=== $Configuration.''otherfamily'' ==='
$Configuration.'otherfamily'
'=== $Configuration.''otherfamily''[0] ==='
$Configuration.'otherfamily'[0]
'=== $Configuration.''otherfamily''[1] ==='
$Configuration.'otherfamily'[1]
' '
'=== $Configuration.Item(''otherfamily'') ==='
$Configuration.Item('otherfamily')
'=== $Configuration.Item(''otherfamily'')[0] ==='
$Configuration.Item('otherfamily')[0]
'=== $Configuration.Item(''otherfamily'')[1] ==='
$Configuration.Item('otherfamily')[1]
' '
'=== $Configuration[3] ==='
$Configuration[3]
'=== $Configuration[3][0] ==='
$Configuration[3][0]
'=== $Configuration[3][1] ==='
$Configuration[3][1]
' '
'=== $Configuration.Item(3) ==='
$Configuration.Item(3)
'=== $Configuration.Item(3).Item(0) ==='
$Configuration.Item(3).Item(0)
'=== $Configuration.Item(3).Item(1) ==='
$Configuration.Item(3).Item(1)

View file

@ -0,0 +1,80 @@
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::iter::FromIterator;
use std::path::Path;
fn main() {
let path = String::from("file.conf");
let cfg = config_from_file(path);
println!("{:?}", cfg);
}
fn config_from_file(path: String) -> Config {
let path = Path::new(&path);
let file = File::open(path).expect("File not found or cannot be opened");
let content = BufReader::new(&file);
let mut cfg = Config::new();
for line in content.lines() {
let line = line.expect("Could not read the line");
// Remove whitespaces at the beginning and end
let line = line.trim();
// Ignore comments and empty lines
if line.starts_with("#") || line.starts_with(";") || line.is_empty() {
continue;
}
// Split line into parameter name and rest tokens
let tokens = Vec::from_iter(line.split_whitespace());
let name = tokens.first().unwrap();
let tokens = tokens.get(1..).unwrap();
// Remove the equal signs
let tokens = tokens.iter().filter(|t| !t.starts_with("="));
// Remove comment after the parameters
let tokens = tokens.take_while(|t| !t.starts_with("#") && !t.starts_with(";"));
// Concat back the parameters into one string to split for separated parameters
let mut parameters = String::new();
tokens.for_each(|t| { parameters.push_str(t); parameters.push(' '); });
// Splits the parameters and trims
let parameters = parameters.split(',').map(|s| s.trim());
// Converts them from Vec<&str> into Vec<String>
let parameters: Vec<String> = parameters.map(|s| s.to_string()).collect();
// Setting the config parameters
match name.to_lowercase().as_str() {
"fullname" => cfg.full_name = parameters.get(0).cloned(),
"favouritefruit" => cfg.favourite_fruit = parameters.get(0).cloned(),
"needspeeling" => cfg.needs_peeling = true,
"seedsremoved" => cfg.needs_peeling = true,
"otherfamily" => cfg.other_family = Some(parameters),
_ => (),
}
}
cfg
}
#[derive(Clone, Debug)]
struct Config {
full_name: Option<String>,
favourite_fruit: Option<String>,
needs_peeling: bool,
seeds_removed: bool,
other_family: Option<Vec<String>>,
}
impl Config {
fn new() -> Config {
Config {
full_name: None,
favourite_fruit: None,
needs_peeling: false,
seeds_removed: false,
other_family: None,
}
}
}