static char rcsid[] = "$Id: F-lex.c,v 1.56 2003/03/26 18:54:20 msato Exp $";
/* 
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */
/* lexical analyzer */
int OMP_flag = TRUE;
int OMN_flag = TRUE;

#ifdef ENABLE_QREAL
#define ST_BUF_SIZE	1048576
#else
#define ST_BUF_SIZE	65536
#endif /* ENABLE_QREAL */

#ifdef ENABLE_QREAL
#define LINE_BUF_SIZE   65536
#else
#define LINE_BUF_SIZE   4096 
#endif /* ENABLE_QREAL */

int st_class;		/* token for classify statement */
int need_keyword = FALSE;
int need_type_len = FALSE;

int fixed_line_len = 72;

#define QUOTE	'\002'	/* special quote mark */

struct keyword_token {
    char *k_name;
    int k_token;
};

#define IS_CONT_LINE(x) ((x)[5] != ' ' && (x)[5] != '0')

extern struct keyword_token dot_keywords[],keywords[];
extern struct keyword_token OMP_keywords[];

int exposed_comma,exposed_eql;
int paren_level;
char *bufptr;			/* pointer running in st_buffer */
char st_buffer[ST_BUF_SIZE];	/* one statement buffer */
char st_buffer_org[ST_BUF_SIZE];	/* one statement buffer  */
char stn_cols[7];			/* line number colums */
char line_buffer[LINE_BUF_SIZE];	/* pre_read line buffer */
char buffio[LINE_BUF_SIZE];

static int line_count = 0;
lineno_info *current_line;
int pre_read = 0;
lineno_info read_lineno;

struct saved_file_state {
    FILE *save_fp;
    lineno_info *save_line;
    int save_pre_read;
    lineno_info save_lineno;
    char *save_buffer;
    char *save_stn_cols;
} file_state[N_NESTED_FILE];

int n_nested_file = 0;

/* input file table */
int     n_files = 0;
char    *file_names[MAX_N_FILES];

enum lex_state 
{
    LEX_NEW_STATEMENT = 0,
    LEX_FIRST_TOKEN,	
    LEX_OTHER_TOKEN,	
    LEX_OMP_TOKEN,
    LEX_RET_EOS 
};

enum lex_state lexstate;
int st_OMP_flag;

/* read_line return value */
#define ST_EOF 	0
#define ST_INIT	1
#define ST_CONT	2
#define ST_ONE  ST_INIT

static int	read_initial_line _ANSI_ARGS_((void));
static int	classify_statement _ANSI_ARGS_((void));
static int	token _ANSI_ARGS_((void));
static int	get_keyword _ANSI_ARGS_((struct keyword_token *ks));

static void	string_to_integer _ANSI_ARGS_((long int *p, long int *hp, char *cp, int radix));
static double	convert_str_double _ANSI_ARGS_((char *s));

static int	readline _ANSI_ARGS_((void));
static void	save_format_str _ANSI_ARGS_((void));

static int	OMP_token _ANSI_ARGS_((void));
static void 	restore_file(void);

static int	ScanFortranLine _ANSI_ARGS_((char *src, char *srcHead,
					     char *dst, char *dstHead, char *dstMax,
					     int *inQuotePtr, int *quoteCharPtr,
					     int *inHollerithPtr, int *hollerithLenPtr, 
					     char **newCurPtr, char **newDstPtr));

static void
debugOutStatement()
{
    fprintf(debug_fp, "%6d:'%s'|\"%s\"\n",
	    read_lineno.ln_no,
	    stn_cols,
	    line_buffer);
    fflush(debug_fp);
}


void
initialize_lex()
{
    /* set lineno info as default */
    read_lineno.ln_no = 0;
    if(source_file_name != NULL)
	read_lineno.file_id = get_file_id(source_file_name);
    else
	read_lineno.file_id = get_file_id("<stdin>"); 

    lexstate = LEX_NEW_STATEMENT;
    exposed_comma = FALSE;
    exposed_eql = FALSE;
    paren_level = 0;
}

/* #define LEX_DEBUG */
static int yylast;

/* lexical analyer */
int
yylex()
{
    yylast = yylex0();
#ifdef LEX_DEBUG
    printf("%c[%d]",(yylast < ' ' || yylast >= 0xFF) ? ' ': yylast, yylast);
    fflush(stdout);
#endif
    return(yylast);
}

static int
yylex0()
{
    int t;
    static int tkn_cnt; 

    switch(lexstate){
    case LEX_NEW_STATEMENT:
    again:
	if(read_initial_line() == ST_EOF){
	    if(n_nested_file > 0){
		restore_file();
		goto again;
	    }
	    return(ST_EOF);
	}
	
	
	/* set bufptr st_buffer */
	bufptr = st_buffer;
	if(st_OMP_flag){
	    lexstate = LEX_OMP_TOKEN;
	    return(OMPKW_LINE);
	}
	tkn_cnt = 0;
	lexstate = LEX_FIRST_TOKEN;
	return(STATEMENT_LABEL_NO);

    case LEX_FIRST_TOKEN:
    first:
	tkn_cnt = 1;
	st_class = classify_statement();
	if (st_class == FORMAT) {
	    save_format_str();
	    lexstate = LEX_RET_EOS;
	} else if (st_class == EOS) {
	    lexstate = LEX_NEW_STATEMENT;
	} else {
	    lexstate = LEX_OTHER_TOKEN;
	}
	return(st_class);

    case LEX_OTHER_TOKEN:
	/* check 'if' '(' ... ')' */
	if((st_class == LOGIF || (st_class == ELSEIF)) 
	   && paren_level == 0 && tkn_cnt > 3)
	  goto first;
	tkn_cnt++;
	/* check 'assign' ... 'to' */
	if(st_class == ASSIGN && tkn_cnt == 3
	   && bufptr[0] == 't' && bufptr[1] == 'o'){
	    bufptr += 2;
	    return(TO);
	}
	t = token();
	if (t == FORMAT){
	    /*
	     * "format" shouldn't be here...
	     */
	    save_format_str();
	    lexstate = LEX_RET_EOS;
	} else if (t == EOS) {
	    lexstate = LEX_NEW_STATEMENT;
	}
	return(t);

    case LEX_RET_EOS:
	lexstate = LEX_NEW_STATEMENT;
	return(EOS);

    case LEX_OMP_TOKEN:
	t = OMP_token();
	if(t == EOS) lexstate = LEX_NEW_STATEMENT;
	return t;

    default:
	fatal("lexstate");
    }
    return UNKNOWN;
}

static void
save_format_str()
{
    char *fmt = strchr(st_buffer_org, '(');
    char *end = NULL;

    if (formatString != NULL) {
	free(formatString);
    }

    if (fmt == NULL) {
	error("illegal format statement \"%s\"\n", st_buffer_org);
	return;
    } else {
	end = strrchr(fmt, ')');
	if (end == NULL) {
	    error("illegal format statement \"%s\"\n", st_buffer_org);
	    return;
	}
	end++;
	*end = '\0';
    }
    formatString = strdup(fmt);
}

/* flush line */
static void
flush_line()
{
    lexstate = LEX_RET_EOS;
    need_keyword = FALSE;
    need_type_len = FALSE;
}

char *lex_get_line()
{
    char *s;

    s = strdup(bufptr);
    lexstate = LEX_RET_EOS;	/* force terminate */
    return(s);
}


void
yyerror(s) 
     char *s;
{ 
    error("%s",s);
}

char *lexline(n)
     int *n;
{
    *n = strlen(bufptr);
    return(bufptr);
}


static int
token()
{
    register char ch, *p;
    int tkn_len,t;
    
    if(need_keyword) {	/* require keyword */
	need_keyword = 0;
	t = get_keyword(keywords);
	if(t != UNKNOWN) return(t);
    }

    if(need_type_len == TRUE){	/* for type_length */
	need_type_len = FALSE;
	ch = *bufptr;
	if(ch == '\0') return EOS;
	if(ch >= '0' && ch <= '9'){
	    t = 0;
	    while(isdigit((int)*bufptr)) t = t*10 + *bufptr++ - '0';
	    yylval.val = GEN_NODE(INT_CONSTANT, t);
	    return CONSTANT;
	} else return *bufptr++;
    }

    switch(ch = *bufptr++) {
    case '\0':
	return(EOS);
    case QUOTE:
	for(p = buffio; (ch = *bufptr++) != QUOTE ;)
	  *p++ = ch;
	*p = 0;
	if(*bufptr == 'x'){
	    bufptr++;
	    t = 0;
	    for(p = buffio; *p != 0; p++){	 /* 'hhhhh'X */
		if(*p >= 'a' && *p <= 'f') t = t*16+*p-'a';
		else if(*p >= 'A' && *p <= 'F') t = t*16+*p-'a';
		else if(*p >= '0' && *p <= '9') t = t*16+*p-'0';
		else error("bad char '%c%' in hex constant",*p);
	    }
	    yylval.val = GEN_NODE(INT_CONSTANT, t);
	    return(CONSTANT);
	}
	yylval.val = GEN_NODE(STRING_CONSTANT,strdup(buffio));
	return(CONSTANT);	/* hollerith */
    case '=':
	if(*bufptr == '=') { 	
	    /* "==" */
	    bufptr++;
	    return (EQ);	
	} 
	return('=');
    case '(': 
	paren_level++; 
	return('(');
    case ')': 
	paren_level--; 
	return(')');
    case '+': 
    case '-': 
    case ',': 
    case '$': 
    case ':': 
    case '&': 
    case '|': 
	return(ch);
	  
    case '*':
	if(*bufptr == '*') {
	    bufptr++;
	    return(POWER);
	}
	return('*');
    case '/':
	if(*bufptr == '/') {
	    bufptr++;
	    return(CONCAT);
	} 
	return('/');
    case '.':
	if(isdigit((int)*bufptr)) goto number;
	else return(get_keyword(dot_keywords));
    case '>':
	if(*bufptr == '='){
	    bufptr++;
	    return GE;
	} 
	return(GT);
    case '<':
	if(*bufptr == '='){
	    bufptr++;
	    return(LE);
	} 
	return(LT);
    case '!':
	if(*bufptr == '='){
	    bufptr++;
	    return(NE);
	} 
	return(NOT);
	  
    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
	{ 
	    int have_dot;
	    int have_exp;
	    int have_dbl;
#ifdef ENABLE_QREAL
	    int have_qreal;
#endif /* ENABLE_QREAL */

	    number:		/* reading number */
	    have_dot = FALSE;
	    have_exp = FALSE;
	    have_dbl = FALSE;
#ifdef ENABLE_QREAL
	    have_qreal = FALSE;
#endif /* ENABLE_QREAL */
	      
	    bufptr--;		/* back */
	    p = buffio;
	    while((ch = *bufptr) != '\0'){
		if(ch == '.'){
		    if(have_dot) break; 
		    else if(isalpha((int)bufptr[1]) && 
			    isalpha((int)bufptr[2])) 
		      break;
		    have_dot = TRUE;
#ifdef ENABLE_QREAL
		} else if (ch == 'd' || ch == 'e' || ch == 'q') {
#else
		} else if(ch == 'd'|| ch =='e'){
#endif /* ENABLE_QREAL */
		    if(bufptr[1] == '+'||bufptr[1] =='-'){
			if(isdigit((int)bufptr[2])){
			    *p++ = 'E';
			    bufptr++;
			    *p++ = *bufptr++;
			} else  break;
		    } else if(isdigit((int)bufptr[1])) {
			*p++ = 'E';
			bufptr++;
		    } else break;
		    have_exp = TRUE;
		    if(ch == 'd') have_dbl = TRUE;
#ifdef ENABLE_QREAL
		    else if (ch == 'q') have_qreal = TRUE;
#endif /* ENABLE_QREAL */
		    while(isdigit((int)*bufptr)) *p++ = *bufptr++;
		    break;
		} else if(!isdigit((int)ch))
		  break;
		*p++ = ch;
		bufptr++;
	    }
	    *p = '\0';
#ifdef ENABLE_QREAL
	    if (have_qreal) {
		/*
		 * For accuracy, keep value as string.
		 * Convert to _omQReal_t when:
		 *	runtime
		 *	DATA statement
		 */
		yylval.val = make_qreal_enode(F_QREAL_CONSTANT, buffio);
	    } else
#endif /* ENABLE_QREAL */
	    if (have_dbl) { 
		yylval.val = make_float_enode(F_DOUBLE_CONSTANT,
					      convert_str_double(buffio));
	    } else if (have_dot || have_exp) {
		yylval.val = make_float_enode(F_FLOAT_CONSTANT,
					      convert_str_double(buffio));
	    } else {
		long int v, vH;
		v = vH = 0;
		string_to_integer(&v, &vH, buffio, 10);
#ifdef HAS_INT64
		if (defaultIntType == TYPE_LONGLONG) {
		    /*
		     * Integer constant is ALWAYS LONGLONG_CONSTANT.
		     */
		    yylval.val = make_longlong_enode(v, vH);
		} else {
		    if (vH) {
			yylval.val = make_longlong_enode(v, vH);
		    } else {
			yylval.val = GEN_NODE(INT_CONSTANT, v);
		    }
		}			
#else
		if (vH) {
		    warning("integer constant too large.");
		    yylval.val = GEN_NODE(INT_CONSTANT, v);
		} else {
		    yylval.val = GEN_NODE(INT_CONSTANT, v);
		}
#endif /* HAS_INT64 */
	    }
	    return(CONSTANT);
	    
	default:
	    error("bad char %c(0x%x)",ch,ch&0xFF);
	    /* GO TROUGH AS alphanumeric */
	      
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
	case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
	case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
	case 's': case 't': case 'u': case 'v': case 'w': case 'x':
	case 'y': case 'z': case '_':
	    p = buffio;
	    for(*p++ = ch, tkn_len = 1;
		isalpha((int)*bufptr) || isdigit((int)*bufptr) || *bufptr == '_'; 
		*p++ = *bufptr++){
		if(tkn_len++ > MAX_NAME_LEN) {
		    error("name is too long");
		}
	    }
	    *p = 0;		/* termination */
	      
	    if(strncmp(buffio,"function",8) == 0 && isalpha((int)buffio[8]) &&
	       bufptr[0] == '(' && (bufptr[1] == ')' || isalpha((int)bufptr[1]))){
		/* back */
		bufptr -= (tkn_len - 8);
		return(FUNCTION);
	    }
	    if(tkn_len == 1 && *bufptr == QUOTE){
		long int v, vH;
		int radix = 0;
		/* x'hhhhh' constant */
		v = vH = 0;
		switch(buffio[0]) {
		case 'z': 
		case 'x':  radix = 16; break;
		case 'o':  radix = 8; break;
		case 'b': radix = 2; break;
		default: error("bad bit id");
		}
		tkn_len = 0;
		for(p = buffio, bufptr++; 
		    (ch = *bufptr++) != QUOTE; *p++ = ch)
		  if(tkn_len++ > 32) {
		      error("too long constant");
		      break;
		  }
		*p = 0;		/* termination */
		if (radix == 0) {
		    error("can't determine radix");
		}

		string_to_integer(&v, &vH, buffio, radix);
		if (vH) {
#ifdef HAS_INT64
		    yylval.val = make_longlong_enode(v, vH);
#else
		    warning("integer constant too large.");
		    yylval.val = GEN_NODE(INT_CONSTANT, v);
#endif /* HAS_INT64 */
		} else {
		    yylval.val = GEN_NODE(INT_CONSTANT, v);
		}
		return(CONSTANT);
	    } 
	    yylval.val = GEN_NODE(IDENT,f_find_symbol(buffio));
	    return(IDENTIFIER);
	}
    }
}


static void
string_to_integer(p, hp, cp, radix)
     long int *p;
     long int *hp;
     char *cp;
     int radix;
{
    char    ch;
    int	    x;
    unsigned int v0, v1, v2, v3;

    v0 = v1 = v2 = v3 = 0;	/* clear */
    for( ; (ch = *cp) != 0 ; cp++ ){
	if (isdigit((int)ch))
	    x = ch - '0';
	else if ( isupper((int)ch) )
	    x = ch - 'A' + 10;
	else
	    x = ch - 'a' + 10;
	v0 = v0 * radix + x;
	v1 = v1 * radix + ((v0 >> 16) & 0xFFFF);
	v2 = v2 * radix + ((v1 >> 16) & 0xFFFF);
	v3 = v3 * radix + ((v2 >> 16) & 0xFFFF);
	v0 &= 0xFFFF;
	v1 &= 0xFFFF;
	v2 &= 0xFFFF;
	if ( v3 & 0xFFFF0000 ){
	    error("too large integer constant");
	    break;
	}
    }
    *p = (v1 << 16) | v0;
    *hp = (v3 << 16) | v2;
#if 0
    fprintf(stderr, "lex: H = %x, L = %x\n", *hp, *p);
#endif
}


static double
convert_str_double(s)
     char *s;
{
    char v[100];
    register char *t;
    int n;

    if((n = strlen(s)) > 90){
	  error("too many digits in floating constant");
	  n = 90;
      }
    for(t = v ; n-- > 0 ; s++)
      *t++ = (*s=='d' ? 'e' : *s);
    *t = '\0';
    return(atof(v));
}


/*
 * This routine does classify the fortran statements according to the ad-hoc
 * nature.
 */

static int
classify_statement()
{
    register char *p;
    
    p = bufptr;
    if(p[0] == '\0') return(EOS);
    
    /* check 'if(...)'
     * 	'if(1) = xxx', 'if' is a array name.
     *  'if() 123,.. , 'if' is arithmetic if 
     *  'if() v = ..., 'if' is logical if
     */
    if(p[0] == 'i' && p[1] == 'f' && p[2] == '(') { 
	/* assignment or if statement */
	for(p += 3,paren_level = 1; paren_level != 0; p++) {
	    if(*p == '\0') {
		error("unmatch paren");
		return(EOS);
	    } else if(*p == ')') paren_level--;
	    else if(*p == '(') paren_level++;
	    else if(*p == QUOTE) {
		/* skip in string */
		while(*++p != QUOTE){
		    if(*p == '\0'){
			error("syntax error in string");
			return EOS;
		    }
		}
	    }
	}
	if(*p == '=') st_class = LET; /* if(1) = ??? */
	else if(isdigit((int)*p))  st_class = ARITHIF;
	else st_class = LOGIF;
	if(st_class != LET) {
	    bufptr += 2;	/* cut keyword */
	}
    } else if(exposed_eql){
	/* check 'DO xxx = xxx,xxx' */
	if(exposed_comma && p[0] == 'd' && p[1] == 'o') {
	    st_class = DO;
	    bufptr += 2;
	} else  st_class = LET;
    } else {
	st_class = get_keyword(keywords);
	if(st_class == GOTO) {
	    if(*bufptr == '(')
	      st_class = COMPGOTO;
	    else if(isalpha((int)*bufptr))
	      st_class = ASGOTO;
	}
    }
    return(st_class);
}

/* cut keyword from st_buffer */
/* serch for keyword (BAKA search) */
int
get_keyword(ks)
     struct keyword_token *ks;
{
    register char *p,*q;
    struct keyword_token *kp;
    
    if(!isalpha((int)*bufptr)) return(UNKNOWN);

    for(kp = ks; kp->k_name; kp++){
	q = kp->k_name;
	if(*q == '_') break;	/* debug hook */
	for(p = bufptr;		/* */; p++,q++){
	    if(*q == '\0') {	/* found */
		bufptr = p;	/* cut off keyword */
		return(kp->k_token);
	    } 
	    else if(*p != *q) break;
	    /* else continue */
	}
    }
    return(UNKNOWN);
}


/*
 * for include statement
 */
void include_file(char *name)
{
    FILE *fp;
    struct saved_file_state *p;

    if((fp = fopen(name,"r")) == NULL){
	error("cannot open file '%s'",name);
	return;
    }
    if(n_nested_file >= N_NESTED_FILE){
	error("too nested include file (max = %d)",N_NESTED_FILE);
	return;
    }
    p = &file_state[n_nested_file++];
    p->save_fp = source_file;
    p->save_line = current_line;
    p->save_pre_read = pre_read;
    p->save_lineno = read_lineno;
    if(p->save_buffer == NULL){
	if((p->save_buffer = (char *)malloc(sizeof(char)*LINE_BUF_SIZE)) == NULL){
	    fatal("cannot allocate memory");
	}
    }
    bcopy(line_buffer,p->save_buffer,LINE_BUF_SIZE);
    if(p->save_stn_cols == NULL){
	if((p->save_stn_cols = (char *)malloc(sizeof(char)*7)) == NULL) {
	    fatal("cannot allocate memory");
	}
    }
    bcopy(stn_cols, p->save_stn_cols, 7);
    /* set new state */
    source_file = fp;
    read_lineno.file_id = get_file_id(name);
    read_lineno.ln_no = 0;
    pre_read = 0;
}

static void restore_file()
{
    struct saved_file_state *p;

    fclose(source_file);
    p = &file_state[--n_nested_file];
    source_file = p->save_fp;
    current_line = p->save_line;
    pre_read = p->save_pre_read;
    read_lineno = p->save_lineno;
    bcopy(p->save_buffer,line_buffer,LINE_BUF_SIZE);
    bcopy(p->save_stn_cols,stn_cols,7);
}

/*
 * Filees name table
 */
int get_file_id(char *file)
{
    int i;
    for(i = 0; i < n_files; i++){
        if(strcmp(file_names[i],file) == 0) return i;
    }
    if(n_files >= MAX_N_FILES)
      fatal("too many files (max No. of files = %d)",MAX_N_FILES);
    file_names[i = n_files++] = strdup(file);
    return i;
}


/* 
 * line reader
 */
static int
read_initial_line()
{
    int rv = ST_EOF;
    int st_len, i;
    char *p, *q;
    int inH = FALSE;
    int hLen = 0;
    int qChar = '\0';
    char *bufMax = st_buffer + ST_BUF_SIZE - 1;
    char oBuf[65536];
    int newLen;
    int lnLen;
    int inQuote = FALSE;

    exposed_comma = 0;
    exposed_eql = 0;
    paren_level = 0;
    
    top:
    if (!pre_read) {
	pre_read = 0;
	if ((rv = readline()) == ST_EOF) {
	    return ST_EOF;
	}
	if (rv == ST_CONT) {
	    error("missing initial line");
	    goto top;
	}
    }

    st_no = 0;
    st_len = 0;
    st_OMP_flag = FALSE;

    if (strncmp(&stn_cols[1],"$omp",4) == 0 || 
	(OMN_flag && strncmp(&stn_cols[1],"$omn",4) == 0)){
	st_OMP_flag = TRUE;
	goto copy_body;
    }
    
    /* get line number */
    for (p = stn_cols, i = 0; *p != '\0' && i < 5; p++, i++) {
	if (!isspace((int)*p)) {
	    if (isdigit((int)*p)) {
		st_no = 10 * st_no + (*p - '0');
	    } else {
		error("no digit in statement nubmer field");
		st_no = 0;
		break;
	    }
	}
    }
    if (debug_flag) {
	debugOutStatement();
    }
    
    /* first line number is set */
    current_line = new_line_info(read_lineno.file_id,read_lineno.ln_no);

    copy_body:
    /* copy to statement buffer */
    p = line_buffer;
    q = st_buffer;
    newLen = st_len = ScanFortranLine(p, p, q, q, bufMax,
				      &inQuote, &qChar, &inH, &hLen, &p, &q);
    st_buffer[newLen] = '\0';
    if (st_len >= ST_BUF_SIZE) {
	goto Done;
    }
    memcpy(oBuf, st_buffer, newLen);

    while ((rv = readline()) == ST_CONT) {
	if (strncmp(&stn_cols[1],"$omp",4) == 0 ||
	    (OMN_flag && strncmp(&stn_cols[1],"$omn",4) == 0)){
	    if (st_OMP_flag) {
		goto copy_body_cont;
	    }
	    error("OMP sentinels missing initial line, ignored");
	    break;
	} else if (st_OMP_flag) {
	    error("continue line follows OMP sentinels, ignored");
	    break;
	}
	
	for (p = stn_cols, i = 0; *p != '\0' && i < 5; p++, i++) {
	    if (!isspace((int)*p)) {
		warning("statement label in continuation line is ingored");
		break;
	    }
	}
	if (debug_flag) {
	    debugOutStatement();
	}

	copy_body_cont:
	/* copy to statement buffer */
	p = oBuf + st_len;
	lnLen = strlen(line_buffer);
	memcpy(p, line_buffer, lnLen);
	*(p + lnLen) = '\0';
	newLen = ScanFortranLine(p, oBuf, q, st_buffer, bufMax, 
				 &inQuote, &qChar, &inH, &hLen, &p, &q);
	st_len += newLen;
	if (st_len >= ST_BUF_SIZE) {
	    goto Done;
	}
	memcpy(oBuf, st_buffer, st_len);
    }
    *q = '\0';			/* termination */

    Done:
    if (inQuote == TRUE) {
	error("un-closed quotes");
    }
    if (inH == TRUE) {
	warning("un-terminated Hollerith code.");
	if (st_len < ST_BUF_SIZE) {
	    *q++ = QUOTE;
	    *q = '\0';
	}
    }

    memcpy(st_buffer_org, st_buffer, st_len);
    st_buffer_org[st_len] = '\0';

#if 0
    fprintf(stderr, "debug: read_initial got '%s'\n", st_buffer);
#endif
#ifdef LEX_DEBUG
    fprintf(stderr,"line after:");
    for(p = st_buffer; *p != 0; p++)
	if(*p < ' ') fprintf(stderr,"\\%03o",*p);
	else fprintf(stderr,"%c",*p);
    fprintf(stderr,"\n");
#endif    

    pre_read = (rv == ST_INIT) ? 1 : 0;
    return ST_ONE;
}


/* read one line from input, check comment line */
static int
readline()
{
    register int c,i;
    char *bp,*p;
#if 0
    char *bp_end;
#endif
    int free_format = 0;
    int maxChars = 0;

next_line:
    read_lineno.ln_no++;
next_line0:
    line_count++;
    memset(line_buffer, 0, LINE_BUF_SIZE);
    memset(stn_cols, ' ', 6);
    stn_cols[6] = '\0';
    c = getc(source_file);
    switch(c){
    case '&':
	free_format++;
	stn_cols[5] = 'x';
	break;
    case '#':
	for(bp = line_buffer; bp < &line_buffer[LINE_BUF_SIZE]; ){
	    if((c = getc(source_file)) == '\n') break;
	    *bp++ = c;
	}
	*bp = '\0';
	bp = line_buffer;
	while(*bp == ' ' || *bp == '\t' || *bp == '\b')
	    bp++;        /* skip space */
	if(isdigit((int)(*bp))){
	    i = 0; /* line # reset default '0' */
	    while(isdigit((int)(*bp)))
		i = i * 10 + *bp++ - '0';
	    read_lineno.ln_no = i;
	    while(*bp == ' ') bp++;    /* skip space, again */
	    if (*bp == '"'){   /* parse file name */
		bp++;
		p = buffio;       /* rewrite buffer file name */
		while(*bp != '"' && *bp != 0) *p++ = *bp++;
		*p = '\0';
		read_lineno.file_id = get_file_id(buffio);
		/* if first line is #line, then set it
		   as original source file name */
		if(line_count == 1)
		    source_file_name = strdup(buffio);
	    }
	} else warning("bad # line");
	goto next_line0;
	
    case 'c':
    case 'C':
    case '*':
    case '!':
	if(OMP_flag){
	    ungetc('c',source_file);
	    goto read_num_column;
	}
    case ';':
    read_comment:
	while((c = getc(source_file)) != '\n') {
	    if(c == EOF) return(ST_EOF);
	}
	goto next_line;
    case EOF:
	return(ST_EOF);
    case '\n':
	/* blank line */
	goto next_line;
    default:
	/* read line number column */
	ungetc(c,source_file);
    read_num_column:
	for(i = 0; i < 6 ;i++) {
	    c = getc(source_file);
	    if (c == EOF) {
		warning("unexpected eof");
		return(ST_EOF);
	    } else if (c == '\n') {
		while (i < 6) stn_cols[i++] = ' ';
		ungetc(c,source_file);
		break;
	    } else if (c == '\t') {
		/* TAB in col 1-6 skips to column 7 */
		while (i < 6) stn_cols[i++] = ' ';
		free_format++;
		break;
	    } else {
		stn_cols[i] = isupper(c) ? tolower(c): c;
	    }
	}
	if (stn_cols[0] == 'c') {
	    if (strncmp(&stn_cols[1], "$omp", 4) == 0 ||
		(OMN_flag && strncmp(&stn_cols[1], "$omn", 4) == 0)){
		/*
		 * OpenMP sentinel no doubt
		 */
		goto KeepOnGoin;
	    }
	    if (stn_cols[1] == '$') {
		/*
		 * Check OpenMP fixed source form conditional compilation.
		 */
		int numSpace = 0;
		int numDigit = 0;

		/*
		 * erase leading "c$".
		 */
		memset(stn_cols, ' ', 2);

		/*
		 * If there are any non-numerical character within
		 * column three to five, this line is just a comment.
		 */
		for (i = 2; i < 5; i++) {
		    if (stn_cols[i] == ' ') {
			numSpace++;
		    } else if (isdigit((int)stn_cols[i])) {
			numDigit++;
		    }
		}

		if (numSpace == 3) {
		    /* OpenMP conditional compile */
		    goto KeepOnGoin;
		} else if ((numSpace + numDigit) == 3) {
		    if (IS_CONT_LINE(stn_cols)) {
			/*
			 * Invalid continuation line. treat as a
			 * comment.
			 */
			warning("statement label in continuation line in OpenMP conditional compilation. ignored as a comment.");
			goto read_comment;
		    }
		    goto KeepOnGoin;
		}
		/*
		 * This line seemed to be an OpenMP sentinel/conditional
		 * compilation, but it is not :)
		 */
		warning("unknown sentinel/directive 'c$%s'",
			&stn_cols[2]);
	    }
	    goto read_comment;
	}
    }

    KeepOnGoin:
    stn_cols[6] = '\0';		/* terminate */
    /* 
     * read body of line
     */
    bp = line_buffer;
    if (free_format > 0) {
	maxChars = 126;		/* 132 column limit */
    } else {
	maxChars = fixed_line_len - 6;		/* 72 column limit */
    }
#if 0
    bp_end = line_buffer + maxChars;
#endif
    if (c == '\n') {
	line_buffer[0] = 0;
    } else {
	char scanBuf[4096];
	int scanLen = 0;
	int getNL = FALSE;
	char *lb = fgets(scanBuf, 4096, source_file);
	if (lb == NULL) {
	    return ST_EOF;
	}
	scanLen = strlen(scanBuf);
	if (scanBuf[scanLen - 1] == '\n') {
	    getNL = TRUE;
	    scanBuf[scanLen - 1] = '\0';
	    scanLen--;
	    if (scanLen > maxChars) {
		goto Crip;
	    }
	} else {
	    Crip:
	    scanBuf[maxChars] = '\0';
	    scanLen = maxChars;
	}
	memcpy(line_buffer, scanBuf, scanLen);
	line_buffer[scanLen] = '\0';
	if (getNL != TRUE) {
	    while((c = getc(source_file)) != '\n') {
                if (c == EOF) {
                    warning("unexpected EOF");
                    return(ST_EOF);
                }
	    }
	}
	bp = line_buffer + scanLen;
#if 0
	fprintf(stderr, "debug: line buffer '%s'\n", line_buffer);
#endif
    }
#if 0
    if(!free_format){ 
	/* formated care image */
	while(bp < bp_end)  *bp++ = ' ';
    }
#endif
    
    /* skip null line */
    for (bp = line_buffer; *bp != 0; bp++) {
	if (!isspace((int)*bp)) break;
    }

    if (*bp == 0) {
	/* brank line */
	for (p = stn_cols, i = 0;
	    *p != '\0' && i < 5;
	     p++,i++ ) {
	    if (*p != ' ') {
		warning("statement number in brank line is ingored");
		break;
	    }
	}
	goto next_line;
    }
    
    if (IS_CONT_LINE(stn_cols)) {
	return(ST_CONT);
    } else {
	return(ST_INIT);
    }
}


/* TOKEN DATA */
struct keyword_token dot_keywords[] =
{
    {"and.", AND}, 
    {"or.", OR}, 
    {"not.", NOT}, 
    {"true.", TRUE_CONSTANT},
    {"false.", FALSE_CONSTANT}, 
    {"eq.", EQ}, 
    {"ne.", NE}, 
    {"lt.", LT}, 
    {"le.", LE}, 
    {"gt.", GT}, 
    {"ge.", GE}, 
    {"neqv.", NEQV}, 
    {"eqv.", EQV}, 
    {NULL, 0}
};

struct keyword_token keywords[ ] = 
{
    { "assign",  	ASSIGN  },
    { "backspace(",  	BACKSPACE_P },
    { "backspace",  	BACKSPACE },
    { "blockdata",  	BLOCK },
    { "call",  		CALL },
    { "character",  	KW_CHARACTER, },
    { "close",  	CLOSE, },
    { "common",  	COMMON },
    { "complex",  	KW_COMPLEX },
    { "continue",  	CONTINUE  },
    { "data",  		DATA },
    { "dimension",  	DIMENSION  },
    { "doubleprecision",  KW_DOUBLE  },
    { "doublecomplex", 	KW_DCOMPLEX },
    { "multipleprecision", KW_MULTI },
    { "dowhile", 	DOWHILE },
    { "elseif",  	ELSEIF },
    { "else",  		ELSE },
    { "enddo",  	ENDDO },
    { "endfile(",  	ENDFILE_P  },
    { "endfile",  	ENDFILE  },
    { "endif",  	ENDIF },
    { "end",  		END  },
    { "entry",  	ENTRY },
    { "equivalence",  	EQUIV  },
    { "external",  	EXTERNAL  },
    { "format",  	FORMAT  },
    { "function",  	FUNCTION  },
    { "goto",  		GOTO  },
    { "implicit",  	IMPLICIT },
    { "include",  	INCLUDE },
    { "inquire", 	INQUIRE },
    { "intrinsic", 	INTRINSIC },
    { "integer", 	KW_INTEGER  },
    { "logical", 	KW_LOGICAL  },
    { "namelist", 	NAMELIST },
    { "none", 		KW_UNDEFINED},
    { "open", 		OPEN },
    { "parameter", 	PARAMETER },
    { "pause", 		PAUSE  },
    { "pointer",	POINTER },
    { "print", 		PRINT  },
    { "program", 	PROGRAM },
    /*    { "punch",  	PUNCH }, */
    { "read(", 		READ_P },
    { "read",  		READ },
    { "real",  		KW_REAL },
    { "return", 	RETURN  },
    { "rewind(", 	REWIND_P  },
    { "rewind", 	REWIND  },
    { "save",  		SAVE },
    /*    { "static", 	KW_STATIC },*/
    { "stop",  		STOP },
    { "subroutine", 	SUBROUTINE  },
    { "then",  		THEN },
    { "undefined", 	KW_UNDEFINED },
    { "write(",  	WRITE_P },
    { "write",  	WRITE },
    { 0, 0 }};

/* 
 * OpenMP token
 */
static int 
OMP_token()
{
    int t;

    if(*bufptr == '\0') return EOS;
    if(*bufptr == '('){
	bufptr++;
	paren_level++;
	return ('(');
    }
    if(paren_level == 0 || need_keyword){
	need_keyword = 0;
	t = get_keyword(OMP_keywords);
	if(t != UNKNOWN) return t;
#ifdef not
	/* else if paralel_level > 2, return token() */
	if(paren_level <= 1){
	    error("OpenMP keyword is required");
	    return t;
	}
#endif
    }
    return token();
}

struct keyword_token OMP_keywords[ ] = 
{
    {"parallel",	OMPKW_PARALLEL },
    {"end",		OMPKW_END },
    {"private",		OMPKW_PRIVATE },
    {"shared",		OMPKW_SHARED },
    {"default",		OMPKW_DEFAULT },
    {"none",		OMPKW_NONE },
    {"firstprivate",	OMPKW_FIRSTPRIVATE },
    {"reduction",	OMPKW_REDUCTION },
    {"if",		OMPKW_IF },
    {"copyin",		OMPKW_COPYIN },
    {"do",		OMPKW_DO },
    {"lastprivate",	OMPKW_LASTPRIVATE },
    {"schedule",	OMPKW_SCHEDULE },
    {"static",		OMPKW_STATIC },
    {"dynamic",		OMPKW_DYNAMIC },
    {"guided",		OMPKW_GUIDED },
    {"ordered",		OMPKW_ORDERED },
    {"runtime",		OMPKW_RUNTIME },
    {"sections",	OMPKW_SECTIONS },
    {"section",		OMPKW_SECTION },
    {"nowait",		OMPKW_NOWAIT },
    {"single",		OMPKW_SINGLE },
    {"master",		OMPKW_MASTER },
    {"critical",	OMPKW_CRITICAL},
    {"barrier",		OMPKW_BARRIER},
    {"atomic",		OMPKW_ATOMIC},
    {"flush",		OMPKW_FLUSH },
    {"threadprivate",	OMPKW_THREADPRIVATE},

    /* for OMNI/TEA extensions */
    {"affinity",	OMPKW_AFFINITY },
    {"mapping",		OMNKW_MAPPING},
    {"block",		OMNKW_BLOCK},
    {"cyclic",		OMNKW_CYCLIC},
    { 0, 0 }
};


#define TOLOWER(x) (isupper((int)(x))) ? tolower((int)(x)) : (x)

typedef struct {
    char *key;
    int len;
} unHKey;
static unHKey unHToken[] = {
    {"integer", 7},
    {"real", 4},
    {"double", 6},
    {"logical", 7},
    {"character", 9},
    {NULL, 0}
};


static int
power10(y)
     int y;
{
    if (y == 0) {
	return 1;
    } else if (y < 0) {
	return 0;
    } else {
	int ret = 1;
	int i;
	for (i = 1; i <= y; i++) {
	    ret *= 10;
	}
	return ret;
    }
}

static int 
isDataStatement(char *head)
{
    char *target = "data";
    char *cp;
    for(cp = head; cp != '\0'; cp++){
	if(isspace(*cp)) continue;
	if(tolower(*cp) == *target){
	    target++;
	    if(*target == '\0') return TRUE;
	} else break;
    }
    return FALSE;
}

static int
getHollerithLength(head, cur, inQuote)
     char *head;	/* start pointer of the room. */
     char *cur;		/* current pointer that is pointing 'H'|'h' */
     int inQuote;	/* if TRUE, 'H'|'h' is in quote. */
{
    int sLen = 0;
    int nTen = 0;

    cur--;
    if (inQuote == TRUE) {
	/*
up	 * in quote, only successive [0-9]+[Hh] is a valid hollerith,
	 * and always we can treat this as a hollerith.
	 */
	while (cur >= head) {
	    if (isdigit((int)*cur)) {
		sLen += power10(nTen) * ((int)*cur - '0');
		cur--;
		nTen++;
	    } else {
		break;
	    }
	}
	return sLen;
    }

    /*
     * get length.
     */
    while (cur >= head) {
	if (isspace((int)*cur)) {
	    cur--;
	} else if (isdigit((int)*cur)) {
	    sLen += power10(nTen) * ((int)*cur - '0');
	    cur--;
	    nTen++;
	} else {
	    break;
	}
    }
    if (sLen <= 0) {
	return 0;
    }

    /*
     * check backword.
     */
    while (cur >= head) {
	if (isspace((int)*cur)) {
	    cur--;
	} else {
	    break;
	}
    }

    if (cur <= head &&
	!(isalpha((int)*cur))) {
	/*
	 * buffer like 	^[ \t]*[0-9]+[Hh].*$
	 *
	 * Buffer start with a hollerith. may be syntax error, but
	 * definitely a hollerith.
	 */
	return sLen;
    }

    if (isalpha((int)*cur)) {
	/*
	 * buffer like ^.*[A-Za-z]+[ \t]*[0-9]+[Hh].*$
	 * must not be a hollerith.
	 */
	return 0;
    } else if ((*cur == '*' && (isdigit(*(cur-1))||isDataStatement(head))) || 
	       *cur == '=' || *cur == '/' || 
	       *cur == ',' || *cur == '(') { /* ??? */
	/*
	 * buffer like ^.*^[*]+[ \t]*[0-9]+[Hh].*$
	 * must be a hollerith.
	 */
	return sLen;
    } else {
	/*
	 * buffer like ^.*[*]+[ \t]*[0-9]+[Hh].*$
	 * first '.*' might be a type specifier or an identifier.
	 */
	int i;
	char rbuf[65536];
	char *rbp = rbuf;
	char buf[65536];
	char *bp = buf;
	int found = 0;
	char *kStart;

	cur++;
	while (cur >= head) {
	    if (isspace((int)*cur)) {
		cur--;
	    } else if (isalpha((int)*cur)) {
		*rbp++ = *cur--;
	    } else {
		break;
	    }
	}
	*rbp = '\0';
	rbp--;
	
	while (rbp >= rbuf) {
	    *bp++ = *rbp--;
	}
	*bp = '\0';

	for (i = 0; unHToken[i].key != NULL; i++) {
	    kStart = bp - unHToken[i].len;
	    if (kStart < buf) {
		continue;
	    } else {
		if (strncasecmp(kStart, unHToken[i].key,
				unHToken[i].len) == 0) {
		    found++;
		    break;
		}
	    }
	}
	if (found > 0) {
	    return sLen;
	} else {
	    return 0;
	}
    }
}


static int
getEscapeValue(cur, valPtr, newPtr)
     char *cur;		/* current scan point. */
     int *valPtr;	/* return value pointer. */
     char **newPtr;	/* next scan point return. */
{
    int val = 0;
    if (*cur != '\\') {
	if (valPtr != NULL) {
	    *valPtr = (int)(*cur++);
	}
	if (newPtr != NULL) {
	    *newPtr = cur;
	}
	return TRUE;
    }

    cur++;

    switch ((int)*cur) {
	case '\\': {
	    val = '\\';
	    cur++;
	    break;
	}
	case 't': {
	    val = '\t';
	    cur++;
	    break;
	}
	case 'b': {
	    val = '\b';
	    cur++;
	    break;
	}
	case 'f': {
	    val = '\f';
	    cur++;
	    break;
	}
	case 'n': {
	    val = '\n';
	    cur++;
	    break;
	}
	case 'r': {
	    val = '\r';
	    cur++;
	    break;
	}
	case '0': {
	    val = '\0';
	    cur++;
	    break;
	}
	default: {
	    val = '\\';
	    break;
	}
    }
    
    if (newPtr != NULL) {
	*newPtr = cur;
    }
    if (valPtr != NULL) {
	*valPtr = val;
    }
    
    return TRUE;
}


static int
unHollerith(cur, head, dst, dstHead, dstMax, inQuotePtr, quoteChar,
	    inHollerithPtr, hollerithLenPtr, newCurPtr, newDstPtr)
     char *cur;
     char *head;
     char *dst;
     char *dstHead;
     char *dstMax;
     int *inQuotePtr;
     int quoteChar;
     int *inHollerithPtr;
     int *hollerithLenPtr;
     char **newCurPtr;
     char **newDstPtr;
{
    int hLen = getHollerithLength(head, cur, *inQuotePtr);
    int nGet = 0;
    int rQC = '\0';

#if 0
    fprintf(stderr, "debug: hLen = %d\n", hLen);
#endif
    if (hLen == 0) {
	/*
	 * not a hollerith.
	 */
	if (*inQuotePtr == TRUE) {
	    *dst++ = *cur;
	} else {
	    *dst++ = TOLOWER(*cur);
	}
	cur++;
	nGet = 1;
	goto Done;
    }

#if 0
    fprintf(stderr, "debug: dst = 0x%08x, dHead = 0x%08x\n", dst, dstHead);
#endif
    while (dst >= dstHead) {
	dst--;
	if (isdigit((int)*dst)) {
	    continue;
	} else {
	    break;
	}
    }
    dst++;
#if 0
    fprintf(stderr, "debug: dst = '%s'\n", dst);
#endif
    if (quoteChar == '\'') {
	rQC = '"';
    } else {
	rQC = '\'';
    }

    cur++;
    if (*inQuotePtr == FALSE) {
	*dst++ = QUOTE;
    } else {
	*dst++ = rQC;
    }
    *inHollerithPtr = TRUE;
    while (*cur != '\0' &&
	   nGet < hLen &&
	   dst <= dstMax) {
	if (*cur == quoteChar) {
	    *dst++ = quoteChar;
	    nGet++;
	    if (*inQuotePtr == TRUE) {
		if (*(cur + 1) == quoteChar) {
		    cur++;
		} else {
		    *inQuotePtr = FALSE;
		    dst--;
		    cur++;
		    nGet = hLen;
		    goto Done;
		}
	    }
	    cur++;
	    continue;
	} else if (*cur == '\\') {
	    int val;
	    (void)getEscapeValue(cur, &val, &cur);
	    *dst++ = val;
	    nGet++;
	    continue;
	} else {
	    *dst++ = *cur++;
	    nGet++;
	}
    }

    Done:
    *hollerithLenPtr = hLen - nGet;
    if (*hollerithLenPtr < 0) {
	*hollerithLenPtr = 0;
    }
    if (*hollerithLenPtr == 0) {
	*inHollerithPtr = FALSE;
	if (hLen > 0 && dst <= dstMax) {
	    if (*inQuotePtr == FALSE) {
		*dst++ = QUOTE;
	    } else {
		*dst++ = rQC;
	    }
	}
    }

    *newCurPtr = cur;
    *newDstPtr = dst;
    return TRUE;
}


static int
checkInQuote(cur, dst, inQuotePtr, quoteCharPtr, newCurPtr, newDstPtr)
     char *cur;
     char *dst;
     int *inQuotePtr;
     int *quoteCharPtr;
     char **newCurPtr;
     char **newDstPtr;
{
    if (*inQuotePtr == FALSE) {
	*inQuotePtr = TRUE;
	*quoteCharPtr = *cur;
	cur++;
	*dst++ = QUOTE;
    } else {
	if (*cur == *quoteCharPtr) {
	    cur++;
	    if (*cur != *quoteCharPtr) {
		*dst++ = QUOTE;
		*inQuotePtr = FALSE;
		*quoteCharPtr = '\0';
	    } else {
		*dst++ = *quoteCharPtr;
		cur++;
	    }
	} else {
	    *dst++ = *cur++;
	}
    }
    *newCurPtr = cur;
    *newDstPtr = dst;
    return TRUE;
}


int
ScanFortranLine(src, srcHead, dst, dstHead, dstMax, inQuotePtr, quoteCharPtr,
		inHollerithPtr, hollerithLenPtr, newCurPtr, newDstPtr)
     char *src;
     char *srcHead;
     char *dst;
     char *dstHead;
     char *dstMax;
     int *inQuotePtr;
     int *quoteCharPtr;
     int *inHollerithPtr;
     int *hollerithLenPtr;
     char **newCurPtr;
     char **newDstPtr;
{
    char *cpDst = dst;

    while (*src != '\0' && dst <= dstMax) {
	if (isspace((int)*src)) {
	    if (*inQuotePtr == FALSE && *inHollerithPtr == FALSE) {
		src++;
	    } else {
		goto copyOne;
	    }
	} else if (*src == '!' || *src == ';') {
	    if (*inQuotePtr == FALSE && *inHollerithPtr == FALSE) {
		break;
	    } else {
		goto copyOne;
	    }
	} else if (*src == '\'' || *src == '"') {
	    if (*inHollerithPtr == FALSE) {
		checkInQuote(src, dst, inQuotePtr, quoteCharPtr, &src, &dst);
	    } else {
		goto copyOne;
	    }
	} else if (*src == 'h' || *src == 'H') {
	    if (*inHollerithPtr == FALSE) {
		unHollerith(src, srcHead, dst, dstHead, dstMax, inQuotePtr, *quoteCharPtr,
			    inHollerithPtr, hollerithLenPtr, &src, &dst);
	    } else {
		goto copyOne;
	    }
	} else if (*src == '\\') {
	    if (*inQuotePtr == TRUE || *inHollerithPtr == TRUE) {
		int val;
		getEscapeValue(src, &val, &src);
		*dst++ = val;
	    } else {
		goto copyOne;
	    }
	} else {
	    if (*inQuotePtr != TRUE && *inHollerithPtr != TRUE) {
		if (*src == '(') {
		    ++paren_level;
		} else if (*src == ')') {
		    --paren_level;
		} else if (paren_level == 0) {
		    if (*src == '=') {
			exposed_eql++;
		    } else if (*src == ',') {
			exposed_comma++;
		    }
		}
	    }
	    copyOne:
	    if (*inQuotePtr == TRUE || *inHollerithPtr == TRUE) {
		*dst++ = *src;
	    } else {
		*dst++ = TOLOWER(*src);
	    }
	    src++;
	    if (*inHollerithPtr == TRUE) {
		if (*hollerithLenPtr > 0) {
		    (*hollerithLenPtr)--;
		}
		if (*hollerithLenPtr <= 0) {
		    *hollerithLenPtr = 0;
		    *inHollerithPtr = FALSE;
		    if (*inQuotePtr == FALSE) {
			*dst++ = QUOTE;
		    } else {
			if (*quoteCharPtr == '\'') {
			    *dst++ = '"';
			} else {
			    *dst++ = '\'';
			}
		    }
		}
	    }
	}
    }
    *dst = '\0';
    *newCurPtr = src;
    *newDstPtr = dst;
    return dst - cpDst;
}

