Kjetil's Information Center: A Blog About My Projects

Colorized Character Dumper

I have noticed that when i use the hexdump tool, it is most often together with the -C option just to look at the characters. Hexdump is a bit limited, so I decided to make another tool specialized for character dumping. Instead of just printing a dot for non-printable characters, this tool will use a colorized printable character. Escape characters for instance, are represented in green. The tool also tries to use as much terminal real-estate as possible, so it will look at the current column width to expand itself.

Here is how it looks when dumping a binary file. (Actually itself.):

Screenshot in an xterm.


To keep it simple, I just used the standard ANSI way of drawing colors. An alternative would have been to use (n)curses. Check out the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

static void fputc_color(int c, FILE *fh)
{
  /* Based on vim's "unprintable" definition instead of ctype's isprint(). */

  if (c < 0x20) /* Escape characters. */
    fprintf(fh, "\e[32m%c\e[0m", c + 0x40);
  else if (c < 0x7F) /* Standard ASCII characters. */
    fputc(c, fh);
  else if (c == 0x7F) /* The DEL character. */
    fprintf(fh, "\e[32m%c\e[0m", '?');
  else if (c < 0xA1) /* Special unprintable characters and the NBSP. */
    fprintf(fh, "\e[31m%c\e[0m", c - 0x40);
  else /* "High" (Latin-1) characters. */
    fprintf(fh, "\e[33m%c\e[0m", c);
}

static void dump_file(FILE *fh, int columns, int in_color)
{
  int c, n;

  columns -= 12; /* Subtract the extra visualization. */
  n = 0;
  while ((c = fgetc(fh)) != EOF) {
    if (n % columns == 0) {
      if (n > 0)
        printf("|\n");
      printf("%08x |", n);
    }

    if (in_color) {
      fputc_color(c, stdout);
    } else {
      if (isprint(c))
        fputc(c, stdout);
      else
        fputc('.', stdout);
    }

    n++;
  }

  if (n % columns < columns)
    printf("|\n");
}

static void display_banner(char *text, int columns)
{
  int i;

  printf("--- %s ", text);
  for (i = (strlen(text) + 5); i < columns; i++)
    printf("-");
  printf("\n");
}

int main(int argc, char *argv[])
{
  int i, columns, in_color; 
  char *p;
  FILE *fh;

  p = getenv("COLUMNS");
  if (p == NULL)
    columns = 80; /* Default, and most likely. */
  else
    columns = atoi(p);

  in_color = 0; 
  if (isatty(STDOUT_FILENO)) {
    p = getenv("TERM");
    if (p != NULL) {
      if (strncmp(p, "xterm", 5) == 0)
        in_color = 1;
    }
  }

  if (argc > 1) {
    for (i = 1; i < argc; i++) {
      fh = fopen(argv[i], "r");
      if (fh == NULL)
        continue;
      if (argc > 2)
        display_banner(argv[i], columns);
      dump_file(fh, columns, in_color);
      fclose(fh);
    }
  } else
    dump_file(stdin, columns, in_color);

  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 20/06-2009, Article Link