/* mstring.c */

/* The purpose of this file is to provide subroutines for handling
   strings whose space is allocated with malloc - in this way we remove
   all limitations on length of strings */

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

#include "simple.h"
#include "make.h"
#include "functions.h"

#define tfree(x) if (x) free(x),x=NULL
#define LLEN 256		/* Max file line length */
#define MLEN 10*LLEN		/* Max multi-line length */

/************************************************************/
/* getline
 *
 *	Get a line from stream.
 *	Remove comments.
 *	See if there's a continuation mark ('\' or '-').
 *	Get rid of trailing spaces.
 *	Repeat if there was a continuation mark.
 *
 */
char *getline(FILE *stream)
{
  char *ptr, *comment;
  static char output[MLEN];
  char buf[LLEN];
  int continued_line = TRUE;

  if(feof(stream))
    return(NULL);
  output[0] = '\0';		/* The final result will be here. */
  while(continued_line) {
    buf[0] = '\0';		/* Each physical line goes here. */
    fgets(buf,LLEN,stream);
    if(strlen(buf) == 0)
      break;			/* All done. */
    /* Truncate at the leftmost comment character. */
    if(comment = strchr(buf,'#')) /* Either '#' or '!' or both. */
      *comment = '\0';
    if(comment = strchr(buf,'!'))
      *comment = '\0';
    /* See if it's a continuation line. */
    ptr = lastnonspace(buf);
    if(*ptr == '\\' || *ptr == '-') {
      *ptr = ' ';		/* Change to space to trim space off end */
      continued_line = TRUE;
    }
    else {
      continued_line = FALSE;
    }

    /* Re-evaluate the last non-space, if we changed the line. */
    if(continued_line)
      ptr = lastnonspace(buf);

    switch(*ptr) {
    case '\0':
      break;			/* Don't gotta do nuthin' */
    case ' ':
    case '\t':
    case '\n':
      *ptr = '\0';
      break;
    default:
      *(ptr+1) = '\0';		/* Everything after is whitespace */
      break;
    }

    if((buf[0] == '\0') && !continued_line)
      break;			/* All done. Return what we have already. */

    /* Put the continuation character back in to be replaced at excretion */
    if(continued_line)
      strcat(buf,"\\");

    /* Move the current line to the output buffer */
    if(strlen(output)+strlen(buf) < MLEN)
      strcat(output,buf);
    else
      error("Continued line too long");
  } 
  return(strperm(output));
}
/************************************************************/
/* lastnonspace
 *
 *	Returns ptr to the last character on a line that isn't whitespace.
 */
char *lastnonspace(char *line)
{
  char *ptr;
  int len;
  
  if((len = strlen(line)) == 0) return(line);
  ptr = line + strlen(line) - 1; /* Point to last character */
  while(isspace(*ptr)) {
    if(ptr == line) break;
    ptr--;
  }
  return(ptr);
}
/************************************************************/
char *mstrcat(char *p, char *q)
{
  char *r = talloc (strlen(p) + strlen(q) + 1);
  strcpy(r,p);
  strcat(r,q);
  return r;
}
/************************************************************/
/* allocate space for s, return new pointer */
char *strperm(char *s)
{
  char *t = talloc(strlen(s)+1);
  if (s==NULL) error ("strperm - NULL pointer");
  strcpy(t,s);
  return t;
}
/************************************************************/
char passpace(char **p)
{
  if (*p==NULL) error("NULL arg to passpace");
  while (isspace (**p)) (*p)++;
  return(**p);
}
/************************************************************/
char passnonsp(char **p)
{
  if (p==NULL  ||  *p==NULL) error("NULL arg to passnonsp");
  while (**p && !isspace(**p)) (*p)++;
  return **p;
}
/************************************************************/
void error(char *errmsg)
{
  extern char *cmd_fname;

  fputs("%MAKE, ", stderr);
  fputs(errmsg,stderr);
  if(cmd_fname)delete(cmd_fname);
  exit(2);
}
/************************************************************/
char *talloc(int i)
{
  char *p;
  char *malloc();
  p = malloc(i);
  if (p==NULL) error ("no more memory");
  return p;
}
/************************************************************/
char *findnonwhite(char *string)
{
  int pos = strspn(string,WHITESPACE);
  if(pos == strlen(string))
    return(NULL);
  else
    return(string+pos);
}
/************************************************************/
char *findwhite(char *string)
{
  int pos = strcspn(string,WHITESPACE);
  if(pos == strlen(string))
    return(NULL);
  else
    return(string+pos);
}
