%{
/*
 * lexer.l
 *
 * (c) Copyright 1988-1994 Adobe Systems Incorporated.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, distribute, and sublicense this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notices appear in all copies and that
 * both those copyright notices and this permission notice appear in
 * supporting documentation and that the name of Adobe Systems Incorporated
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  No trademark license
 * to use the Adobe trademarks is hereby granted.  If the Adobe trademark
 * "Display PostScript"(tm) is used to describe this software, its
 * functionality or for any other purpose, such use shall be limited to a
 * statement that this software works in conjunction with the Display
 * PostScript system.  Proper trademark attribution to reflect Adobe's
 * ownership of the trademark shall be given whenever any such reference to
 * the Display PostScript system is made.
 * 
 * ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 * ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON- INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE
 * TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT
 * PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
 * 
 * Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
 * Incorporated which may be registered in certain jurisdictions
 * 
 * Author:  Adobe Systems Incorporated
 */
/* $XFree86: xc/config/pswrap/lexer.l,v 1.10 2001/10/28 03:32:03 tsi Exp $ */

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

#include "pswpriv.h"
#include "pswparser.h"

/* flex of EMX seems to be too old */
#if defined(FLEX_SCANNER) && !defined(YY_FLEX_LEX_COMPAT) && !defined(__UNIXOS2__)
int yylineno = 1;
#endif

static int parens = 0;		/* current paren balancing */
static char *sbody;		/* cur pointer into string_temp */
static int curleng = 0;		/* current scanned string length */
static int strlineno;		/* start line of current string */

static int nonComment = true;	/* are we outside C comments? */
static int newLine = true;	/* are we starting a new line? */
static int hexstringerrs = 0;	/* found an error in this hex string */

int errorCount = 0;		/* number of non-fatal errors */

#ifdef PSWDEBUG
int lexdebug = 1;		/* debug flag */
#define DEBUGP(x) {if (lexdebug) { fprintf x ; };}
#else /* PSWDEBUG */
#define DEBUGP(x)
#endif /* PSWDEBUG */

#undef YYLMAX
#define YYLMAX 16384

/* ErrIntro prints a standard intro for error messages;
 * change it if your system uses something else.  We have many options:
 *
 * to match Macintosh:  #define FMT "File \"%s\"; Line %d # "
 * to match BSD cc:	#define FMT "\"%s\", line %d: "
 * to match gcc:	#define FMT "%s:%d: "
 * to match Mips cc:	#define FMT "pswrap: Error: %s, line %d: "
 */
#define INTRO	"# In function %s -\n"

#ifdef macintosh
#define FMT "File \"%s\"; Line %d # "
#else /* macintosh */
#define FMT "\"%s\", line %d: "
#endif /* macintosh */

void ErrIntro(int line)
{
    if (! reportedPSWName && currentPSWName) {
		reportedPSWName = 1;
		fprintf(stderr,INTRO,currentPSWName);
    }
    fprintf(stderr,FMT,ifile,line);
    errorCount++;
}


void yyerror(char *errmsg)
{
    ErrIntro(yylineno);
    fprintf(stderr,"%s near text \"%s\"\n",errmsg,yytext);
}
%}

%o 3500
%a 2500

%START PS STR HEX DEF SUB

DIGIT		([0-9])
HEXDIGIT	([a-fA-F0-9])
RADIXDIGIT	([a-zA-Z0-9])
LETTER		([a-zA-Z])
SIGN		([+-])
EXP		([eE]{SIGN}?{DIGIT}+)
W		([ \t\n\r])

%%

<PS>^defineps 	{	/* inside a wrap - just a PS token */
	if (nonComment) {
	    newLine = false;
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object, yytext);
	    DEBUGP((stderr,"PSNAME ->%s\n",yytext));
	    return PSNAME;
	}
}

<DEF>^defineps	{ 	/* treat as a C name inside the def part */
	if (nonComment) {
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object, yytext);
	    DEBUGP((stderr,"CNAME ->%s\n",yytext));
	    return CNAME;
	}
}

<STR>^defineps	{	/* part of string text */
	register int i = yyleng;
	register unsigned char *c = (unsigned char *) yytext;

	if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	curleng += yyleng;
	for (; i > 0; --i) *sbody++ = *c++;
}

^{W}*defineps { 		/* start of a wrap definition */
	if (nonComment) {
	    BEGIN DEF;
	    DEBUGP((stderr,"DEFINEPS\n"));
	    return DEFINEPS;
	} else ECHO;
}

<PS>endps {		/* end of a wrap */
	if (nonComment){
	    if (newLine) {
	        newLine = false;
	    	DEBUGP((stderr,"ENDPS\n"));
	    	BEGIN 0;
	    	return ENDPS;
	    } else {
	    	DEBUGP((stderr,"PSNAME %s\n",yytext));
	    	yylval.object = psw_malloc(strlen(yytext) + 1);
	    	strcpy(yylval.object, yytext);
	    	return PSNAME;
	    }
	}	    
}

<DEF>^endps	{ 	/* treat as a C name inside the def part? */
	if (nonComment) {
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object, yytext);
	    DEBUGP((stderr,"CNAME ->%s\n",yytext));
	    return CNAME;
	}
}

<STR>^defineps	{	/* part of string text */
	register int i = yyleng;
	register unsigned char *c = (unsigned char *) yytext;

	if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	curleng += yyleng;
	for (; i > 0; --i) *sbody++ = *c++;
}

endps { 		/* BOGUS end of a wrap */
	if (nonComment) {
	    ErrIntro(yylineno);
	    fprintf(stderr,"endps without matching defineps\n");
	} else ECHO;
}

%{
    	/* real definition stuff */
%}

<DEF>"/*" {		/* C comments on */
	nonComment = false;
}

<DEF>"*/" {		/* C comments off */
	nonComment = true;
}

<DEF>"("	{	/* formals start */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->(\n"));
	    return '(';
	}
}

<DEF>")"	{	/* formals end */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->)\n"));
	    BEGIN PS;
	    return ')';
	}
}

<DEF>[|,;[\]*:]	{	/* special single chars */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->%c\n",yytext[0]));
	    return (yytext[0]);
	}
}

%{
	/* formals type names */
%}

<DEF>"static" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->static\n"));
	    return STATIC;
	}
}

<DEF>"boolean" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->boolean\n"));
	    return BOOLEAN;
	}
}

<DEF>"char" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->char\n"));
	    return CHAR;
	}
}

<DEF>"int" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->int\n"));
	    return INT;
	}
}

<DEF>"short" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->short\n"));
	    return SHORT;
	}
}

<DEF>"long" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->long\n"));
	    return LONG;
	}
}

<DEF>"unsigned" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->unsigned\n"));
	    return UNSIGNED;
	}
}

<DEF>"float" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->float\n"));
	    return FLOAT;
	}
}

<DEF>"double" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->double\n"));
	    return DOUBLE;
	}
}

<DEF>"userobject" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->userobject\n"));
	    return USEROBJECT;
	}
}

<DEF>"numstring" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->numstring\n"));
	    return NUMSTRING;
	}
}

<DEF>"DPSContext" {
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->DPScontext\n"));
	    return PSCONTEXT;
	}
}

%{
	/* C format numbers in formals part */
%}

<DEF>[1-9]{DIGIT}*	{ 		/* base 10 number */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->CINTEGER %s\n",yytext));
	    yylval.intobj = atoi((char *) yytext);
	    return CINTEGER;
	}
}

<DEF>0{DIGIT}*		{		/* octal number (and zero) */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->CINTEGER %s\n",yytext));
	    /* I have no idea why the cast is needed in the next line,
	       but it seems to make the compiler happy on alpha */
	    sscanf((char *) yytext,"%lo",&yylval.intobj);
	    return CINTEGER;
	}
}

<DEF>0[xX]{HEXDIGIT}+	{		/* hex number */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->CINTEGER %s\n",yytext));
	    /* See previous comment... */
	    sscanf((char *) yytext,"0x%lx",&yylval.intobj);
	    return CINTEGER;
	}
}

<DEF>[a-zA-Z_][a-zA-Z0-9_]* {	/* C identifier */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->CNAME %s\n",yytext));
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object, yytext);
	    return CNAME;
	}
}

<DEF>{W}+ {
}

<DEF>[^a-zA-Z0-9_|,;[\]*:\(\)/ \t\n\r]+ {	/* all else - ERROR */
	if (nonComment) {
	    DEBUGP((stderr,"DEF ->JUNK %s\n",yytext));
	    ErrIntro(yylineno);
	    fprintf(stderr,"invalid characters '%s' in definition\n",
	    	yytext);
	}
}


%{
	/* body part - PostScript code */
%}

<PS>\%.* {			/* a PS comment, ignore */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"comment %s\n",yytext));
	    /* yylval = yytext; return PSCOMMENT; */
	}
}

<PS>{SIGN}?{DIGIT}+ {		/* a decimal integer */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PSINTEGER ->%s\n",yytext));
	    yylval.intobj = atoi((char *) yytext);
	    return PSINTEGER;
	}
}

<PS>{DIGIT}+#{RADIXDIGIT}+ {	/* a radix number */
#define MAX_ULONG ((unsigned long) -1)
	unsigned base = 0;
	unsigned char *s = (unsigned char *) yytext;
	register unsigned long x, limit;
	register unsigned long i = 0;

	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PSINTEGER radix->%s\n",yytext));
	    while (*s != '#') {
		base *= 10;
		base += *s++ - '0';
		if (base > 32) break;
	    }

	    if ((base < 2) || (base > 36)) {goto error;}
	    else {
		limit = MAX_ULONG / base;
		s++; /* skip # sign */
		while (*s != '\0') {
		    if (i > limit) goto error;
		    i *= base;
		    if (isdigit(*s)) { x = *s - '0';}
		    else { x = (*s | 040) - ('a'-10);}
		    if (x >= base) goto error;
		    if (x > (MAX_ULONG - i)) goto error;
		    i += x;
		    s++;
		}
	    }
	    yylval.intobj = i;
	    return PSINTEGER;

	    error:;
	    ErrIntro(yylineno);
	    fprintf(stderr,"invalid radix number %s\n",yytext);
	    yylval.intobj = 0; /* ERROR */
	    return PSINTEGER;
	}
}

<PS>(({SIGN}?{DIGIT}+(\.{DIGIT}*)?{EXP}?)|({SIGN}?{DIGIT}*\.{DIGIT}+{EXP}?)) {
				/* a float */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PSREAL ->%s\n",yytext));
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object,yytext);
	    return PSREAL;
	}
}

<PS>"(" {			/* start PS string */
	if (nonComment) {
	    newLine = false;
	    BEGIN STR;
	    parens=1;
	    sbody = string_temp;
	    curleng = 0;
	    strlineno = yylineno;
	    *sbody = '\0';
	}
}

%{
	/* inside PS strings */
%}

<STR>"(" {			/* balancing parens */
	if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	curleng += yyleng;
	parens++;
	*sbody++ = '(';
}

<STR>")" {			/* balancing parens or end string */
	if ((--parens) == 0) {
		BEGIN PS;
		*sbody = '\0';
		yylval.object = psw_malloc(strlen(string_temp) + 1);
		strcpy(yylval.object, string_temp);
		DEBUGP((stderr,"PSSTRING ->%s\n",string_temp));
		return PSSTRING;
	}
	else if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	curleng += yyleng;
	*sbody++ = ')';
}

<STR>"\\"[0-7]([0-7]?)([0-7]?) {	/* quoted octal byte */
	if (4 + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	curleng += 4;
	sprintf(sbody,"\\%3.3s",(yytext + 1));
	while (*sbody) {
	    if (*sbody == ' ') *sbody = '0';
	    sbody++;
	}
}


<STR>"\\\n"	{ /* ignore quoted newline */
}

<STR>"\\\\"	{ /* keep quoted backslashes in */
	if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	*sbody++ = '\\';
	*sbody++ = '\\';
	curleng += 2;
}


<STR>("\\".) {		/* other quoted char, including parens */
	if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	switch (yytext[1]) {
	case 'b':
	    *sbody++ = '\b';
	    break;
	case 'f':
	    *sbody++ = '\f';
	    break;
	case 'n':
	    *sbody++ = '\012';
	    break;
	case 'r':
	    *sbody++ = '\015';
	    break;
	case 't':
	    *sbody++ = '\t';
	    break;
	default:
	    *sbody++ = yytext[1];
	    break;
	}
	curleng++;
}


<STR>"\n" {		/* newline in a string */
	if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	curleng += yyleng;

	*sbody++ = '\n';
}

<STR>[^()\\\n]+ {		/* anything else */
	register int i = yyleng;
	register unsigned char *c = (unsigned char *) yytext;

	if (yyleng + curleng >= maxstring) {
		stringerr(1);
		BEGIN PS;
		*sbody = '\0';
		return PSSTRING;
	}
	curleng += yyleng;
	for (; i > 0; --i) *sbody++ = *c++;
}

%{
	/* hexidecimal strings "< >" */
%}

<PS>"<" {			/* begin hex string */
	if (nonComment) {
	    newLine = false;
	    BEGIN HEX;
	    sbody = string_temp;
	    *sbody = '\0';
	    curleng = 0;
	    strlineno = yylineno;
	    hexstringerrs = 0;
	}
}

<HEX>{W}+ {			/* hex whitespace */
	/* ignore */
}

<HEX>[0-9A-Fa-f]+ {		/* hex string data */
	if (yyleng + curleng >= maxstring) {
		stringerr(2);
		*sbody = '\0';
		BEGIN PS;
		return PSHEXSTRING;
	}
	curleng += yyleng;
	strcpy(sbody,yytext);
	sbody += yyleng;
}

<HEX>">" {			/* end hex string */
	BEGIN PS;
	*sbody = '\0';
	DEBUGP((stderr,"PSHEXSTRING ->%s\n",string_temp));
	yylval.object = psw_malloc(strlen(string_temp) + 1);
	strcpy(yylval.object, string_temp);
	return PSHEXSTRING;
}

<HEX>[^a-fA-F0-9> \t\n\r]+	{	/* ERROR */
	if (hexstringerrs == 0) { /* only complain once per string */
	    ErrIntro(yylineno);
	    fprintf(stderr,"invalid characters in hex string '%s'\n",yytext);
	    hexstringerrs++;
	}
}


%{
	/* straight postscript */
%}

<PS>"<<" 	{	/* just a PS token (for level 2 compatablity) */
	if (nonComment) {
	    newLine = false;
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object, yytext);
	    DEBUGP((stderr,"PSNAME ->%s\n",yytext));
	    return PSNAME;
	}
}

<PS>">>" 	{	/* just a PS token (for level 2 compatablity) */
	if (nonComment) {
	    newLine = false;
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object, yytext);
	    DEBUGP((stderr,"PSNAME ->%s\n",yytext));
	    return PSNAME;
	}
}

<PS>[\[\]\{\}] {		/* PS self delimiter */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PS ->%s\n",yytext));
	    return (yytext[0]);
	}
}

<PS>"true"|"false" {		/* boolean */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PSBOOLEAN %s\n",yytext));
	    yylval.intobj = (*yytext == 't');
	    return PSBOOLEAN;
	}
}

<PS>"/"[^ <>\(\)\[\]\{\}\%\/\t\n\r]* {	/* literal name */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PSLITNAME %s\n",yytext));
	    yylval.object = psw_malloc(strlen(yytext));
	    strcpy(yylval.object, yytext+1);
	    return PSLITNAME;
	}
}

<PS>[^ <>\(\)\[\]\{\}\%\/\\\t\n\r]+ {	/* executable name */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PSNAME %s\n",yytext));
	    yylval.object = psw_malloc(strlen(yytext) + 1);
	    strcpy(yylval.object, yytext);
	    return PSNAME;
	}
}

<PS>"*/" {				/* special case */
	if (nonComment) {
	    newLine = false;
	    unput('/');
	    yylval.object = psw_malloc(2);
	    strcpy(yylval.object, "*");
	    return PSNAME;
	}
}

<PS>"\\"[^ <>\(\)\[\]\{\}\%\/\\\t\n\r]+/"[" {	/* \name[index] */
	if (nonComment) {
	    DEBUGP((stderr,"\\PSNAME %s\n",yytext));
	    yylval.object = psw_malloc(strlen(yytext));
	    strcpy(yylval.object, yytext+1);
	    BEGIN SUB;
	    return PSSUBNAME;
	}
}

<SUB>[\[][^ \t\]]*[\]] {	/* [index] */
	if (nonComment) {
	    int len = strlen(yytext);
	    DEBUGP((stderr,"PSINDEX %s\n",yytext));
	    if (len == 2) {
		ErrIntro(yylineno);
		fprintf(stderr,"parameter index expression empty\n");
		yylval.object = "0";
	    }
	    else {
		yylval.object = psw_malloc(len);
		strncpy(yylval.object, yytext+1, len-2);
		yylval.object[len-2] = '\0';
	    }
	    BEGIN PS;
	    return PSINDEX;
	}
}

<SUB>[\[][^\]]*[\]] { /* error */
	if (nonComment) {
	    ErrIntro(yylineno);
	    fprintf(stderr,"parameter index expression error\n");
	    yylval.object = "0";
	    BEGIN PS;
	    return PSINDEX;
	}
}

<PS>[\)\>\\]+	{	/* ERROR */
	if (nonComment) {
	    newLine = false;
	    DEBUGP((stderr,"PS JUNK ->%s\n",yytext));
	    ErrIntro(yylineno);
	    fprintf(stderr,"invalid characters '%s'\n",yytext);
	}
}

<PS>[ \t\r]+ { }

<PS>[\n]+ {
	newLine = true;
}

[ \t]+	{		/* passthru stuff */
	ECHO;
}

\n	{
	outlineno++;
	ECHO;
};

"/*" {		/* C comments on */
	nonComment = false;
	ECHO;
}

"*/" {		/* C comments off */
	if (!nonComment) {
		nonComment = true;
		ECHO;
	}
}

%%

int yywrap (void) {
    if (!feof(yyin))
    	return (0);
/* The following appears not to work with flex.  As it is error
   handling code, we just comment it out. */
#if !defined(FLEX_SCANNER)
    if (yybgin != (yysvec+1)) {	/* make sure we are in state 0 */
	ErrIntro(yylineno);
	fprintf(stderr,"end of input file/missing endps\n");
    }
#endif
    return (1);
}

void stringerr(int type)
{
    ErrIntro(strlineno);
    fprintf(stderr,"%sstring too long (caught at line %d)\n",
    	((type==1)?"":"hex "),yylineno);
    errorCount++;
    return;
}
