/* cq - Binary to C language string quote
 * Masanao Izumo <mo@goice.co.jp>
 */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#define VERSION "2.2"

char *newline_code = "\\n\\\r\n";
enum quote_mode
{
    LIKE_C_LITERAL,
    OCT_MODE,
    HEX_MODE,
    LIKE_LESS
};
int quote_mode = LIKE_C_LITERAL;
int all_quote = 0;

static char* hat_codes = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";

void write_quote_message(int fd, const unsigned char* buff, int n)
{
    int i, c;
    char s[16];
    char* p;

    for(i = 0; i < n; i++)
    {
	c = buff[i];
	p = s;
	if(!all_quote && ' ' <= c && c <= 126)
	{
	    if((c == '"' || c == '\\') && quote_mode == LIKE_C_LITERAL)
	    {
		p[0] = '\\';
		p[1] = c;
		p[2] = '\0';
	    } else {
		p[0] = c;
		p[1] = '\0';
	    }
	}
	else if(c == '\n')
	    p = newline_code;
	else
	{
	    switch(quote_mode)
	    {
	      case LIKE_C_LITERAL:
		switch(c)
		{
		  case '\t': p = "\\t"; break;
		  case '\v': p = "\\v"; break;
		  case '\b': p = "\\b"; break;
		  case '\r': p = "\\r"; break;
		  case '\f': p = "\\f"; break;
		  case 0x07: p = "\\a"; break;
		  case '\\': p = "\\\\"; break;
		  default:
		    sprintf(p, "\\%03o", c);
		    break;
		}
		break;

	      case OCT_MODE:
		sprintf(p, "\\%03o", c);
		break;

	      case HEX_MODE:
		sprintf(p, "\\x%02x", c);
		break;

	      case LIKE_LESS:
		if(c == 127)
		    p = "^?";
		else if(c < 32)
		{
		    p[0] = '^';
		    p[1] = hat_codes[c];
		    p[2] = '\0';
		}
		else
		    sprintf(p, "<%02X>", c);
		break;
	    }
	}
	write(fd, p, strlen(p));
    }
}

void usage(void)
{
    fprintf(stderr, "Usage: cq [-h] [-V] [-c] [-o] [-O] [-x] [-X] [-l] [filename ...]\n");
    exit(1);
}

int main(int argc, char** argv)
{
    unsigned char buff[BUFSIZ];
    int i, n;

    for(i = 1; i < argc; i++)
    {
	if(!strcmp(argv[i], "-h"))
	{
	    usage();
	}
	else if(!strcmp(argv[i], "-V"))
	{
	    printf("cq version %s\n", VERSION);
	    exit(0);
	}
	else if(!strcmp(argv[i], "-o"))
	{
	    quote_mode = OCT_MODE;
	    all_quote = 0;
	    newline_code = "\\012\r\n";
	}
	else if(!strcmp(argv[i], "-O"))
	{
	    quote_mode = OCT_MODE;
	    all_quote = 1;
	    newline_code = "\\012\r\n";
	}
	else if(!strcmp(argv[i], "-x"))
	{
	    quote_mode = HEX_MODE;
	    all_quote = 0;
	    newline_code = "\\x0a\r\n";
	}
	else if(!strcmp(argv[i], "-X"))
	{
	    quote_mode = HEX_MODE;
	    all_quote = 1;
	    newline_code = "\\x0a\r\n";
	}
	else if(!strcmp(argv[i], "-l"))
	{
	    quote_mode = LIKE_LESS;
	}
	else if(!strcmp(argv[i], "-c"))
	{
	    quote_mode = LIKE_C_LITERAL;
	}
	else if(argv[i][0] == '-')
	{
	    usage();
	}
	else
	{
	    break;
	}
    }

    if(i == argc)
    {
	while((n = read(0, buff, BUFSIZ)) > 0)
	    write_quote_message(1, buff, n);
	if(n < 0)
	{
	    perror("read");
	    exit(1);
	}
    }
    else
    {
	int fd;
	char* fname;

	for(; i < argc; i++)
	{
	    fname = argv[i];
	    if((fd = open(fname, 0)) < 0)
	    {
		perror(fname);
		continue;
	    }

	    while((n = read(fd, buff, BUFSIZ)) > 0)
		write_quote_message(1, buff, n);
	    if(n < 0)
		perror("read");
	    close(fd);
	}
    }
    return 0;
}
