#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include "myc_err.h"
#include "myc_str.h"
#include "myc_mem.h"

char *str_splitx(char *str, char sep, char *list[], int *n) {
  int i;
  char *s, *e;

  s=str;
  for (i=0; i<*n; i++) {
    list[i]=s;
    e=strchr(s, sep);
    if (e==NULL) { *n=i+1; return(NULL); }
    s=e+1;
    e--;
    while (e>str && *e==' ') e--; /* trim sequence */
    e[1]='\0';
  }
  return(s);
}

char *str_split1(char *str, char sep) {
  char *s, *e;

  e=strchr(str, sep);
  if (e==NULL) {
    s=NULL;
    e=str+strlen(str);
  } else {
    s=e+1;
  }
  e--;
  while (e>str && *e==' ') e--; /* trim sequence */
  e[1]='\0';
  return(s);
}

int str_ntrim(char *dest, const char *src, int ldest, int lsrc) {
  int i;

  if (lsrc>=ldest) lsrc=ldest-1;
  if (dest!=src) strncpy(dest, src, lsrc);
  dest[lsrc]='\0';
  i=strlen(dest)-1;
  while (i>=0 && dest[i]==' ') i--; /* trim sequence */
  i++;
  dest[i]='\0';
  return(i);
}

int str_npad(char *dest, const char *src, int ldest) {
  int i, lsrc;

  lsrc=strlen(src);
  if (lsrc>=ldest) {
    if (dest!=src) strncpy(dest, src, ldest);
    lsrc=ldest;
  } else {
    if (dest!=src) strcpy(dest, src);
    for (i=lsrc; i<ldest; i++) {
      dest[i]=' ';
    }
  }
  return(lsrc);
}

char *str_nsplit(char *dst, const char *src, char sep, int dstlen) {
  char *s;
  int i;

  s=strchr(src, sep);
  if (s==NULL) {
    s=NULL;
    i=strlen(src);
  } else {
    i=s-src;
    s++; /* skip separator */
  }
  if (i>=dstlen) {
    str_copy(dst, src);
  } else {
    strncpy(dst, src, i);
    dst[i]='\0';
  }
  return(s);
}

char *str_read_until(FILE *fil, char *term, char *buf, char *end) {
  char fmt[24];
  int i, l, siz;
  char ch;

  siz=end-buf-1;
  if (siz<1) return(NULL);
  sprintf(fmt, "%s%d[^%s%s", "%", siz, term, "]%n%c");
  i=fscanf(fil, fmt, buf, &l, &ch);
  if (i<0) { /* eof */
    buf[0]='\0';
    return(&buf[0]);
  } else if (i==0) { /* fscanf returns 0 if first char is terminator */
    buf[0]=fgetc(fil);
    return(&buf[0]);
  } else if (i==1) { /* terminator not found -> read until eof */
    buf[l]='\0';
    return(&buf[l]);
  } else {
    buf[l]=ch;
    if (l==siz && NULL==strchr(term, ch)) return(NULL);
    return(&buf[l]);
  }
}

char *str_read_file(char *file) {
  FILE *fil;
  char *str, *s, *e, *p, *q;
  int i, size;
  struct stat statbuf;
  
  i=stat(file, &statbuf);
  if (i<0) ERR_MSG("file not found");
  size=statbuf.st_size+4;
  ERR_SP(str=MALLOC(size));
  e=&str[size-1];
  ERR_SP(fil=fopen(file, "r"));
  s=str;
  while (1) {
    p=str_read_until(fil, "!", s, e);
    if (p==NULL) break;
    if (*p=='!') {
      q=str_read_until(fil, "\n", p, e);
      if (q==NULL) { p=NULL; break; }
      s=p; *s='\n'; s++;
    } else {
      assert(*p=='\0');
      break;
    }
  }
  ERR_SI(fclose(fil));
  assert(strlen(str)<size);
  return(str);
  OnError: return(NULL);
}

void str_replace_char(char *str, char ch, char rep) {
  char *s;

  assert(ch!='\0' && ch!=rep);
  s=strchr(str, ch);
  while (s!=NULL) {
    *s=rep;
    s=strchr(s, ch);
  }
}

int str_nsubstitute(char *result, char *str, char *old, char *new, int reslen) {
  char *s, *p, *r;
  int l, ln, lo;

  p=str;
  r=result;
  ln=strlen(new);
  lo=strlen(old);
  s=strstr(p, old);
  reslen--;
  if (reslen<0) ERR_MSG("result buffer too short");
  while (s!=NULL) {
    l=s-p;
    if (l>reslen) ERR_MSG("result buffer too short");
    strncpy(r, p, l);
    r+=l; reslen-=l;
    if (ln>reslen) ERR_MSG("result buffer too short");
    strncpy(r, new, reslen);
    r+=ln; reslen-=ln;
    p=s+lo;
    s=strstr(p, old);
  }
  l=strlen(p);
  if (l>reslen) ERR_MSG("result buffer too short");
  strncpy(r, p, l);
  r+=l;
  *r='\0';
  return(r-result);
  OnError:
    result[0]='\0';
    return(-1);
}

void str_nupcase(char *dst, const char *src, int dstlen) {
  dstlen--; /* space for trailing nul */
  while (*src!='\0' && dstlen>0) {
    *dst=toupper((int)*src);
    dst++; src++;
    dstlen--;
  }
  *dst='\0';
}

void str_nlowcase(char *dst, const char *src, int dstlen) {
  dstlen--; /* space for trailing nul */
  while (*src!='\0' && dstlen>0) {
    *dst=tolower((int)*src);
    dst++; src++;
    dstlen--;
  }
  *dst='\0';
}

#ifndef __GNUC__
int strcasecmp(const char *str1, const char *str2) {
  int i;
  char ch1, ch2;
  ch1=tolower(*(str1++)); ch2=tolower(*(str2++));
  i=1;
  while (ch1!='\0' && ch2!='\0' && ch1==ch2) {
    ch1=tolower(*(str1++)); ch2=tolower(*(str2++)); i++;
  }
  if (ch1<ch2) {
    return(-i);
  } else if (ch1>ch2) {
    return(i);
  }
  return(0);
}
#endif

int str_ncpy(char *dst, const char *src, int maxdest) {
  strncpy(dst, src, maxdest);
  if (dst[maxdest-1]!='\0') {
    dst[maxdest-1]='\0';
    ERR_MSG("destination string too short");
  }
  return(0);
  OnError: return(-1);
}

int str_ncat(char *dst, const char *src, int maxdest) {
  dst[maxdest-1]='\0';
  strncat(dst, src, maxdest-strlen(dst)-1);
  if (dst[maxdest-1]!='\0') {
    dst[maxdest-1]='\0';
    ERR_MSG("destination string too short");
  }
  return(0);
  OnError: return(-1);
}
