Bump to 1.14.1
[platform/upstream/augeas.git] / lenses / inifile.aug
1 (*
2 Module: IniFile
3   Generic module to create INI files lenses
4
5 Author: Raphael Pinson <raphink@gmail.com>
6
7 About: License
8   This file is licensed under the LGPL v2+, like the rest of Augeas.
9
10 About: TODO
11   Things to add in the future
12   - Support double quotes in value
13
14 About: Lens usage
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.
17
18 About: Examples
19   The <Test_IniFile> file contains various examples and tests.
20 *)
21
22 module IniFile  =
23
24
25 (************************************************************************
26  * Group:               USEFUL PRIMITIVES
27  *************************************************************************)
28
29 (* Group: Internal primitives *)
30
31 (*
32 Variable: eol
33   End of line, inherited from <Util.eol>
34 *)
35 let eol = Util.doseol
36
37
38 (* Group: Separators *)
39
40
41
42 (*
43 Variable: sep
44   Generic separator
45
46   Parameters:
47     pat:regexp - the pattern to delete
48     default:string - the default string to use
49 *)
50 let sep (pat:regexp) (default:string)
51                        = Sep.opt_space . del pat default
52
53 (*
54 Variable: sep_noindent
55   Generic separator, no indentation
56
57   Parameters:
58     pat:regexp - the pattern to delete
59     default:string - the default string to use
60 *)
61 let sep_noindent (pat:regexp) (default:string)
62                        = del pat default
63
64 (*
65 Variable: sep_re
66   The default regexp for a separator
67 *)
68
69 let sep_re             = /[=:]/
70
71 (*
72 Variable: sep_default
73   The default separator value
74 *)
75 let sep_default        = "="
76
77
78 (* Group: Stores *)
79
80
81 (*
82 Variable: sto_to_eol
83   Store until end of line
84 *)
85 let sto_to_eol         = Sep.opt_space . store Rx.space_in
86
87 (*
88 Variable: to_comment_re
89   Regex until comment
90 *)
91 let to_comment_re = /[^";# \t\n][^";#\n]*[^";# \t\n]|[^";# \t\n]/
92
93 (*
94 Variable: sto_to_comment
95   Store until comment
96 *)
97 let sto_to_comment = Sep.opt_space . store to_comment_re
98
99 (*
100 Variable: sto_multiline
101   Store multiline values
102 *)
103 let sto_multiline = Sep.opt_space
104          . store (to_comment_re
105                . (/[ \t]*\n/ . Rx.space . to_comment_re)*)
106
107 (*
108 Variable: sto_multiline_nocomment
109   Store multiline values without an end-of-line comment
110 *)
111 let sto_multiline_nocomment = Sep.opt_space
112          . store (Rx.space_in . (/[ \t]*\n/ . Rx.space . Rx.space_in)*)
113
114
115 (* Group: Define comment and defaults *)
116
117 (*
118 View: comment_noindent
119   Map comments into "#comment" nodes,
120   no indentation allowed
121
122   Parameters:
123     pat:regexp - pattern to delete before commented data
124     default:string - default pattern before commented data
125
126   Sample Usage:
127   (start code)
128     let comment  = IniFile.comment_noindent "#" "#"
129     let comment  = IniFile.comment_noindent IniFile.comment_re IniFile.comment_default
130   (end code)
131 *)
132 let comment_noindent (pat:regexp) (default:string) =
133   Util.comment_generic_seteol (pat . Rx.opt_space) default eol
134
135 (*
136 View: comment
137   Map comments into "#comment" nodes
138
139   Parameters:
140     pat:regexp - pattern to delete before commented data
141     default:string - default pattern before commented data
142
143   Sample Usage:
144   (start code)
145     let comment  = IniFile.comment "#" "#"
146     let comment  = IniFile.comment IniFile.comment_re IniFile.comment_default
147   (end code)
148 *)
149 let comment (pat:regexp) (default:string) =
150   Util.comment_generic_seteol (Rx.opt_space . pat . Rx.opt_space) default eol
151
152 (*
153 Variable: comment_re
154   Default regexp for <comment> pattern
155 *)
156
157 let comment_re         = /[;#]/
158
159 (*
160 Variable: comment_default
161   Default value for <comment> pattern
162 *)
163 let comment_default    = ";"
164
165 (*
166 View: empty_generic
167   Empty line, including empty comments
168
169   Parameters:
170     indent:regexp     - the indentation regexp
171     comment_re:regexp - the comment separator regexp
172 *)
173 let empty_generic (indent:regexp) (comment_re:regexp) =
174   Util.empty_generic_dos (indent . comment_re? . Rx.opt_space)
175
176 (*
177 View: empty
178   Empty line
179 *)
180 let empty = empty_generic Rx.opt_space comment_re
181
182 (*
183 View: empty_noindent
184   Empty line, without indentation
185 *)
186 let empty_noindent = empty_generic "" comment_re
187
188
189 (************************************************************************
190  * Group:                     ENTRY
191  *************************************************************************)
192
193 (* Group: entry includes comments *)
194
195 (*
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.
201
202   Parameters:
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
207
208   Sample Usage:
209      > let entry = IniFile.entry_generic (key "setting") sep IniFile.comment_re comment
210 *)
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) ]
220
221 (*
222 View: entry_generic
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.
227
228   Parameters:
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
233
234   Sample Usage:
235      > let entry = IniFile.entry_generic (key "setting") sep IniFile.comment_re comment
236 *)
237 let entry_generic (kw:lens) (sep:lens) (comment_re:regexp) (comment:lens) =
238   entry_generic_nocomment kw sep comment_re comment | comment
239
240 (*
241 View: entry
242   Generic INI File entry
243
244   Parameters:
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
248
249   Sample Usage:
250      > let entry = IniFile.entry setting sep comment
251 *)
252 let entry (kw:regexp) (sep:lens) (comment:lens) =
253      entry_generic (key kw) sep comment_re comment
254
255 (*
256 View: indented_entry
257   Generic INI File entry that might be indented with an arbitrary
258   amount of whitespace
259
260   Parameters:
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
264
265   Sample Usage:
266      > let entry = IniFile.indented_entry setting sep comment
267 *)
268 let indented_entry (kw:regexp) (sep:lens) (comment:lens) =
269      entry_generic (Util.indent . key kw) sep comment_re comment
270
271 (*
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.
277
278   Parameters:
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
284
285   Sample Usage:
286      > let entry = IniFile.entry_generic (key "setting") sep IniFile.comment_re comment comment_or_eol
287 *)
288 let entry_multiline_generic (kw:lens) (sep:lens) (comment_re:regexp)
289                             (comment:lens) (eol:lens) =
290      let newline = /\r?\n[ \t]+/
291   in let bare =
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)
298   in let quoted =
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 ]
306    | comment
307   
308
309 (*
310 View: entry_multiline
311   Generic multiline INI File entry
312
313   Parameters:
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
317 *)
318 let entry_multiline (kw:regexp) (sep:lens) (comment:lens) =
319   entry_multiline_generic (key kw) sep comment_re comment (comment|eol)
320
321 (*
322 View: entry_multiline_nocomment
323   Generic multiline INI File entry without an end-of-line comment
324
325   Parameters:
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
329 *)
330 let entry_multiline_nocomment (kw:regexp) (sep:lens) (comment:lens) =
331   entry_multiline_generic (key kw) sep comment_re comment eol
332
333 (*
334 View: entry_list
335   Generic INI File list entry
336
337   Parameters:
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
343 *)
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
348
349 (*
350 View: entry_list_nocomment
351   Generic INI File list entry without an end-of-line comment
352
353   Parameters:
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
358 *)
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)
363
364 (*
365 Variable: entry_re
366   Default regexp for <entry> keyword
367 *)
368 let entry_re           = ( /[A-Za-z][A-Za-z0-9._-]*/ )
369
370
371 (************************************************************************
372  * Group:                      RECORD
373  *************************************************************************)
374
375 (* Group: Title definition *)
376
377 (*
378 View: title
379   Title for <record>. This maps the title of a record as a node in the abstract tree.
380
381   Parameters:
382     kw:regexp - keyword regexp for the label
383
384   Sample Usage:
385     > let title   = IniFile.title IniFile.record_re
386 *)
387 let title (kw:regexp)
388                        = Util.del_str "[" . key kw
389                          . Util.del_str "]". eol
390
391 (*
392 View: indented_title
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
394
395   Parameters:
396     kw:regexp - keyword regexp for the label
397
398   Sample Usage:
399     > let title   = IniFile.title IniFile.record_re
400 *)
401 let indented_title (kw:regexp)
402                        = Util.indent . title kw
403
404 (*
405 View: title_label
406   Title for <record>. This maps the title of a record as a value in the abstract tree.
407
408   Parameters:
409     name:string - name for the title label
410     kw:regexp   - keyword regexp for the label
411
412   Sample Usage:
413     > let title   = IniFile.title_label "target" IniFile.record_label_re
414 *)
415 let title_label (name:string) (kw:regexp)
416                        = label name
417                          . Util.del_str "[" . store kw
418                          . Util.del_str "]". eol
419
420 (*
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
423
424   Parameters:
425     name:string - name for the title label
426     kw:regexp   - keyword regexp for the label
427
428   Sample Usage:
429     > let title   = IniFile.title_label "target" IniFile.record_label_re
430 *)
431 let indented_title_label (name:string) (kw:regexp)
432                        = Util.indent . title_label name kw
433
434
435 (*
436 Variable: record_re
437   Default regexp for <title> keyword pattern
438 *)
439 let record_re          = ( /[^]\r\n\/]+/ - /#comment/ )
440
441 (*
442 Variable: record_label_re
443   Default regexp for <title_label> keyword pattern
444 *)
445 let record_label_re    = /[^]\r\n]+/
446
447
448 (* Group: Record definition *)
449
450 (*
451 View: record_noempty
452   INI File Record with no empty lines allowed.
453
454   Parameters:
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>.
457 *)
458 let record_noempty (title:lens) (entry:lens)
459                        = [ title
460                        . entry* ]
461
462 (*
463 View: record
464   Generic INI File record
465
466   Parameters:
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>.
469
470   Sample Usage:
471     > let record  = IniFile.record title entry
472 *)
473 let record (title:lens) (entry:lens)
474                        = record_noempty title ( entry | empty )
475
476
477 (************************************************************************
478  * Group:                      GENERIC LENSES
479  *************************************************************************)
480
481
482 (*
483
484 Group: Lens definition
485
486 View: lns_noempty
487   Generic INI File lens with no empty lines
488
489   Parameters:
490     record:lens  - record lens to use. See <record_noempty>.
491     comment:lens - comment lens to use. See <comment>.
492
493   Sample Usage:
494     > let lns     = IniFile.lns_noempty record comment
495 *)
496 let lns_noempty (record:lens) (comment:lens)
497                        = comment* . record*
498
499 (*
500 View: lns
501   Generic INI File lens
502
503   Parameters:
504     record:lens  - record lens to use. See <record>.
505     comment:lens - comment lens to use. See <comment>.
506
507   Sample Usage:
508     > let lns     = IniFile.lns record comment
509 *)
510 let lns (record:lens) (comment:lens)
511                        = lns_noempty record (comment|empty)
512
513
514 (************************************************************************
515  * Group:                   READY-TO-USE LENSES
516  *************************************************************************)
517
518 let record_anon (entry:lens) = [ label "section" . value ".anon" . ( entry | empty )+ ]
519
520 (*
521 View: lns_loose
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 ";"
526     - .anon sections
527     - don't allow multiline values
528     - allow indented titles
529     - allow indented entries
530 *)
531 let lns_loose = 
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*
538
539 (*
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 ";"
545     - .anon sections
546     - allow multiline values
547 *)
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*
555