/*
 *  Type Printing for X11 protocol
 *
 *	James Peterson, 1988
 *
 * Copyright (C) 1988 MCC
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of MCC not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  MCC makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL MCC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */
/*
 * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

#include "scope.h"
#include "x11.h"

/*
  For each of the types we need a way to print that type.
  Types are of varieties:

  (1) BUILTIN -- we have a separate routine to interpret and print
  each built-in type.
  (2) ENUMERATED, SET -- we have one routine which prints, given the
  data and the list of values.
  (3) RECORDS -- a separate routine for each to print each field.

*/

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/* print representation of a character for debugging */
static const char *
printrep(unsigned short c)
{
    static char pr[8];

    if (c < 32) {
        /* control characters */
        pr[0] = '^';
        pr[1] = c + 64;
        pr[2] = '\0';
    }
    else if (c < 127) {
        /* printing characters */
        pr[0] = (char) c;
        pr[1] = '\0';
    }
    else if (c == 127)
        return ("<del>");
    else if (c <= 0377) {
        /* upper 128 codes from 128 to 255;  print as \ooo - octal  */
        pr[0] = '\\';
        pr[3] = '0' + (c & 7);
        c = c >> 3;
        pr[2] = '0' + (c & 7);
        c = c >> 3;
        pr[1] = '0' + (c & 3);
        pr[4] = '\0';
    }
    else {
        /* very large number -- print as 0xffff - 4 digit hex */
        snprintf(pr, sizeof(pr), "0x%04x", c);
    }
    return (pr);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/*
  we use indentation for two purposes:

   (1) To show substructure of records inside records ...
   (2) To separate the bytes from the client (on the left) from
       those from the server (on the right).

   Each indentation level is one tab (8 spaces).
*/

#define MaxIndent 10
char Leader[MaxIndent + 1];
static short CurrentLevel = 0;

void
SetIndentLevel(short which)
{
    short i;

    if (which > MaxIndent)
        which = MaxIndent;
    if (which < 0)
        which = 0;
    if (which == CurrentLevel)
        return;

    /* set the indent level to <which> */
    /* -> set the Print Leader to <which> tabs */
    for (i = 0; i < which; i++)
        Leader[i] = '\t';
    Leader[which] = '\0';
    CurrentLevel = which;
}

static void
ModifyIndentLevel(short amount)
{
    SetIndentLevel(CurrentLevel + amount);
}

static short
SizeofLeader(void)
{
    return (CurrentLevel * 8);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/* if we want verbose enough output, we will dump the buffer in hex */

void
DumpItem(const char *name, FD fd, const unsigned char *buf, long n)
{
    if (n == 0)
        return;

    fprintf(stdout, "%s%20s (fd %d): ", Leader, name, fd);

    DumpHexBuffer(buf, n);
    fprintf(stdout, "\n");
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

int
PrintINT8(const unsigned char *buf)
{
    /* print a INT8 -- 8-bit signed integer */
    short n = IByte(buf);

    if (n > 127)
        n = 256 - n;
    fprintf(stdout, "%d", n);
    return 1;
}

int
PrintINT16(const unsigned char *buf)
{
    /* print a INT16 -- 16-bit signed integer */
    long n = IShort(buf);

    if (n > 32767)
        n = 65536 - n;
    fprintf(stdout, "%ld", n);
    return 2;
}

int
PrintINT32(const unsigned char *buf)
{
    /* print a INT32 -- 32-bit signed integer */
    long n = ILong(buf);

    fprintf(stdout, "%ld", n);
    return 4;
}

/* ************************************************************ */

int
PrintCARD8(const unsigned char *buf)
{
    /* print a CARD8 -- 8-bit unsigned integer */
    unsigned short n = IByte(buf);

    fprintf(stdout, "%02x", (unsigned) (n & 0xff));
    return 1;
}

int
PrintCARD16(const unsigned char *buf)
{
    /* print a CARD16 -- 16-bit unsigned integer */
    unsigned long n = IShort(buf);

    fprintf(stdout, "%04x", (unsigned) (n & 0xffff));
    return 1;
}

int
PrintCARD32(const unsigned char *buf)
{
    /* print a CARD32 -- 32-bit unsigned integer */
    unsigned long n = ILong(buf);

    fprintf(stdout, "%08lx", n);
    return (4);
}

/* ************************************************************ */

int
PrintBYTE(const unsigned char *buf)
{
    /* print a BYTE -- 8-bit value */
    short n = IByte(buf);

    fprintf(stdout, "%02x", n);
    return (1);
}

int
PrintCHAR8(const unsigned char *buf)
{
    /* print a CHAR8 -- 8-bit character */
    unsigned short n = IByte(buf);

    fprintf(stdout, "%s", printrep(n));
    return (1);
}

int
PrintSTRING16(const unsigned char *buf)
{
    /* print a CHAR2B -- 16-bit character which is never byte-swapped */
    unsigned short n = IChar2B(buf);

    fprintf(stdout, "%s", printrep(n));
    return 2 + n;
}

int
PrintSTR(const unsigned char *buf)
{
    /* STR have the length (1 byte) then a string of CHAR8 */
    short n;

    short i;

    n = IByte(buf++);
    for (i = 0; i < n; i++)
        fprintf(stdout, "%s", printrep(buf[i]));
    return (n + 1);
}

/* ************************************************************ */

int
PrintWINDOW(const unsigned char *buf)
{
    /* print a WINDOW -- CARD32  plus 0 = None */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else
        fprintf(stdout, "WIN %08lx", n);
    return (4);
}

int
PrintWINDOWD(const unsigned char *buf)
{
    /* print a WINDOWD -- CARD32  plus 0 = PointerWindow, 1 = InputFocus */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "PointerWindow");
    else if (n == 1)
        fprintf(stdout, "InputFocus");
    else
        (void) PrintWINDOW(buf);
    return 4;
}

int
PrintWINDOWNR(const unsigned char *buf)
{
    /* print a WINDOWNR -- CARD32  plus 0 = None, 1 = PointerRoot */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else if (n == 1)
        fprintf(stdout, "PointerRoot");
    else
        (void) PrintWINDOW(buf);
    return 4;
}

int
PrintPIXMAP(const unsigned char *buf)
{
    /* print a PIXMAP -- CARD32  plus 0 = None */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else
        fprintf(stdout, "PXM %08lx", n);
    return 4;
}

int
PrintPIXMAPNPR(const unsigned char *buf)
{
    /* print a PIXMAPNPR -- CARD32  plus 0 = None, 1 = ParentRelative */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else if (n == 1)
        fprintf(stdout, "ParentRelative");
    else
        PrintPIXMAP(buf);
    return 4;
}

int
PrintPIXMAPC(const unsigned char *buf)
{
    /* print a PIXMAPC -- CARD32  plus 0 = CopyFromParent */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "CopyFromParent");
    else
        PrintPIXMAP(buf);
    return 4;
}

int
PrintCURSOR(const unsigned char *buf)
{
    /* print a CURSOR -- CARD32  plus 0 = None */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else
        fprintf(stdout, "CUR %08lx", n);
    return 4;
}

int
PrintFONT(const unsigned char *buf)
{
    /* print a FONT -- CARD32  plus 0 = None */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else
        fprintf(stdout, "FNT %08lx", n);
    return 4;
}

int
PrintGCONTEXT(const unsigned char *buf)
{
    /* print a GCONTEXT -- CARD32 */
    long n = ILong(buf);

    fprintf(stdout, "GXC %08lx", n);
    return 4;
}

int
PrintCOLORMAP(const unsigned char *buf)
{
    /* print a COLORMAP -- CARD32 plus 0 = None */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else
        fprintf(stdout, "CMP %08lx", n);
    return (4);
}

int
PrintCOLORMAPC(const unsigned char *buf)
{
    /* print a COLORMAPC -- CARD32 plus 0 = CopyFromParent */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "CopyFromParent");
    else
        (void) PrintCOLORMAP(buf);
    return 4;
}

int
PrintDRAWABLE(const unsigned char *buf)
{
    /* print a DRAWABLE -- CARD32 */
    long n = ILong(buf);

    fprintf(stdout, "DWB %08lx", n);
    return 4;
}

int
PrintFONTABLE(const unsigned char *buf)
{
    /* print a FONTABLE -- CARD32 */
    long n = ILong(buf);

    fprintf(stdout, "FTB %08lx", n);
    return 4;
}

/* ************************************************************ */

/* for atoms, we print the built-in atoms, as well as any user defined
   ones we've captured during this session. */

int
PrintATOM(const unsigned char *buf)
{
    /* print a ATOM -- CARD32 plus 0 = None */
    long n = ILong(buf);
    const char *name = FindAtomName(n);

    if (name != NULL) {
        if (Verbose > 1)
            fprintf(stdout, "ATM %08lx <%s>", n, name);
        else
            fprintf(stdout, "<%s>", name);
    }
    else
        fprintf(stdout, "ATM %08lx", n);
    return (4);
}

int
PrintATOMT(const unsigned char *buf)
{
    /* print a ATOMT -- CARD32 plus 0 = AnyPropertyType */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "AnyPropertyType");
    else
        (void) PrintATOM(buf);
    return 4;
}

int
PrintVISUALID(const unsigned char *buf)
{
    /* print a VISUALID -- CARD32 plus 0 = None */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "None");
    else
        fprintf(stdout, "VIS %08lx", n);
    return 4;
}

int
PrintVISUALIDC(const unsigned char *buf)
{
    /* print a VISUALIDC -- CARD32 plus 0 = CopyFromParent */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "CopyFromParent");
    else
        PrintVISUALID(buf);
    return 4;
}

int
PrintTIMESTAMP(const unsigned char *buf)
{
    /* print a TIMESTAMP -- CARD32 plus 0 as the current time */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "CurrentTime");
    else
        fprintf(stdout, "TIM %08lx", n);
    return 4;
}

int
PrintRESOURCEID(const unsigned char *buf)
{
    /* print a RESOURCEID -- CARD32 plus 0 = AllTemporary */
    long n = ILong(buf);

    if (n == 0)
        fprintf(stdout, "AllTemporary");
    else
        fprintf(stdout, "RID %08lx", n);
    return 4;
}

int
PrintKEYSYM(const unsigned char *buf)
{
    /* print a KEYSYM -- CARD32 */
    long n = ILong(buf);

    fprintf(stdout, "KYS %08lx", n);
    return (4);
}

int
PrintKEYCODE(const unsigned char *buf)
{
    /* print a KEYCODE -- CARD8 */
    unsigned short n = IByte(buf);

    fprintf(stdout, "%d (%s)", n, printrep(n));
    return (1);
}

int
PrintKEYCODEA(const unsigned char *buf)
{
    /* print a KEYCODEA -- CARD8 plus 0 = AnyKey */
    long n = IByte(buf);

    if (n == 0)
        fprintf(stdout, "AnyKey");
    else
        (void) PrintKEYCODE(buf);
    return 1;
}

int
PrintBUTTON(const unsigned char *buf)
{
    /* print a BUTTON -- CARD8 */
    unsigned short n = IByte(buf);

    fprintf(stdout, "%d (%s)", n, printrep(n));
    return 1;
}

int
PrintBUTTONA(const unsigned char *buf)
{
    /* print a BUTTONA -- CARD8 plus 0 = AnyButton */
    long n = IByte(buf);

    if (n == 0)
        fprintf(stdout, "AnyButton");
    else
        PrintBUTTON(buf);
    return 1;
}

/* this is an interesting cheat -- we call DecodeEvent to print an event */
/* should work, but its never been tried */
int
PrintEVENTFORM(const unsigned char *buf)
{
    /* print an EVENT_FORM -- event format */
    DecodeEvent(-1, buf, (long) -1);
    return 32;
}

int
PrintEVENT(const unsigned char *buf)
{
    uint8_t n = IByte(buf);
    long e = (long) (n & 0x7f);
    struct ValueListEntry *p;

    p = TD[EVENT].ValueList;
    while (p != NULL && p->Value != e)
        p = p->Next;

    if (p != NULL)
        fprintf(stdout, "%s", p->Name);
    else
        fprintf(stdout, "**INVALID** (%d)", n);

    if (n & 0x80)
        fprintf(stdout, "\n%s%20s: %s", Leader, "source", "SendEvent");

    return 1;
}

/* ************************************************************ */

int
PrintENUMERATED(const unsigned char *buf,
                short length, struct ValueListEntry *ValueList)
{
    long n;
    struct ValueListEntry *p;

    if (length == 1)
        n = IByte(buf);
    else if (length == 2)
        n = IShort(buf);
    else
        n = ILong(buf);

    p = ValueList;
    while (p != NULL && p->Value != n)
        p = p->Next;

    if (p != NULL)
        fprintf(stdout, "%s", p->Name);
    else
        fprintf(stdout, "**INVALID** (%ld)", n);

    return length;
}

/* ************************************************************ */

int
PrintSET(const unsigned char *buf,
         short length, struct ValueListEntry *ValueList)
{
    unsigned long n;
    struct ValueListEntry *p;
    Boolean MatchesAll = false;
    Boolean FoundOne = false;

    if (length == 1)
        n = IByte(buf);
    else if (length == 2)
        n = IShort(buf);
    else
        n = ILong(buf);

    if (n != 0) {
        /* first check if the value matches ALL of the bits. */
        MatchesAll = true;
        for (p = ValueList; MatchesAll && (p != NULL); p = p->Next) {
            if ((p->Value & n) == 0)
                MatchesAll = false;
        }

        if (!MatchesAll)
            /* if it matches some, but not all, print only those it matches */
            for (p = ValueList; p != NULL; p = p->Next) {
                if ((p->Value & n) != 0) {
                    if (FoundOne)
                        fprintf(stdout, " | ");
                    fprintf(stdout, "%s", p->Name);
                    FoundOne = true;
                }
            }
    }

    if (MatchesAll)
        fprintf(stdout, "<ALL>");
    else if (!FoundOne)
        fprintf(stdout, "0");

    return length;
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

void
PrintField(const unsigned char *buf,
           short start, short length, short FieldType, const char *name)
{
    if (Verbose == 0)
        return;

    if (length == 0)
        return;

    fprintf(stdout, "%s%20s: ", Leader, name);

    if (debuglevel & 8)
        DumpHexBuffer(&(buf[start]), (long) length);

    switch (TD[FieldType].Type) {
    case BUILTIN:
        (*TD[FieldType].PrintProc) (&buf[start]);
        break;

    case ENUMERATED:
        PrintENUMERATED(&buf[start], length, TD[FieldType].ValueList);
        break;

    case SET:
        PrintSET(&buf[start], length, TD[FieldType].ValueList);
        break;

    case RECORD:
        ModifyIndentLevel(1);
        fprintf(stdout, "\n");
        if (Verbose < 3)
            return;
        (*TD[FieldType].PrintProc) (&buf[start]);
        ModifyIndentLevel(-1);
        break;
    }
    fprintf(stdout, "\n");
    fflush(stdout);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/* print a list of things.  The things are of type <ListType>.
   They start at <buf>.  There are <number> things in the list */

long
PrintList(const unsigned char *buf,
          long number, short ListType, const char *name)
{
    long n;
    long i;
    long sum;

    if (Verbose == 0)
        return (0);

    if (number == 0)
        return (0);

    fprintf(stdout, "%s%20s: (%ld)\n", Leader, name, number);
    if (Verbose < 2)
        return (0);

    ModifyIndentLevel(1);
    sum = 0;
    for (i = 0; i < number; i++) {
        switch (TD[ListType].Type) {
        case BUILTIN:
            n = (*TD[ListType].PrintProc) (buf);
            break;
        case RECORD:
            n = (*TD[ListType].PrintProc) (buf);
            break;
        default:
            fprintf(stdout, "**INVALID**");
            n = 0;
            break;
        }
        buf = buf + n;
        sum = sum + n;
        fprintf(stdout, "%s---\n", Leader);
    }

    ModifyIndentLevel(-1);
    return (sum);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/* print a list of STRs.  Similar to PrintList
   They start at <buf>.  There are <number> things in the list */

long
PrintListSTR(const unsigned char *buf, long number, const char *name)
{
    long n;
    long i;
    long sum;

    if (number == 0)
        return (0);

    fprintf(stdout, "%s%20s: (%ld)\n", Leader, name, number);
    if (Verbose < 2)
        return (0);

    ModifyIndentLevel(1);
    sum = 0;
    for (i = 0; i < number; i++) {
        fprintf(stdout, "%s", Leader);
        n = PrintSTR(buf);
        buf = buf + n;
        sum = sum + n;
        fprintf(stdout, "\n");
    }

    ModifyIndentLevel(-1);
    return (sum);
}


/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */


int
PrintBytes(const unsigned char *buf, long number, const char *name)
{
    /* print a list of BYTE -- 8-bit character */
    long i;
    short column;

    if (number == 0)
        return (0);

    fprintf(stdout, "%s%20s: ", Leader, name);
    column = SizeofLeader() + 25;
    for (i = 0; i < number; i++) {
        if (column > 80) {
            if (Verbose < 2)
                break;
            fprintf(stdout, "\n%s%20s: ", Leader, "");
            column = SizeofLeader() + 25;
        }
        fprintf(stdout, "%02x ", ((unsigned int) buf[i]));
        column += 3;
    }
    fprintf(stdout, "\n");

    return (number);
}


/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */


/* print a String of CHAR8 -- 8-bit characters */

int
PrintString8(const unsigned char *buf, int number, const char *name)
{
    short i;

    if (number == 0)
        return (0);

    fprintf(stdout, "%s%20s: \"", Leader, name);
    for (i = 0; i < number; i++)
        fprintf(stdout, "%s", printrep(buf[i]));
    fprintf(stdout, "\"\n");

    return (number);
}

/* print a String of CHAR16 -- 16-bit characters */

int
PrintString16(const unsigned char *buf, int number, const char *name)
{
    long i;
    unsigned short c;

    if (number == 0)
        return (0);

    fprintf(stdout, "%s%20s: \"", Leader, name);
    for (i = 0; i < number * 2; i += 2) {
        c = IChar2B(&buf[i]);
        fprintf(stdout, "%s", printrep(c));
    }
    fprintf(stdout, "\"\n");

    return (number);
}

void
PrintTString8(const unsigned char *buf, long number, const char *name)
{
    long i;
    int off;

    if (number == 0)
        return;

    off = 0;
    if (TranslateText)
        off = 0x20;
    fprintf(stdout, "%s%20s: \"", Leader, name);
    for (i = 0; i < number; i++)
        fprintf(stdout, "%s", printrep(buf[i] + off));
    fprintf(stdout, "\"\n");
}

/* print a String of CHAR2B -- 16-bit characters */
void
PrintTString16(const unsigned char *buf, long number, const char *name)
{
    long i;
    unsigned short c;
    int off;

    if (number == 0)
        return;

    off = 0;
    if (TranslateText)
        off = 0x20;
    fprintf(stdout, "%s%20s: \"", Leader, name);
    for (i = 0; i < number * 2; i += 2) {
        c = IChar2B(&buf[i]);
        fprintf(stdout, "%s", printrep(c + off));
    }
    fprintf(stdout, "\"\n");
}


/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/*
  A Value List is two things:

  (1) A controlling bitmask.  For each one bit in the control,
  a value is in the list.
  (2) A list of values.
*/

void
PrintValues(const unsigned char *control,
            int clength,
            int ctype, const unsigned char *values, const char *name)
{
    long cmask;
    struct ValueListEntry *p;

    /* first get the control mask */
    if (clength == 1)
        cmask = IByte(control);
    else if (clength == 2)
        cmask = IShort(control);
    else
        cmask = ILong(control);

    /* now if it is zero, ignore and return */
    if (cmask == 0)
        return;

    /* there are bits in the controlling bitmask, figure out which */
    /* the ctype is a set type, so this code is similar to PrintSET */
    fprintf(stdout, "%s%20s:\n", Leader, name);
    ModifyIndentLevel(1);
    for (p = TD[ctype].ValueList; p != NULL; p = p->Next) {
        if ((p->Value & cmask) != 0) {
            short m;

            if (littleEndian)
                m = 0;
            else
                m = 4 - p->Length;
            PrintField(values, m, p->Length, p->Type, p->Name);
            values += 4;
        }
    }
    ModifyIndentLevel(-1);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/* PolyText8 and PolyText16 take lists of characters with possible
   font changes in them. */

void
PrintTextList8(const unsigned char *buf, int length, const char *name)
{
    short n;

    fprintf(stdout, "%s%20s:\n", Leader, name);
    while (length > 1) {
        n = IByte(&buf[0]);
        if (n != 255) {
            PrintField(buf, 1, 1, INT8, "delta");
            PrintTString8(&buf[2], (long) n, "text item 8 string");
            buf += n + 2;
            length -= n + 2;
        }
        else {
            PrintField(buf, 1, 4, FONT, "font-shift-id");
            buf += 4;
            length -= 4;
        }
    }
}

void
PrintTextList16(const unsigned char *buf, int length, const char *name)
{
    short n;

    fprintf(stdout, "%s%20s:\n", Leader, name);
    while (length > 1) {
        n = IByte(&buf[0]);
        if (n != 255) {
            PrintField(buf, 1, 1, INT8, "delta");
            PrintTString16(&buf[2], (long) n, "text item 16 string");
            buf += n + 2;
            length -= n + 2;
        }
        else {
            PrintField(buf, 1, 4, FONT, "font-shift-id");
            buf += 4;
            length -= 4;
        }
    }
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

/* Several extensions have grown property requests mimicking the
   core protocol Window properties, and share this code for printing
   lists of property values */
int
PrintPropertyValues(const unsigned char *buf, uint32_t type /* atom */,
                    uint8_t unit, uint32_t num, const char *name)
{
    short fieldType = 0;

    switch (type) {
    case 4: /* XA_ATOM */
        fieldType = ATOM;
        break;
    case 6: /* XA_CARDINAL */
        switch (unit) {
        case 4: fieldType = CARD32; break;
        case 2: fieldType = CARD16; break;
        case 1: fieldType = CARD8;  break;
        default:
            goto rawbytes;
        }
        break;
    case 19: /* XA_INTEGER */
        switch (unit) {
        case 4: fieldType = INT32; break;
        case 2: fieldType = INT16; break;
        case 1: fieldType = INT8;  break;
        default:
            goto rawbytes;
        }
        break;
    case 31: /* XA_STRING */
        return PrintString8(buf, num * unit, name);
    case 33: /* XA_WINDOW */
        fieldType = WINDOW;
        break;
    default:
        /* Fall through to check for known non-builtin types below */
        break;
    }

    if (fieldType != 0) {
        if (num == 1) {
            PrintField(buf, 0, unit, fieldType, name);
            return unit;
        }
        else
            return PrintList(buf, num, fieldType, name);
    }
    else {
        const char *typename = FindAtomName(type);

        if (typename) {
            if (strcmp(typename, "UTF8_STRING") == 0) {
                if (IsUTF8locale)
                    return PrintString8(buf, num * unit, name);
                else
                    goto rawbytes;
            }
        }
    }

  rawbytes:
    /* When all else fails, print raw bytes */
    return PrintBytes(buf, num * unit, name);
}

/* ************************************************************ */
/*								*/
/*								*/
/* ************************************************************ */

#define MAXline 78

void
DumpHexBuffer(const unsigned char *buf, long n)
{
    long i;
    short column;
    char h[6]; /* one hex or octal character */

    column = 27 + SizeofLeader();
    for (i = 0; i < n; i++) {
        /* get the hex representations */
        snprintf(h, sizeof(h), "%02x", (0xff & buf[i]));

        /* check if these characters will fit on this line */
        if ((column + strlen(h) + 1) > MAXline) {
            /* line will be too long -- print it */
            fprintf(stdout, "\n");
            column = 0;
        }
        fprintf(stdout, "%s ", h);
        column += 3;
    }
}

void
PrintValueRec(uint32_t key, uint32_t cmask, short ctype)
{
    unsigned char *values;
    struct ValueListEntry *p;
    ValuePtr value;

    value = GetValueRec(key);
    if (!value)
        return;
    values = (unsigned char *) value->values;

    /* now if it is zero, ignore and return */
    if (cmask == 0)
        return;

    /* there are bits in the controlling bitmask, figure out which */
    /* the ctype is a set type, so this code is similar to PrintSET */
    ModifyIndentLevel(1);
    for (p = TD[ctype].ValueList; p != NULL; p = p->Next) {
        if ((p->Value & cmask) != 0) {
            short m;

            if (littleEndian)
                m = 0;
            else
                m = 4 - p->Length;
            PrintField(values, m, p->Length, p->Type, p->Name);
        }
        values += 4;
    }
    ModifyIndentLevel(-1);
}
