/* #includes */ /*{{{C}}}*//*{{{*/
#ifndef NO_POSIX_SOURCE
#undef  _POSIX_SOURCE
#define _POSIX_SOURCE   1
#undef  _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 2
#endif

#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <nl_types.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "misc.h"
#include "sentence.h"
/*}}}*/
/* #defines */ /*{{{*/
#define OOPS  catgets(catd,2,1,"%s: internal error, can not compile regular expression: %s\n")
#define NOMEM catgets(catd,2,2,"%s: increasing sentence buffer failed: %s\n")
/*}}}*/

static const char *abbreviations_de[]= /*{{{*/
{
  "Dr",
  "bzw",
  "etc",
  "sog",
  "usw",
  (const char*)0
};
/*}}}*/
static const char *abbreviations_en[]= /*{{{*/
{
  "ch",
  "Ch",
  "ckts",
  "dB",
  "Dept",
  "dept",
  "Depts",
  "depts",
  "Dr",
  "Drs",
  "Eq",
  "eq",
  "etc",
  "et al",
  "Fig",
  "fig",
  "Figs",
  "figs",
  "ft",
  "in",
  "Inc",
  "Jr",
  "jr",
  "mi",
  "Mr",
  "Mrs",
  "Ms",
  "No",
  "no",
  "Nos",
  "nos",
  "Ph",
  "Ref",
  "ref",
  "Refs",
  "refs",
  "St",
  "vs",
  "yr",
  (const char*)0
};
/*}}}*/
static const char *abbreviations_none[]= /*{{{*/
{
  (const char*)0
};
/*}}}*/
static const char **abbreviations;

static int endingInAbbrev(const char *s, size_t length) /*{{{*/
{
  const char **abbrev=abbreviations;
  size_t aLength;
  if (!isalpha(s[length-1])) return 0;
  while (*abbrev!=(const char*)0)
  {
    if ((aLength=strlen(*abbrev))<length)
    {
      if (!isalpha(s[length-2])) return 1;
      if (!isalpha(s[length-aLength-1]) && strncmp(s+length-aLength,*abbrev,aLength)==0) return 1;
    }
    else
    {
      if (length==1) return 1;
      if (aLength==length && strncmp(s,*abbrev,aLength)==0) return 1;
    }      
    ++abbrev;
  }
  return 0;
}
/*}}}*/

void sentence(const char *cmd, nl_catd catd, FILE *in, const char *file, void (*process)(const char *, size_t, const char *, int), const char *lang) /*{{{*/
{
  /* variables */ /*{{{*/
  int voc,oc,c;
  char *sent=malloc(128);
  size_t length=0,capacity=128;
  int inWhiteSpace=0;
  int inParagraph=0;
  int line=1,beginLine=1;
  int err;
  regex_t hashLine;
  char filebuf[_POSIX_PATH_MAX+1];
  /*}}}*/

  if (strncmp(lang,"en",2)==0) abbreviations=abbreviations_en;
  else if (strncmp(lang,"C",1)==0) abbreviations=abbreviations_en;
  else if (strncmp(lang,"de",2)==0) abbreviations=abbreviations_de;
  else abbreviations=abbreviations_none;
  /* compile #line number "file" regular expression */ /*{{{*/
  if ((err=regcomp(&hashLine,"^[ \t]*line[ \t]*\\([0-9][0-9]*\\)[ \t]*\"\\([^\"]*\\)\"",0)))
  {
    char buf[256];
    size_t len=regerror(err,&hashLine,buf,sizeof(buf)-1);
    buf[len]='\0';
    fprintf(stderr,OOPS,cmd,buf);
    exit(2);
  }
  /*}}}*/
  voc='\n';
  c=getc(in);
  while ((oc=c)!=EOF)
  {
    c=getc(in);
    if (oc=='\n') ++line;
    if (voc=='\n' && oc=='#') /* process cpp style #line, continue */ /*{{{*/
    {
      char buf[_POSIX_PATH_MAX+20];
      regmatch_t found[3];

      buf[0]=c; buf[1]='\0';
      (void)fgets(buf+1,sizeof(buf)-1,in);
      if (regexec(&hashLine,buf,3,found,0)==0) /* #line */ /*{{{*/
      {
        size_t len;

        line=strtol(buf+found[1].rm_so,(char**)0,10);
        len=found[2].rm_eo-found[2].rm_so;
        if (len>_POSIX_PATH_MAX) len=_POSIX_PATH_MAX;
        strncpy(filebuf,buf+found[2].rm_so,len);
        filebuf[len]='\0';
        file=filebuf;
      }
      /*}}}*/
      c=getc(in);
      continue;
    }
    /*}}}*/
    if (length)
    {
      if (length>=(capacity-1) && (sent=realloc(sent,capacity*=2))==(char*)0)
      {
        fprintf(stderr,NOMEM,cmd,strerror(errno));
        exit(2);
      }
      if (isspace(oc))
      {
        if (!inWhiteSpace)
        {
          sent[length++]=' ';
          inWhiteSpace=1;
        }
      }
      else if ((oc=='.' || oc==':' || oc=='!' || oc=='?') && (c==EOF || isspace(c)) && (oc!='.' || !endingInAbbrev(sent,length)))
      {
        if (inWhiteSpace) --length;
        sent[length++]=oc;
        sent[length]='\0';
        process(sent,length,file,beginLine);
        length=0;
      }
      else
      {
        sent[length++]=oc;
        inWhiteSpace=0;
      }
    }
    else if (isupper(oc))
    {
      inParagraph=0;
      sent[length++]=oc;
      inWhiteSpace=0;
      beginLine=line;
    }
    else if (!inParagraph && oc=='\n' && c==oc)
    {
      process("",0,file,line);
      inParagraph=1;
    }
    voc=oc;
  }
  if (!inParagraph) process("",0,file,line);
  regfree(&hashLine);
}
/*}}}*/
