/***************************************************************************
 * DBS: Distributed Benchmark System
 * Copyright (c) 1995, 1996, 1997 Yukio Murayama
 * Copyright (c) 1995, 1996, 1997 Nara Institute of Science and Technology
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided only with the following
 * conditions are satisfied:
 *
 * 1. Both the copyright notice and this permission notice appear in
 *    all copies of the software, derivative works or modified versions,
 *    and any portions thereof, and that both notices appear in
 *    supporting documentation.
 * 2. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgement:
 *      This product includes software developed by Nara Institute of 
 *      Science and Technology and its contributors.
 * 3. Neither the name of Nara Institute of Science and Technology nor 
 *    the names of its contributors may be used to endorse or promote 
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND NARA 
 * INSTITUTE OF SCIENCE AND TECHNOLOGY DISCLAIMS ANY LIABILITY OF 
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF 
 * THIS SOFTWARE. ALSO, THERE IS NO WARRANTY IMPLIED OR OTHERWISE, 
 * NOR IS SUPPORT PROVIDED.
 *
 * Feedback of the results generated from any improvements or
 * extensions made to this software would be much appreciated.
 * Any such feedback should be sent to:
 *
 *  Yukio Murayama
 *  E-mail:  <yukio-m@is.aist-nara.ac.jp>
 *  URL:     <http://shika.aist-nara.ac.jp/member/yukio-m/index.html>
 *  Address: Graduate School of Information Science, 
 *           Nara Institute of Science and Technology,
 *           Takayama 8916-5, Ikoma, Nara, Japan
 *
 * Nara Institute of Science and Technology has the rights to 
 * redistribute these changes.
 ***************************************************************************/
/*****************************************************************
 * Distributed Benchmark System
 * Scan Command File
 * $Revision: 1.22 $
 * $Date: 1997/07/11 00:54:12 $
 * $Author: yukio-m $
 *****************************************************************/

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include<stdio.h>
#include<stdlib.h>

#if !defined(sony_news) && !defined(__sun)
#include<strings.h>
#else
#include <string.h>
#endif

#if (!defined(BSD) || (BSD < 199306))
#error BSD
#include <malloc.h>
#endif

#include <math.h>
#include "dbs.h"
#include "dbsc.h"

/*
 *  Prototype Definition
 */

int  ffgetc               __P((FILE *fp));
int  uungetc              __P((int c, FILE *fp));
void p_error              __P((char *s));

void skip                 __P((char *str, FILE *f));
int  getword              __P((char *buf, char *str, FILE *fp));

void init_sendrecv_param  __P((struct send_recv *sr));
void init_scan_param      __P((struct scan_cmd *scan));
void check_sendrecv_param __P((struct send_recv *sr));
void check_scan           __P((struct scan_cmd *scan));
int  scan_sub_out         __P((char *word, struct scan_cmd **scan));

int  scan_sub_root        __P((FILE *fp, char *word, struct scan_cmd **scan));
int  scan_sub_sendrecv    __P((FILE *fp, char *word, struct send_recv *sr, int state));
int  total_size           __P((struct scan_cmd *scan));
int  get_pattern          __P((struct scan_traffic **traffic, FILE *fp));
int  total_message        __P((struct scan_cmd *scan));

/*
 *  Grobal Variable for error messages Setting
 */

static int  line   = 1;
static int  column = 1;
static int  bcolumn = 1;
static char error_buf[MAX_COLUMN+1]="";
static int  cmd_n = 0;

/*
 *  Grobal Variable for automaton
 */

enum state { OUT=0, ROOT=1, SENDER=2, RECEIVER=3 } state;

/*****************************************************************
 * Scan MAIN-ROUTINE
 *
 * Parser Command file 
 *****************************************************************/
int scan(scan, fp)
struct scan_cmd **scan;
FILE *fp;
{
    char word[CHAR_ARRAY];
    int c;

    state = OUT;

    DEBUGMSG2(8, fprintf(stderr, "State %d\n",state));
    DEBUGMSG2(8, fprintf(stderr, "Scan File ----------------------------------------------\n"));

    /*
     *  Scan Main Routine
     */
    while (1) {
	skip(" \n\t;", fp);
	if (getword(word, " \n\t;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", fp) == EOF)
	    return cmd_n;
	
	while (1) {
	    DEBUGMSG2(8, fprintf(stderr, "State %d\n",state));
	    
	    switch (state) {
	      case OUT:
		state = scan_sub_out(word, scan);
		break;
	      case ROOT:
		state = scan_sub_root(fp, word, scan);
		break;
	      case SENDER:
		state = scan_sub_sendrecv(fp, word, &((*scan)->sender),  SENDER);
		break;
	      case RECEIVER:
		state = scan_sub_sendrecv(fp, word, &((*scan)->receiver), RECEIVER);
		break;
	      default:
		p_error("Internal Error!! (scan)");
		exit(1);
	    }
	    skip(" \n\t;", fp);
	    if (getword(word, " {=\n\t;}", fp) == EOF)
		break;
	    if (state == OUT)
		break;
	    if (strcmp(word, "") == 0) {
		c=ffgetc(fp);
		DEBUGMSG2(8, fprintf(stderr, "(G:%c:G)",c));

		if (c == '}')
		    strcpy(word, "}");
		else if (c  == '{')
		    strcpy(word, "{");
		else {
		    p_error("missing '}' or '{'");
		    exit(1);
		}
	    }
	}
	check_scan(*scan);
	
	/*  out:; GOTO LABEL */
	
	(*scan)->total_size    = total_size(*scan);
	(*scan)->total_message = total_message(*scan);
	
	if (debug >= 8) {
	    fprintf(stderr, "Command Data -------------------------------------------\n");
	    fprintf(stderr, "Host Name        = %-12s %-12s\n", (*scan)->sender.hostname,     (*scan)->receiver.hostname);
	    fprintf(stderr, "Host Name Command= %-12s %-12s\n", (*scan)->sender.hostname_cmd, (*scan)->receiver.hostname_cmd);
	    fprintf(stderr, "Transport        = %-12d\n",       (*scan)->transport);
	    fprintf(stderr, "Port Number      = %-12d %-12d\n", (*scan)->sender.port,         (*scan)->receiver.port);
	    fprintf(stderr, "Start Time       =(%-12d %-12d)\n",(*scan)->start_time.tv_sec,   (*scan)->start_time.tv_usec);
	    fprintf(stderr, "End Time         =(%-12d %-12d)\n",(*scan)->end_time.tv_sec,     (*scan)->start_time.tv_usec);
	    fprintf(stderr, "Send Times       = %-12d \n",      (*scan)->send_times);
	    fprintf(stderr, "Traffic Number   = %-12d %-12d\n", (*scan)->sender.traffic_n,    (*scan)->receiver.traffic_n);
	    fprintf(stderr, "Total Size       = %-12d\n",       (*scan)->total_size);
	    fprintf(stderr, "Total Message    = %-12d\n",       (*scan)->total_message);
	    fprintf(stderr, "Output File Name = %-12s\n",       (*scan)->filename);
	    fprintf(stderr, "Connection Mode  = %-12d\n",       (*scan)->connection_mode);
	    fprintf(stderr, "Send Buffer Size = %-12d %-12d\n", (*scan)->sender.send_buff,    (*scan)->sender.recv_buff);
	    fprintf(stderr, "Recv Buffer Size = %-12d %-12d\n", (*scan)->receiver.send_buff,  (*scan)->receiver.recv_buff);
	    fprintf(stderr, "Soket Debug Mode = %-12d %-12d\n", (*scan)->sender.so_debug,     (*scan)->receiver.so_debug);
	    fprintf(stderr, "TCP Trace Mode   = %-12d %-12d\n", (*scan)->sender.tcp_trace,    (*scan)->receiver.tcp_trace);
	    fprintf(stderr, "TCP No Delay     = %-12d %-12d\n", (*scan)->sender.no_delay,     (*scan)->receiver.no_delay);
	    fprintf(stderr, "--------------------------------------------------------\n");
	}
	scan = &((*scan)->next);
    }
}


/*****************************************************************
 * Scan subroutine
 *****************************************************************/
/*
 * state == OUT
 */
int scan_sub_out(word, scan)
char *word;
struct scan_cmd **scan;
{
    if (strcmp(word, "{") == 0) {
	/*
	 *  Create Scan_Cmd 
	 */
	if ((*scan = (struct scan_cmd *)malloc(sizeof(struct scan_cmd))) == NULL) {
	    perror("malloc: scan_cmd");
	    exit(1);
	}
	cmd_n++;
	init_scan_param(*scan);

	return ROOT;
    }

    p_error("syntax error.");
    exit(1);
}

/*
 * state == ROOT
 */
int scan_sub_root(fp, word, scan)
FILE *fp;
char *word;
struct scan_cmd **scan;
{
    double a;

    if (strcmp(word, "sender") == 0) {
	skip(" \n\t{", fp);
	return SENDER;
    }

    if (strcmp(word, "receiver") == 0) {
	skip(" \n\t{", fp);
	return RECEIVER;
    }

    if (strcmp(word, "file") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	strcpy((*scan)->filename, word);
    } else if (strcmp(word, "protocol") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	if (strcmp("TCP", word) == 0) {
	    (*scan)->transport = TCP;
	} else if (strcmp("UDP", word) == 0) {
	    (*scan)->transport = UDP;
	} else {
	    p_error("Parameter error. You can set TCP or UDP.");
	    exit(1);
	} 
    } else if (strcmp(word, "connection_mode") == 0 || strcmp(word, "connect_mode") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	if (strcmp("BEFORE", word) == 0) {
	    (*scan)->connection_mode = BEFORE;
	} else if (strcmp("AFTER", word) == 0) {
	    (*scan)->connection_mode = AFTER;
	} else {
	    p_error("Parameter error. You can set BEFORE or AFTER.");
	    exit(1);
	} 
    } else if (strcmp(word, "server") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	if (strcmp("RECEIVER", word) == 0) {
	    (*scan)->serverflg = RECEIVE;
	} else if (strcmp("SENDER", word) == 0) {
	    (*scan)->serverflg = SEND;
	} else {
	    p_error("Parameter error. You can set SENDER or RECEIVER.");
	    exit(1);
	} 
    } else if (strcmp(word, "start_time") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	timeval_put(&((*scan)->start_time), (a = (double)atof((char *)word)));
	if (a < - 0.1) {
	    p_error("start_time error. start_time must be greater or equal than 0.0");
	    exit(1);
	}
    } else if (strcmp(word, "end_time") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	timeval_put(&((*scan)->end_time), (a = (double)atof((char *)word)));
	if (a < 0.0) {
	    p_error("end_time error. end_time must be greater than 0.0");
	    exit(1);
	}
	if (a > WARN_END_TIME) 
	    fprintf(stderr, "WARNING:end_time = %f is large\n",a );

	if (a > MAX_END_TIME) {                        /* XXX You can modify MAX_END_TIME in dbs.h */
	    p_error("end_time error. end_time must be less than 3600.0"); 
	    exit(1);
	}
    } else if (strcmp(word, "send_times") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	(*scan)->send_times = atoi(word);
	if ((*scan)->send_times < 0) {
	    p_error("send_times error. send_times must be greater or equal than 0");
	    exit(1);
	}
    } else if (strcmp(word, "}") == 0) {
	return OUT;	/* GGG GOTO OUT */
    } else {
	p_error("Unknown keyword.");
	exit(1);
    }

    return ROOT;
}

/*
 * state == SEND or RECV
 */
int scan_sub_sendrecv(fp, word, sr, state)
FILE *fp;
char *word;
struct send_recv *sr;
int state;
{
    if (strcmp(word, "hostname_cmd") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	strcpy(sr->hostname_cmd, word);
    } else if (strcmp(word, "hostname") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	strcpy(sr->hostname, word);
    } else if (strcmp(word, "port") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->port = atoi(word);
	if (sr->port < 0 || sr->port > 0xffff) {
	    p_error("port number error.");
	    exit(1);
	}
    } else if (strcmp(word, "send_buff") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->send_buff = atoi(word);
    } else if (strcmp(word, "recv_buff") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->recv_buff = atoi(word);
    } else if (strcmp(word, "mem_align") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->mem_align = atoi(word);
    } else if (strcmp(word, "align_offset") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->align_offset = atoi(word);
    } else if (strcmp(word, "align_pad") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->align_pad = atoi(word);
    } else if (strcmp(word, "no_delay") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	if (strcmp(word, "ON") == 0) {
	    sr->no_delay = ON;
	} else if (strcmp(word, "OFF") == 0) {
	    sr->no_delay = OFF;
	} else {
	    p_error("Parameter error. You can set ON or OFF.");
	    exit(1);
	}
    } else if (strcmp(word, "mss") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->mss = atoi(word);
    } else if (strcmp(word, "so_debug") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	if (strcmp(word, "ON") == 0) {
	    sr->so_debug = ON;
	} else if (strcmp(word, "OFF") == 0) {
	    sr->so_debug = OFF;
	} else {
	    p_error("Parameter error. You can set ON or OFF.");
	    exit(1);
	}
    } else if (strcmp(word, "tcp_trace") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	if (strcmp(word, "ON") == 0)
	    sr->tcp_trace = ON;
	else if (strcmp(word, "OFF") == 0)
	    sr->tcp_trace = OFF;
	else if (strcmp(word, "AFTER") == 0)
	    sr->tcp_trace = AFTER;
	else {
	    p_error("Parameter error. You can set ON, OFF or AFTER.");
	    exit(1);
	}
    } else if (strcmp(word, "record_buff") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->record_buff = atoi(word);
    } else if (strcmp(word, "trace_buff") == 0) {
	skip(" =\n\t", fp);
	getword(word, " \n\t;", fp);
	sr->trace_buff = atoi(word);
    } else if (strcmp(word, "pattern") == 0)
	sr->traffic_n = get_pattern(&sr->traffic, fp);
    else if (strcmp(word, "}") == 0) {
	check_sendrecv_param(sr);
	return ROOT;
    } else {
	p_error("Unknown Keyword.");
	exit(1);
    }

    return state;
}

/*****************************************************************
 * Get traffic pattern
 *****************************************************************/
/*
 * Calculate Sending Total Size (byte)
 */
int total_size(scan)
struct scan_cmd *scan;
{
    int i, sum;
    struct scan_traffic **traffic;

    traffic = &(scan->sender.traffic);

    sum=0;
    for (i=0; i < scan->sender.traffic_n; i++, traffic = &(*traffic)->next)
	sum += (*traffic)->size;

    return sum*scan->send_times;
}

/*
 * Calculate Sending Message (Times)
 */
int total_message(scan)
struct scan_cmd *scan;
{
    int i, sum;
    struct scan_traffic **traffic;

    traffic = &(scan->sender.traffic);

    sum = 0;
    for (i=0; i < scan->sender.traffic_n; i++, traffic = &(*traffic)->next) {
	sum += ((int)ceil((double)((*traffic)->size)/(double)((*traffic)->packet)));
    }

    return sum*scan->send_times;
}

/*
 * Get Traffic pattern
 */
int get_pattern(traffic_org, fp)
struct scan_traffic **traffic_org;
FILE *fp;
{
    struct scan_traffic **traffic;
    int n=0, i, s=OFF;
    char word[256];

    traffic = traffic_org;

    skip(" =\n\t{", fp);
    while(1) {
	if (getword(word, " \n\t,;}", fp) == EOF) {
	    fprintf(stderr, "ERROR \'%s\'\n",word);
	    fprintf(stderr, "line %d column %d\n", line, column);
	    exit(1);
	}
    	if (strcmp(word, "") == 0) {
	    if (ffgetc(fp) == '}')
		break;
	    else {
		fprintf(stderr, "ERROR \'%s\'\n",word);
		fprintf(stderr, "line %d column %d\n", line, column);
		exit(1);
	    }
	}

	if ((*traffic = (struct scan_traffic *)malloc(sizeof(struct scan_traffic))) == NULL) {
	    p_error("Memory Allocation Error");
	    perror("malloc:traffic");
	    exit(1);
	}

	(*traffic)->next = (struct scan_traffic *) NULL;
	
	(*traffic)->size = atoi(word);
        if ((*traffic)->size <= 0) {
	    p_error("Data Size must be greater then 0");
            exit(1);
        }
	skip(" \n\t,", fp);
	getword(word, " \n\t,;}", fp);

	(*traffic)->packet = atoi(word);
        if ((*traffic)->packet < 0) {
	    if (state == RECEIVER) {
		s = ON;
	    } else {
		p_error("Sender's Packet Size must be greater then 0");
		exit(1);
	    }
	}
	skip(" \n\t,", fp);
	getword(word, " \n\t,;}", fp);

	(*traffic)->esleep = atof(word);
	skip(" \n\t,", fp);
	getword(word, " \n\t,;}", fp);

	(*traffic)->isleep = atof(word);
	skip(" =\n\t;", fp);

	traffic = &(*traffic)->next;
	n++;

        if (s == ON && n != 1) {
	    p_error("When you set stream mode, you can set one traffic pattern only.");
            exit(1);
        }
    }

    if (debug >= 8) {
	traffic = traffic_org;

	fprintf(stderr, "\n");
	fprintf(stderr, "Traffic Pattern-----------------------------------------\n");
	fprintf(stderr, "%8s %8s %8s %8s\n",
		"SIZE", "PAKET", "ESLEEP", "ISLEEP");
	for (i=0; i<n; i++) {
	    fprintf(stderr, "%8d %8d %8f %8f\n",
		    (*traffic)->size, (*traffic)->packet, (*traffic)->esleep, (*traffic)->isleep);
	    traffic = &(*traffic)->next;
	}
	fprintf(stderr, "--------------------------------------------------------\n");
    }

    return n;
}

/*****************************************************************
 * Initializeatoin of Parameter
 *****************************************************************/
/*
 * Initialize struct scan_cmd
 */
void init_scan_param(scan)
struct scan_cmd *scan;
{
    init_sendrecv_param(&scan->sender);
    init_sendrecv_param(&scan->receiver);
    timeval_put(&(scan->start_time), 0.0);
    timeval_put(&(scan->end_time),  10.0);
    strcpy(scan->filename, "");
    scan->transport       = 0;
    scan->connection_mode = BEFORE;
    scan->serverflg       = RECEIVE;
    scan->next = (struct scan_cmd *) NULL;
}

/*
 * Initialize struct send_recv
 */
void init_sendrecv_param(sr)
struct send_recv *sr;
{
    strcpy(sr->hostname, "");
    strcpy(sr->hostname_cmd, "");
    sr->traffic_n   = 0;
    sr->port        = 0;
    sr->send_buff   = 0;
    sr->recv_buff   = 0;
    sr->mss         = 0;
    sr->mem_align   = 0;
    sr->align_offset= 0;
    sr->align_pad   = 0;
    sr->record_buff = 0;
    sr->trace_buff  = 0;
    sr->so_debug    = OFF;
    sr->tcp_trace   = OFF;
    sr->no_delay    = OFF;
    sr->status      = S_CLOSE;
}

/*****************************************************************
 * Check Parameter
 *****************************************************************/
/*
 * Check send_recv
 */
void check_sendrecv_param(sr)
struct send_recv *sr;
{
    if (strcmp(sr->hostname_cmd, "") == 0) {
	if (strcmp(sr->hostname, "") != 0) {
	    strcpy(sr->hostname_cmd, sr->hostname);
	} else {
	    p_error("'hostname' is not defined.");
	    exit(1);
	}
    }
    if (sr->traffic_n == 0) {
	p_error("'pattern' is not defined.");
	exit(1);
    }
}

/*
 * Check scan_cmd
 */
void check_scan(scan)
struct scan_cmd *scan;
{
    if (strcmp(scan->sender.hostname, "") == 0) {
	p_error("'sender' is not defined.");
	exit(1);
    }

    if (strcmp(scan->receiver.hostname, "") == 0) {
	p_error("'receiver' is not defined.");
	exit(1);
    }

    if (strcmp(scan->filename, "") == 0) {
	p_error("'file' is not defined.");
	exit(1);
    }

    if (scan->transport == 0) {
	p_error("'protocol' is not defined.");
	exit(1);
    }

    if (scan->transport == UDP) {
	scan->sender.so_debug    = OFF;
	scan->sender.tcp_trace   = OFF;
	scan->sender.no_delay    = OFF;
	scan->receiver.so_debug  = OFF;
	scan->receiver.tcp_trace = OFF;
	scan->receiver.no_delay  = OFF;
    }

    if (((scan->transport == UDP || scan->serverflg == RECEIVE) && scan->receiver.port == 0)||
	((scan->transport == UDP || scan->serverflg == SEND   ) && scan->sender.port == 0)) {
	p_error("server 'port = 0' error.");
	exit(1);
    }
}

/*****************************************************************
 *    Parser
 *****************************************************************/
/*
 *  skip "Strings"
 */
void skip(str, fp)
char *str;
FILE *fp;
{
    char *p;
    int c;

    DEBUGMSG2(8, fprintf(stderr, "(S:"));

    while ((c=ffgetc(fp)) != EOF) {
	if (c == '#') {               /*    #   */
	    if (debug >= 8) {
		fprintf(stderr, "(C:");
		while (c != EOF && c != '\n') {
		    fputc(c, stderr);
		    c = ffgetc(fp);
		}
		fprintf(stderr, ":C)\n");
	    } else {
		while (c != EOF && c != '\n')
		    c = ffgetc(fp);
	    }
	} else {
	    p = str;
	    while (*p != c) {
		if (*p == '\0') {
		    DEBUGMSG2(8, fprintf(stderr, ":S)"));
		    uungetc(c, fp);
		    return;
		}
		p++;
	    }
	    DEBUGMSG2(8, fputc(c, stderr));
	}
    }
}

/*
 *  get a word before "str" -> buf
 */
int getword(buf, str, fp)
char *buf, *str;
FILE *fp;
{
    int c, l=0;
    char *p;

    if (feof(fp) != 0)
	return EOF;

    DEBUGMSG2(8, fprintf(stderr, "(G:"));

    while ((c = *buf = ffgetc(fp)) != EOF) {
	/*
	 *  '#' is a mark meaning the beginning of comments. 
	 *
         */
	if (c == '#') {               /*    #   */
	    if (debug >= 8) {
		fprintf(stderr, "(C:");
		while (c != EOF && c != '\n') {
		    fputc(c, stderr);
		    c = ffgetc(fp);
		}
		fprintf(stderr, ":C)\n");
	    } else {
		while(c != EOF && c != '\n')
		    c = ffgetc(fp);
	    }
	} else {
	    for (p = str; *p != '\0'; p++) {
		if (*p == c) {
		    uungetc(c, fp);
		    *buf = '\0';
		    DEBUGMSG2(8, fprintf(stderr, ":G)"));
		    return !EOF;
	        }
            }
	    DEBUGMSG2(8, fputc(c, stderr));
   	    if (l >= CHAR_ARRAY-1) {
		p_error("word is too long");
		exit(1);
	    }
	    l++;
	    buf++;
	}
    }

    DEBUGMSG2(8, (fprintf(stderr, ":G)"),fputc('\n', stderr)));

    *buf = '\0';

    return !EOF;
}

/*****************************************************************
 *    mini subroutine
 *****************************************************************/
/*
 * fgetc + count(line, column)
 * Size of error_buf is MAX_COLUMN + 1.
 */
int ffgetc(fp)
FILE *fp;
{
    int c,i;

    c = fgetc(fp);

    if (c == '\n') {
	line++;
	bcolumn = column;
	column = 1;
	error_buf[column-1] = c;
	error_buf[column]   = '\0';
    } else if (c == '\t') {
	for (i = column; i <= (column/8+1)*8; i++) {
	    if (i >= CHAR_ARRAY) {
		p_error("line is too long");
		exit(1);
	    }
	    error_buf[i-1] = ' ';
	}
	error_buf[i-1]   = '\0';
	column = i;
    } else {
	if (column >= CHAR_ARRAY) {
	    p_error("line is too long");
	    exit(1);
	}
	error_buf[column-1] = c;
	error_buf[column]   = '\0';
	column++;
    }
    
    return c;
}

/*
 * ungetc + uncount(line, column)
 */
int uungetc(c, fp)
int c;
FILE *fp;
{
    if (c == '\n') {
	line--;
	column = bcolumn;
    } else {
	column--;
    }

    return ungetc(c, fp);
}

/*
 * print error(line, column)
 */
void p_error(s)
char *s;
{
    int i;

    fprintf(stderr, "Error: line %d column %d (or before):\n %s\n", line, column, s);
    fprintf(stderr, "%s\n", error_buf);

    for (i=1; i < column; i++) {
	printf(" ");
    }
    printf("^\n");
}



syntax highlighted by Code2HTML, v. 0.9.1