Information wants to be free...

Curses Snow Crash

On my way to work one morning I got the idea for this program. Can it actually be called a program? It's more like a novelty, but so simple to make. Take a look at the excellent cmatrix for a similar, but much more advanced program.

The name is from Neal Stephenson's novel, where I imagine that the "Snow Crash" would look something like the output of this program.

Use it as a screen saver or just for fun, so check out the code:

#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <curses.h>

int main(int argc, char *argv[])
{
  int x, y, maxy, maxx;

  srand(time(NULL));

  initscr();
  atexit((void *)endwin);
  noecho();
  timeout(0);

  while (1) {
    if (getch() != ERR)
      return 0; /* Stop on any key press. */

    getmaxyx(stdscr, maxy, maxx);
    for (y = 0; y < maxy; y++) {
      for (x = 0; x < maxx; x++) {
        mvaddch(y, x, (rand() % 0x5E) + 0x21); /* 0x21 -> 0x7E */
      }
    }
    refresh();
    usleep(40000); /* ~24 frames per second. */
  }

  return 0;
}
          


Compiled and run; it will look something like this for each frame, just random characters splattered over the screen:

Snow Crash!


Here's a statically linked Win32 binary for convenience.

Topic: Scripts and Code, by Kjetil @ 01/12-2013, Article Link

Curses Cave Scroller

I like experimenting with curses, so I made yet another curses-based game. This time a side scroller, so check out this screenshot:

Screenshot of cave scroller.


Get the source code under a MIT License here.

Topic: Open Source, by Kjetil @ 02/11-2013, Article Link

Duplicate File Finder

This is kind of an improved version of the shell one-liner I made some years ago, except it does not remove the files, just lists them.

This new script uses Python, and it is almost too easy, since Python includes a file compare module in it's standard library. Take a look at the code:

#!/usr/bin/python

import os
import filecmp

class DupFinder(object):
    def __init__(self, dirname):
        self.dirname = dirname
        self.files = dict()

    def run(self):
        os.path.walk(self.dirname, self._walker, None)
        for dupes in self.files.values():
            if len(dupes) == 1:
                continue
            for path1 in dupes:
                for path2 in dupes:
                    if path1 == path2:
                        continue
                    if filecmp.cmp(path1, path2, False):
                        print "%s == %s" % (path1, path2)

    def _walker(self, arg, dirname, names):
        for file in names:
            try:
                path = os.path.join(dirname, file)
                size = os.path.getsize(path)
                if size == 0:
                    continue # Ignore empty files.
                if size in self.files:
                    self.files[size].append(path)
                else:
                    self.files[size] = [path]
            except OSError:
                continue
    
    def _compare(self, path1, path2):
        print path1, path2

if __name__ == "__main__":
    import sys

    if len(sys.argv) != 2:
        print "Usage: %s <directory>" % (sys.argv[0])
        sys.exit(1)

    dp = DupFinder(sys.argv[1])
    dp.run()

    sys.exit(0)
          


Topic: Scripts and Code, by Kjetil @ 05/10-2013, Article Link

File Renaming Using an Editor

Here is a script that builds on the principle of using a text editor to rename files. This is sometimes useful, since editors have more advanced features available, like block mode editing, compared to a shell.

The script generates another script that performs the renaming, but before the script is called, it's loaded into the editor. It operates on the current directory or another one if it's specified as an argument.

#!/bin/bash

SCRIPT="_rename.bash"

if [ ! -z "$1" ]; then
  cd "$1"
fi

if [ -e "./$SCRIPT" ]; then
  echo "$SCRIPT already exists, delete it if necessary."
  exit 1;
fi

LONGEST=0
for FILE in *; do
  if [ ! -e "$FILE" ]; then
    echo "Error: Directory empty!"
    exit 1;
  fi

  FILE="${FILE//\\/\\\\}"
  FILE="${FILE//\'/\'}"

  if [ ${#FILE} -gt $LONGEST ]; then
    LONGEST=${#FILE}
  fi
done

echo "#!/bin/bash" > "./$SCRIPT"
for FILE in *; do
  if [ "$FILE" = "$SCRIPT" ]; then
    continue
  fi

  FILE="${FILE//\\/\\\\}"
  FILE="${FILE//\'/\'}"

  echo -n "mv -v $'$FILE' " >> "./$SCRIPT"
  PAD=$(( $LONGEST - ${#FILE} ))
  while [ $PAD -gt 0 ]; do
    echo -n " " >> "./$SCRIPT"
    PAD=$(( $PAD - 1 ))
  done

  echo "$'$FILE'" >> "./$SCRIPT"
done

chmod +x "./$SCRIPT"

if [ "${EDITOR##*/}" = "vim" ]; then
  $EDITOR -c "set nowrap" "./$SCRIPT"
else
  $EDITOR "./$SCRIPT"
fi

bash "./$SCRIPT"
rm -f "./$SCRIPT"
          


Topic: Scripts and Code, by Kjetil @ 02/09-2013, Article Link

Morse Input

I got this strange idea of using a telegraph key to input characters to the computer, after inheriting such a key from my grandfather.

So I made a SDL based program to try it out. SDL makes it easy to display a kind of oscilloscope screen to show the incoming pulses, and also generate a simple tone for "realism". The actual signal input can be provided in two ways, either by just hitting space or by using an actual telegraph key connected as a switch between the CTS and RTS signals on a RS-232 serial port! The program attempts to convert the incoming pulses using some timing rules, into characters that are output on standard out.

Here is the source code released under a MIT lisence.

Here is a screenshot of the SDL "oscilloscope":

Morse Oscilloscope


This is the ancient telegraph key that I used:

Morse Key


Topic: Open Source, by Kjetil @ 03/08-2013, Article Link

DOS Game Cheats

This is article is a response to the one about binary patching tools that I made earlier. The reason these tools were created in the first place, was to have an easy way to specify cheat patches for some old DOS games. Below you will find "infinite lives" patches for three games that I managed to run in a debugger and crack open. Use the binary patching tool to read these and apply the changes.

Blues Brothers:

# Apply to "1.EXE", Original md5sum: 4317d3fa97fff52b5785cc31d3110ece

# Get infinite lives:
000062cd: 90 90 90 90
0000a061: 90 90 90 90

# Remove infinite lives:
#000062cd: 26 fe 4f 61
#0000a061: 26 80 7f 61
          


Fantasy World Dizzy:

# Apply to "DIZZY.EXE", Original md5sum: d614048d262945dccf069fe968f747cb

# Get infinite lives:
00006038: 90 90 90 90 90 90

# Remove infinite lives:
#00006038: fe 0e 84 4d 78 5e
          


Soccer Kid:

# Apply to "KID.EXE", Original md5sum: b5c443e0c8a9099706f22b952b68b2c1

# Get infinite lives:
00001257: 90 90

# Remove infinite lives:
#00001257: 2c 01
          


Topic: Configuration, by Kjetil @ 01/07-2013, Article Link

Binary Patching Tools

Here is a couple of tools I created for patching binary files. The diffing tool will compare two binary files and print a list on standard out with differences. The differences is displayed with an offset and hex values of changed bytes. This output can be used together with the patching tool to patch existing files, since it uses the same format on standard in to determine where to make changes.

Here is the differ source code:

#include <stdio.h>
#include <stdbool.h>

int main(int argc, char *argv[])
{
  FILE *fh_old, *fh_new;
  int c_old, c_new;
  unsigned int address;
  bool prev_different;
  
  if (argc != 3) {
    fprintf(stderr, "Usage: %s <old file> <new file>\n", argv[0]);
    return 1;
  }

  fh_old = fopen(argv[1], "rb");
  if (fh_old == NULL) {
    fprintf(stderr, "Error: Unable to open file for reading: %s\n", argv[1]);
    return 1;
  }

  fh_new = fopen(argv[2], "rb");
  if (fh_new == NULL) {
    fprintf(stderr, "Error: Unable to open file for reading: %s\n", argv[2]);
    fclose(fh_old);
    return 1;
  }

  prev_different = false;
  address = 0;
  while ((c_old = fgetc(fh_old)) != EOF) {
    c_new = fgetc(fh_new);
    if (c_new == EOF) {
      fprintf(stderr, "Warning: New file shorter than old file.\n");
      break;
    }

    if (c_old != c_new) {
      if (prev_different) {
        printf(" %02x", c_new);
      } else {
        printf("%08x: %02x", address, c_new);
      }
      prev_different = true;
    } else {
      if (prev_different) {
        printf("\n");
      }
      prev_different = false;
    }

    address++;
  }

  c_new = fgetc(fh_new);
  if (c_new != EOF) {
    fprintf(stderr, "Warning: Old file shorter than new file.\n");
  }

  fclose(fh_old);
  fclose(fh_new);

  return 0;
}
          


Here is the patcher source code:

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

#define CODE_MAX 256

typedef struct subst_s {
  unsigned int address;
  size_t code_len;
  unsigned char *code;
  struct subst_s *next;
} subst_t;

static subst_t *patch_read(FILE *fh)
{
  subst_t *current, *first;
  char line[CODE_MAX];
  unsigned char code[CODE_MAX];
  unsigned int address, byte;
  int consumed, start;
  size_t code_len;

  current = (subst_t *)malloc(sizeof(subst_t));
  if (current == NULL) {
    fprintf(stderr, "Error: malloc() failed.\n");
    return NULL;
  }
  current->address = 0xffffffff;
  current->code_len = 0;
  current->code = NULL;
  current->next = NULL;

  first = current;

  while (fgets(line, CODE_MAX, fh) != NULL) {
    if (strlen(line) <= 0)
      continue;
    if (line[0] == '#')
      continue;
    if (sscanf(line, "%x:%n", &address, &consumed) <= 0)
      continue;

    code_len = 0;
    start = consumed;
    while (sscanf(&line[start], "%2x%n", &byte, &consumed) > 0) {
      code[code_len] = byte;
      start += consumed;
      code_len++;
      if (code_len >= CODE_MAX)
        break;
    }

    if (code_len > 0) {
      current->address = address;
      current->code_len = code_len;
      current->code = (unsigned char *)malloc(code_len * sizeof(unsigned char));
      if (current->code == NULL) {
        fprintf(stderr, "Error: malloc() failed.\n");
        return NULL;
      }
      memcpy(current->code, code, code_len);

      current->next = (subst_t *)malloc(sizeof(subst_t));
      if (current->next == NULL) {
        fprintf(stderr, "Error: malloc() failed.\n");
        return NULL;
      }
      current->next->address = 0xffffffff;
      current->next->code_len = 0;
      current->next->code = NULL;
      current->next->next = NULL;

      current = current->next;
    }
  }

  return first;
}

static void patch_free(subst_t *patch)
{
  subst_t *prev;
  prev = NULL;
  while (patch->next != NULL) {
    if (prev != NULL)
      free(prev);
    if (patch->code != NULL)
      free(patch->code);
    prev = patch;
    patch = patch->next;
  }
  free(prev);
  free(patch);
}

int patch_lookup(subst_t *patch, int address)
{
  int i;
  while (patch->next != NULL) {
    for (i = 0; i < patch->code_len; i++) {
      if (patch->address + i == address)
        return (int)patch->code[i];
    }
    patch = patch->next;
  }
  return EOF;
}

int main(int argc, char *argv[])
{
  subst_t *patch;
  FILE *fh_old, *fh_new;
  int c_old, c_new;
  unsigned int address;
  
  if (argc != 3) {
    fprintf(stderr, "Usage: %s <old file> <new file>\n", argv[0]);
    return 1;
  }

  fh_old = fopen(argv[1], "rb");
  if (fh_old == NULL) {
    fprintf(stderr, "Error: Unable to open file for reading: %s\n", argv[1]);
    return 1;
  }

  fh_new = fopen(argv[2], "wb");
  if (fh_new == NULL) {
    fprintf(stderr, "Error: Unable to open file for writing: %s\n", argv[2]);
    fclose(fh_old);
    return 1;
  }

  patch = patch_read(stdin);

  address = 0;
  while ((c_old = fgetc(fh_old)) != EOF) {
    c_new = patch_lookup(patch, address);
    if (c_new != EOF) {
      fputc(c_new, fh_new);
    } else {
      fputc(c_old, fh_new);
    }
    address++;
  }

  patch_free(patch);

  fclose(fh_old);
  fclose(fh_new);

  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 17/06-2013, Article Link

Directory Tree Diff

Another month, another tool. Here is a small program that recursively compares two directory trees and prints the changes. Specifically; missing files/directories, additional files/directories and changed files are listed with a different color and prefix, like this screenshot example shows:

Screenshot of difftree.


Files are compared by first checking the size, then a byte by byte comparison.

Here's the code:

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>

static void diff_print(int level, char symbol, char *name, int is_dir)
{
  while (level-- > 0)
    printf("  ");

#ifdef WINNT
  printf("%c%s", symbol, name);
#else
  switch (symbol) {
  case '=':
    printf("%c%s", symbol, name);
    break;
  case '*':
    printf("%c\e[1;31m%s\e[0m", symbol, name);
    break;
  case '+':
    printf("%c\e[1;34m%s\e[0m", symbol, name);
    break;
  case '-':
    printf("%c\e[1;35m%s\e[0m", symbol, name);
    break;
  }
#endif

  if (is_dir)
    printf("/");

  printf("\n");
}

static int diff_file(char *path1, char *path2)
{
  FILE *fh1, *fh2;
  int c1, c2;

  fh1 = fopen(path1, "rb");
  if (fh1 == NULL) {
    fprintf(stderr, "Warning: Unable to open file: %s\n", path1);
    return 0;
  }

  fh2 = fopen(path2, "rb");
  if (fh2 == NULL) {
    fprintf(stderr, "Warning: Unable to open file: %s\n", path2);
    return 0;
  }

  while ((c1 = fgetc(fh1)) != EOF) {
    c2 = fgetc(fh2);
    if (c1 != c2) {
      fclose(fh1);
      fclose(fh2);
      return 1;
    }
  }

  fclose(fh1);
  fclose(fh2);
  return 0;
}

static void diff_dir(char *path1, char *path2, int level)
{
  DIR *dh;
  struct dirent *entry;
  struct stat st1, st2;
  char fullpath1[PATH_MAX], fullpath2[PATH_MAX];

  dh = opendir(path1);
  if (dh == NULL) {
    fprintf(stderr, "Warning: Unable to open directory: %s\n", path1);
    return;
  }

  while ((entry = readdir(dh))) {
    if (entry->d_name[0] == '.')
      continue; /* Ignore files with leading dot. */

#ifdef WINNT
    snprintf(fullpath1, PATH_MAX, "%s\\%s", path1, entry->d_name);
    snprintf(fullpath2, PATH_MAX, "%s\\%s", path2, entry->d_name);
#else
    snprintf(fullpath1, PATH_MAX, "%s/%s", path1, entry->d_name);
    snprintf(fullpath2, PATH_MAX, "%s/%s", path2, entry->d_name);
#endif

    if (stat(fullpath1, &st1) == -1) {
      fprintf(stderr, "Warning: Unable to stat() path: %s\n", fullpath1);
      continue;
    }

    if (S_ISDIR(st1.st_mode)) {
      if (stat(fullpath2, &st2) == -1) {
        diff_print(level, '+', entry->d_name, 1);
      } else {
        if (S_ISDIR(st2.st_mode)) {
          diff_print(level, '=', entry->d_name, 1);
          diff_dir(fullpath1, fullpath2, level + 1);
        } else {
          diff_print(level, '+', entry->d_name, 1);
        }
      }
      
    } else if (S_ISREG(st1.st_mode)) {
      if (stat(fullpath2, &st2) == -1) {
        diff_print(level, '+', entry->d_name, 0);
      } else {
        if (S_ISREG(st2.st_mode)) {
          if (st1.st_size == st2.st_size) {
            if (diff_file(fullpath1, fullpath2)) {
              diff_print(level, '*', entry->d_name, 0);
            } else {
              diff_print(level, '=', entry->d_name, 0);
            }
          } else {
            diff_print(level, '*', entry->d_name, 0);
          }
        } else {
          diff_print(level, '+', entry->d_name, 0);
        }
      }
    }
  }
  closedir(dh);

  dh = opendir(path2);
  if (dh == NULL) {
    fprintf(stderr, "Warning: Unable to open directory: %s\n", path2);
    return;
  }

  while ((entry = readdir(dh))) {
    if (entry->d_name[0] == '.')
      continue; /* Ignore files with leading dot. */

#ifdef WINNT
    snprintf(fullpath1, PATH_MAX, "%s\\%s", path1, entry->d_name);
    snprintf(fullpath2, PATH_MAX, "%s\\%s", path2, entry->d_name);
#else
    snprintf(fullpath1, PATH_MAX, "%s/%s", path1, entry->d_name);
    snprintf(fullpath2, PATH_MAX, "%s/%s", path2, entry->d_name);
#endif

    if (stat(fullpath2, &st2) == -1) {
      fprintf(stderr, "Warning: Unable to stat() path: %s\n", fullpath2);
      continue;
    }

    if (S_ISDIR(st2.st_mode)) {
      if (stat(fullpath1, &st1) == -1) {
        diff_print(level, '-', entry->d_name, 1);
      } else {
        if (! S_ISDIR(st1.st_mode)) {
          diff_print(level, '-', entry->d_name, 1);
        }
      }
      
    } else if (S_ISREG(st2.st_mode)) {
      if (stat(fullpath1, &st1) == -1) {
        diff_print(level, '-', entry->d_name, 0);
      } else {
        if (! S_ISREG(st1.st_mode)) {
          diff_print(level, '-', entry->d_name, 0);
        }
      }
    }
  }
  closedir(dh);

  return;
}

int main(int argc, char *argv[])
{
  if (argc < 3) {
    fprintf(stderr, "Usage: %s <directory 1> <directory 2>\n", argv[0]);
    return 1;
  }

  diff_dir(argv[1], argv[2], 0);
  return 0;
}
          


I have also compiled a Windows binary binary for convenience, but note that this does not support colorized output!

Topic: Scripts and Code, by Kjetil @ 01/05-2013, Article Link

Playlist Frontend

Here is a simple playlist frontend (in curses, as usual) for other media players. It communicates with the player program through signals, so the functionality is rather limited, just stop and start, but it's very generic. I have tested the program against mplayer and mpg123.

The main feature, and the reason I coded this in the first place, was to get a filter function. This means taking a large playlist with filenames and entering a (search) text to filter out which songs to listen to.

Here is a screenshot:

Screenshot of playlist frontend.


The code is available under a MIT License here.

Topic: Open Source, by Kjetil @ 01/04-2013, Article Link

Recursive FTP Download with Python

This is a feature that I have missed many times, as it is not a built-in feature of the standard Python FTP library. I decided to hack together a solution myself, and it was easier than expected. Everything is downloaded in binary mode, since ASCII mode causes so many issues!

Enjoy:

#!/usr/bin/python

import ftplib
import os

def ftp_get_files(directory):
	print "Going Into:", directory
	ftp.cwd(directory)
	os.mkdir(directory)
	os.chdir(directory)

	listing = []
	ftp.dir(listing.append)
	for entry in listing: 
		filename = entry.split(None, 8)[-1]
		perm = entry.split()[0]

		if filename == '.' or filename == '..':
			continue

		if perm[0] == "d": # Directory
			ftp_get_files(filename)

		elif perm[0] == "-": # File
			print "Fetch:", filename
			ftp.retrbinary('RETR %s' % (filename), open(filename, 'wb').write)

	print "Going Back:", directory
	ftp.cwd("..")
	os.chdir("..")

if __name__ == "__main__":
	import sys

	if len(sys.argv) < 6:
		print "Usage: %s <hostname> <user> <pass> <initial dir> <download dir>" % (sys.argv[0])
		sys.exit(1)

	ftp = ftplib.FTP(sys.argv[1])
	ftp.login(sys.argv[2], sys.argv[3])
	ftp.cwd(sys.argv[4])
	ftp_get_files(sys.argv[5])
	ftp.close()

	sys.exit(0)

          


Topic: Scripts and Code, by Kjetil @ 09/03-2013, Article Link

Commodore 64 RS-232 Interface

By using a TTL-level RS-232 to USB converter, like this one, it was surprisingly easy to connect the Commodore 64 to a Linux box. I read somewhere that an inverter was required on the RxD/TxD signals, but during my testing with this converter, that was not necessary. This means you only need wires, and no additional electronics!

The RS-232 interface is found on the C64's user port, and the wiring is as follows:

C64 Signal:   C64 User Port:   Converter:
-----------------------------------------
Ground        A + N            Gnd
RxD           B + C            TxD
TxD           M                RxD 
          


The best terminal program for the Commodore 64 is NovaTerm, since it supports VT102/ANSI emulation and such.

Assuming the converter is connected to a Linux box and detected as /dev/ttyUSB0, the connection can be tested like this at 1200 baud, which is the most common:

screen /dev/ttyUSB0 1200
          


To provide a login prompt at 1200 baud and supporting ANSI escape characters, something like this can be run as root:

agetty -L ttyUSB0 1200 ansi
          


Here's a "screenshot" of NovaTerm 9.6c connected to a Linux box and running top:

Screenshot of top via NovaTerm.


Topic: Configuration, by Kjetil @ 19/01-2013, Article Link

Recursive Binary String Search

This program is similar to the other one I made a while back, except this one searches for matches against a binary (string) value instead. For ease of use, the binary string is specified using a hexadecimal string.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <unistd.h>
#include <dirent.h>

static int hex_value(int c)
{
  if (c >= 0x30 && c <= 0x39) /* '0' - '9' */
    return c - 0x30;
  else if (c >= 0x41 && c <= 0x46) /* 'A' - 'F' */
    return (c - 0x41) + 10;
  else if (c >= 0x61 && c <= 0x66) /* 'a' - 'f' */
    return (c - 0x61) + 10;
  else
    return -1;
}

static int convert_from_hex(char *hex_string, 
  unsigned char *bin_string, int bin_string_len)
{
  int n1, n2, high;

  high = -1;
  n1 = n2 = 0;
  while (hex_string[n1] != '\0') {
    if (hex_value(hex_string[n1]) >= 0) {
      if (high == -1) {
        high = hex_string[n1];
      } else {
        bin_string[n2] = hex_value(high) * 16 + hex_value(hex_string[n1]);
        if (n2 >= bin_string_len)
          break;
        n2++;
        high = -1;
      }
    }
    n1++;
  }

  return n2;
}

static void search(char *path, unsigned char *bin_string, int len)
{
  int c, match, name_shown, address, match_address;
  FILE *fh;

  fh = fopen(path, "rb");
  if (fh == NULL) {
    fprintf(stderr, "Warning: Unable to open file: %s\n", path);
    return;
  }

  match = address = name_shown = match_address = 0;
  while ((c = fgetc(fh)) != EOF) {
    if (c == bin_string[match]) {
      if (match_address == 0)
        match_address = address;
      match++;
      if (match >= len) {
        if (! name_shown) {
          printf("%s\n", path);
          name_shown = 1;
        }
        printf("0x%x\n", match_address);
        match = 0;
        match_address = 0;
      }
    } else {
      match = 0;
      match_address = 0;
    }
    address++;
  }
  fclose(fh);
}

static void recurse(char *path, unsigned char *bin_string, int len)
{
  char full_path[PATH_MAX];
  struct dirent *entry;
  struct stat st;
  DIR *dh;

  dh = opendir(path);
  if (dh == NULL) {
    fprintf(stderr, "Warning: Unable to open directory: %s\n", path);
    return;
  }

  while ((entry = readdir(dh))) {
    if (entry->d_name[0] == '.')
      continue; /* Ignore files with leading dot. */

#ifdef WINNT
    snprintf(full_path, PATH_MAX, "%s\\%s", path, entry->d_name);
#else
    snprintf(full_path, PATH_MAX, "%s/%s", path, entry->d_name);
#endif

    stat(full_path, &st);
    if (S_ISDIR(st.st_mode)) {
      recurse(full_path, bin_string, len);
    } else if (S_ISREG(st.st_mode)) {
      search(full_path, bin_string, len);
    }
  }

  closedir(dh);
  return;
}

int main(int argc, char *argv[])
{
  int len;
  unsigned char *bin_string;
  struct stat st;

  if (argc != 3) {
    fprintf(stderr, "Usage: %s <directory or file> <hex string>\n", argv[0]);
    return 1;
  }

  len = strlen(argv[2]) / 2;
  bin_string = malloc(sizeof(unsigned char) * len);
  if (bin_string == NULL) {
    fprintf(stderr, "Error: Unable to malloc().\n");
    return 1;
  }

  len = convert_from_hex(argv[2], bin_string, len);
  if (len == 0) {
    fprintf(stderr, "Error: Invalid input hex string.\n");
    return 1;
  }

  stat(argv[1], &st);
  if (S_ISDIR(st.st_mode)) {
    recurse(argv[1], bin_string, len);
  } else if (S_ISREG(st.st_mode)) {
    search(argv[1], bin_string, len);
  } else {
    fprintf(stderr, "Error: Not a directory or regular file.\n");
    free(bin_string);
    return 1;
  }

  free(bin_string);

  return 0;
}
          


Topic: Scripts and Code, by Kjetil @ 05/01-2013, Article Link