The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
//    Copyright (c) 2008-2010 Dominique Dumont.
//    Copyright (c) 2011-2012 Raphaƫl Pinson.
// 
//    This library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Lesser Public License as
//    published by the Free Software Foundation; either version 2.1 of
//    the License, or (at your option) any later version.
// 
//    Config-Model is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//    Lesser Public License for more details.
// 
//    You should have received a copy of the GNU Lesser Public License
//    along with Config-Model; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
//    02110-1301 USA

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#define NEED_newCONSTSUB
#include "ppport.h"

#include <string.h>
#include <stdio.h>
#include <augeas.h>

/* Defines from Augeas' internal.h */
#ifndef AUGEAS_META_TREE
#define AUGEAS_META_TREE "/augeas"
#endif
#ifndef AUGEAS_SPAN_OPTION
#define AUGEAS_SPAN_OPTION AUGEAS_META_TREE "/span"
#endif
#ifndef AUGEAS_ENABLE
#define AUG_ENABLE "enable"
#endif
#ifndef AUGEAS_DISABLE
#define AUG_DISABLE "disable"
#endif

typedef augeas   Config_Augeas ;
typedef PerlIO*  OutputStream;

MODULE = Config::Augeas PACKAGE = Config::Augeas PREFIX = aug_

 # See http://blogs.sun.com/akolb/entry/pitfals_of_the_perl_xs
 # 
 # Define any constants that need to be exported.  By doing it this way
 # we can avoid the overhead of using the DynaLoader package, and in
 # addition constants defined using this mechanism are eligible for
 # inlining by the perl interpreter at compile time.

BOOT:
  {
    HV *stash;
    stash	       = gv_stashpv("Config::Augeas", TRUE);
    newCONSTSUB(stash, "AUG_NONE",         newSViv(AUG_NONE));
    newCONSTSUB(stash, "AUG_SAVE_BACKUP",  newSViv(AUG_SAVE_BACKUP));
    newCONSTSUB(stash, "AUG_SAVE_NEWFILE", newSViv(AUG_SAVE_NEWFILE));
    newCONSTSUB(stash, "AUG_TYPE_CHECK",   newSViv(AUG_TYPE_CHECK));
    newCONSTSUB(stash, "AUG_NO_STDINC",    newSViv(AUG_NO_STDINC));
    newCONSTSUB(stash, "AUG_SAVE_NOOP",    newSViv(AUG_SAVE_NOOP));
    newCONSTSUB(stash, "AUG_NO_LOAD",      newSViv(AUG_NO_LOAD));
    newCONSTSUB(stash, "AUG_NO_MODL_AUTOLOAD", newSViv(AUG_NO_MODL_AUTOLOAD));
    newCONSTSUB(stash, "AUG_ENABLE_SPAN", newSViv(AUG_ENABLE_SPAN));


    /* Error reporting */
    newCONSTSUB(stash, "AUG_NOERROR",   newSViv(AUG_NOERROR));
    newCONSTSUB(stash, "AUG_ENOMEM",    newSViv(AUG_ENOMEM));
    newCONSTSUB(stash, "AUG_EINTERNAL", newSViv(AUG_EINTERNAL));
    newCONSTSUB(stash, "AUG_EPATHX",    newSViv(AUG_EPATHX));
    newCONSTSUB(stash, "AUG_ENOMATCH",  newSViv(AUG_ENOMATCH));
    newCONSTSUB(stash, "AUG_EMMATCH",   newSViv(AUG_EMMATCH));
    newCONSTSUB(stash, "AUG_ESYNTAX",   newSViv(AUG_ESYNTAX));
    newCONSTSUB(stash, "AUG_ENOLENS",   newSViv(AUG_ENOLENS));
    newCONSTSUB(stash, "AUG_EMXFM",     newSViv(AUG_EMXFM));
    newCONSTSUB(stash, "AUG_ENOSPAN", newSViv(AUG_ENOSPAN));

  }

Config_Augeas*
aug_init(root = NULL ,loadpath = NULL ,flags = 0)
      char* root 
      char* loadpath
      unsigned int flags


MODULE = Config::Augeas PACKAGE = Config::AugeasPtr PREFIX = aug_

void
aug_DESTROY(aug)
      Config_Augeas* aug
    CODE:
      //printf("destroying aug object\n");
      aug_close(aug);

int
aug_defvar(aug, name, expr)
      Config_Augeas* aug
      const char* name
      const char* expr

 # returns an array ( return value, created ) 
void
aug_defnode(aug, name, expr, value)
      Config_Augeas* aug
      const char* name
      const char* expr
      const char* value
    PREINIT:
      int created ;
      int ret ;
    PPCODE:
      created = 1 ;
      ret = aug_defnode(aug, name, expr, value, &created ) ;
      if (ret >= 0 ) {
        XPUSHs(sv_2mortal(newSVnv(ret)));
        XPUSHs(sv_2mortal(newSVnv(created)));
      }

const char*
aug_get(aug, path)
      Config_Augeas* aug
      char* path
    PREINIT:
      int ret ;
    CODE:
      RETVAL = NULL ;
      ret = aug_get(aug, path, &RETVAL);
    OUTPUT:
      RETVAL

int
aug_set(aug, path, c_value)
      Config_Augeas* aug
      const char* path
      char* c_value

int 
aug_insert(aug, path, label, before)
      Config_Augeas* aug
      const char* path
      const char* label
      int before

int 
aug_rm(aug, path);
      Config_Augeas *aug
      const char *path

int 
aug_mv(aug, src, dst);
      Config_Augeas *aug
      const char *src
      const char *dst

SV*
aug_span(aug, path);
      Config_Augeas* aug
      char* path
    PREINIT:
      int ret ;
      char *filename = NULL;
      const char *option = NULL;
      uint label_start, label_end, value_start, value_end, span_start, span_end;
      HV *span_hash;
    CODE:
      // Check that span is enabled
      if (aug_get(aug, AUGEAS_SPAN_OPTION, &option) != 1) {
	 croak ("Error: option %s not found\n", AUGEAS_SPAN_OPTION);
      }
      if (strcmp(AUG_DISABLE, option) == 0) {
	 croak ("Error: Span is not enabled.\n");
      }
      ret = aug_span(aug, path, &filename, &label_start, &label_end, &value_start, &value_end, &span_start, &span_end);
      span_hash = newHV();
      if (filename) {
         (void)hv_store(span_hash, "filename", 8, newSVpv(filename, strlen(filename)), 0);
         free(filename) ;
      }
      (void)hv_store(span_hash, "label_start", 11, newSViv(label_start), 0);
      (void)hv_store(span_hash, "label_end", 9, newSViv(label_end), 0);
      (void)hv_store(span_hash, "value_start", 11, newSViv(value_start), 0);
      (void)hv_store(span_hash, "value_end", 9, newSViv(value_end), 0);
      (void)hv_store(span_hash, "span_start", 10, newSViv(span_start), 0);
      (void)hv_store(span_hash, "span_end", 8, newSViv(span_end), 0);
      RETVAL = newRV_noinc((SV *)span_hash);
    OUTPUT:
      RETVAL
      

void
aug_match(aug, pattern);
      Config_Augeas *aug
      const char *pattern
    PREINIT:
        char** matches;
        char** err_matches;
        const char*  err_string ;
        int i ;
        int ret ;
	int cnt;
	char die_msg[1024] ;
	char tmp_msg[128];
    PPCODE:
    
        cnt = aug_match(aug, pattern, &matches);

        if (cnt == -1) {
	   sprintf(die_msg, "aug_match error with pattern '%s':\n",pattern);
    	   cnt = aug_match(aug,"/augeas//error/descendant-or-self::*",&err_matches);
	   for (i=0; i < cnt; i++) {
               ret = aug_get(aug, err_matches[i], &err_string) ;
	       sprintf(tmp_msg,"%s = %s\n", err_matches[i], err_string );
	       if (strlen(die_msg) + strlen(tmp_msg) < 1024 )
	       	       strcat(die_msg,tmp_msg);
	   }
	   croak ("%s",die_msg);
        }

        // printf("match: Pattern %s matches %d times\n", pattern, cnt);
    
        for (i=0; i < cnt; i++) {
            XPUSHs(sv_2mortal(newSVpv(matches[i], 0)));
            free((void*) matches[i]);
        }
        free(matches);

int
aug_count_match(aug, pattern);
      Config_Augeas *aug
      const char *pattern
    CODE:
        RETVAL = aug_match(aug, pattern,NULL);
    OUTPUT:
        RETVAL


int 
aug_save( aug );
      Config_Augeas *aug

int 
aug_load( aug );
      Config_Augeas *aug


 # See example 9 in perlxstut man page
int
aug_print(aug, stream, path);
        Config_Augeas *aug
	OutputStream stream
	const char* path
    PREINIT:
        FILE *fp ;
    CODE:
        fp = PerlIO_findFILE(stream);
        if (fp != (FILE*) 0) {
             RETVAL = aug_print(aug, fp, path);
         } else {
             RETVAL = -1;
         }
    OUTPUT:
        RETVAL

int
aug_srun(aug, stream, text);
        Config_Augeas *aug
	OutputStream stream
	const char* text
    PREINIT:
        FILE *fp ;
    CODE:
        fp = PerlIO_findFILE(stream);
        if (fp != (FILE*) 0) {
             RETVAL = aug_srun(aug, fp, text);
         } else {
             RETVAL = -1;
         }
    OUTPUT:
        RETVAL

 # Error reporting

int 
aug_error( aug );
      Config_Augeas *aug

const char*
aug_error_message(aug)
      Config_Augeas* aug

const char*
aug_error_minor_message(aug)
      Config_Augeas* aug

const char*
aug_error_details(aug)
      Config_Augeas* aug