3 Generic module to create INI files lenses
5 Author: Raphael Pinson <raphink@gmail.com>
8 This file is licensed under the LGPL v2+, like the rest of Augeas.
11 Things to add in the future
12 - Support double quotes in value
15 This lens is made to provide generic primitives to construct INI File lenses.
16 See <Puppet>, <PHP>, <MySQL> or <Dput> for examples of real life lenses using it.
19 The <Test_IniFile> file contains various examples and tests.
25 (************************************************************************
26 * Group: USEFUL PRIMITIVES
27 *************************************************************************)
29 (* Group: Internal primitives *)
33 End of line, inherited from <Util.eol>
38 (* Group: Separators *)
47 pat:regexp - the pattern to delete
48 default:string - the default string to use
50 let sep (pat:regexp) (default:string)
51 = Sep.opt_space . del pat default
54 Variable: sep_noindent
55 Generic separator, no indentation
58 pat:regexp - the pattern to delete
59 default:string - the default string to use
61 let sep_noindent (pat:regexp) (default:string)
66 The default regexp for a separator
73 The default separator value
83 Store until end of line
85 let sto_to_eol = Sep.opt_space . store Rx.space_in
88 Variable: to_comment_re
91 let to_comment_re = /[^";# \t\n][^";#\n]*[^";# \t\n]|[^";# \t\n]/
94 Variable: sto_to_comment
97 let sto_to_comment = Sep.opt_space . store to_comment_re
100 Variable: sto_multiline
101 Store multiline values
103 let sto_multiline = Sep.opt_space
104 . store (to_comment_re
105 . (/[ \t]*\n/ . Rx.space . to_comment_re)*)
108 Variable: sto_multiline_nocomment
109 Store multiline values without an end-of-line comment
111 let sto_multiline_nocomment = Sep.opt_space
112 . store (Rx.space_in . (/[ \t]*\n/ . Rx.space . Rx.space_in)*)
115 (* Group: Define comment and defaults *)
118 View: comment_noindent
119 Map comments into "#comment" nodes,
120 no indentation allowed
123 pat:regexp - pattern to delete before commented data
124 default:string - default pattern before commented data
128 let comment = IniFile.comment_noindent "#" "#"
129 let comment = IniFile.comment_noindent IniFile.comment_re IniFile.comment_default
132 let comment_noindent (pat:regexp) (default:string) =
133 Util.comment_generic_seteol (pat . Rx.opt_space) default eol
137 Map comments into "#comment" nodes
140 pat:regexp - pattern to delete before commented data
141 default:string - default pattern before commented data
145 let comment = IniFile.comment "#" "#"
146 let comment = IniFile.comment IniFile.comment_re IniFile.comment_default
149 let comment (pat:regexp) (default:string) =
150 Util.comment_generic_seteol (Rx.opt_space . pat . Rx.opt_space) default eol
154 Default regexp for <comment> pattern
157 let comment_re = /[;#]/
160 Variable: comment_default
161 Default value for <comment> pattern
163 let comment_default = ";"
167 Empty line, including empty comments
170 indent:regexp - the indentation regexp
171 comment_re:regexp - the comment separator regexp
173 let empty_generic (indent:regexp) (comment_re:regexp) =
174 Util.empty_generic_dos (indent . comment_re? . Rx.opt_space)
180 let empty = empty_generic Rx.opt_space comment_re
184 Empty line, without indentation
186 let empty_noindent = empty_generic "" comment_re
189 (************************************************************************
191 *************************************************************************)
193 (* Group: entry includes comments *)
196 View: entry_generic_nocomment
197 A very generic INI File entry, not including comments
198 It allows to set the key lens (to set indentation
199 or subnodes linked to the key) as well as the comment
200 separator regexp, used to tune the store regexps.
203 kw:lens - lens to match the key, including optional indentation
204 sep:lens - lens to use as key/value separator
205 comment_re:regexp - comment separator regexp
206 comment:lens - lens to use as comment
209 > let entry = IniFile.entry_generic (key "setting") sep IniFile.comment_re comment
211 let entry_generic_nocomment (kw:lens) (sep:lens)
212 (comment_re:regexp) (comment:lens) =
213 let bare_re_noquot = (/[^" \t\r\n]/ - comment_re)
214 in let bare_re = (/[^\r\n]/ - comment_re)+
215 in let no_quot = /[^"\r\n]*/
216 in let bare = Quote.do_dquote_opt_nil (store (bare_re_noquot . (bare_re* . bare_re_noquot)?))
217 in let quoted = Quote.do_dquote (store (no_quot . comment_re+ . no_quot))
218 in [ kw . sep . (Sep.opt_space . bare)? . (comment|eol) ]
219 | [ kw . sep . Sep.opt_space . quoted . (comment|eol) ]
223 A very generic INI File entry
224 It allows to set the key lens (to set indentation
225 or subnodes linked to the key) as well as the comment
226 separator regexp, used to tune the store regexps.
229 kw:lens - lens to match the key, including optional indentation
230 sep:lens - lens to use as key/value separator
231 comment_re:regexp - comment separator regexp
232 comment:lens - lens to use as comment
235 > let entry = IniFile.entry_generic (key "setting") sep IniFile.comment_re comment
237 let entry_generic (kw:lens) (sep:lens) (comment_re:regexp) (comment:lens) =
238 entry_generic_nocomment kw sep comment_re comment | comment
242 Generic INI File entry
245 kw:regexp - keyword regexp for the label
246 sep:lens - lens to use as key/value separator
247 comment:lens - lens to use as comment
250 > let entry = IniFile.entry setting sep comment
252 let entry (kw:regexp) (sep:lens) (comment:lens) =
253 entry_generic (key kw) sep comment_re comment
257 Generic INI File entry that might be indented with an arbitrary
261 kw:regexp - keyword regexp for the label
262 sep:lens - lens to use as key/value separator
263 comment:lens - lens to use as comment
266 > let entry = IniFile.indented_entry setting sep comment
268 let indented_entry (kw:regexp) (sep:lens) (comment:lens) =
269 entry_generic (Util.indent . key kw) sep comment_re comment
272 View: entry_multiline_generic
273 A very generic multiline INI File entry
274 It allows to set the key lens (to set indentation
275 or subnodes linked to the key) as well as the comment
276 separator regexp, used to tune the store regexps.
279 kw:lens - lens to match the key, including optional indentation
280 sep:lens - lens to use as key/value separator
281 comment_re:regexp - comment separator regexp
282 comment:lens - lens to use as comment
283 eol:lens - lens for end of line
286 > let entry = IniFile.entry_generic (key "setting") sep IniFile.comment_re comment comment_or_eol
288 let entry_multiline_generic (kw:lens) (sep:lens) (comment_re:regexp)
289 (comment:lens) (eol:lens) =
290 let newline = /\r?\n[ \t]+/
292 let word_re_noquot = (/[^" \t\r\n]/ - comment_re)+
293 in let word_re = (/[^\r\n]/ - comment_re)+
294 in let base_re = (word_re_noquot . (word_re* . word_re_noquot)?)
295 in let sto_re = base_re . (newline . base_re)*
296 | (newline . base_re)+
297 in Quote.do_dquote_opt_nil (store sto_re)
299 let no_quot = /[^"\r\n]*/
300 in let base_re = (no_quot . comment_re+ . no_quot)
301 in let sto_re = base_re . (newline . base_re)*
302 | (newline . base_re)+
303 in Quote.do_dquote (store sto_re)
304 in [ kw . sep . (Sep.opt_space . bare)? . eol ]
305 | [ kw . sep . Sep.opt_space . quoted . eol ]
310 View: entry_multiline
311 Generic multiline INI File entry
314 kw:regexp - keyword regexp for the label
315 sep:lens - lens to use as key/value separator
316 comment:lens - lens to use as comment
318 let entry_multiline (kw:regexp) (sep:lens) (comment:lens) =
319 entry_multiline_generic (key kw) sep comment_re comment (comment|eol)
322 View: entry_multiline_nocomment
323 Generic multiline INI File entry without an end-of-line comment
326 kw:regexp - keyword regexp for the label
327 sep:lens - lens to use as key/value separator
328 comment:lens - lens to use as comment
330 let entry_multiline_nocomment (kw:regexp) (sep:lens) (comment:lens) =
331 entry_multiline_generic (key kw) sep comment_re comment eol
335 Generic INI File list entry
338 kw:regexp - keyword regexp for the label
339 sep:lens - lens to use as key/value separator
340 sto:regexp - store regexp for the values
341 list_sep:lens - lens to use as list separator
342 comment:lens - lens to use as comment
344 let entry_list (kw:regexp) (sep:lens) (sto:regexp) (list_sep:lens) (comment:lens) =
345 let list = counter "elem"
346 . Build.opt_list [ seq "elem" . store sto ] list_sep
347 in Build.key_value_line_comment kw sep (Sep.opt_space . list) comment
350 View: entry_list_nocomment
351 Generic INI File list entry without an end-of-line comment
354 kw:regexp - keyword regexp for the label
355 sep:lens - lens to use as key/value separator
356 sto:regexp - store regexp for the values
357 list_sep:lens - lens to use as list separator
359 let entry_list_nocomment (kw:regexp) (sep:lens) (sto:regexp) (list_sep:lens) =
360 let list = counter "elem"
361 . Build.opt_list [ seq "elem" . store sto ] list_sep
362 in Build.key_value_line kw sep (Sep.opt_space . list)
366 Default regexp for <entry> keyword
368 let entry_re = ( /[A-Za-z][A-Za-z0-9._-]*/ )
371 (************************************************************************
373 *************************************************************************)
375 (* Group: Title definition *)
379 Title for <record>. This maps the title of a record as a node in the abstract tree.
382 kw:regexp - keyword regexp for the label
385 > let title = IniFile.title IniFile.record_re
387 let title (kw:regexp)
388 = Util.del_str "[" . key kw
389 . Util.del_str "]". eol
393 Title for <record>. This maps the title of a record as a node in the abstract tree. The title may be indented with arbitrary amounts of whitespace
396 kw:regexp - keyword regexp for the label
399 > let title = IniFile.title IniFile.record_re
401 let indented_title (kw:regexp)
402 = Util.indent . title kw
406 Title for <record>. This maps the title of a record as a value in the abstract tree.
409 name:string - name for the title label
410 kw:regexp - keyword regexp for the label
413 > let title = IniFile.title_label "target" IniFile.record_label_re
415 let title_label (name:string) (kw:regexp)
417 . Util.del_str "[" . store kw
418 . Util.del_str "]". eol
421 View: indented_title_label
422 Title for <record>. This maps the title of a record as a value in the abstract tree. The title may be indented with arbitrary amounts of whitespace
425 name:string - name for the title label
426 kw:regexp - keyword regexp for the label
429 > let title = IniFile.title_label "target" IniFile.record_label_re
431 let indented_title_label (name:string) (kw:regexp)
432 = Util.indent . title_label name kw
437 Default regexp for <title> keyword pattern
439 let record_re = ( /[^]\r\n\/]+/ - /#comment/ )
442 Variable: record_label_re
443 Default regexp for <title_label> keyword pattern
445 let record_label_re = /[^]\r\n]+/
448 (* Group: Record definition *)
452 INI File Record with no empty lines allowed.
455 title:lens - lens to use for title. Use either <title> or <title_label>.
456 entry:lens - lens to use for entries in the record. See <entry>.
458 let record_noempty (title:lens) (entry:lens)
464 Generic INI File record
467 title:lens - lens to use for title. Use either <title> or <title_label>.
468 entry:lens - lens to use for entries in the record. See <entry>.
471 > let record = IniFile.record title entry
473 let record (title:lens) (entry:lens)
474 = record_noempty title ( entry | empty )
477 (************************************************************************
478 * Group: GENERIC LENSES
479 *************************************************************************)
484 Group: Lens definition
487 Generic INI File lens with no empty lines
490 record:lens - record lens to use. See <record_noempty>.
491 comment:lens - comment lens to use. See <comment>.
494 > let lns = IniFile.lns_noempty record comment
496 let lns_noempty (record:lens) (comment:lens)
501 Generic INI File lens
504 record:lens - record lens to use. See <record>.
505 comment:lens - comment lens to use. See <comment>.
508 > let lns = IniFile.lns record comment
510 let lns (record:lens) (comment:lens)
511 = lns_noempty record (comment|empty)
514 (************************************************************************
515 * Group: READY-TO-USE LENSES
516 *************************************************************************)
518 let record_anon (entry:lens) = [ label "section" . value ".anon" . ( entry | empty )+ ]
522 A loose, ready-to-use lens, featuring:
523 - sections as values (to allow '/' in names)
524 - support empty lines and comments
525 - support for [#;] as comment, defaulting to ";"
527 - don't allow multiline values
528 - allow indented titles
529 - allow indented entries
532 let l_comment = comment comment_re comment_default
533 in let l_sep = sep sep_re sep_default
534 in let l_entry = indented_entry entry_re l_sep l_comment
535 in let l_title = indented_title_label "section" (record_label_re - ".anon")
536 in let l_record = record l_title l_entry
537 in (record_anon l_entry)? . l_record*
540 View: lns_loose_multiline
541 A loose, ready-to-use lens, featuring:
542 - sections as values (to allow '/' in names)
543 - support empty lines and comments
544 - support for [#;] as comment, defaulting to ";"
546 - allow multiline values
548 let lns_loose_multiline =
549 let l_comment = comment comment_re comment_default
550 in let l_sep = sep sep_re sep_default
551 in let l_entry = entry_multiline entry_re l_sep l_comment
552 in let l_title = title_label "section" (record_label_re - ".anon")
553 in let l_record = record l_title l_entry
554 in (record_anon l_entry)? . l_record*