/* Copyright 1999 Enhanced Software Technologies Inc. 
 * Released for public use under a BSD-style license.
 * See file "LICENSE.ocotillo" for more information.
 *
 * dstring.c -- dynamically allocated string functions. 
 *
 * RCS CHANGE LOG
 * $Log$
 * Revision 1.1  2001/01/06 10:46:00  eric
 * Initial checkin of ocotillo.
 *
 * Revision 1.8  1999/11/18 16:23:36  eric
 * More mods to make it work. Start of the 'rsh' functionality.
 *
 * Revision 1.7  1999/11/09 18:57:29  eric
 * Re-wrote 'trim' routines :-(.
 *
 * Revision 1.6  1999/11/03 23:26:43  eric
 * Fixed some problems with headers and dstring.c :-(.
 *
 * Revision 1.5  1999/09/30 20:32:28  eric
 * Refreshed after Richard foobared the CVS archive
 *
 * Revision 1.4  1999/09/28 19:33:53  eric
 * Add d_lpad and d_rpad routines.
 *
 * Revision 1.3  1999/09/13 16:08:23  eric
 * Added Ocotillo licensing info.
 *
 * Revision 1.2  1999/08/27 23:22:59  eric
 * Misc. changes...
 *
 * Revision 1.1  1999/08/25 22:08:48  eric
 * Initial revision, dynamic string routines for "C".
 *
 * $Date$
 */

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

#include "dstring.h"

/* Returns a line of EXACTLY the length of input. Does have a limit, so we
 * don't exhaust all virtual memory. Does use more CPU time alloc'ing and
 * un-alloc'ing our temporary buffer, but so it goes :-(. 
 */
char *d_getline(FILE *file, int limit) {
  char *result;  /* result of fgets */
  char *retvalue; /* return value, our new string. */
  char *buffer = (char *)malloc(limit+1);
  int newlen;

  if (!buffer) {
    return NULL; /* sorry, could not malloc!  */
  }
  result = fgets(buffer,limit,file);
  if (!result) {  /* We have an error! Return it to our caller */
    free(buffer);
    return NULL; /* Sorry! */
  }
  newlen=strlen(buffer);
  retvalue=(char *)malloc(newlen+1);
  if (!retvalue) {
    free(buffer);
    return NULL; /* sorry, could not malloc ! */
  } 
  result=strcpy(retvalue,buffer);
  free(buffer);
  return result;
}


/********************************************************************
 **** NOTICE ON D_LPAD AND D_RPAD!***
 * These share a great deal of common code. In fact, there is
 * exactly one line different between the two. This SHOULD be re-written
 * to actually call a "d_pad" routine that has "left" and "right" flag
 * e.g. 0=pad left, 1=pad right.  If anybody feels like doing it, go
 * for it (and do an inline function or macro to convert d_lpad to
 * d_pad(src,ch,len,direction).  
 *********************************************************************/

/* add whitespace or other chars to left of a string to pad it out to a
 * given length. 
 */

char *d_lpad(char *src, int ch, int len) {
  char *result;
  char *lpad;   /* leftmost chars to be inserted */
  int curlen;
  int numneeded;
  int i;

  curlen=strlen(src);
  if (curlen >= len) {
    return d_dup(src); /* just duplicate it and return. */
  }

  /* if we get here, it's shorter, and we need to pad some stuff. 
   * if it's 5 chars and we need 10, we would need a string of
   * 10-5 (5) chars, plus allocate an extra for the trailing space. 
   */
  numneeded=len-curlen;
  lpad=(char *) malloc(numneeded+1); 
  if (!lpad) {
    return NULL;  /* Out-of-memory error! */
  }
  
  for (i=0;i<numneeded;i++) {
    lpad[i]=(char)ch;
  }
  lpad[numneeded]=0;
  result=d_cat(lpad,src);
  free(lpad);            /* eliminate a potential memory leak! */
  return result;         /* and done!  */
}

/* add whitespace or other chars to right of a string to pad it out to a
 * given length. 
 */

char *d_rpad(char *src, int ch, int len) {
  char *result;
  char *rpad;   /* leftmost chars to be inserted */
  int curlen;
  int numneeded;
  int i;

  curlen=strlen(src);
  if (curlen >= len) {
    return d_dup(src); /* just duplicate it and return. */
  }

  /* if we get here, it's shorter, and we need to pad some stuff. 
   * if it's 5 chars and we need 10, we would need a string of
   * 10-5 (5) chars, plus allocate an extra for the trailing space. 
   */
  numneeded=len-curlen;
  rpad=(char *)malloc(numneeded+1); 
  if (!rpad) {
    return NULL;  /* Out-of-memory error! */
  }
  
  for (i=0;i<numneeded;i++) {
    rpad[i]=(char)ch;
  }
  rpad[numneeded]=0;
  result=d_cat(src,rpad);
  free(rpad);            /* eliminate a potential memory leak! */
  return result;         /* and done!  */
}

/* Trim all the whitespace off the LEFT of a string, returning trimmed
 * str. 
 */

char *d_ltrim(char *src) {
  char *dest;
  char *walkptr;

  walkptr=src;
  while (*walkptr && isspace(*walkptr)) {
    walkptr++;
  }

  dest=(char *)malloc(strlen(walkptr)+1);
  if (!dest) {
    return NULL; /* sorry, out of memory! */
  }
  strcpy(dest,walkptr); /* copy the string */
  return dest; /* and return! */
}

/* Trim all the white space off the RIGHT of a string, returning trimmed
 * str. Actually returns a pointer to same amount of malloc'ed space,
 * I don't think that's going to be a problem... 
 */
char *d_rtrim(char *src) {
  int i,j,len;
  char *dest;

  len=strlen(src);
  dest=(char *)malloc(len+1);
  if (!dest) return NULL; /* sorry, out of memory! */
  strcpy(dest,src); /* we malloc same amount of space :-( */
  
  if (!*dest) {
    return dest; /* empty string! */
  } 



  i=0;
  j=len;
  while ( (i < j) && isspace(dest[j-1])) {
    j--;
  }
  dest[j]=0;
  return dest;

}  
  
/* trim whitespace from left and right of a string, returning trimmed str 
 * we call ltrim last because it returns a block of memory that's the 
 * actual length of our newly-trimmed string (rtrim doesn't, due to
 * efficiency reasons). 
 */

char *d_trim(char *src) {
  char *ltrim;
  char *rtrim;
  
  if (!*src) {
    return src;
  } else {
    rtrim=d_rtrim(src);
    if (!*rtrim) {  /* if it stripped whole thingy, just return */
      return rtrim;
    }
    ltrim=d_ltrim(rtrim);
    free(rtrim);
    return ltrim;
  }
}

/* Duplicate a string, malloc'ing the buffer with it. */
char *d_dup(char *str) {
  char *buffer;
  char *result;
  int newlen;

  newlen=strlen(str);
  buffer=(char *)malloc(newlen+1);
  if (!buffer) {
    return NULL;
  }
  result=strcpy(buffer,str);
  return result;
}

char *d_cat(char *str1, char *str2) {
  int len1;
  int len2;
  char *buffer;
  char *result;

  len1=strlen(str1);
  len2=strlen(str2);
  buffer=(char *)malloc(len1+len2+1);
  if (!buffer) {
    return NULL; /* Out-of-memory error */
  }

  result=strcpy(buffer,str1); /* do the first copy */
  result=strcat(buffer,str2); /* do the second. */

  return result;  /* and return the result! */
}

/* split line into two pieces. */
struct d_split_array *d_split(char *src, int ch) {
  unsigned char c;
  int i,n;
  int len;

  char *str1;
  char *str2;
  char *strptr;

  struct d_split_array *retvalue;
  
  len=strlen(src);
  c=ch;
  n=-1; /* guardian value. */
  for (i=0;src[i];i++) {
    if (src[i]==c) {
      n=i;
      break;
    }
  }
  if (n==-1) {
    return NULL;  /* sorry, could not split on that character! */
  }

  str1=(char *) malloc(n+1); /* do the first string */
  if (!str1) {
    return NULL; /* could not malloc! */
  }
  
  for (i=0;i<n;i++) {
    str1[i]=src[i];
  }
  str1[n]=0; /* chop off the trailing character! */
  
  str2=(char *) malloc(len-n); /* for the rest... */
  if (!str2) {
    free(str1);
    return NULL;
  }
  strptr=str2;
  for (i=n+1;i<len;i++) {
    *strptr++=src[i];
  }
  *strptr=0; /* and voila! */ 
  /* now to malloc the return array: */
  retvalue = (struct d_split_array *)malloc(sizeof(struct d_split_array));
  if (!retvalue) { /* can't malloc! */
    free(str1);
    free(str2);
    return NULL;
  }
  retvalue->str1=str1;
  retvalue->str2=str2;
  return retvalue;
}

/* Split a string into three pieces... */				  
struct d_split2_array *d_split2(char *src, int ch)
{
  struct d_split_array *d1;
  struct d_split_array *d2;
  struct d_split2_array *result;

  d1=d_split(src,ch);  /* do the first split */
  if (!d1) {
    return NULL; /* sorry! */
  }
  d2=d_split(d1->str2,ch); /* split the second half into two strings */
  if (!d2) {          /* un-leak memory, sigh */
    free(d1->str1);
    free(d1->str2);
    free(d1);
    return NULL;
  }
  /* we now have three strings, now to do something with them: */
  result=(struct d_split2_array *)malloc(sizeof(struct d_split2_array));
  if (!result) { /* arg, could not malloc! */
    free(d1->str1);
    free(d1->str2);
    free(d1);
    free(d2->str1);
    free(d2->str2);
    free(d2);  /* maybe freed up enough memory? */
  }
  /* we malloced, now copy and free up other stuff: */
  result->str1=d1->str1;
  result->str2=d2->str1;
  result->str3=d2->str2;
  free(d1->str2); /* this was a temp string no longer needed */
  free(d1);
  free(d2); 
  return result;
}

#ifdef TEST_FUNCTION

void main(void) {
  char *result;
  char *dup;
  char *trimmed;
  char *dupdup;
  struct d_split_array *ary1;

  result=d_getline(stdin,1024);
  trimmed=d_trim(result);  /* trimm! */
  dup=d_dup(trimmed);
  printf("dup=%s\n",dup);
  dupdup=d_cat(trimmed,dup);
  printf("dupdup=%s\n",dupdup);
  ary1=d_split(result,':');
  printf("str1=%s  str2=%s\n",ary1->str1,ary1->str2);
}

#endif

  
  
