#include <strings.h>
#include <ctype.h>
#include <pwd.h>
#include <sys/types.h>

#ifdef DMALLOC
#include <dmalloc.h>
#endif

#include "messages.h"
#include "tcltk.h"
#include "illist.h"
#include "llist.h"
#include "window.h"
#include "channel.h"
#include "quirc.h"

void strlow(char *str) {

  int i;

  for ( i = 0; i < (signed)strlen(str) ; i++) 
    str[i] = tolower(str[i]);

}


char *strstrnc(char *haystack, const char *needle) {
  char *h1;
  char *n1;
  char *result;
  long rv;

  h1 = strdup(haystack);
  n1 = strdup(needle);
  strlow(h1);
  strlow(n1);

  result = strstr(h1,n1);

  if (result) {
    rv = abs(h1 - result);
    free(h1);
    free(n1);
    return haystack + rv;
  } else {
    free(h1);
    free(n1);
    return NULL;
  }
  return NULL;
}


char *n_range(char *str, int a, int b) {

  char buffer[2048]= {0};
  int i;

  if ( b > (signed)strlen(str) || a > b )
    return strdup("");

  for ( i = a ; i < b ; i++ ) {
    buffer[i - a] = str[i];
  }

  buffer[i] = 0;
  return strdup(buffer);
}

list<string> *complete(list<string> &thelist, string &match, int ccase = 0, int stonly = 0) {

  list<string> *lstst;  // List containing strings from thelist which contain the substring match.
  list<string> *lstok;  // List containing strings from thelist which have match at the start of the string.
  string *strptr;
  string blah;
  char *pos;
  char *temp;
  char *mat;

  mat = strdup(match.c_str());
  if (!ccase) strlow(mat);
  lstok = new list<string>;
  lstst = new list<string>;

  thelist.init_trav();

  while ((strptr=thelist.trav())) {
    temp = strdup(strptr->c_str());
    if (!ccase) strlow(temp); 
    if ((pos=strstr(temp, mat))) {
      pos = (char*) (temp - pos); 
      blah = *strptr;
      if (!pos) { 
	lstst->insert_start(blah);
      } else {
	lstok->insert_start(blah);
      }
    }
    delete temp;
  }
  delete mat;

  if (lstst->getsize() || stonly ) {
    delete lstok;
    return lstst;
  } else {
    delete lstst;
    return lstok;
  }
  return NULL;
}



string expandmatch(list<string> &thelist, const char *thesub, int ccase = 0, int stonly = 0) {

  char *tmpfull = 0;
  char *ptr = 0;
  char *strtmp = 0;
  char *sub = 0;
  char *newsub = 0;
  int lentw, lensub, i;
  list<string> *tmplist;
  string stringtmp;
  string max;
  char *testword = 0;
  char *orig = 0;

  if (thelist.getsize() == 0) { 
    max = thesub;
    return max;
  }

  thelist.init_trav();
  testword = strdup(thelist.trav()->c_str());
  if (thelist.getsize() == 1) {
    max = testword;
  } else {
    orig = strdup(testword);
    sub = strdup(thesub);
    if (!ccase) strlow(sub);
    if (!ccase) strlow(testword);
    
    
    lentw = strlen(testword);
    lensub = strlen(sub);
    max = sub;
    strtmp = new char[lentw+1];
    tmpfull = testword;
    while (*tmpfull) {
      ptr = tmpfull = strstrnc(tmpfull,sub);
      if (tmpfull != testword && tmpfull != NULL) {
	i = 1;
	while((ptr - i + 1) != testword) {
	  strncpy(strtmp,orig+abs(testword-(ptr-i)),i+lensub);
	  //strncpy(strtmp,ptr-i,i+lensub);
	  strtmp[lensub+i]=0;
	  stringtmp = strtmp;
	  tmplist = complete(thelist, stringtmp, ccase, stonly);
	  if (thelist.getsize() == tmplist->getsize()) {
	    max = stringtmp;
	  }
	  delete tmplist;
	  i++;
	}
      }
      if (!tmpfull) break;
      tmpfull++;
    }
    
    newsub = strdup(max.c_str());
    lensub = strlen(newsub);
    tmpfull = testword;
    while (*tmpfull) {
      ptr = tmpfull = strstrnc(tmpfull,newsub);
      if (tmpfull != NULL) {
	i = 1; 
	while(*(ptr+i+lensub-1)) {
	  //strncpy(strtmp,ptr,i+lensub);
	  strncpy(strtmp,orig+abs(testword-(ptr)),i+lensub);
	  strtmp[i+lensub]=0;
	  stringtmp = strtmp;
	  tmplist = complete(thelist, stringtmp, ccase, stonly);
	  if (thelist.getsize() == tmplist->getsize()) {
	    max = stringtmp;
	  }
	  delete tmplist;
	  i++;
	}
      }
      if (!tmpfull) break;
      tmpfull++;
    }
  }
  if (sub) free (sub);
  if (strtmp) delete strtmp;
  if (newsub) free(newsub);
  if (testword) delete testword;
  if (orig) delete orig;
  return max;
}

int TT_Proc_complete(TT_PROC_OBJS) {
  list<string> somelist;
  string somestring;
  string *strptr;
  list<string> *newlst;
  Tcl_Obj *listptr;
  Tcl_Obj *objptr;
  int n;
  int lsize;
  int ccase;
  int stonly;
  string matchstr;
  Tcl_Obj **elems;

  if(objc!=5) {
  Tcl_SetResult(interp,"Usage: complete <list> <matchstring> <case sensitive> <startonly>",TCL_STATIC);
    return TCL_ERROR;
  }
  if (Tcl_ListObjGetElements(interp,objv[1],&lsize, &elems)!= TCL_OK) return TCL_ERROR;
  for (n=0;n<lsize;n++) {
    somestring = Tcl_GetStringFromObj(elems[n],NULL);
    //printf("%s\n", somestring.c_str());
    somelist.insert_start(somestring);
  }
  if (Tcl_GetIntFromObj(interp, objv[3], &ccase) != TCL_OK) return TCL_ERROR;
  if (Tcl_GetIntFromObj(interp, objv[4], &stonly) != TCL_OK) return TCL_ERROR;
  matchstr = Tcl_GetStringFromObj(objv[2],NULL);
  //printf("%s\n", matchstr.c_str());
  //printf("%d\n", ccase);
  //printf("%d\n", stonly);

  newlst = complete(somelist,matchstr,ccase,stonly); 
  
  newlst->init_trav();
  listptr=Tcl_NewListObj(0,0);
  while((strptr=newlst->trav())) {
    objptr=Tcl_NewStringObj((char *) strptr->c_str(),strlen(strptr->c_str()));
    if(Tcl_ListObjAppendElement(interp,listptr,objptr)==TCL_ERROR) return TCL_ERROR;
  }
  Tcl_SetObjResult(interp,listptr);

  delete newlst;
  return TCL_OK;
  
}

int TT_Proc_expandmatch(TT_PROC_OBJS) {
  list<string> somelist;
  string somestring;
  string result;
  Tcl_Obj *objptr;
  int n;
  int lsize;
  int ccase;
  int stonly;
  string matchstr;
  Tcl_Obj **elems;

  if(objc!=5) {
  Tcl_SetResult(interp,"Usage: expandmatch <list> <matchstring> <case sensitive> <startonly>",TCL_STATIC);
    return TCL_ERROR;
  }
  if (Tcl_ListObjGetElements(interp,objv[1],&lsize, &elems)!= TCL_OK) return TCL_ERROR;
  for (n=0;n<lsize;n++) {
    somestring = Tcl_GetStringFromObj(elems[n],NULL);
    //printf("%s\n", somestring.c_str());
    somelist.insert_start(somestring);
  }
  if (Tcl_GetIntFromObj(interp, objv[3], &ccase) != TCL_OK) return TCL_ERROR;
  if (Tcl_GetIntFromObj(interp, objv[4], &stonly) != TCL_OK) return TCL_ERROR;
  matchstr = Tcl_GetStringFromObj(objv[2],NULL);
  //printf("%s\n", matchstr.c_str());
  //printf("%d\n", ccase);
  //printf("%d\n", stonly);

  result = expandmatch(somelist,matchstr.c_str(),ccase,stonly);   

  objptr=Tcl_NewStringObj((char *) result.c_str(),strlen(result.c_str()));
  Tcl_SetObjResult(interp,objptr);

  return TCL_OK;
  
}

int TT_Proc_n_complete(TT_PROC_ARGS) {
  char *origtext=0;
  int origpos;
  int rpos;
  char *befortext=0;
  char *aftertext=0;
  char *matchtext=0;
  char *retrntext=0;
  static char *oldretrntext;
  static int oldpos;
  static int cycnum;
  static char *oldorigtext;
  static int oldorigpos;
  int cycle =0;
  int n,i,j= 0 ,len;
  list<string> thelist;
  list<string> *lstptr;
  string str;
  string rval;
  string tmpstr;
  string *strptr=0;
  twindow window;
  twindow *windowp;
  tchan channel;
  tchan *channelp;  
  tnick *nickp;
  char colon[1000] = {0};

  if (!strcmp(currentwindow,".main")) { 
    if (origtext) free(origtext);
    if (aftertext) free(aftertext);
    if (matchtext) free(matchtext);
    if (befortext) free(befortext);
    return TCL_OK;
  }
  
  if (TT_IntF(TT_ARGS,"dcc_complete")) return TCL_OK;

  // Obtain the text and cursor position from the Tk widget
  // in which the completion key was pressed.
  origtext = strdup(TT_StrF(TT_ARGS,"%s.entry get",currentwindow));
  origpos  = (TT_IntF(TT_ARGS,"%s.entry index insert",currentwindow));


  if (oldretrntext && !strcmp(origtext,oldretrntext) && oldpos == origpos) {
    if (origtext) free(origtext);
    origtext = strdup(oldorigtext);
    origpos = oldorigpos;
    cycle = 1;
  } else {
    oldorigpos = origpos;
    if (oldorigtext) free(oldorigtext);
    oldorigtext = strdup(origtext);
  }

  n = origpos;
  i = origpos;
  len = strlen(origtext);

  while ( i < len && origtext[i] != ' ') i++;

  if ( n > 0 ) {
    j = n;
    while ( j >= 0 && origtext[j-1] != ' ' && j > 0 ) j--;
  } else j = 0;
  
  befortext = n_range(origtext,0,j);
  matchtext = n_range(origtext,j,i);
  aftertext = n_range(origtext,i,len);
  
  str = matchtext;
  strcpy(window.pathname,currentwindow);
  windowp = windows.find(window);
  
  if (!strncmp(matchtext,"#",1)) {
    if ( !strncmp(currentwindow,".channel",8) && strlen(matchtext) == 1) {
      tmpstr = windowp->name;
      thelist.insert_start(tmpstr);
    } else {
      windowp->server->chanlist.init_trav();
      while ( (channelp = windowp->server->chanlist.trav())) {
	tmpstr = channelp->name;
	thelist.insert_start(tmpstr);
      }
    }
    lstptr = complete(thelist, str, 0,1);
    if (lstptr->getsize()) {
      rval = expandmatch(*lstptr, str.c_str(),0,1);
    }
    if (lstptr->getsize() == 1) {
      strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_postfix"));
    }
  } else if ( !strncmp(currentwindow,".channel",8)) {
    
    strcpy(channel.pathname,currentwindow);
    if ((channelp = windowp->server->chanlist.find(channel)) == NULL) {
      if (origtext) free(origtext);
      if (aftertext) free(aftertext);
      if (matchtext) free(matchtext);
      if (befortext) free(befortext);
      return TCL_OK;
    }
    
    
    channelp->nicklist.init_trav();
    while ( (nickp=channelp->nicklist.trav()) ) {
      tmpstr = nickp->getname();
      thelist.insert_start(tmpstr);
    }
    lstptr = complete(thelist, str, 0,0);

        lstptr->init_trav();
  
	// while (strptr=lstptr->trav()) {
	//printf("%s\n", strptr->c_str());
	//}

    if (lstptr->getsize()) {
      rval = expandmatch(*lstptr, str.c_str(),0,0);

      if (lstptr->getsize() == 1) {
	if ( j <=0 ) {
	  strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_seperator"));
	} else {
	  strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_postfix"));
	}
      }
    }
  } else if ( !strncmp(currentwindow,".status",7)) {
    tmpstr = windowp->server->getmynick();
    thelist.insert_start(tmpstr);
    if (str == "") str = tmpstr;
    lstptr = complete(thelist, str, 0,0);
    if (lstptr->getsize()) {
      rval = expandmatch(*lstptr, str.c_str(),0,0);
    }
    if (lstptr->getsize() == 1) {
      strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_postfix"));
    }
  } else if ( !strncmp(currentwindow,".chat",5)) {
    tmpstr = windowp->name + 1;
    if (str == "") str = tmpstr;
    thelist.insert_start(tmpstr);
    tmpstr = windowp->server->getmynick();
    thelist.insert_start(tmpstr);
    lstptr = complete(thelist, str, 0,0);
    if (lstptr->getsize()) {
      rval = expandmatch(*lstptr, str.c_str(),0,0);
    }
    if ( j <=0 && lstptr->getsize() == 1) {
      strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_seperator"));
    } else {
      strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_postfix"));
    }
  } else if ( !strncmp(currentwindow,".query",6)) {
    tmpstr = windowp->name;
    if (str == "") str = tmpstr;
    thelist.insert_start(tmpstr);
    tmpstr = windowp->server->getmynick();
    if (tmpstr != str) thelist.insert_start(tmpstr);
    lstptr = complete(thelist, str, 0,0);
    if (lstptr->getsize()) {
      rval = expandmatch(*lstptr, str.c_str(),0,0);
    }
    if (lstptr->getsize() == 1) {
      if (j <=0 ) {
	strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_seperator"));
      } else {
	strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_postfix"));
      }
    }
  } else {
    fprintf(stderr,"Invalid window type encountered in completion code.\n");
    exit(1);
  }
  
  rpos = strlen(rval.c_str()) + j + strlen(colon);
  
  if (rval == "") { 
    rval = matchtext;
    rpos = origpos;
  }
  retrntext = (char *) malloc (strlen(befortext) + strlen(rval.c_str()) + strlen(aftertext) + strlen(colon) + 10);
  rpos = strlen(rval.c_str()) + j + strlen(colon);
  
  if (cycle && lstptr->getsize() > 0 && TT_Int(TT_ARGS,"set ::dynamic::nick_complete_cycle") == 1) {
    if (cycnum >= lstptr->getsize()) cycnum = 0;
    lstptr->init_trav();
    for (int i = 0; i <= cycnum; i++) {
      strptr = lstptr->trav();
    }
    if(cycnum<0) {
      fprintf(stderr,"cycnum is less than 0 in completion code.\n");
      exit(1);
    }
    rval = strptr->c_str();
    cycnum++;
    if ( j <=0 && strncmp(rval.c_str(),"#",1)) strcpy(colon,TT_Str(TT_ARGS,"set ::dynamic::nick_complete_seperator"));
    if (retrntext) free(retrntext);
    retrntext = (char *) malloc (strlen(befortext) + strlen(rval.c_str()) + strlen(aftertext) + strlen(colon) + 10);
      rpos = strlen(rval.c_str()) + j + strlen(colon);
  }

  strcpy(retrntext,befortext);
  strcat(retrntext,rval.c_str());
  strcat(retrntext,colon);
  strcat(retrntext,aftertext);

  if (oldretrntext) free(oldretrntext);
  oldretrntext = strdup(retrntext);
  oldpos = rpos;

  TT_EvalF(TT_ARGS,"%s.entry delete 0 end",currentwindow);
  TT_EvalF(TT_ARGS,"%s.entry insert 0 %q",currentwindow, retrntext);
  TT_EvalF(TT_ARGS,"%s.entry icursor %d",currentwindow, rpos);

  if (lstptr) free(lstptr);
  if (aftertext) free(aftertext);
  if (origtext) free(origtext);
  if (matchtext) free(matchtext);
  if (befortext) free(befortext);
  if (retrntext) free(retrntext);


  return TCL_OK;
}

int TT_Proc_getpwents(TT_PROC_OBJS) {
  char *field;
  Tcl_Obj *listptr;
  Tcl_Obj *objptr;
  string *strptr;
  string temp;
  list<string> *newlst;
  struct passwd *pwents;

  if(objc!=2) {
    Tcl_SetResult(interp,"Usage: getpwents <field>",TCL_STATIC);
    return TCL_ERROR;
  }
  if (!(field = strdup(Tcl_GetStringFromObj(objv[1],NULL)))) return TCL_ERROR;
  
  if (strcmp(field, "pw_name") && strcmp(field, "pw_passwd") && strcmp(field, "pw_uid") && strcmp(field, "pw_gid") && strcmp(field, "pw_gecos") && strcmp(field, "pw_dir") && strcmp(field, "pw_shell")) {
    Tcl_SetResult(interp,"Error: field must be one of: pw_name, pw_passwd, pw_uid, pw_gid, pw_dir, pw_shell, pw_gecos",TCL_STATIC);
    return TCL_ERROR;
  }
  newlst = new list<string>;
  setpwent();
  while ((pwents = getpwent())) {
    temp = pwents->pw_name;
    newlst->insert_start(temp);
  }
  endpwent();

  newlst->init_trav();
  listptr=Tcl_NewListObj(0,0);
  while((strptr=newlst->trav())) {
    objptr=Tcl_NewStringObj((char *) strptr->c_str(),strlen(strptr->c_str()));
    if(Tcl_ListObjAppendElement(interp,listptr,objptr)==TCL_ERROR) return TCL_ERROR;
  }
  Tcl_SetObjResult(interp,listptr);
  
  delete newlst;
  return TCL_OK;

}


syntax highlighted by Code2HTML, v. 0.9.1