/*


 Copyright (C) 1990 Texas Instruments Incorporated.

 Permission is granted to any individual or institution to use, copy, modify,
 and distribute this software, provided that this complete copyright and
 permission notice is maintained, intact, in all copies and supporting
 documentation.

 Texas Instruments Incorporated provides this software "as is" without
 express or implied warranty.


 *  
 * Utility functions used by defmacros.
 *
 * Edit history
 * Created: LGO 30-Mar-89 -- Initial design and implementation.
 *
 */

#include "defmacio.h"

/*
 * Put a string in the input buffer used by getchar
 * This lets us use scan_next for arbitrary strings.
 */
void set_inbuffer(instring)
    char* instring;
{
  static FILEINFO inbuf = {0, 0, 0, 0, 0, 0, 0, 0};
  inbuf.bptr = inbuf.buffer = instring;
  MacInFile = &inbuf;
}

/*
// Skip whitespace characters and comments, returning the next character
*/
char
skip_blanks()
{
  register char c;
 again:
  while ((c = getchar()) != EOF && isspace(c));
  if (c == '/') {
    char nc = getchar();
    if (nc == '/') {		/* If // comment, skip to end of line */
      while ((c = getchar()) != EOF && c != '\n');
      goto again;
    } else if (nc == '*') {	/* If / * comment, skip to */
      char p = getchar();
      while ((c = getchar()) != EOF) {
	if (c == '/' && p == '*') break;
	p = c;
      }
      goto again;
    } else			/* Else putback character after / */
      unget();
  } else if (c == '\\') {
    char nc = getchar();
    if (nc == '\n') goto again;		  /* If \\\n, ignore */
    unget();
  }
  return(c);
}

/*
 * Skip to next newline
 */
char
skip_line()
{
  char c;
  while ((c = getchar()) != EOF)
    if (c == '\n') break;
  return(c);
}

/*
 * Copy the next alphanumeric token
 * the argument, str is a pointer to a string where the token is to be copied.
 * Returns a pointer to the END of the token, which contains the NEXT character.
 */
char*
copytoken(str)
  char* str;
{
  int is_bad = TRUE;
  char c = skip_blanks();
  while (isalnum(c) ||
	 (c == '$') ||
	 (c == '_')) {
    *str++ = c;
    c = getchar();
    is_bad = FALSE;
  }
  if(is_bad) {
    if (c == EOF)
      fprintf(stderr, "End of file while scanning for token\n");
    else
      fprintf(stderr, "Illegal character in token: '%c'\n", c);
    return NULL;
  }
  unget();
  *str = EOS;
  return(str);
}

STRING work_string = NULL;

static char*
scan_next_string(delim)
  char delim; 			        /* Current delimiter */
{
  char c; 			        /* Current character */
  char p;				/* Previous character */
  char* workp = work_string->buffp;	/* result buffer */
  char* workend = work_string->buffend; /* end of result buffer */

  for (p = EOS; (c = getchar()) != EOF; p = c) {
    if (workp >= workend-1) {
      work_string->buffp = workp;
      grow_STRING(work_string, 0);
      workp = work_string->buffp;
      workend = work_string->buffend;
    }
    *workp++ = c;
    if (delim == c &&			/* Quit when delimeter found */
	p != '\\' &&			/* and it's not quoted */
	(delim != '/' || p == '*')) { 	/* Special test for end of comment */
      *workp = EOS;
      work_string->buffp = workp;
      return workp;
    }
  }
  /* end of file when we get here */
  *workp = EOS;
  work_string->buffp = workp;
  return workp;
}


char*
scan_next_internal(delim, is_top_level)
  char delim; 			        /* Current delimiter */
  int is_top_level;			/* flag */
{
  char newdelim;			/* New delimiter to look for */
  int is_string = FALSE;		/* New is_string flag */
  register char c;			/* Current character */
  register char* workp = work_string->buffp;	 /* result buffer */
  register char* workend = work_string->buffend; /* end of result buffer */

  while ((c = getchar()) != EOF) {
    if (workp >= workend-1) {
      work_string->buffp = workp;
      grow_STRING(work_string, 0);
      workp = work_string->buffp;
      workend = work_string->buffend;
    }
    *workp++ = c;
    if (delim == c) {			/* Quit when delimeter found */
      *workp = EOS;
      work_string->buffp = workp;
      return workp;
    }
    switch (c) {	       /* Look for new delimeters when not is_string */
    case '{':  newdelim = '}'; break;
    case '[':  newdelim = ']'; break;
    case '(':  newdelim = ')'; break;
    case '<':
      if (delim != '>')
	continue;
      newdelim = '>'; break;
    case '\'':  newdelim = '\''; is_string = TRUE; break;
    case '\"':  newdelim = '\"'; is_string = TRUE; break;
    case '/':				  /* start of comment? */
      c = getchar();
      unget();
      if (c == '/') {			  /* standard comment */
	newdelim = '\n';
	is_string = TRUE;
	break;
      } else if (c == '*') {		  /* c++ comment */
	newdelim = '/';
	is_string = TRUE;
	break;
      }
      continue;
    default:				  /* comma grabs args */
      continue;
    }					  /* end switch */
    if (is_top_level && newdelim == delim) {
      is_top_level = FALSE;
      continue;		/* at top_level, with left side of delimiter */
    }
    work_string->buffp = workp;
    if (is_string)
      workp = scan_next_string(newdelim);
    else
      workp = scan_next_internal(newdelim, FALSE);
    workend = work_string->buffend;
    is_string = FALSE;
  } /* end while */
  /* end of file when we get here */
  *workp = EOS;
  work_string->buffp = workp;
  return workp;
} 

void scan_start() {
  if(work_string == NULL)		  /* Setup buffer */
    work_string = make_STRING(4096);
  work_string->buffp = work_string->buff;
}

/*
 * Get the next token, stopping at delim
 */
char* scan_next(delim)
  char delim;				  /* delimiter */
{
  scan_start();
  skip_blanks();			/* skip leading whitespace */
  unget();
  if (delim == ' ') {			  /* ' ' means grab token */
    work_string->buffp = copytoken(work_string->buffp);
    if ((delim = getchar()) != '<') {	  /* Grab <...> */
      unget();
    } else {
      *work_string->buffp++ = delim;
      scan_next_internal('>', FALSE);
    }
  } else {
    scan_next_internal(delim, TRUE);
  }
  work_string->buffp++;
  return work_string->buff;
}
/*
 * Get the next token, stopping at delim
 * Call scan_start before using this function
 * The string returned is inside work_string, and
 * will be clobbered on the next call to scan_start.
 */
char* scan_list(delim)
  char delim;				  /* delimiter */
{
  unsigned int start = work_string->buffp - work_string->buff;
  skip_blanks();			/* skip leading whitespace */
  unget();
  if (delim == ' ') {			  /* ' ' means grab token */
    if (work_string->buffend - work_string->buffp <= 128)
      grow_STRING(work_string, 128);	  /* Ensure at least 128 chars */
    work_string->buffp = copytoken(work_string->buffp);
  }
  else
    scan_next_internal(delim, TRUE);
  work_string->buffp++;
  return (work_string->buff+start);
}

char*
scan_token(delims)
  char* delims;				  /* delimiters */
{
  register char* workp;
  char* work;
  register char c;
  if(work_string == NULL)		  /* Setup buffer */
    work_string = make_STRING(3000);
  workp = work_string->buff;
  c = skip_blanks();			/* skip leading whitespace */
  while(c != EOF) {
    register char* p;
    for(p=delims; *p!=EOS; p++)
      if (*p == c) goto done;
    *workp++ = c;
    if (ispunct(c))			/* If {([<'" Use scan_next_internal */
      for(p="{}()[]<>\'\'\"\""; *p!=EOS; p++)
	if (*p++ == c) {
	  work_string->buffp = workp;
	  if (*p == '\'' || *p == '\"')
	    workp = scan_next_string(*p);
	  else
	    workp = scan_next_internal(*p, FALSE);
	  break;
	}
    c = getchar();
  }
 done:
  unget();
  work = work_string->buff;
  while(workp>work && isspace(*(workp-1))) workp--;
  *workp = EOS;				  /* trim trailing blanks */
  return savestring(work);
}

void
put_string(string)
  char* string;
{
  char c;
  putchar('\"');
  while ((c=*string++) != EOS)
    switch (c) {
      case '\"': putchar('\\');
      default:  putchar(c);
    }
  putchar('\"');
}

/* c_upcase -- Convert all alphabetical characters to uppercase
 * Input:      Character string 
 * Output:     Updated string
 */
char*
c_upcase (s)
  char* s;
{
  char* p = s;			/* Point to beginning of string */
  while (*p != EOS) {		/* While there are still valid characters */
    if (islower (*p))		/* if this is lower case */
      *p = toupper (*p);	/* convert to uppercase */
    p++;			/* Advance pointer */
  }
  return s;			/* Return reference to modified string */
}


/* c_downcase -- Convert all alphabetical characters to lowercase
 * Input:        Character string
 * Output:       Updated string
 */

char*
c_downcase (s)
  char* s;
{
  char* p = s;			/* Point to beginning of string */
  while (*p != EOS) {		/* While there are still valid characters */
    if (isupper (*p))		/* if this is upper case */
      *p = tolower (*p);	/* convert to lowercase  */
    p++;			/* Advance pointer */
  }
  return s;			/* Return reference to modified string */
}

/* c_capitalize -- Capitalize all words in a string. A word is defined as
 *                 a sequence of characters separated by white space
 * Input:          Character string
 * Output:         Updated string
 */

char*
c_capitalize (s)
  char* s;
{
  char* p = s;				/* Point to beginning of string */
  while (TRUE) {			/* Infinite loop */
    for ( ; *p && isspace(*p); p++);	/* Skip white space */
    if (*p == EOS) 		        /* If end of string */
      return s;				/* Return string */
    if (islower (*p))			/* If lower case */
      *p = toupper (*p);		/* Convert character */
    for ( ; *p && !isspace (*p); p++);	/* Search for white space */
  }
}

/*
 * c_trim_all -- Removes any occurrence of the character(s) in "c" from "s"
 * Input:    Source string and token string
 * Output:   Modified string "s"
 */
char* c_trim_all (s, c)
  char* s;
  char* c;
{
  char* t0;
  int old_length = strlen (s);		  /* Old string "s" length */
  int new_length = 0;			  /* Counts characters in result */
  char* p = getmem(old_length+1);	  /* Temporary holding array */
  int i;
  for (i = 0; i < old_length; i++) {  /* For each character in string */
    for (t0 = c; *t0 && s[i] != *t0; t0++); /* Scan for match */
    if (*t0 == EOS)			  /* If no match found */
      p[new_length++] = s[i];		  /* Copy character */
  }
  p[new_length] = EOS;			  /* NULL terminate string */
  strcpy (s, p);			  /* Copy new string */
  free(p);				  /* Deallocate temporary memory */
  return s;				  /* Return pointer to new string */
}

/*
 * Primitive dynamic string support
 */

STRING make_STRING(size)
  int size;
{ STRING result = (STRING) getmem(sizeof(struct buf_string));
  char* buff = getmem(size);
  *buff = EOS;
  result->buff = buff;
  result->buffp = buff;
  result->buffend = buff+size;
  return result;
}

void destroy_STRING(string)
  STRING string;
{
  free(string->buff);
  free(string);
}

void grow_STRING(string, len)
  STRING string;
  unsigned int len;
{
  extern char* realloc(/* char*, int */);
  char* old = string->buff;
  unsigned int size = len + ((string->buffend - old) * 2);
  char* new;
  new = realloc(old, size);
  if (new == NULL) cfatal("Heap exausted while growing string", "");
  string->buffp = new + (string->buffp - old);
  string->buff = new;
  string->buffend = new + size;
}

void append_STRING(string, chars)
  STRING string;
  char* chars;
{  unsigned int len = strlen(chars);
   if (string->buffend - string->buffp <= len)
     grow_STRING(string, len);
   strcpy(string->buffp, chars);
   string->buffp += len;
}

/*
// Copy whitespace and comments into a string, returning the next character
*/
char
append_blanks(string)
  STRING string;
{
  register char c;
 again:
  while ((c = getchar()) != EOF && isspace(c))
    append_char(string, c);
  if (c == '/') {
    char nc = getchar();
    if (nc == '/') {		/* If // comment, skip to end of line */
      append_STRING(string, "//");
      while ((c = getchar()) != EOF) {
	append_char(string, c);
	if (c == '\n') break;
      }
      goto again;
    } else if (nc == '*') {	/* If / * comment, skip to */
      char p = getchar();
      append_STRING(string, "/*");
      append_char(string, p);
      while ((c = getchar()) != EOF) {
	append_char(string, c);
	if (c == '/' && p == '*') break;
	p = c;
      }
      goto again;
    } else			/* Else putback character after / */
      unget();
  } else if (c == '\\') {
    char nc = getchar();
    if (nc == '\n') goto again;		  /* If \\\n, ignore */
    unget();
  }
  *(string->buffp) = EOS;
  return(c);
}
