/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    hd.c

Abstract:

        This module contains the functions that implement the hd program.
        This program displays the contents of files in decimal, hexadecimal
        and character formats. The contents of the files are displayed in
        records of 16 bytes each. Associated to each record, there is an
        address that represents the offset of the first byte in the
        record relative to the begining of the file. Each record can also be
        displayed as printable ASCII characters.
        hd can be called with the following arguments:

                -ad:    displays the address of each record in decimal;
                -ax:    displays the address of each record in hex;
                -ch:    displays bytes as ASCII characters;
                -cC:    displays bytes as ASCII C characters (\n, \t, etc);
                -ce:    displays bytes as ASCII codes (EOT, CR, SOH, etc);
                -cr:    displays bytes as ASCII control characters (^A, ^N, etc);
                -bd:    interprets data in each record as byte, and displays
                                each byte as a decimal number;
                -bx:    interprets data in each record as byte, and displays
                                each byte as an hex number;
                -wd:    interprets data in each record as word, and displays
                                each word as a decimal number;
                -wx:    interprets data in each record as word, and displays
                                each word as an hex number;
                -ld:    interprets data in each record as double words, and displays
                                each double word as a decimal number;
                -wx:    interprets data in each record as a double word, and displays
                                each double word as an hex number;
                -A:     Displays data in each record also as printable ASCII
                                characters at the end of each line.
                -s <offset>: defines the offset of the first byte to be displayed;
                -n <bumber>: defines the number of bytes to be displayed;
                -i      does not print redundant lines;
                -?, -h or -H: displays a help message.

        If no argument is defined, hd assumes as default: -ax -A -bx


Authors:

    Jaime F. Sasson (jaimes) 12-Nov-1990
    David J. Gilman (davegi) 12-Nov-1990

Environment:

    C run time library

Revision History:


--*/



#include        <stdio.h>
#include        <assert.h>
#include        <ctype.h>
#include        <conio.h>
#include        <string.h>
#include        <stdlib.h>
#include        "hd.h"

#define FALSE 0


/*************************************************************************
*
*                                       G L O B A L   V A R I A B L E S
*
*************************************************************************/


unsigned long   Offset = 0;                     // -s option
unsigned                Count = 0;              // -n option
BASE                    AddrFormat;             // -a option
FORMAT                  DispFormat;             // -c, -b, -w or -l options
YESNO                   DumpAscii;              // -A option
int                     IgnoreRedundantLines;   // -i option

unsigned char   auchBuffer[BUFFER_SIZE];        // Buffer that contains data read
                                                // from the file being displayed

unsigned long   cbBytesInBuffer;                // Total number of bytes in the
                                                // buffer

unsigned char*  puchPointer;                    // Points to the next character in
                                                // the buffer to be read

unsigned long   cStringSize;                    // Size of a string pointed by a
                                                // pointer in the ASCII table used
                                                // for the translation (asciiChar,
                                                // asciiC, asciiCode or asciiCtrl)
                                                // The contents of this variable is
                                                // meaningful only if -ch, -cC, -ce
                                                // or -cr was specified.
                                                // It is meaningless in all other
                                                // cases (no ascii translation is
                                                // being performed, and the ascii
                                                // tables are not needed)


/*************************************************************************
*
*                               A S C I I       C O N V E R S I O N   T A B L E S
*
*************************************************************************/


char*   asciiChar[ ] = {
        "   ", "  ", "  ", "  ", "  ", "  ", "  ", "  ",
        "  ", "   ", "   ", "  ", "  ", "   ", "  ", "  ",
        "  ", "  ", "  ", "  ", "  ", "  ", "  ", "  ",
        "  ", "  ", "   ", "  ", "  ", "  ", "  ", "  ",
        "   ", "!  ", "\"  ", "#  ", "$  ", "%  ", "&  ", "'  ",
        "(  ", ")  ", "*  ", "+  ", "'  ", "-  ", ".  ", "/  ",
        "0  ", "1  ", "2  ", "3  ", "4  ", "5  ", "6  ", "7  ",
        "8  ", "9  ", ":  ", ";  ", "<  ", "=  ", ">  ", "?  ",
        "@  ", "A  ", "B  ", "C  ", "D  ", "E  ", "F  ", "G  ",
        "H  ", "I  ", "J  ", "K  ", "L  ", "M  ", "N  ", "O  ",
        "P  ", "Q  ", "R  ", "S  ", "T  ", "U  ", "V  ", "W  ",
        "X  ", "Y  ", "Z  ", "[  ", "\\  ", "]  ", "^  ", "_  ",
        "`  ", "a  ", "b  ", "c  ", "d  ", "e  ", "f  ", "g  ",
        "h  ", "i  ", "j  ", "k  ", "l  ", "m  ", "n  ", "o  ",
        "p  ", "q  ", "r  ", "s  ", "t  ", "u  ", "v  ", "w  ",
        "x  ", "y  ", "z  ", "{  ", "|  ", "}  ", "~  ", "_  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "   "
};



char*   asciiC[ ] = {
        "   ", "  ", "  ", "  ", "  ", "  ", "  ", "\\a  ",
        "\\b  ", "\\t ", "\\n ", "\\v ", "\\f ", "   ", "  ", "  ",
        "  ", "  ", "  ", "  ", "  ", "  ", "  ", "  ",
        "  ", "  ", "   ", "  ", "  ", "  ", "  ", "  ",
        "   ", "!  ", "\\\" ", "#  ", "$  ", "%  ", "&  ", "\' ",
        "(  ", ")  ", "*  ", "+  ", "'  ", "-  ", ".  ", "/  ",
        "0  ", "1  ", "2  ", "3  ", "4  ", "5  ", "6  ", "7  ",
        "8  ", "9  ", ":  ", ";  ", "<  ", "=  ", ">  ", "?  ",
        "@  ", "A  ", "B  ", "C  ", "D  ", "E  ", "F  ", "G  ",
        "H  ", "I  ", "J  ", "K  ", "L  ", "M  ", "N  ", "O  ",
        "P  ", "Q  ", "R  ", "S  ", "T  ", "U  ", "V  ", "W  ",
        "X  ", "Y  ", "Z  ", "[  ", "\\\\ ", "]  ", "^  ", "_  ",
        "`  ", "a  ", "b  ", "c  ", "d  ", "e  ", "f  ", "g  ",
        "h  ", "i  ", "j  ", "k  ", "l  ", "m  ", "n  ", "o  ",
        "p  ", "q  ", "r  ", "s  ", "t  ", "u  ", "v  ", "w  ",
        "x  ", "y  ", "z  ", "{  ", "|  ", "}  ", "~  ", "_  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "   "
};



char*   asciiCode[ ] = {
        "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
        "BS ", "HT ", "LF ", "VT ", "FF ", "CR ", "SO ", "SI ",
        "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
        "CAN", "EM ", "SUB", "ESC", "FS ", "GS ", "RS ", "US ",
        "   ", "!  ", "\"  ", "#  ", "$  ", "%  ", "&  ", "'  ",
        "(  ", ")  ", "*  ", "+  ", "'  ", "-  ", ".  ", "/  ",
        "0  ", "1  ", "2  ", "3  ", "4  ", "5  ", "6  ", "7  ",
        "8  ", "9  ", ":  ", ";  ", "<  ", "=  ", ">  ", "?  ",
        "@  ", "A  ", "B  ", "C  ", "D  ", "E  ", "F  ", "G  ",
        "H  ", "I  ", "J  ", "K  ", "L  ", "M  ", "N  ", "O  ",
        "P  ", "Q  ", "R  ", "S  ", "T  ", "U  ", "V  ", "W  ",
        "X  ", "Y  ", "Z  ", "[  ", "\\  ", "]  ", "^  ", "_  ",
        "`  ", "a  ", "b  ", "c  ", "d  ", "e  ", "f  ", "g  ",
        "h  ", "i  ", "j  ", "k  ", "l  ", "m  ", "n  ", "o  ",
        "p  ", "q  ", "r  ", "s  ", "t  ", "u  ", "v  ", "w  ",
        "x  ", "y  ", "z  ", "{  ", "|  ", "}  ", "~  ", "_  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "   "
};


char*   asciiCtrl[ ] = {
        "^@ ", "^A ", "^B ", "^C ", "^D ", "^E ", "^F ", "^G ",
        "^H ", "^I ", "^J ", "^K ", "^L ", "^M ", "^N ", "^O ",
        "^P ", "^Q ", "^R ", "^S ", "^T ", "^U ", "^V ", "^W ",
        "^X ", "^Y ", "^Z ", "^[ ", "^\\ ", "^] ", "^^ ", "^_ ",
        "   ", "!  ", "\"  ", "#  ", "$  ", "%  ", "&  ", "'  ",
        "(  ", ")  ", "*  ", "+  ", "'  ", "-  ", ".  ", "/  ",
        "0  ", "1  ", "2  ", "3  ", "4  ", "5  ", "6  ", "7  ",
        "8  ", "9  ", ":  ", ";  ", "<  ", "=  ", ">  ", "?  ",
        "@  ", "A  ", "B  ", "C  ", "D  ", "E  ", "F  ", "G  ",
        "H  ", "I  ", "J  ", "K  ", "L  ", "M  ", "N  ", "O  ",
        "P  ", "Q  ", "R  ", "S  ", "T  ", "U  ", "V  ", "W  ",
        "X  ", "Y  ", "Z  ", "[  ", "\\  ", "]  ", "^  ", "_  ",
        "`  ", "a  ", "b  ", "c  ", "d  ", "e  ", "f  ", "g  ",
        "h  ", "i  ", "j  ", "k  ", "l  ", "m  ", "n  ", "o  ",
        "p  ", "q  ", "r  ", "s  ", "t  ", "u  ", "v  ", "w  ",
        "x  ", "y  ", "z  ", "{  ", "|  ", "}  ", "~  ", "_  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ",
        "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "�  ", "   "
};




void
ConvertASCII (
        char                    line[],
        unsigned char   buf[],
        unsigned long   cb,
        char*                   pTable[]
        )

/*++

Routine Description:

        This routine converts the bytes received in a buffer
        into an ASCII representation (Char, C, Code or CTRL).


Arguments:

        line - Buffer that will receive the converted characters.

        buf - A buffer that contains the data to be converted.

        cb - Number of bytes in the buffer

        pTable - Pointer to the table to be used in the conversion


Return Value:

    None

--*/


{
        unsigned long   ulIndex;

        sprintf( line,
                        MSG_DATA_ASCII_FMT,
                        pTable[ buf[ 0 ]], pTable[ buf[ 1 ]],
                        pTable[ buf[ 2 ]], pTable[ buf[ 3 ]],
                        pTable[ buf[ 4 ]], pTable[ buf[ 5 ]],
                        pTable[ buf[ 6 ]], pTable[ buf[ 7 ]],
                        pTable[ buf[ 8 ]], pTable[ buf[ 9 ]],
                        pTable[ buf[ 10 ]], pTable[ buf[ 11 ]],
                        pTable[ buf[ 12 ]], pTable[ buf[ 13 ]],
                        pTable[ buf[ 14 ]], pTable[ buf[ 15 ]]);
        //
        // If the number of bytes in the buffer is less than the maximum size
        // of the record, then delete the characters that were converted
        // but are not to be displayed.
        //
        if (cb < RECORD_SIZE) {
                //
                //      -1: to eliminate the \0
                //      +1: to count the SPACE character between two strings
                //
                ulIndex = (sizeof( MSG_ADDR_FIELD ) - 1 ) + cb*(cStringSize + 1);
                while ( line[ ulIndex ] != NUL ) {
                        line[ ulIndex ] = SPACE;
                        ulIndex++;
                }
        }
}




void
ConvertBYTE (
        char                    line[],
        unsigned char   buf[],
        unsigned long   cb,
        unsigned long   ulBase
        )

/*++

Routine Description:

        This routine converts each byte received in a buffer
        into a number. The base used in the conversion is received as
        parameter.



Arguments:

        line - Buffer that will receive the converted characters.

        buf - A buffer that contains the data to be converted.

        cb - Number of bytes in the buffer

        ulBase - Defines the base to be used in the conversion



Return Value:

    None

--*/


{
        unsigned long   ulIndex;
        char*                   pchMsg;
        unsigned long   ulNumberOfDigits;

        switch( ulBase ) {

                case DEC:
                        ulNumberOfDigits = 3;                   // needs 3 decimal digits to
                                                                                        // represent a byte
                        pchMsg = MSG_DATA_BYTE_DEC_FMT; // message that contains the format
                        break;

                case HEX:
                        ulNumberOfDigits = 2;                   // needs 2 hexdigits to
                                                                                        // represent a byte
                        pchMsg = MSG_DATA_BYTE_HEX_FMT; // message that contains the format
                        break;

                default:
                        printf( "Unknown base\n" );
                        assert( FALSE );
                        break;
        }

        sprintf( line,
                         pchMsg,
                         buf[ 0 ], buf[ 1 ],
                         buf[ 2 ], buf[ 3 ],
                         buf[ 4 ], buf[ 5 ],
                         buf[ 6 ], buf[ 7 ],
                         buf[ 8 ], buf[ 9 ],
                         buf[ 10 ], buf[ 11 ],
                         buf[ 12 ], buf[ 13 ],
                         buf[ 14 ], buf[ 15 ]);
        //
        // If this is the last record to be displayed, then delete the
        // characters that were translated but are not to be displayed.
        //
        if (cb < RECORD_SIZE) {
                ulIndex = (sizeof( MSG_ADDR_FIELD ) - 1 ) +
                                        cb*(ulNumberOfDigits + 1 );
                while ( line[ ulIndex ] != NUL ) {
                        line[ ulIndex ] = SPACE;
                        ulIndex++;
                }
        }
}




void
ConvertWORD (
        char                    line[],
        unsigned char   buf[],
        unsigned long   cb,
        unsigned long   ulBase
        )

/*++

Routine Description:

        This routine converts the data received in a buffer
        into numbers. The data in the buffer are interpreted as words.
        If the buffer contains an odd number of bytes, then the last byte
        is converted as a byte, not as word.
        The base used in the conversion is received as parameter.



Arguments:

        line - Buffer that will receive the converted characters.

        buf - A buffer that contains the data to be converted.

        cb - Number of bytes in the buffer

        ulBase - Defines the base to be used in the conversion



Return Value:

    None

--*/


{
        unsigned long   ulIndex;
        char*                   pchMsg;
        char*                   pchMsgHalf;
        unsigned long   ulNumberOfDigits;

        switch( ulBase ) {

                case DEC:
                        ulNumberOfDigits = 5;                           // needs 5 decimal digits to
                                                                                                // represent a word
                        pchMsg = MSG_DATA_WORD_DEC_FMT;         // message with the string
                                                                                                // format
                        pchMsgHalf = MSG_SINGLE_BYTE_DEC_FMT; // message with the format of
                        break;                                                          // half a word in decimal

                case HEX:
                        ulNumberOfDigits = 4;                           // needs 4 hex digits to
                                                                                                // represent a word
                        pchMsg = MSG_DATA_WORD_HEX_FMT;         // message the string format
                        pchMsgHalf = MSG_SINGLE_BYTE_HEX_FMT; // message with the format of
                                                                                                // half a word in hex
                        break;

                default:
                        printf( "Unknown base\n" );
                        assert( FALSE );
                        break;
        }

        sprintf( line,
                         pchMsg,
                         (( unsigned short* ) ( buf )) [ 0 ],
                         (( unsigned short* ) ( buf )) [ 1 ],
                         (( unsigned short* ) ( buf )) [ 2 ],
                         (( unsigned short* ) ( buf )) [ 3 ],
                         (( unsigned short* ) ( buf )) [ 4 ],
                         (( unsigned short* ) ( buf )) [ 5 ],
                         (( unsigned short* ) ( buf )) [ 6 ],
                         (( unsigned short* ) ( buf )) [ 7 ]);
        //
        // If this record contains less bytes than the maximum record size,
        // then it is the last record to be displayed. In this case we have
        // to verify if the record contains an even number of bytes. If it
        // doesn't, then the last byte must be interpreted as a byte and not
        // as a word.
        // Also, the characters that were converted but are not to be displayed,
        // have to be deleted.
        //
        if (cb < RECORD_SIZE) {
                ulIndex = (sizeof( MSG_ADDR_FIELD ) - 1 ) +
                                        (cb/2)*(ulNumberOfDigits + 1 );
                if (cb%2 != 0) {
                        ulIndex += sprintf( line + ulIndex,
                                                                pchMsgHalf,
                                                                buf[ cb-1 ]);
                        line[ ulIndex ] = SPACE;
                }
                //
                // Delete characters that are not to be displayed
                //
                while ( line[ ulIndex ] != NUL ) {
                        line[ ulIndex ] = SPACE;
                        ulIndex++;
                }
        }
}




void
ConvertDWORD (
        char                    line[],
        unsigned char   buf[],
        unsigned long   cb,
        unsigned long   ulBase
        )

/*++

Routine Description:

        This routine converts the data received in a buffer
        into numbers. The data in the buffer is interpreted as double words.
        If the buffer contains less bytes than the maximum size of the record,
        then it is the last record, and we may need to convert again the last
        3 bytes in the buffer.
        If the number of bytes in the buffer is not multiple of 4, then the
        last bytes in the buffer are converted as a byte, word, or word and
        byte, as appropriate.
        The characters that were converted but are not to be displayed have to
        be removed from the buffer.
        The base used in the conversion is received as parameter.



Arguments:

        line - Buffer that will receive the converted characters.

        buf - A buffer that contains the data to be converted.

        cb - Number of bytes in the buffer

        ulBase - Defines the base to be used in the conversion



Return Value:

    None

--*/


{
        unsigned long   ulIndex;
        char*                   pchMsg;
        char*                   pchMsgByte;
        char*                   pchMsgWord;
        char*                   pchMsgWordByte;
        unsigned long   ulNumberOfDigits;

        switch( ulBase ) {

                case DEC:
                        ulNumberOfDigits = 10;                          // needs 10 decimal digits to
                                                                                                // represent a dword
                        pchMsg = MSG_DATA_DWORD_DEC_FMT;        // message with the string
                                                                                                // format
                        pchMsgByte = MSG_SINGLE_BYTE_DEC_FMT; // message with the format
                                                                                                  // of a single byte in
                                                                                                  // decimal
                        pchMsgWord = MSG_SINGLE_WORD_DEC_FMT; // message that contains
                                                                                                  // the format of a single
                                                                                                  // word in decimal
                        pchMsgWordByte = MSG_WORD_BYTE_DEC_FMT;
                        break;

                case HEX:
                        ulNumberOfDigits = 8;                           // needs 8 hex digits to
                                                                                                // represent a dword
                        pchMsg = MSG_DATA_DWORD_HEX_FMT;        // message the string format
                        pchMsgByte = MSG_SINGLE_BYTE_HEX_FMT; // message with the format
                                                                                                  // of a single byte in hex
                        pchMsgWord = MSG_SINGLE_WORD_HEX_FMT; // message with the format
                                                                                                  // of a single word in hex
                        pchMsgWordByte = MSG_WORD_BYTE_HEX_FMT;
                        break;

                default:
                        printf( "Unknown base\n" );
                        assert( FALSE );
                        break;
        }

        sprintf( line,
                         pchMsg,
                         (( unsigned long* ) ( buf )) [ 0 ],
                         (( unsigned long* ) ( buf )) [ 1 ],
                         (( unsigned long* ) ( buf )) [ 2 ],
                         (( unsigned long* ) ( buf )) [ 3 ]);
        //
        // If the buffer contains less bytes than the maximum record size,
        // the it is the last buffer to be displayed. In this case, check if
        // if the buffer contains a number o bytes that is multiple of 4.
        // If it doesn't, then converts the last bytes as a byte, a word, or
        // a word and a byte, as appropriate.
        //
        if (cb < RECORD_SIZE) {
                ulIndex = (sizeof( MSG_ADDR_FIELD ) - 1 ) +
                        (cb/4)*(ulNumberOfDigits + 1 );
                switch( cb%4 ) {

                        case 1:
                                ulIndex += sprintf( line + ulIndex,
                                                                        pchMsgByte,
                                                                        buf[ cb-1 ]);
                                line[ ulIndex ] = SPACE;
                                break;

                        case 2:
                                ulIndex += sprintf( line + ulIndex,
                                                                        pchMsg,
                                                                        (( unsigned short* ) ( buf )) [ (cb/2) - 1 ]);
                                line[ ulIndex ] = SPACE;
                                break;

                        case 3:
                                ulIndex += sprintf( line + ulIndex,
                                                                        pchMsgWordByte,
                                                                        (( unsigned short* ) ( buf )) [ (cb/2) - 1],
                                                                        buf[ cb-1 ]);
                                line[ ulIndex ] = SPACE;
                                break;

                        default:                                // buf contains multiple of 4 bytes
                                break;
                }
                //
                // Delete the charecters that were converted but are not to be
                // displayed.
                //
                while ( line[ ulIndex ] != NUL) {
                        line[ ulIndex ] = SPACE;
                        ulIndex++;
                }
        }
}




void
ConvertPRINT (
        char                    line[],
        unsigned char   buf[],
        unsigned long   cb
        )

/*++

Routine Description:

        This routine converts each byte received in a buffer into a
        printable character.


Arguments:

        line - Buffer that will receive the converted characters.

        buf - A buffer that contains the data to be converted.

        cb - Number of bytes in the buffer


Return Value:

    None

--*/

{


        sprintf( line,
                        MSG_PRINT_CHAR_FMT,
                        isprint( buf[ 0 ] ) ? buf[ 0 ] : DOT,
                        isprint( buf[ 1 ] ) ? buf[ 1 ] : DOT,
                        isprint( buf[ 2 ] ) ? buf[ 2 ] : DOT,
                        isprint( buf[ 3 ] ) ? buf[ 3 ] : DOT,
                        isprint( buf[ 4 ] ) ? buf[ 4 ] : DOT,
                        isprint( buf[ 5 ] ) ? buf[ 5 ] : DOT,
                        isprint( buf[ 6 ] ) ? buf[ 6 ] : DOT,
                        isprint( buf[ 7 ] ) ? buf[ 7 ] : DOT,
                        isprint( buf[ 8 ] ) ? buf[ 8 ] : DOT,
                        isprint( buf[ 9 ] ) ? buf[ 9 ] : DOT,
                        isprint( buf[ 10 ] ) ? buf[ 10 ] : DOT,
                        isprint( buf[ 11 ] ) ? buf[ 11 ] : DOT,
                        isprint( buf[ 12 ] ) ? buf[ 12 ] : DOT,
                        isprint( buf[ 13 ] ) ? buf[ 13 ] : DOT,
                        isprint( buf[ 14 ] ) ? buf[ 14 ] : DOT,
                        isprint( buf[ 15 ] ) ? buf[ 15 ] : DOT);
        //
        // If the buffer contains less characters than the maximum record size,
        // then delete the characters that were converted but are not to be
        // displayed
        //
        if (cb < RECORD_SIZE) {
                while ( line[ cb ] != NUL ) {
                        line[ cb ] = SPACE;
                        cb++;
                }
        }
}






void
Translate (
        FORMAT                  fmt,
        unsigned char   buf[ ],
        unsigned long   cb,
        char                    line[ ]
        )

/*++

Routine Description:

        This function converts the bytes received in a buffer
        into a printable representation, that corresponds to one
        of the formats specified by the parameter fmt.


Arguments:

        fmt - The format to be used in the conversion

        buf - A buffer that contains the data to be converted.

        cb - Number of bytes in the buffer

        line - Buffer that will receive the converted characters.


Return Value:

        None

--*/


{
        assert( buf );
        assert( line );

        switch( fmt ) {

                case ASCII_CHAR:
                        ConvertASCII( line, buf, cb, asciiChar );
                        break;

                case ASCII_C:
                        ConvertASCII( line, buf, cb, asciiC );
                        break;

                case ASCII_CODE:
                        ConvertASCII( line, buf, cb, asciiCode );
                        break;

                case ASCII_CTRL:
                        ConvertASCII( line, buf, cb, asciiCtrl );
                        break;

                case BYTE_DEC:
                        ConvertBYTE( line, buf, cb, DEC );
                        break;

                case BYTE_HEX:
                        ConvertBYTE( line, buf, cb, HEX );
                        break;

                case WORD_DEC:
                        ConvertWORD( line, buf, cb, DEC );
                        break;

                case WORD_HEX:
                        ConvertWORD( line, buf, cb, HEX );
                        break;

                case DWORD_DEC:
                        ConvertDWORD( line, buf, cb, DEC );
                        break;

                case DWORD_HEX:
                        ConvertDWORD( line, buf, cb, HEX );
                        break;

                case PRINT_CHAR:
                        ConvertPRINT( line, buf, cb );
                        break;


                default:
                        printf( "Bad Format\n" );
                        assert( FALSE );
                        break;
        }
}





void
PutAddress (
        char                    line[],
        unsigned long   ulAddress,
        BASE                    Base
        )

/*++

Routine Description:

        This routine adds to the buffer received the offset of the first
        byte (or character) already in the buffer. This offset represents
        the position of the byte in the file, relatively to the begining
        of the file.


Arguments:

        Base - The base to be used to represent the offset.

        line - Buffer containing the converted characters to be displayed in
                   the screen

        ulAddress - Offset to be added to the begining of the buffer


Return Value:

    None

--*/

{
        unsigned long   ulIndex;

        assert( line);

        switch( Base ) {

                case DEC:
                        ulIndex = sprintf( line,
                                                           MSG_ADDR_DEC_FMT,
                                                           ulAddress );
                        break;

                case HEX:
                        ulIndex = sprintf( line,
                                                           MSG_ADDR_HEX_FMT,
                                                           ulAddress);
                        break;

                default:
                        printf( "Bad Address Base\n" );
                        assert( FALSE );
                        break;
        }
        line[ ulIndex ] = SPACE;   // Get rid of the NUL added by sprintf
}






void
PutTable (
        char                    line[],
        unsigned char   buf[],
        unsigned long   cb
        )

/*++

Routine Description:

        This routine adds to the end of the buffer received, the ASCII
        representation of all printable characters already in the buffer.
        Characters that are not printable (smaller than 0x20 or greater than
        0x7f) are displayed as a dot.


Arguments:

        line - Buffer containing the characters to be displayed in one line
                   of the screen

        buf - The buffer that contains a record of bytes (maximum of 16)
                  read from the file being displayed.

    ulAddress - Number of bytes in buf.


Return Value:

    None

--*/

    {

        unsigned long   ulIndex;

        assert( line );
        assert( buf );

        ulIndex = strlen (line);
        Translate( PRINT_CHAR, buf, cb, (line + ulIndex));
}




void
InterpretArgument (
        char*   pchPointer
        )

/*++

Routine Description:

    This routine interprets an argument typed by the user (exept -n
    and -s) and initializes some variables accordingly.


Arguments:

    pchPointer - Pointer to the argument to be interpreted.


Return Value:

    None

--*/

        {
        //
        // pchPointer will point to the character that follows '-'
        //
        pchPointer++;
        if( strcmp( pchPointer, "ax" ) == 0 ) {
                AddrFormat = HEX;
        }
        else if( strcmp( pchPointer, "ad" ) == 0 ) {
                AddrFormat = DEC;
        }
        else if( strcmp( pchPointer, "ch" ) == 0 ) {
                DispFormat = ASCII_CHAR;
                cStringSize = strlen( asciiChar[0] );
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "cC" ) == 0 ) {
                DispFormat = ASCII_C;
                cStringSize = strlen( asciiC[0] );
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "ce" ) == 0 ) {
                DispFormat = ASCII_CODE;
                cStringSize = strlen( asciiCode[0] );
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "cr" ) == 0 ) {
                DispFormat = ASCII_CTRL;
                cStringSize = strlen( asciiCtrl[0] );
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "bd" ) == 0 ) {
                DispFormat = BYTE_DEC;
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "bx" ) == 0 ) {
                DispFormat = BYTE_HEX;
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "wd" ) == 0 ) {
                DispFormat = WORD_DEC;
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "wx" ) == 0 ) {
                DispFormat = WORD_HEX;
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "ld" ) == 0 ) {
                DispFormat = DWORD_DEC;
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "lx" ) == 0 ) {
                DispFormat = DWORD_HEX;
                DumpAscii = ( DumpAscii == NOT_DEFINED ) ? NO : DumpAscii;
        }
        else if( strcmp( pchPointer, "A" ) == 0 ) {
                DumpAscii = YES;
        }
        else if( strcmp( pchPointer, "i" ) == 0 ) {
                IgnoreRedundantLines = 1;
        }
        else if( strcmp( pchPointer, "?" ) || strcmp( pchPointer, "h" ) ||
                         strcmp( pchPointer, "H" ) ) {
                puts( HELP_MESSAGE );
                exit( 0 );
        }
        else {
                fprintf( stderr, "hd: error: invalid argument '%s'\n", --pchPointer );
                exit( - 1 );
        }
}





unsigned long
GetRecord (
        unsigned char*  puchRecord,
        FILE*                   pf
        )

/*++

Routine Description:

        This routine fills the buffer whose pointer was received as parameter,
        with characters read from the file being displayed. Blocks of data
        are initially read from the file being displayed, and kept in a buffer.
        A record is filled with characters obtained from this buffer.
        Whenever this buffer gets empty, a new access to file is performed
        in order to fill this buffer.


Arguments:

        puchRecord - Pointer to the record to be filled
        pf - Pointer to the file that is being displayed


Return Value:

        Total number of characters put in the record.

--*/

{
unsigned long   cbBytesCopied;

   //
   // If the buffer contains enogh characters to fill the record, then
   // copy the appropriate number of bytes.
   //
        if( cbBytesInBuffer >= RECORD_SIZE ) {
                for( cbBytesCopied = 0; cbBytesCopied < RECORD_SIZE; cbBytesCopied++ ) {
                        *puchRecord++ = *puchPointer++;
                        cbBytesInBuffer--;
                }
        }

        //
        // else, the buffer does not contain enough characters to fill the record
        //
        else {
                //
                // Copy to the remaining characters in the buffer to the record
                //
                for( cbBytesCopied = 0; cbBytesInBuffer > 0; cbBytesInBuffer-- ) {
                        *puchRecord++ = *puchPointer++;
                        cbBytesCopied++;
                }
                //
                // Read more data from the file and fill the record
                //
                if( !feof( pf ) ) {
                        cbBytesInBuffer = fread( auchBuffer,
                                                                         sizeof( char ),
                                                                         BUFFER_SIZE,
                                                                         pf );
                        puchPointer = auchBuffer;
                        while( ( cbBytesInBuffer != 0 ) && (cbBytesCopied < RECORD_SIZE) ) {
                                *puchRecord++ = *puchPointer++;
                                cbBytesInBuffer--;
                                cbBytesCopied++;
                        }
                }
        }
        return( cbBytesCopied );
}







int
hd(
        FILE *  pf
        )
/*++ hd
*
* Routine Description:
*       takes the file/stream pointed to by pf and `hd's it to stdout.
*
* Arguments:
*               FILE *  pf      -
*
* Return Value:
*               int - to be determined, always zero for now
* Warnings:
--*/
{
        unsigned char   buf[ RECORD_SIZE ];
        char                    line[ LINE_SIZE ];
        char            Previousline[ LINE_SIZE ];
        int             printedstar;

        unsigned long   CurrentAddress;
        unsigned long   cNumberOfBlocks;
        unsigned                cLastBlockSize;
        unsigned long   cb;

        //
        //      Determine number of records to be displayed, and size of
        //      last record
        //

        CurrentAddress = Offset;
        cNumberOfBlocks = Count / RECORD_SIZE;
        cLastBlockSize = Count % RECORD_SIZE;
        if( cLastBlockSize ) {
                cNumberOfBlocks++;
        }
        else {
                cLastBlockSize = RECORD_SIZE;
        }

        //
        //      Initialize global variables related to auchBuffer
        //

        cbBytesInBuffer = 0;
        puchPointer = auchBuffer;

        //
        //      Position the file in the correct place, and display
        //      its contents according to the arguments specified by the
        //      user
        //

        if ( pf != stdin ) {
                if (fseek( pf, Offset, SEEK_SET ) == -1) return 0;
        }
        //...maybe enable skipping Offset number of bytes for stdin...

        printedstar = 0;

        while( ( (cb = GetRecord( buf, pf )) != 0) && cNumberOfBlocks ) {
                cNumberOfBlocks--;
                if ( cNumberOfBlocks == 0 ) {
                        cb = ( cb < cLastBlockSize ) ? cb : cLastBlockSize;
                }
                Translate( DispFormat, buf, cb, line );

                if (IgnoreRedundantLines && (strcmp( Previousline, line ) == 0)) {

                    if (!printedstar) { printf("*\n"); }
                    printedstar = 1;

                } else {

                    printedstar = 0;

                    strcpy( Previousline, line );

                    PutAddress( line, CurrentAddress, AddrFormat );
                    if ( (DumpAscii == YES) || (DumpAscii == NOT_DEFINED) )
                            {
                            PutTable ( line, buf, cb );
                    }
                    puts( line );
                }

                CurrentAddress += RECORD_SIZE;
        }
        return 0;
}
/* end of "int hd()" */




void
__cdecl main(
        int             argc,
        char*   argv[ ]
        )

/*++

Routine Description:

        This routine interprets all arguments entered by the user, and
        displays the files specified in the appropriate format.
        The contents of each file is displayed interpreted as a set of
        record containing 16 bytes each.


Arguments:

    argc - number of arguments in the command line
    argv[] - array of pointers to the arguments entered by the user


Return Value:

    None

--*/


{
        FILE*                   pf;
//.     unsigned char   buf[ RECORD_SIZE ];
//.     char                    line[ LINE_SIZE ];
        int                             ArgIndex;
        int                             status;

//.     unsigned long   CurrentAddress;
//.     unsigned long   cNumberOfBlocks;
//.     unsigned                cLastBlockSize;
//.     unsigned long   cb;
        unsigned long   Value;
        unsigned char*  pPtrString;

//.     printf( "\n\n" );                               //.gratuitous newlines removed
                                                                        // Initialization of global variables
        Offset = 0;
        Count = (unsigned long)-1;                      // Maximum file size
        AddrFormat = HEX;
        DispFormat = BYTE_HEX;
        DumpAscii = NOT_DEFINED;
        IgnoreRedundantLines = 0;

        ArgIndex = 1;
        while ( (ArgIndex < argc) && (( *argv[ ArgIndex ] == '-' )) ) {

                //
                // Determine the type of argument
                //

                if( (*(argv[ ArgIndex ] + 1) == 's') ||
                        (*(argv[ ArgIndex ] + 1) == 'n') ) {

                                //
                                // If argument is -s or -n, interprets the number that
                                // follows the argument
                                //

                                if ( (ArgIndex + 1) >= argc ) {
                                        fprintf(stderr,
                                                "hd: error: missing count/offset value after -%c\n",
                                                *(argv[ ArgIndex ] + 1) );
                                        exit (-1);
                                }
                                Value = strtoul( argv[ ArgIndex + 1 ], &pPtrString, 0 );
                                if( *pPtrString != 0 ) {
                                        fprintf(stderr,
                                                "hd: error: invalid count/offset value after -%c\n",
                                                *(argv[ ArgIndex ] + 1) );
                                        exit( -1 );
                                }
                                if( *(argv[ ArgIndex ] + 1) == 's' ) {
                                        Offset = Value;
                                }
                                else {
                                        Count = Value;
                                }
                                ArgIndex += 2;
                }
                else {

                        //
                        // Interprets argument other than -s or -n
                        //

                        InterpretArgument ( argv[ ArgIndex ] );
                        ArgIndex++;
                }
        }

        if ( ArgIndex >= argc ) {
//.             printf ( "Error: file name is missing \n" );
                status = hd( stdin );
                exit( 0 );
        }


        //
        //      For each file, do
        //

        while ( ArgIndex < argc ) {

                //
                //      Open file
                //

                if ( !( pf = fopen( argv[ ArgIndex ], "rb" ) ) ) {
                        fprintf(stderr, "hd: error: invalid file name '%s'\n",
                                argv[ ArgIndex ] );
                        ArgIndex++;
                        continue;                               //. don't abort if it's only a bad filename
//.                     exit( -1 );
                }

                //
                //      Print file name
                //

//.             printf( "\n\n" );
                printf( "%s: \n", argv[ ArgIndex ] );
                ArgIndex++;

                status = hd( pf );

//.             //
//.             //      Determine number of records to be displayed, and size of
//.             //      last record
//.             //
//.
//.             CurrentAddress = Offset;
//.             cNumberOfBlocks = Count / RECORD_SIZE;
//.             cLastBlockSize = Count % RECORD_SIZE;
//.             if( cLastBlockSize ) {
//.                     cNumberOfBlocks++;
//.             }
//.             else {
//.                     cLastBlockSize = RECORD_SIZE;
//.             }
//.
//.             //
//.             //      Initialize global variables related to auchBuffer
//.             //
//.
//.             cbBytesInBuffer = 0;
//.             puchPointer = auchBuffer;
//.
//.             //
//.             //      Position the file in the correct place, and display
//.             //      its contents according to the arguments specified by the
//.             //      user
//.             //
//.
//.             fseek( pf, Offset, SEEK_SET );
//.             while( ( (cb = GetRecord( buf, pf )) != 0) && cNumberOfBlocks ) {
//.                     cNumberOfBlocks--;
//.                     if ( cNumberOfBlocks == 0 ) {
//.                             cb = ( cb < cLastBlockSize ) ? cb : cLastBlockSize;
//.                     }
//.                     Translate( DispFormat, buf, cb, line );
//.                     PutAddress( line, CurrentAddress, AddrFormat );
//.                     if ( (DumpAscii == YES) || (DumpAscii == NOT_DEFINED) )
//.                             {
//.                             PutTable ( line, buf, cb );
//.                     }
//.                     puts( line );
//.
//.                     CurrentAddress += RECORD_SIZE;
//.             }
        }
}
/* end of "void main()" */