/**
 * hex-head.c  hex output of the head command.
 * 
 * Written and Copyright (c) 2009 by Aaron Conole <apconole@yahoo.com>
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the license, or (at your 
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY  WARRANTY; without even the IMPLIED WARRANTY of MERCHANTIBILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#ifndef BUFSIZ
#define BUFSIZ 8192
#endif

const char hex_head_usage[] = ": [OPTIONS] [FILE]...\n";

int c_gi = 0, c_cnt=0;
long c_len = 0;
int scnt = 0;
char str[17];

int dump(void* b, int len, FILE *dump){
    unsigned char *buf = b;
    int cnt = 0;
    
    FILE *out = stdout;
    
    if(dump != NULL)
        out = dump;

    for ( ; c_gi < c_len; c_gi++ ){
        if ( c_cnt % 16 == 0 ){
            fprintf(out, "  %s\n%04X: ", str, c_cnt);
            memset(str, 0, 17);
            scnt = 0;
        }
        if ( buf[cnt] < ' '  ||  buf[cnt] >= 127 )
            str[scnt%16] = '.';
        else
            str[scnt%16] = buf[cnt];
        fprintf(out, "%02X ", buf[cnt++]);
        c_cnt++; scnt++; --len;
    }

    fflush(out);
    return len;
}

int head(FILE *src, int len, int lines)
{
    long lread = 0;
    size_t rlen = 0;
    int olen = 0;
    char buf[BUFSIZ];

    c_len = c_gi = c_cnt = 0;
    memset(str, 0, 17);
    scnt = 0;

    lread = ftell(src);
    len += lread;
    c_len = len;

    while((lines > 0) && 
          (fgets(buf, sizeof(buf), src) != 0))
    {
        rlen = ftell(src) - lread;
        lread += rlen;
        if((len != -1) && (lread > len)) /*last group of bytes*/
        {
            rlen = rlen - (lread - len);
            lines = 0;
        }else if(len == -1)
        {
            c_len = lread;
        }
        olen = dump(buf, rlen, stdout);
        if(buf[rlen - 1] == '\n')
            lines--;
    }
    if(olen == 0)
        fprintf(stdout, "  %s\n", str);
    else
        fprintf(stdout, "  %*s", 16+(16-olen%16)*2, str);
    
    fflush(stdout);
    fprintf(stdout, "\n");
    return 0;
}

int main(int argc, char *argv[])
{
    char *buf;
    char opt;
    int len = -1, lines = 10, tmplen, i;
    char pr_head = 1;
    FILE *fp;
    if( argc <= 1 )
    {
        fprintf(stderr, "%s%s\n", argv[0], hex_head_usage);
        return -1;
    }

    for (i = 1; i < argc; ++i)
    {
        if(argv[i][0] == '-')
        {
            opt = argv[i][1];
            switch(opt)
            {
            case 'v':
                pr_head = 1;
                break;

            case 'q':
                pr_head = 0;
                break;
                
            case '-':
            {
                if(!strncmp("bytes", argv[i]+2, 5))
                {
                    tmplen = 0;
                    tmplen = atoi(argv[i]+8);
                    len = tmplen;
                }else if(!strncmp("lines", argv[i]+2, 5))
                {
                    lines = atoi(argv[i]+8);
                    return -1;
                }
                else
                {
                    printf("invalid options\n");
                    return -1;
                }
            }
            break;
            case 'c':
                tmplen = 0;
                if(++i < argc)
                    tmplen = atoi(argv[i]);
                if(tmplen < 1)
                {
                    printf("Invalid length field.\n");
                    return -1;
                }
                len = tmplen;
                break;
            case 'n':
                tmplen = 0;
                if(++i < argc)
                    tmplen = atoi(argv[i]);
                if(tmplen < 1)
                {
                    printf("Invalid lines field.\n");
                    return -1;
                }
                lines = tmplen;
                break;
            case 'h':
                printf("invalid options\n");
                return -1;
            }
        }
        else
        {
            break;
        }
    }

    if(i >= argc)
    {
        head(stdin, len, lines);
    }
    else
    {
        for(;i<argc;++i)
        {
            FILE *src = fopen(argv[i], "r");
            
            if(src == NULL)
            {
                fprintf(stderr, "%s: %s: %s\n",
                        argv[0], argv[i], strerror(errno));
            }
            else
            {
                if(pr_head)
                {
                    fprintf(stdout, "==> %s <==\n", argv[i]);
                }
                if(head(src, len, lines) == -1)
                    return -1;
                if(i < argc - 1)
                {
                    fprintf(stdout, "\n");
                }
            }
        }
    }    
    return 0;
}

