Added Augeas.count_matches
[ocaml-augeas.git] / augeas_c.c
1 /* Augeas OCaml bindings
2  * Copyright (C) 2008 Red Hat Inc., Richard W.M. Jones
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  * $Id: augeas_c.c,v 1.1 2008/05/06 10:48:20 rjones Exp $
19  */
20
21 #include "config.h"
22
23 #include <augeas.h>
24
25 #include <caml/alloc.h>
26 #include <caml/memory.h>
27 #include <caml/mlvalues.h>
28 #include <caml/fail.h>
29 #include <caml/callback.h>
30 #include <caml/custom.h>
31
32 typedef augeas *augeas_t;
33
34 /* Raise an Augeas.Error exception. */
35 static void
36 raise_error (const char *msg)
37 {
38   caml_raise_with_string (*caml_named_value ("Augeas.Error"), msg);
39 }
40
41 /* Map OCaml flags to C flags. */
42 static int flag_map[] = {
43   /* AugSaveBackup */  AUG_SAVE_BACKUP,
44   /* AugSaveNewFile */ AUG_SAVE_NEWFILE,
45   /* AugTypeCheck */   AUG_TYPE_CHECK
46 };
47
48 /* Wrap and unwrap augeas_t handles, with a finalizer. */
49 #define Augeas_t_val(rv) (*(augeas_t *)Data_custom_val(rv))
50
51 static void
52 augeas_t_finalize (value tv)
53 {
54   augeas_t t = Augeas_t_val (tv);
55   if (t) aug_close (t);
56 }
57
58 static struct custom_operations custom_operations = {
59   "augeas_t_custom_operations",
60   augeas_t_finalize,
61   custom_compare_default,
62   custom_hash_default,
63   custom_serialize_default,
64   custom_deserialize_default
65 };
66
67 static value Val_augeas_t (augeas_t t)
68 {
69   CAMLparam0 ();
70   CAMLlocal1 (rv);
71   /* We could choose these so that the GC can make better decisions.
72    * See 18.9.2 of the OCaml manual.
73    */
74   const int used = 0;
75   const int max = 1;
76
77   rv = caml_alloc_custom (&custom_operations,
78                           sizeof (augeas_t), used, max);
79   Augeas_t_val(rv) = t;
80
81   CAMLreturn (rv);
82 }
83
84 /* val create : string -> string option -> flag list -> t */
85 CAMLprim value
86 ocaml_augeas_create (value rootv, value loadpathv, value flagsv)
87 {
88   CAMLparam1 (rootv);
89   char *root = String_val (rootv);
90   char *loadpath;
91   int flags = 0, i;
92   augeas_t t;
93
94   /* Optional loadpath. */
95   loadpath =
96     loadpathv == Val_int (0)
97     ? NULL
98     : String_val (Field (loadpathv, 0));
99
100   /* Convert list of flags to C. */
101   for (; flagsv != Val_int (0); flagsv = Field (flagsv, 1)) {
102     i = Int_val (Field (flagsv, 0));
103     flags |= flag_map[i];
104   }
105
106   t = aug_init (root, loadpath, flags);
107
108   if (t == NULL)
109     raise_error ("Augeas.create");
110
111   CAMLreturn (Val_augeas_t (t));
112 }
113
114 /* val close : t -> unit */
115 CAMLprim value
116 ocaml_augeas_close (value tv)
117 {
118   CAMLparam1 (tv);
119   augeas_t t = Augeas_t_val (tv);
120
121   if (t) {
122     aug_close (t);
123     Augeas_t_val(tv) = NULL;    /* So the finalizer doesn't double-free. */
124   }
125
126   CAMLreturn (Val_unit);
127 }
128
129 /* val get : t -> path -> value option */
130 CAMLprim value
131 ocaml_augeas_get (value tv, value pathv)
132 {
133   CAMLparam2 (tv, pathv);
134   CAMLlocal2 (optv, v);
135   augeas_t t = Augeas_t_val (tv);
136   char *path = String_val (pathv);
137   const char *val;
138   int r;
139
140   r = aug_get (t, path, &val);
141   if (r == 1) {                 /* Return Some val */
142     v = caml_copy_string (val);
143     optv = caml_alloc (1, 0);
144     Field (optv, 0) = v;
145   } else if (r == 0)            /* Return None */
146     optv = Val_int (0);
147   else if (r == -1)             /* Error or multiple matches */
148     raise_error ("Augeas.get");
149   else
150     failwith ("Augeas.get: bad return value");
151
152   CAMLreturn (optv);
153 }
154
155 /* val exists : t -> path -> bool */
156 CAMLprim value
157 ocaml_augeas_exists (value tv, value pathv)
158 {
159   CAMLparam2 (tv, pathv);
160   CAMLlocal1 (v);
161   augeas_t t = Augeas_t_val (tv);
162   char *path = String_val (pathv);
163   int r;
164
165   r = aug_get (t, path, NULL);
166   if (r == 1)                   /* Return true. */
167     v = Val_int (1);
168   else if (r == 0)              /* Return false */
169     v = Val_int (0);
170   else if (r == -1)             /* Error or multiple matches */
171     raise_error ("Augeas.exists");
172   else
173     failwith ("Augeas.exists: bad return value");
174
175   CAMLreturn (v);
176 }
177
178 /* val insert : t -> ?before:bool -> path -> string -> unit */
179 CAMLprim value
180 ocaml_augeas_insert (value tv, value beforev, value pathv, value labelv)
181 {
182   CAMLparam4 (tv, beforev, pathv, labelv);
183   augeas_t t = Augeas_t_val (tv);
184   char *path = String_val (pathv);
185   char *label = String_val (labelv);
186   int before;
187
188   before = beforev == Val_int (0) ? 0 : Int_val (Field (beforev, 0));
189
190   if (aug_insert (t, path, label, before) == -1)
191     raise_error ("Augeas.insert");
192
193   CAMLreturn (Val_unit);
194 }
195
196 /* val rm : t -> path -> int */
197 CAMLprim value
198 ocaml_augeas_rm (value tv, value pathv)
199 {
200   CAMLparam2 (tv, pathv);
201   augeas_t t = Augeas_t_val (tv);
202   char *path = String_val (path);
203   int r;
204
205   r = aug_rm (t, path);
206   if (r == -1)
207     raise_error ("Augeas.rm");
208
209   CAMLreturn (Val_int (r));
210 }
211
212 /* val matches : t -> path -> path list */
213 CAMLprim value
214 ocaml_augeas_match (value tv, value pathv)
215 {
216   CAMLparam2 (tv, pathv);
217   CAMLlocal3 (rv, v, cons);
218   augeas_t t = Augeas_t_val (tv);
219   char *path = String_val (path);
220   char **matches;
221   int r, i;
222
223   r = aug_match (t, path, &matches);
224   if (r == -1)
225     raise_error ("Augeas.matches");
226
227   /* Copy the paths to a list. */
228   rv = Val_int (0);
229   for (i = 0; i < r; ++i) {
230     v = caml_copy_string (matches[i]);
231     free (matches[i]);
232     cons = caml_alloc (2, 0);
233     Field (cons, 1) = rv;
234     Field (cons, 0) = v;
235     rv = cons;
236   }
237
238   free (matches);
239
240   CAMLreturn (rv);
241 }
242
243 /* val count_matches : t -> path -> int */
244 CAMLprim value
245 ocaml_augeas_count_matches (value tv, value pathv)
246 {
247   CAMLparam2 (tv, pathv);
248   augeas_t t = Augeas_t_val (tv);
249   char *path = String_val (path);
250   int r;
251
252   r = aug_match (t, path, NULL);
253   if (r == -1)
254     raise_error ("Augeas.count_matches");
255
256   CAMLreturn (Val_int (r));
257 }
258
259 /* val save : t -> unit */
260 CAMLprim value
261 ocaml_augeas_save (value tv)
262 {
263   CAMLparam1 (tv);
264   augeas_t t = Augeas_t_val (tv);
265
266   if (aug_save (t) == -1)
267     raise_error ("Augeas.save");
268
269   CAMLreturn (Val_unit);
270 }