%{
/****************************************************************** 
 *
 * nlvky.l
 *
 *     Lex tokens for parsing the file nlv-keys.cfg
 *
 *
 * Author : Dan Gunter
 * Created: September 1998
 *
 * Modification history
 * --------------------
 *
 ****************************************************************** 
 */

#include <stdlib.h>
#include <string.h>
#include <nlvky.tab.h>
#include <const.h>  /* for line type */
#include <util.h>   /* strupper */
#include <except.h>
  
int lineno;         /* Line number in input file */
int tokenpos = 0;   /* Position of an offending token */
char linebuf[1024]; /* Storage of '\n'-terminated 1 line */

/* Macro which increments tokenpos by length of current token */
#define TP tokenpos += yyleng

/* Hackorino for Solaris (why?) */
#ifndef nlvkylval
#define nlvkylval yylval
YYSTYPE yylval;
#endif

%}

DBLQUOTE [\"]

%%

"{"       { TP; return LIST_START; }	
"}"       { TP; return LIST_END; }
eventset  { TP; return EVENTSET; }
events    { TP; return EVENTS; }
groupalias { TP; return HOSTALIAS; }
id        { TP; return ID; }
group     { TP; return HOSTID; }
calc      { TP; return CALC; }
show      { TP; nlvkylval.strval = strdup("show");
                return CACTION; }
precision { TP; nlvkylval.strval = strdup("precision");
                return CACTION; }
title     { TP; return TITLE; }
type      { TP; return TYPE; }
val       { TP; return VAL; }
annotate  { TP; return ANNOTATE; }
timeordered { TP; return TIMEORD; }
autoscale { TP; return AUTOSCALE; }
rotateclients { TP; return ROTATECL; }
analysis  { TP; return ANALYSIS; }
epsilon   { TP; return EPSILON; }
keys      { TP; return KEYS; }
baselinerange { TP; return BRANGE; }
rejectlimit   { TP; return RLIMIT; }
eofexit       { TP; return EOFEXIT; }
firstevent    { TP; return FIRSTEVENT; }

line|point|load { TP;
                  nlvkylval.ival = ( nlvkytext[0] == 'p' ? NLV_POINT : (
                        nlvkytext[1] == 'i' ? NLV_LINE : NLV_LOAD ) );
                  return( KSTYPE );
                }
on|true|yes  { TP; nlvkylval.ival = 1; return( BOOL ); }
min     { TP; nlvkylval.ival = -1; return( MMCHOICE ); }
max     { TP; nlvkylval.ival =  1; return( MMCHOICE ); }
both    { TP; nlvkylval.ival =  2; return( MMCHOICE ); }
neither { TP; nlvkylval.ival =  0; return( MMCHOICE ); }

[0-9]+       { TP; nlvkylval.ival = atoi( nlvkytext );
               return INT;
             }
-?[0-9]+\.[0-9]+ { TP; nlvkylval.dval = atof( nlvkytext );
                 return FLOAT;
               }
off|false|no { TP; nlvkylval.ival = 0; return( BOOL ); }
{DBLQUOTE}[a-zA-Z0-9._\-\+ ]*{DBLQUOTE} { TP;
                  nlvkylval.strval = (char *)strdup( nlvkytext );
                  return QUOTED_STRING;
                }
[a-zA-Z0-9_.\-\+&]+ { TP; 
                  nlvkylval.strval = (char *)strdup( nlvkytext );
                  /* strupper( nlvkylval.strval ); */
                  return NAME;
                }
[ \t]+       { TP; }

\n.*       { strcpy(linebuf, yytext+1 ); /* save the next line */
              yyless(1);
              lineno++;
              tokenpos = 0;
           }
#.*       ;
.          { nlvkyerror("Unrecognized character"); } /* make them be CLEAN */
%%
/*
 *----------------------------------------------------------------------
 *
 * nlvkyerror -- Error reporting routine.
 *
 * Calls: fprintf
 *
 * Algorithm: Use global variables lineno, linebuf, tokenpos to
 *            make a more meaningful error message.
 *            If the first character of the input argument is
 *            a '*', then don't attempt diagnostics beyond the
 *            line and position of the error.
 *
 *----------------------------------------------------------------------
 */

#define STREQ(s,t) (strcmp(s,t)==0)
nlvkyerror( char *msg ) 
{ 
  char * fw;     /* first word in saved buffer */
  int    diag=1; /* show diagnostic? */ 

  /* 
   * special code: '*' in first character means don't
   * print a diagnostic
   */
  if ( msg && *msg=='*' ) {
    diag=0;
    msg++; /* don't worry about leak, we're leaving anyhoo */
  }

  fprintf(stderr, "\n*** Line %d: %s:\n%s\n", lineno, msg, linebuf);
  fprintf(stderr, "%*s\n", 1+tokenpos, "^" );
  /*
   * See if we can give more diagnostic messages 
   * based on the first word of the line
   */
  if ( diag && (fw = strtok( linebuf, " \t" )) ) {
    if ( STREQ(msg,"Invalid key"))
       fprintf(stderr,"Format: +key_name OR -key_name\n\n");
    /* 
     * If the error is right after the first keyword, the problem
     * is probably on the previous line.
     */
    else if ( tokenpos == strlen(fw) )
      fprintf(stderr,"The error may be in a malformed list or missing element\nin the previous line(s).\n\n");
    else if ( STREQ(fw,"hostalias") )
      fprintf(stderr,"Format: hostalias alias_to_name [ alias_from_1 .. alias_from_N ]\n\n");
    else if ( STREQ(fw,"set") )
      fprintf(stderr,"Format: set -|+set_name\n\n");
    else if ( STREQ(fw,"calc") )
      fprintf(stderr,"Format: calc name parameter value, e.g.: calc throughput show 0\n\n");
    else if ( STREQ(fw,"id") )
      fprintf(stderr,"Format: id field_name OR id [ field_name_1 .. field_name_N ]\n\n");
    else if ( STREQ(fw,"hostid") )
      fprintf(stderr,"Format: hostid field_name OR hostid [ field_name_1 .. field_name_N ]\n\n");
  }
  exit( -1 );
}
#undef STREQ

nlvkywrap() { return; }

#undef TP
