/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <stdio.h>
#include "types.h"
#include <debug.h>
#include <parsefile.h>
struct Flag
  { char code;
    char size;
    short offset;
    char *help;
  };
#define SetAllFlagsChar 'A'
#define SetLogFileChar 'f'
short LogFlags[5] = {0, 0, 0, 0, 0};
/*struct FormattedFile *LogFile = 0;*/
FILE *LogFile = 0;
struct Flag LogFlagsTable[] =
  {
    'e', 2, LogExprIndex*2,
	"Print all expressions (before=2;after=1) optimization",
    'i', 2, LogVerboseIndex*2,
	"Be verbose: More information messages",
    's', 2, LogCodeIndex*2,
	"Print generated assembler (for interactive commands)",
    'x', 2, LogCodeGenIndex*2,
	"Log compiled expressions and moves",
    'r', 2, LogRefCountIndex*2,
	"Log reference count changes: {Free|Copy}Token",
    SetLogFileChar, 0, -1,		"Specify new log file name",
    SetAllFlagsChar, 0, -1, "Set (change) every flag",
    'h', 0, -1, "Print list of flags and ask for changes",
    0, 0, 0, 0
  };

void PrintFlags(void *object, struct Flag *format, FILE *file)
{
    register struct Flag *ptr = format;
    for ( ; ptr->code != '\0'; ptr++) {
	char *pos = (char*)object + ptr->offset;
	fprintf(file, "%c", ptr->code);
	switch (ptr->size) {
	  case 4: fprintf(file, "[%d]", *(long*)pos); break;
	  case 2: fprintf(file, "[%d]", *(short*)pos); break;
	  case 1: fprintf(file, "[%d]", *(char*)pos); break;
	}
	fprintf(file, "\t%s\n", ptr->help);
    }
}

char * StringGetInt(char *string, int *ptr)
  {
    if (*string >= '0' && *string <= '9')
      { int i = 0;
	do i = i * 10 + (*string++ - '0');
	while (*string >= '0' && *string <= '9');
	*ptr = i;
      }
    return string;
  }

int StringInterpChange(char** sptr, int val, char* dfault)
  { char *nstr, *string = *sptr, *start = string;
    for (;;)
      { int ch = *string;
	if (ch == '+' || ch == '-' || ch == '^' || ch == '|' || ch == '&')
	  { int i = 1;
	    string++;
	    string = StringGetInt(string, &i);
	    switch (ch)
	      {
	      case '+': val += i; break;
	      case '-': val -= i; break;
	      case '^': val ^= i; break;
	      case '&': val &= i; break;
	      case '|': val |= i; break;
	      }
	    continue;
	  }
	nstr = StringGetInt(string, &val);
	if (nstr == string) break;
	string = nstr;
      }
    if (string == start && dfault != NULL)
	val = StringInterpChange(&dfault, val, NULL);
    *sptr = string;
    return val;
  }

void InterpField(char** sptr, void *object, struct Flag *field, char *dfault)
{
    char *addr = (char*)object + field->offset;
    switch (field->size)
      {
      case 4:
	*(long*)addr = StringInterpChange(sptr, *(long*)addr, dfault);
	return;
      case 2:
	*(short*)addr = StringInterpChange(sptr, *(short*)addr, dfault);
	return;
      case 1:
	*(char*)addr = StringInterpChange(sptr, *(char*)addr, dfault);
	return;
      }
}

char* SetAllFlags(char* string, void *table, struct Flag *format)
  { register struct Flag *ptr = format; char *nstr;
    for ( ; ptr->code != '\0'; ptr++)
      {
	if (ptr->offset == -1) continue;
	InterpField(&string, table, ptr, "1");
      }
    return nstr;
  }

char* SetFlags(char *string, void *object, struct Flag *format)
  { register struct Flag *ptr;
  retry:
    while (*string)
      {
	for (ptr = format; ptr->code != *string; ptr++)
	    if (ptr->code == '\0')
		if (WhiteSpace(*string))
		  {
		    do string++; while WhiteSpace(*string);
		    goto retry;
		  }
		else return string;
	if (ptr->offset == -1) return string;
	string++;
	InterpField(&string, object, ptr, "1");
      }
    return NULL;
  }

void LogSetFlags(register char *string)
  /* 'string' could be trashed if 'f' flag exists */
{
    char *nstr;
    for ( ; *string; string = nstr)
      {
	nstr = SetFlags(string, LogFlags, LogFlagsTable);
	if (nstr == NULL) return;
	if (nstr != string) continue;
	if (*string == SetAllFlagsChar)
	  { nstr = SetAllFlags(string+1, LogFlags, LogFlagsTable);
	    continue;
	  }
	if (*string == SetLogFileChar) /* redirect LogFile to named file */
	  { int delim;
	    string++;
	    delim = *string++;
	    if (delim == '\0' || WhiteSpace(delim)) nstr = string - 1;
	    else
	        for (nstr = string; ; )
		    if (*nstr == '\0') break;
		    else if (*nstr == delim) {*nstr++ = '\0'; break;}
	    if ((FILE*)LogFile == stderr) LogFile = NULL;
	    if (LogFile != NULL) { fclose(LogFile); LogFile = NULL; }
	    if (nstr > string)
	      {
		LogFile = fopen(string, "a");
		if (LogFile == NULL)
		    fprintf(stderr,
		   "Failed to open log file \"%s\" -- using stderr instead.\n",
		    string);
	      }
	    if (LogFile == NULL) LogFile = stderr;
	    continue;
	  }
	fprintf(stderr,
	    "-- Logging flags -- {code[current value]  description}\n");
	PrintFlags(LogFlags, LogFlagsTable, stderr);
#if 0
	fprintf(stderr, "Give new option string: ");
	fflush(stderr);
	string = (char*)ReadLine(stdin);
	if (string != NULL) goto retry;
	fprintf(stderr, "[Aborted option string]\n");
#endif
	return;
      }
}

#if 0
#include <format.h>

struct Flag FormattedMenu[] =
  {
    'p', 4, 20, "cursor position in line", 
    'w', 4, 24, "max line width",
    'i', 4, 28, "left indentation",
    'b', 1, 36, "output base (radix)",
    'd', 1, 37, "detail",
    'n', 2, 34, "max nesting level",
    '\0', 0, 0
  };

EditStdoutFields(string)
    char *string;
  { char *nstr;
    if (!IsFormattedFile(stdout))
      {
	fprintf(stderr, "EditStdoutFields: stdout not a FormattedFile!\n");
	return;
      }
  retry:
    for ( ; *string; string = nstr)
      {
	nstr = SetFlags(string, stdout, FormattedMenu);
	if (nstr == NULL) return;
	if (nstr != string) continue;
	fprintf(stderr,
	    "-- stdout flags -- {code[current value]  description}\n");
	PrintFlags(stdout, FormattedMenu, stderr);
	fprintf(stderr, "Give new option string: ");
	fflush(stderr);
	string = (char*)ReadLine(stdin);
	if (string != NULL) goto retry;
	fprintf(stderr, "[Aborted option string]\n");
	return;
      }
  }
#endif
