Add Augeas.mv
[ocaml-augeas.git] / augeas-c.c
1 /* Augeas OCaml bindings
2  * Copyright (C) 2008-2017 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 #include <stdbool.h>
33
34 #ifdef __GNUC__
35   #define NORETURN __attribute__ ((noreturn))
36 #else
37   #define NORETURN
38 #endif
39
40 extern CAMLprim value ocaml_augeas_create (value rootv, value loadpathv, value flagsv);
41 extern CAMLprim value ocaml_augeas_close (value tv);
42 extern CAMLprim value ocaml_augeas_defnode (value tv, value namev, value exprv, value valv);
43 extern CAMLprim value ocaml_augeas_defvar (value tv, value namev, value exprv);
44 extern CAMLprim value ocaml_augeas_get (value tv, value pathv);
45 extern CAMLprim value ocaml_augeas_exists (value tv, value pathv);
46 extern CAMLprim value ocaml_augeas_insert (value tv, value beforev, value pathv, value labelv);
47 extern CAMLprim value ocaml_augeas_mv (value tv, value srcv, value destv);
48 extern CAMLprim value ocaml_augeas_rm (value tv, value pathv);
49 extern CAMLprim value ocaml_augeas_match (value tv, value pathv);
50 extern CAMLprim value ocaml_augeas_count_matches (value tv, value pathv);
51 extern CAMLprim value ocaml_augeas_save (value tv);
52 extern CAMLprim value ocaml_augeas_load (value tv);
53 extern CAMLprim value ocaml_augeas_set (value tv, value pathv, value valuev);
54 extern CAMLprim value ocaml_augeas_transform (value tv, value lensv, value filev, value modev);
55 extern CAMLprim value ocaml_augeas_source (value tv, value pathv)
56 #ifndef HAVE_AUG_SOURCE
57   NORETURN
58 #endif
59 ;
60
61 typedef augeas *augeas_t;
62
63 /* Map C aug_errcode_t to OCaml error_code. */
64 static const int error_map[] = {
65   /* AugErrInternal */ AUG_EINTERNAL,
66   /* AugErrPathX */    AUG_EPATHX,
67   /* AugErrNoMatch */  AUG_ENOMATCH,
68   /* AugErrMMatch */   AUG_EMMATCH,
69   /* AugErrSyntax */   AUG_ESYNTAX,
70   /* AugErrNoLens */   AUG_ENOLENS,
71   /* AugErrMXfm */     AUG_EMXFM,
72   /* AugErrNoSpan */   AUG_ENOSPAN,
73   /* AugErrMvDesc */   AUG_EMVDESC,
74   /* AugErrCmdRun */   AUG_ECMDRUN,
75   /* AugErrBadArg */   AUG_EBADARG,
76   /* AugErrLabel */    AUG_ELABEL,
77   /* AugErrCpDesc */   AUG_ECPDESC,
78 };
79 static const int error_map_len = sizeof error_map / sizeof error_map[0];
80
81 /* Raise an Augeas.Error exception, and optionally close the
82  * specified handle.
83  */
84 static void
85 raise_error_and_maybe_close (augeas_t t, const char *msg, bool close_handle)
86 {
87   value *exn = caml_named_value ("Augeas.Error");
88   value args[4];
89   const int code = aug_error (t);
90   const char *aug_err_minor;
91   const char *aug_err_details;
92   int ocaml_code = -1;
93   int i;
94
95   if (code == AUG_ENOMEM) {
96     if (close_handle)
97       aug_close (t);
98     caml_raise_out_of_memory ();
99   }
100
101   aug_err_minor = aug_error_minor_message (t);
102   aug_err_details = aug_error_details (t);
103
104   for (i = 0; i < error_map_len; ++i)
105     if (error_map[i] == code) {
106       ocaml_code = i;
107       break;
108     }
109
110   if (ocaml_code != -1)
111     args[0] = Val_int (ocaml_code);
112   else {
113     args[0] = caml_alloc (1, 0);
114     Store_field (args[0], 0, Val_int (code));
115   }
116   args[1] = caml_copy_string (msg);
117   args[2] = caml_copy_string (aug_err_minor ? : "");
118   args[3] = caml_copy_string (aug_err_details ? : "");
119
120   if (close_handle)
121     aug_close (t);
122
123   caml_raise_with_args (*exn, 4, args);
124 }
125 #define raise_error(t, msg) raise_error_and_maybe_close(t, msg, false)
126
127 static void
128 raise_init_error (const char *msg)
129 {
130   value *exn = caml_named_value ("Augeas.Error");
131   value args[4];
132
133   args[0] = caml_alloc (1, 0);
134   Store_field (args[0], 0, Val_int (-1));
135   args[1] = caml_copy_string (msg);
136   args[2] = caml_copy_string ("augeas initialization failed");
137   args[3] = caml_copy_string ("");
138
139   caml_raise_with_args (*exn, 4, args);
140 }
141
142 static const char *
143 Optstring_val (value strv)
144 {
145   if (strv == Val_int (0))      /* None */
146     return NULL;
147   else                          /* Some string */
148     return String_val (Field (strv, 0));
149 }
150
151 /* Map OCaml flags to C flags. */
152 static const int flag_map[] = {
153   /* AugSaveBackup */  AUG_SAVE_BACKUP,
154   /* AugSaveNewFile */ AUG_SAVE_NEWFILE,
155   /* AugTypeCheck */   AUG_TYPE_CHECK,
156   /* AugNoStdinc */    AUG_NO_STDINC,
157   /* AugSaveNoop */    AUG_SAVE_NOOP,
158   /* AugNoLoad */      AUG_NO_LOAD,
159   /* AugNoModlAutoload */ AUG_NO_MODL_AUTOLOAD,
160   /* AugEnableSpan */  AUG_ENABLE_SPAN,
161   /* AugNoErrClose */  AUG_NO_ERR_CLOSE,
162   /* AugTraceModuleLoading */ AUG_TRACE_MODULE_LOADING,
163 };
164
165 /* Wrap and unwrap augeas_t handles, with a finalizer. */
166 #define Augeas_t_val(rv) (*(augeas_t *)Data_custom_val(rv))
167
168 static void
169 augeas_t_finalize (value tv)
170 {
171   augeas_t t = Augeas_t_val (tv);
172   if (t) aug_close (t);
173 }
174
175 static struct custom_operations custom_operations = {
176   (char *) "augeas_t_custom_operations",
177   augeas_t_finalize,
178   custom_compare_default,
179   custom_hash_default,
180   custom_serialize_default,
181   custom_deserialize_default,
182   custom_compare_ext_default,
183 };
184
185 static value Val_augeas_t (augeas_t t)
186 {
187   CAMLparam0 ();
188   CAMLlocal1 (rv);
189   /* We could choose these so that the GC can make better decisions.
190    * See 18.9.2 of the OCaml manual.
191    */
192   const int used = 0;
193   const int max = 1;
194
195   rv = caml_alloc_custom (&custom_operations,
196                           sizeof (augeas_t), used, max);
197   Augeas_t_val(rv) = t;
198
199   CAMLreturn (rv);
200 }
201
202 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
203
204 /* val create : string -> string option -> flag list -> t */
205 CAMLprim value
206 ocaml_augeas_create (value rootv, value loadpathv, value flagsv)
207 {
208   CAMLparam1 (rootv);
209   const char *root = String_val (rootv);
210   const char *loadpath = Optstring_val (loadpathv);
211   int flags = 0, i;
212   augeas_t t;
213
214   /* Convert list of flags to C. */
215   for (; flagsv != Val_int (0); flagsv = Field (flagsv, 1)) {
216     i = Int_val (Field (flagsv, 0));
217     flags |= flag_map[i];
218   }
219
220   /* Pass AUG_NO_ERR_CLOSE so we raise a detailed Augeas.Error. */
221   t = aug_init (root, loadpath, flags | AUG_NO_ERR_CLOSE);
222
223   if (t == NULL)
224     raise_init_error ("Augeas.create");
225
226   if (aug_error (t) != AUG_NOERROR) {
227     raise_error_and_maybe_close (t, "Augeas.init", true);
228   }
229
230   CAMLreturn (Val_augeas_t (t));
231 }
232
233 /* val close : t -> unit */
234 CAMLprim value
235 ocaml_augeas_close (value tv)
236 {
237   CAMLparam1 (tv);
238   augeas_t t = Augeas_t_val (tv);
239
240   if (t) {
241     aug_close (t);
242     Augeas_t_val(tv) = NULL;    /* So the finalizer doesn't double-free. */
243   }
244
245   CAMLreturn (Val_unit);
246 }
247
248 /* val defnode : t -> string -> string -> string option -> int * bool */
249 CAMLprim value
250 ocaml_augeas_defnode (value tv, value namev, value exprv, value valv)
251 {
252   CAMLparam4 (tv, namev, exprv, valv);
253   CAMLlocal2 (optv, v);
254   augeas_t t = Augeas_t_val (tv);
255   const char *name = String_val (namev);
256   const char *expr = String_val (exprv);
257   const char *val = Optstring_val (valv);
258   int r, created;
259
260   r = aug_defnode (t, name, expr, val, &created);
261   if (r == -1) {
262     raise_error (t, "Augeas.defnode");
263   }
264
265   v = caml_alloc (2, 0);
266   Store_field (v, 0, Val_int (r));
267   Store_field (v, 1, Val_bool (created));
268
269   CAMLreturn (v);
270 }
271
272 /* val defvar : t -> string -> string option -> int option */
273 CAMLprim value
274 ocaml_augeas_defvar (value tv, value namev, value exprv)
275 {
276   CAMLparam3 (tv, namev, exprv);
277   CAMLlocal2 (optv, v);
278   augeas_t t = Augeas_t_val (tv);
279   const char *name = String_val (namev);
280   const char *expr = Optstring_val (exprv);
281   int r;
282
283   r = aug_defvar (t, name, expr);
284   if (r > 0) {          /* Return Some val */
285     v = Val_int (r);
286     optv = caml_alloc (1, 0);
287     Field (optv, 0) = v;
288   } else if (r == 0)    /* Return None */
289     optv = Val_int (0);
290   else if (r == -1)             /* Error or multiple matches */
291     raise_error (t, "Augeas.defvar");
292   else
293     caml_failwith ("Augeas.defvar: bad return value");
294
295   CAMLreturn (optv);
296 }
297
298 /* val get : t -> path -> value option */
299 CAMLprim value
300 ocaml_augeas_get (value tv, value pathv)
301 {
302   CAMLparam2 (tv, pathv);
303   CAMLlocal2 (optv, v);
304   augeas_t t = Augeas_t_val (tv);
305   const char *path = String_val (pathv);
306   const char *val;
307   int r;
308
309   r = aug_get (t, path, &val);
310   if (r == 1 && val) {          /* Return Some val */
311     v = caml_copy_string (val);
312     optv = caml_alloc (1, 0);
313     Field (optv, 0) = v;
314   } else if (r == 0 || !val)    /* Return None */
315     optv = Val_int (0);
316   else if (r == -1)             /* Error or multiple matches */
317     raise_error (t, "Augeas.get");
318   else
319     caml_failwith ("Augeas.get: bad return value");
320
321   CAMLreturn (optv);
322 }
323
324 /* val exists : t -> path -> bool */
325 CAMLprim value
326 ocaml_augeas_exists (value tv, value pathv)
327 {
328   CAMLparam2 (tv, pathv);
329   CAMLlocal1 (v);
330   augeas_t t = Augeas_t_val (tv);
331   const char *path = String_val (pathv);
332   int r;
333
334   r = aug_get (t, path, NULL);
335   if (r == 1)                   /* Return true. */
336     v = Val_int (1);
337   else if (r == 0)              /* Return false */
338     v = Val_int (0);
339   else if (r == -1)             /* Error or multiple matches */
340     raise_error (t, "Augeas.exists");
341   else
342     failwith ("Augeas.exists: bad return value");
343
344   CAMLreturn (v);
345 }
346
347 /* val insert : t -> ?before:bool -> path -> string -> unit */
348 CAMLprim value
349 ocaml_augeas_insert (value tv, value beforev, value pathv, value labelv)
350 {
351   CAMLparam4 (tv, beforev, pathv, labelv);
352   augeas_t t = Augeas_t_val (tv);
353   const char *path = String_val (pathv);
354   const char *label = String_val (labelv);
355   int before;
356
357   before = beforev == Val_int (0) ? 0 : Int_val (Field (beforev, 0));
358
359   if (aug_insert (t, path, label, before) == -1)
360     raise_error (t, "Augeas.insert");
361
362   CAMLreturn (Val_unit);
363 }
364
365 /* val mv : t -> path -> path -> unit */
366 CAMLprim value
367 ocaml_augeas_mv (value tv, value srcv, value destv)
368 {
369   CAMLparam3 (tv, srcv, destv);
370   augeas_t t = Augeas_t_val (tv);
371   const char *src = String_val (srcv);
372   const char *dest = String_val (destv);
373
374   if (aug_mv (t, src, dest) == -1)
375     raise_error (t, "Augeas.mv");
376
377   CAMLreturn (Val_unit);
378 }
379
380 /* val rm : t -> path -> int */
381 CAMLprim value
382 ocaml_augeas_rm (value tv, value pathv)
383 {
384   CAMLparam2 (tv, pathv);
385   augeas_t t = Augeas_t_val (tv);
386   const char *path = String_val (pathv);
387   int r;
388
389   r = aug_rm (t, path);
390   if (r == -1)
391     raise_error (t, "Augeas.rm");
392
393   CAMLreturn (Val_int (r));
394 }
395
396 /* val matches : t -> path -> path list */
397 CAMLprim value
398 ocaml_augeas_match (value tv, value pathv)
399 {
400   CAMLparam2 (tv, pathv);
401   CAMLlocal3 (rv, v, cons);
402   augeas_t t = Augeas_t_val (tv);
403   const char *path = String_val (pathv);
404   char **matches;
405   int r, i;
406
407   r = aug_match (t, path, &matches);
408   if (r == -1)
409     raise_error (t, "Augeas.matches");
410
411   /* Copy the paths to a list. */
412   rv = Val_int (0);
413   for (i = 0; i < r; ++i) {
414     v = caml_copy_string (matches[i]);
415     free (matches[i]);
416     cons = caml_alloc (2, 0);
417     Field (cons, 1) = rv;
418     Field (cons, 0) = v;
419     rv = cons;
420   }
421
422   free (matches);
423
424   CAMLreturn (rv);
425 }
426
427 /* val count_matches : t -> path -> int */
428 CAMLprim value
429 ocaml_augeas_count_matches (value tv, value pathv)
430 {
431   CAMLparam2 (tv, pathv);
432   augeas_t t = Augeas_t_val (tv);
433   const char *path = String_val (pathv);
434   int r;
435
436   r = aug_match (t, path, NULL);
437   if (r == -1)
438     raise_error (t, "Augeas.count_matches");
439
440   CAMLreturn (Val_int (r));
441 }
442
443 /* val save : t -> unit */
444 CAMLprim value
445 ocaml_augeas_save (value tv)
446 {
447   CAMLparam1 (tv);
448   augeas_t t = Augeas_t_val (tv);
449
450   if (aug_save (t) == -1)
451     raise_error (t, "Augeas.save");
452
453   CAMLreturn (Val_unit);
454 }
455
456 /* val load : t -> unit */
457 CAMLprim value
458 ocaml_augeas_load (value tv)
459 {
460   CAMLparam1 (tv);
461   augeas_t t = Augeas_t_val (tv);
462
463   if (aug_load (t) == -1)
464     raise_error (t, "Augeas.load");
465
466   CAMLreturn (Val_unit);
467 }
468
469 /* val set : t -> -> path -> value option -> unit */
470 CAMLprim value
471 ocaml_augeas_set (value tv, value pathv, value valuev)
472 {
473   CAMLparam3 (tv, pathv, valuev);
474   augeas_t t = Augeas_t_val (tv);
475   const char *path = String_val (pathv);
476   const char *val = Optstring_val (valuev);
477
478   if (aug_set (t, path, val) == -1)
479     raise_error (t, "Augeas.set");
480
481   CAMLreturn (Val_unit);
482 }
483
484 /* val transform : t -> string -> string -> transform_mode -> unit */
485 CAMLprim value
486 ocaml_augeas_transform (value tv, value lensv, value filev, value modev)
487 {
488   CAMLparam4 (tv, lensv, filev, modev);
489   augeas_t t = Augeas_t_val (tv);
490   const char *lens = String_val (lensv);
491   const char *file = String_val (filev);
492   const int excl = Int_val (modev) == 1 ? 1 : 0;
493
494   if (aug_transform (t, lens, file, excl) == -1)
495     raise_error (t, "Augeas.transform");
496
497   CAMLreturn (Val_unit);
498 }
499
500 /* val source : t -> path -> path option */
501 CAMLprim value
502 ocaml_augeas_source (value tv, value pathv)
503 {
504 #ifdef HAVE_AUG_SOURCE
505   CAMLparam2 (tv, pathv);
506   CAMLlocal2 (optv, v);
507   augeas_t t = Augeas_t_val (tv);
508   const char *path = String_val (pathv);
509   char *file_path;
510   int r;
511
512   r = aug_source (t, path, &file_path);
513   if (r == 0) {
514     if (file_path) {    /* Return Some file_path */
515       v = caml_copy_string (file_path);
516       optv = caml_alloc (1, 0);
517       Field (optv, 0) = v;
518       free (file_path);
519     } else              /* Return None */
520       optv = Val_int (0);
521   }
522   else                  /* Error */
523     raise_error (t, "Augeas.source");
524
525   CAMLreturn (optv);
526 #else
527   caml_failwith ("Augeas.source: function not implemented");
528 #endif
529 }