/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <stdio.h>
#include "types.h"
#include <parsefile.h>
//#include "hash.h"

extern "C" int GetNonDecimal(int ch, int base)
 /* Treat ch as digit with given base. Return integer value, or -1 on error */
{
    if (ch >= 'a')
	ch -= 'a' - 10;
    else if (ch >= 'A')
	ch -= 'A' - 10;
    else if (ch >= '0' && ch <= '9')
	ch -= '0';
    else
	return -1;
    if (ch >= base)
	return -1;
    return ch;
}

#define IsEscaped 0x100
#define ReturnEscaped(x) return IsEscaped+(x)
int GetEscaped(register FILE *f)
/* 
 * Does the same as fgetc(f), except recognizes and handles '\' sequences.
 * I.e. if '\' is seen, the next character(s) are examined.
 * These determine the result, to which IsEscaped is added.
 * E.g. "\n" returns <line-feed>+IsEscaped.
 */
  { register ch; int i, d;
    ch = getc(f);
    if (ch != '\\')
	return ch;
    switch (ch = getc(f))
      {
        case 0:
	case EOF:	ReturnEscaped('\\');
        case '\\':      ReturnEscaped('\\');	/* '\' itself */
        case 'b':       ReturnEscaped('\b');	/* back-space */
        case 't':       ReturnEscaped('\t');	/* horitonatal tab */
	case 'E':			/* -allow \E, since termcap does */
        case 'e':       ReturnEscaped('\033');	/* escape */
        case 'r':       ReturnEscaped('\r');	/* carriage return */
        case 'n':       ReturnEscaped('\n');	/* line feed */
	case 'f':	ReturnEscaped('\f');	/* form feed */
	case '?':	ReturnEscaped(127);	/* rub-out */
	case 'q':				/* quote */
	    ch = getc(f);
	    if (ch == EOF) ReturnEscaped('q');
	    ReturnEscaped(ch);
	case '^':				/* ^a etc */
	    ch = getc(f);
	    if (ch == EOF) ReturnEscaped('^');
	    ReturnEscaped(ch & 037);
	case 'x':
	    ch = fgetc(f);			/* get 1st hex digit */
	    d = GetNonDecimal(ch, 16);
            if (d < 0)
	      {
		ungetc(ch, f);
		ReturnEscaped('x');
	      }
	    i = d;
	    ch = fgetc(f);			/* get 2nd hex digit */
	    d = GetNonDecimal(ch, 16);
            if (d < 0)
	      {
		ungetc(ch, f);
		ReturnEscaped(i);
	      }
	    i <<= 4;
	    i += d;
	    ReturnEscaped(i);
        default:
            if (ch < '0' || ch > '7')
		ReturnEscaped(ch);
	    i = ch - '0';
	    ch = fgetc(f);			/* get 2nd octal digit */
            if (ch < '0' || ch > '7')
	      {
		ungetc(ch, f);
		ReturnEscaped(i);
	      }
	    i <<= 3;
	    i += ch - '0';
	    ch = fgetc(f);			/* get 3rd octal digit */
            if (ch < '0' || ch > '7')
	      {
		ungetc(ch, f);
		ReturnEscaped(i);
	      }
	    i <<= 3;
	    i += ch - '0';
	    ReturnEscaped(i);
      }
  }

int ParseBackslash(register InStream *ff)
{
#define GET_NEXT(ch) ch = ff->get()
#define UNDO_CHAR(ch) ff->putback(ch)
#include "backslash.h"
#undef GET_NEXT
#undef UNDO_CHAR
#undef YIELD_CHAR
}

// Read a backslash-escape-sequence starting with *in_ptr,
// ending with in_fence.  Udate *in_ptr to next unused character.
// Return the resulting character; or -1 on end; -2 on error.
// Initially, (*in_ptr)[-1] is assumed to be the '\\'.

extern "C" int
GetBackslash(const char **in_ptr, const char *in_fence)
{
    register char *in = *in_ptr;
    int c;
#define GET_NEXT(ch) ch = (in >= in_fence ? -1 : *in++)
#define UNDO_CHAR(ch) in--
#define YIELD_CHAR(ch) c = ch; goto done;
#define NO_HANDLE_CONTINUATION
#define NO_BACKSLASH_DEFAULT

#include "backslash.h"

#undef NO_BACKSLASH_DEFAULT
#undef NO_HANDLE_CONTINUATION
#undef GET_NEXT
#undef UNDO_CHAR
#undef YIELD_CHAR
  done:
    *in_ptr = in;
    return ch;
}

extern "C" void ScanBackslash(char **out_ptr,
			      const char **in_ptr, const char *in_fence)
{
    register char *out = *out_ptr;
    register char *in = *in_ptr;
#define GET_NEXT(ch) ch = (in >= in_fence ? -1 : *in++)
#define UNDO_CHAR(ch) in--
#define YIELD_CHAR(ch) *out++ = ch; goto loop
    for (;;) {
	int c;
	GET_NEXT(c);
	if (c == '\"') {
	    /* ??? UNDO_CHAR(c); */
	    break;
	}
	else if (c == -1)
	    break;
	else if (c == '\\') {
#if 1
	    *in_ptr = in;
	    c = GetBackslash(in_ptr, in_fence);
	    if (c == EOF) break; /* ERROR */
	    else if (c == -2) break; /* FIXME */
	    in = *in_ptr;
#else
#include "backslash.h"
#endif
	}
	else
	    YIELD_CHAR(c);
      loop:;
    }
    *out_ptr = out;
    *in_ptr = in;
#undef GET_NEXT
#undef UNDO_CHAR
#undef YIELD_CHAR
}

int ParseEscaped(register InStream *ff)
/* 
 * Does the same as fgetc("f"), except recognizes and handles '\' sequences.
 * I.e. if '\' is seen, the next character(s) are examined.
 * These determine the result, to which IsEscaped is added.
 * E.g. "\n" returns <line-feed>+IsEscaped.
 * Returns -2 if not proper character.
 */
{
    int ch;
    ch = ff->get();
    if (ch != '\\')
	return ch;
    return IsEscaped+ParseBackslash(ff);
}
