/************************************************************************/
/*                                                                      */
/*      Bakslash -- Provide C-style "\" escape encode/decode            */
/*                                                                      */
/*      Although this module pretends to be a generic, reusable         */
/*      module, it is in fact specific to the interface offered         */
/*      by "grep" in more than a few ways.  Requires some more          */
/*      interface routines and/or configuration options to be           */
/*      more useful.                                                    */
/*                                                                      */
/*      (Note for the pedantic: I've used "Bakslash" instead of         */
/*      "Backslash" since I'm trying to keep a strict one-to-one        */
/*      correspondence between module names and file names.)            */
/*                                                                      */
/*      The implementation of this module could be optimised by         */
/*      using the Grouse FSA.  behoffski has not done so because        */
/*      this is an after-hours project and he's focussing on the        */
/*      major function instead of nuances.  Would be nice, though.      */
/*                                                                      */
/*      Copyright (C) Grouse Software 1995-9.  All rights reserved.     */
/*      Written for Grouse by behoffski (Brenton Hoff).                 */
/*                                                                      */
/*      Free software: no warranty; use anywhere is ok; spread the      */
/*      sources; note any mods; share variations and derivatives        */
/*      (including sending to behoffski@grouse.com.au).                 */
/*                                                                      */
/************************************************************************/

#include "ascii.h"
#include "bakslash.h"
#include <compdef.h>
#include "ctype.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"

#define HEX2BYTE(ch) ((BYTE) (((ch)<='9') ? (ch)-'0' : tolower(ch) - 'a' + 10))


/************************************************************************/
/*                                                                      */
/*      Decode -- Convert text containing \ escapes to binary           */
/*                                                                      */
/************************************************************************/
public_scope BYTE
Bakslash_Decode(CHAR **ppLine)
{
        CHAR *pLine = *ppLine;
        CHAR ch;

        /*Default to consuming one character*/
        *ppLine = pLine + 1;

        switch (*pLine) {
        case '\\':
                /*"\" literal*/
                return (BYTE) '\\';
                break;

        case 'a':
                /*Alert (bell)*/
                return (BYTE) '\a';
                break;

        case 'b':
                /*Backspace*/
                return (BYTE) '\b';
                break;

        case 'f':
                /*Form feed*/
                return (BYTE) '\f';
                break;

        case 'n':
                /*Newline*/
                return (BYTE) '\n';
                break;

        case 'r':
                /*Carriage return*/
                return (BYTE) '\r';
                break;

        case 't':
                /*Horizontal tab*/
                return (BYTE) '\t';
                break;

        case 'v':
                /*Vertical tab*/
                return (BYTE) '\v';
                break;

        case 'x':
        case 'X':
                /*One- or two-digit hex sequence*/
                ch = *++pLine;
                if (! isxdigit(ch)) {
                        /*?? Not properly formed... treat as escaped "x"?*/
                        return (BYTE) pLine[-1];
                }
                /*Does the sequence contain two digits?*/
                if (isxdigit(pLine[1])) {
                        /*Yes, consume them and decode now*/
                        *ppLine += 2;

                        /*?? Should consume ALL hex digits*/

                        return (HEX2BYTE(ch) << 4) | HEX2BYTE(pLine[1]);

                } else {
                        /*No, consume one digit and return conversion*/
                        *ppLine += 1;
                        return HEX2BYTE(pLine[0]);

                }
                break;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
                /*First digit of octal sequence... is second also octal?*/
                ch = *++pLine;
                if ((ch < '0') || (ch > '7')) {
                        /*No, merely single octal digit*/
                        return (BYTE) (pLine[-1] - '0');
                }

                /*Two-digit sequence... are all three chars one octal number?*/
                if ((pLine[-1] > '3') || (pLine[1] < '0') ||
                                (pLine[1] > '7')) {
                        /*No, report two-digit code*/
                        *ppLine += 1;
                        return ((((BYTE) (pLine[-1] - '0')) << 3) |
                                      ((BYTE) (ch - '0')));

                }

                /*Three-digit octal sequence found*/
                *ppLine += 2;
                return (((BYTE) (pLine[-1] - '0')) << 6) |
                       (((BYTE) (       ch - '0')) << 3) |
                        ((BYTE) ( pLine[1] - '0'));

                break;

        case NUL:
                /*Unexpected end of line*/
                fprintf(stderr, "%s: trailing backslash\n", 
                        "ggrep");
                exit(2);
                break;

        default:
                /*Return character WITHOUT special meaning*/
                return (BYTE) *pLine;
        }

        /*NOTREACHED*/
        return 0xff;

} /*Decode*/


/************************************************************************/
/*                                                                      */
/*      DecodeLine -- Edit line to replace escape seq's                 */
/*                                                                      */
/*      Returns FALSE if the decoding was abandoned for any reason.     */
/*                                                                      */
/************************************************************************/
public_scope BOOL
Bakslash_DecodeLine(CHAR *pLine)
{
        CHAR *pDecoded = pLine;

        for (;;) {
                switch (*pLine) {
                case NUL:
                        /*End of line*/
                        *pDecoded = NUL;
                        return TRUE;
                        break;

                case '\\':
                        /*Start of escape sequence found*/
                        pLine++;
                        *pDecoded++ = Bakslash_Decode(&pLine);
                        break;

                default:
                        /*Copy literal to decoded string*/
                        *pDecoded++ = *pLine++;
                        break;

                }

        }

        return TRUE;

} /*DecodeLine*/


/************************************************************************/
/*                                                                      */
/*      EncodeByte -- Provide printable encoding for byte               */
/*                                                                      */
/*      Encoding will be typically two to no more than six bytes.       */
/*                                                                      */
/************************************************************************/
public_scope void
Bakslash_EncodeByte(BYTE b, CHAR *pEncoding)
{
        /*Select encoding according to value*/
        switch (b) {
        case NUL:
                strcpy(pEncoding, "\\0");
                break;

        case BEL:
                strcpy(pEncoding, "\\a");
                break;

        case BS:
                strcpy(pEncoding, "\\b");
                break;

        case FF:
                strcpy(pEncoding, "\\f");
                break;

        case CR:
                strcpy(pEncoding, "\\r");
                break;

        case LF:
                strcpy(pEncoding, "\\n");
                break;

        case TAB:
                strcpy(pEncoding, "\\t");
                break;

        case VT:
                strcpy(pEncoding, "\\v");
                break;

        case '\\':
                strcpy(pEncoding, "\\\\");
                break;

        case '"':
                strcpy(pEncoding, "\\\"");
                break;

        case '\'':
                strcpy(pEncoding, "\\'");
                break;

        default:
                /*Is the character printable?*/
                if (isprint(b)) {
                        /*Yes, report it as-is*/
                        sprintf(pEncoding, "%c", b);
                        break;
                }

                /*Report character as a \x escape*/
                sprintf(pEncoding, "\\x%02x", b);
                break;

        }

} /*EncodeByte*/


/************************************************************************/
/*                                                                      */
/*      Init -- Prepare module for operation                            */
/*                                                                      */
/************************************************************************/
public_scope void
Bakslash_Init(void)
{
        /*No initialisation required*/

} /*Init*/