#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "pcre2.h"

#define OS_Module 0x1E

static _kernel_oserror pcre_swi_errors[] = {
  {0x880000, "Unknown PCRE SWI call"},
  {0x880001, "Error compiling pattern: "},
  {0x880002, "Unable to copy substring"},
  {0x880003, "Unable to create data block"},
  {0x880004, "Invalid output vector number"},
  {0x880005, "Unknown command"},
  {0x880006, "Error while matching: "}
};

_kernel_oserror *errblock;

void configopt(int confopt, char *text, int flag) {
  unsigned int confout;

  pcre2_config(confopt, &confout);
  if(flag==1) {
    printf("%s: ", text);
    if(confout)
      printf("Yes\n");
    else
      printf("No\n");
  } else {
    printf("%s: %d\n", text, confout);
  }
}

_kernel_oserror *cmdHandler(const char *arg_string, int argc, int cmd_no, void *pw) {

  if(cmd_no != 0)
    return(&(pcre_swi_errors[5]));
  configopt(PCRE2_CONFIG_JIT, "JIT support", 1);
  configopt(PCRE2_CONFIG_UNICODE, "Unicode support", 1);
  configopt(PCRE2_CONFIG_LINKSIZE, "Internal link size", 0);
  configopt(PCRE2_CONFIG_NEWLINE, "Default newline sequence", 0);
  configopt(PCRE2_CONFIG_MATCHLIMIT, "Internal resource limit", 0);
  configopt(PCRE2_CONFIG_PARENSLIMIT, "Parentheses nesting limit", 0);
  configopt(PCRE2_CONFIG_RECURSIONLIMIT, "Maximum recursion depth", 0);
  configopt(PCRE2_CONFIG_STACKRECURSE, "Use stack for recursion", 1);
  return(NULL);
}


_kernel_oserror* swi_compile(_kernel_swi_regs *r, void *pw) {
  int errcode;
  PCRE2_SIZE erroffset;
  const pcre2_code *handle;
  char errpos[16];

  handle=pcre2_compile((PCRE2_SPTR)r->r[0], (PCRE2_SIZE)r->r[1], r->r[2], &errcode,
                       &erroffset, NULL);
  r->r[0]=(int) handle;
  if (!handle) {
    memcpy(errblock, &(pcre_swi_errors[1]), sizeof(_kernel_oserror));
    pcre2_get_error_message(errcode, (PCRE2_UCHAR *) (errblock->errmess)+25, 229);
    sprintf(errpos, ", offset %d", erroffset);
    strcat(errblock->errmess, errpos);
    return(errblock);
  }
  return(NULL);
}

_kernel_oserror* swi_match(_kernel_swi_regs *r, void *pw) {
  int vecsused;
  PCRE2_SIZE *ovector;

  if(r->r[5]==0) {
    r->r[5]=(int)pcre2_match_data_create_from_pattern((pcre2_code *)r->r[0], NULL);
  }
  vecsused=pcre2_match((pcre2_code *)r->r[0], (PCRE2_SPTR)r->r[1], r->r[2], r->r[3], r->r[4],
                       (pcre2_match_data *)r->r[5], NULL);
  r->r[0]=vecsused;
  if(vecsused<-1) {
    /* Handle any error while matching */ 
    memcpy(errblock, &(pcre_swi_errors[6]), sizeof(_kernel_oserror));
    pcre2_get_error_message(vecsused, (PCRE2_UCHAR *) (errblock->errmess)+22, 232);
    return(errblock);
  } else if(vecsused>=0) {
    /* Match suceeded */
    ovector=pcre2_get_ovector_pointer((pcre2_match_data *)r->r[5]);
    r->r[3]=ovector[1];
  }
  return(NULL);
}

_kernel_oserror* swi_dfa_match(_kernel_swi_regs *r, void *pw) {
  int vecsused;
  int *wsp;
  PCRE2_SIZE *ovector;

  wsp=malloc(1024);
  vecsused=pcre2_dfa_match((pcre2_code *)r->r[0], (PCRE2_SPTR)r->r[1], r->r[2], r->r[3], r->r[4],
                           (pcre2_match_data *)r->r[5], NULL, wsp, 256);
  free(wsp);
  r->r[0]=vecsused;
  if(vecsused<-1) {
    /* Handle any error while matching */ 
    memcpy(errblock, &(pcre_swi_errors[6]), sizeof(_kernel_oserror));
    pcre2_get_error_message(vecsused, (PCRE2_UCHAR *) (errblock->errmess)+22, 232);
    return(errblock);
  } else if(vecsused>=0) {
    /* Match suceeded */
    ovector=pcre2_get_ovector_pointer((pcre2_match_data *)r->r[5]);
    r->r[3]=ovector[1];
  }
  return(NULL);
}


_kernel_oserror* swi_copy_substring(_kernel_swi_regs *r, void *pw) {
  int rc, vecnum;
  PCRE2_SIZE size, *ovector;
  PCRE2_UCHAR *buffer;
  pcre2_match_data *match_data;

  match_data=(pcre2_match_data *)r->r[0];
  vecnum=r->r[2];
  buffer=(PCRE2_UCHAR *)r->r[3];
  rc=pcre2_substring_length_bynumber(match_data, vecnum, &size);
  if(rc<0) {
    r->r[0]=rc;
    return(&(pcre_swi_errors[2]));
  }
  if(buffer==NULL) {
    r->r[4]=size+1;
    return(NULL);
  }
  if(size+1 > r->r[4])
    return(&(pcre_swi_errors[2]));
  ovector=pcre2_get_ovector_pointer(match_data);
  memcpy(buffer, (PCRE2_UCHAR *)r->r[1]+ovector[vecnum*2], size);
  buffer[size]='\0';
  r->r[4]=size;
  return(NULL);
}

_kernel_oserror* swi_get_ovector(_kernel_swi_regs *r, void *pw) {
  PCRE2_SIZE *ovector;
  int vecnum, length;

  ovector=pcre2_get_ovector_pointer((pcre2_match_data *)r->r[0]);
  length=pcre2_get_ovector_count((pcre2_match_data *)r->r[0]);
  vecnum=r->r[1];
  if(vecnum>=length) {    
    r->r[1]=-1;
    return(&(pcre_swi_errors[4]));
  }
  r->r[0]=ovector[vecnum*2];
  r->r[1]=ovector[vecnum*2+1];
  return(NULL);
}

_kernel_oserror* swi_free_code(_kernel_swi_regs *r, void *pw) {

  pcre2_code_free((pcre2_code *)r->r[0]);
  return(NULL);
}

_kernel_oserror* swi_create_data(_kernel_swi_regs *r, void *pw) {
  pcre2_match_data *data;

  if(r->r[0] == 0)
    data=pcre2_match_data_create_from_pattern((pcre2_code *)r->r[1], NULL);
  else
    data=pcre2_match_data_create(r->r[0], NULL);
  if(data) {
    r->r[0]=(int)data;
    return(NULL);
  } else {
    r->r[0]=0;
    return(&(pcre_swi_errors[3]));
  }
}

_kernel_oserror* swi_free_data(_kernel_swi_regs *r, void *pw) {

  pcre2_match_data_free((pcre2_match_data *)r->r[0]);
  return(NULL);
}

_kernel_oserror* mod_init(char *commandTail, int poduleBase, void *pw) {
  _kernel_swi_regs r;
  _kernel_oserror *errptr;

  r.r[0]=6;
  r.r[3]=sizeof(_kernel_oserror);
  errptr=_kernel_swi(OS_Module, &r, &r);
  if(!errptr)
    errblock=(_kernel_oserror *)r.r[2];
  return(errptr);
}

_kernel_oserror* mod_final(int fatal, int podule, void *pw) {
  _kernel_swi_regs r;

  r.r[0]=7;
  r.r[2]=(int) errblock;
  _kernel_swi(OS_Module, &r, &r);
  return NULL;
}

_kernel_oserror* swiHandler(int swiNum, _kernel_swi_regs *r, void *pw) {
  _kernel_oserror* (*swiRoutine)(_kernel_swi_regs *r, void *pw);

  switch (swiNum) {
    case 0: swiRoutine=swi_compile; break;
    case 1: swiRoutine=swi_match; break;
    case 2: swiRoutine=swi_dfa_match; break;
    case 3: swiRoutine=swi_copy_substring; break;
    case 4: swiRoutine=swi_get_ovector; break;
    case 5: swiRoutine=swi_free_code; break;
    case 6: swiRoutine=swi_create_data; break;
    case 7: swiRoutine=swi_free_data; break;

    default: return(&(pcre_swi_errors[0]));
  }
  return(swiRoutine(r, pw));
}
