Information wants to be free...

Random Line Filter

This is a special tool I hacked together to solve a very specific problem. It is basically a filter that will select one random line of the input and print it. The naive way of solving this would be to store all the lines in memory and then randomly select one of them, but the problem with this method is it's O(n) memory usage. Fortunately, I discovered another interesting algorithm that does the same thing, but with O(1) memory usage, and yet provides perfectly balanced randomness.

Take a look at the code:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>

int consume_line(FILE *fh)
  int c;
  while ((c = fgetc(fh)) != EOF) {
    if (c == '\n')
      return 0;
  return -1;

int main(void)
  char file[PATH_MAX];
  int eof = 0, count = 2;


  if (fgets(file, PATH_MAX, stdin) == NULL)
    return 1;

  while (! eof) {
    if (random() % count == 0) {
      if (fgets(file, PATH_MAX, stdin) == NULL)
        eof = 1;
    } else {
      if (consume_line(stdin) == -1)
        eof = 1;

  printf("%s", file);
  return 0;

The tool can provide a simple way of playing random MP3 files, without the need for a shuffle function in the playback program. Like this example, using mplayer:

while true; do mplayer "`find . -name "*.mp3" | ./randline`"; done

This was the original intention of the tool, but I later discovered that mplayer does in fact have a shuffle mode! Well, it's too late now, as the tool has already been created.

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

Send and Receive files with Netcat

Here is a very simple way to send single files over the network, from one host to another. All you need is the netcat tool (also known as "nc" on the command line) and a couple of shell scripts. The transfer method is like FTP (over a straight TCP connection), but without the control channel.

Sender script:

if [ -z "$1" ]; then
  echo "Usage: $0 <file to send>"
  exit 1
nc $REMOTE_HOST 10001 -q 1 < "$1"

Receiver script:

if [ -z "$1" ]; then
  echo "Usage: $0 <file to receive>"
  exit 1
if [ -e "$1" ]; then
  echo "error: file exists."
  exit 1
  nc -p 10001 -l > "$1"

In these examples, I am using a static host as the receiver ($REMOTE_HOST), so I don't need to specify it all the time. I have also decided on a static port number (10001) to use on both ends. The port number should probably be over 1024, so a normal user can create a listening socket on it. Also note that the receiver script always needs to be started before the sender script, if not, the connection will be refused.

Topic: Configuration, by Kjetil @ 29/11-2009, Article Link

Aspire One D250 Tweaks

I recently bought a new netbook, an Acer Aspire One D250 (HD version). Unfortunately, it was not possible to get it with Linux pre-installed, but I quickly re-formatted the hard-drive and installed Slackware 13.0. As usual with new hardware on Linux, some devices were not supported out of the box. After some tweaking however, I got it all up and running.

There were problems with both the ethernet and the wireless network interfaces, and also with the built in microphone controlled by the sound-card. Here is the "lspci -nn" information of the affected hardware devices:

0:1b.0 Audio device [0403]: Intel Corporation 82801G (ICH7 Family) High Definition Audio Controller [8086:27d8] (rev 02)
01:00.0 Network controller [0280]: Broadcom Corporation BCM4312 802.11b/g [14e4:4315] (rev 01)
03:00.0 Ethernet controller [0200]: Attansic Technology Corp. Atheros AR8132 / L1c Gigabit Ethernet Adapter [1969:1062] (rev c0)

The Wi-Fi interface can be fixed by installing the proprietary driver from Broadcom, instead of using the open source "b43" driver. (The b43 driver did not support this particular chip-set in the beginning of September when I installed everything, but this may have changed now.) I used the "hybrid-portsrc-x86_32-v5_10_91_9.tar.gz" driver with the "" patch, and it has so far worked fine. I used the following steps to install it (some of the same information can also be found in the driver's readme file):

patch -p1 < patch_2.6.29_kernels
make -C /lib/modules/ M=`pwd` clean
make -C /lib/modules/ M=`pwd`
sudo cp wl.ko /lib/modules/
sudo depmod

The ethernet interface suffers from strange bugs in the driver (atl1c) shipped with the default kernel ( Sometimes the chip itself disappears from the PCI bus, and does not show up until a complete power on/off has been done. The "ifconfig" command also shows crazy RX and TX values for the interface. I had to blacklist the kernel driver (by adding a file with "blacklist atl1c in the /etc/modprobe.d/ directory) and install the proprietary Atheros driver to fix it. I used the "AR81Family-linux-v1.0.0.10.tar.gz" file, which makes a new kernel module/driver named "atl1e".

The final thing I had issues with was the internal microphone, but it was fixed simply by updating the ALSA driver and library to version 1.0.21.

Topic: Configuration, by Kjetil @ 03/10-2009, Article Link

Amilo L7300 Tweaks

I have a Fujitsu Siemens Amilo L7300 laptop with Slackware Linux (kernel version 2.6.27) installed on it. As usual with laptop hardware, there are quirks and other special things, mostly because of the complex ACPI system.

In the /etc/rc.d/rc.local script that runs at start-up, I have put some commands that helps with some of the issues with this particular laptop. Hopefully this information can be of use for others also.

One issue is that the fans are turned on at power on, but they are never turned off. Normally they should be activated on demand when temperature rises and then deactivated as the temperature drops. They need to be turned on, then off and then on again manually to fix them.

Another issue is an annoying bug with the keyboard and touch-pad. Around 10% of the times the system has booted, they do not work at all, no reaction. The root cause seems to be the i8042 controller, but luckily it can be "reset".

Finally there is custom keyboard button (with a fan or atomic symbol on it) that is not recognised by the kernel. There is a command to map it, so that it can be used in X afterwards.

Here is parts of the rc.local script:


echo "Reset ACPI fan status."
echo 3 >> /proc/acpi/fan/FN1/state
echo 3 >> /proc/acpi/fan/FN2/state
sleep 1
echo 0 >> /proc/acpi/fan/FN1/state
echo 0 >> /proc/acpi/fan/FN2/state
sleep 1
echo 3 >> /proc/acpi/fan/FN1/state
echo 3 >> /proc/acpi/fan/FN2/state

echo "Reset i8042 keyboard and touch-pad."
echo -n "i8042" > /sys/bus/platform/drivers/i8042/unbind 
sleep 1
echo -n "i8042" > /sys/bus/platform/drivers/i8042/bind 

echo "Assign keycode for extra key on keyboard."
/usr/bin/setkeycodes 6d 120

Topic: Configuration, by Kjetil @ 01/09-2009, Article Link

X Terminal Directory Hack

When working under the X Window System environment with multiple terminals opened, I usually work in the same directory on the file system. Instead of manually changing directory every time a new terminal is opened, I had an idea to do it with shell functions like this:

function mk { pwd | xclip; }
function rt { cd `xclip -o`; }

These functions will use the X clipboard to store and retrieve the current directory using the xclip tool. This way, the current directory can be marked (mk) in the terminal currently in use and retrieved (rt) in the new terminal afterwards.

Topic: Configuration, by Kjetil @ 17/08-2009, Article Link

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("%08x |", n);

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


  if (n % columns < columns)

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

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

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. */
    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)
      if (argc > 2)
        display_banner(argv[i], columns);
      dump_file(fh, columns, in_color);
  } else
    dump_file(stdin, columns, in_color);

  return 0;

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

Python Menu System

To fit my specific requirements for a media center PC, I made a simple custom menu system. I also used the opportunity to learn some Python in the process. Basically, it reads a textual configuration file and sets up a graphical menu using Tk. The menu entries will execute a specific command when clicked, and can be wrapped in a file or numeric selection mechanism, if required.

Sorry for not using objects in the code, and I will admit that it's ugly with global variables in Python. But I'm a C-guy, and old habits die hard. :-)


import sys
import os
import Tkinter
import tkFileDialog

_num_value = 1
_num_label = None
_num_top = None

def read_config_file(path):
    fh = open(path, "r")
    list = []
    for s in fh:
    return list
  except IOError:
    print "Error: Unable to open config file for reading!"
    return None

def menu_direct(command):
  print "Info: Direct execute: " + command

def num_increment():
  global _num_value, _num_label
  _num_value += 1

def num_decrement():
  global _num_value, _num_label
  _num_value -= 1

def num_exec(command):
  global _num_value, _num_top
  new = command.replace("$", str(_num_value))
  print "Info: Num execute: " + new

def menu_num_select(command):
  global _num_top, _num_label, _num_value

  _num_top = Tkinter.Tk()

  label = Tkinter.Label(_num_top, text="Number:")
  label.pack(fill="x", expand=True)

  plus = Tkinter.Button(_num_top, text="+", command=num_increment)
  plus.pack(fill="x", expand=True)

  _num_label = Tkinter.Label(_num_top, text=str(_num_value))
  _num_label.pack(fill="x", expand=True)

  plus = Tkinter.Button(_num_top, text="-", command=num_decrement)
  plus.pack(fill="x", expand=True)

  f = lambda x = command: num_exec(x)
  go = Tkinter.Button(_num_top, text="GO!", command=f)
  go.pack(fill="x", expand=True)


def menu_file_select(command):
  file = tkFileDialog.askopenfilename()
  if file == ():
  new = command.replace("$", str(file))
  print "Info: File execute: " + new

def main():
  if len(sys.argv) != 2:
    print "Usage: %s <config file>" % sys.argv[0]

  list = read_config_file(sys.argv[1])
  if list == None:

  top = Tkinter.Tk()
  for sublist in list:
    if sublist[0] == "Direct":
      # NOTE: This does not work:
      # f = lambda: menu_direct(sublist[2])
      f = lambda x = sublist[2]: menu_direct(x)
    elif sublist[0] == "NumSelect":
      f = lambda x = sublist[2]: menu_num_select(x)
    elif sublist[0] == "FileSelect":
      f = lambda x = sublist[2]: menu_file_select(x)
      print "Error: Unknown menu type!"
    button = Tkinter.Button(top, text=sublist[1], command=f)
    button.pack(fill="x", expand=True)


if __name__ == "__main__":

Here is a typical config file (not very unlike the one I currently use):

FileSelect#Play File#/usr/bin/xine -pfq "$"
NumSelect#Play DVD#/usr/local/bin/mplayer -fs -zoom dvd://$
Direct#Power Off#/sbin/poweroff

I used to use tabulators instead of '#' characters to separate the columns, but this really messes up any copy/paste/transfer, etc, so I just had to pick a character that is unlikely to be used in a command. Please just change the code if you don't like it. The '$' characters, are interpolated with the value from the file or numeric selection mechanism.

The final graphical representation of the menu will actually depend heavily on what version of Tk is installed, what window manager is used and font is used as default. Here is how it looks on one of my systems:

Python menu in fluxbox.

Topic: Scripts and Code, by Kjetil @ 03/05-2009, Article Link

ASCII-fication Filter

I made a filter program to convert normal images into random colored ASCII characters. It is similar to aalib, but aalib does not use random characters, and uses only a few colors, (in order to comply with text terminals I think).

It operates on plain PGM (Portable Greymap) image files only, since this is easy to parse. You can involve the convert tool from ImageMagick to use any kind of image file, like this:

convert foo.jpg -compress none pgm:- | ./pgm-asciify | convert pgm:- foo.png

Please note that you will get the best results by using big images with high contrast.

The code is released under a MIT licence, and can be fetched here.

Here is an example:

Keyboard asciified.

Filtered from this image:

Keyboard in normal appearance.

Topic: Open Source, by Kjetil @ 19/04-2009, Article Link

Number Conversion Tool

Here is another homemade tool that I use when dealing with numbers during programming, analysis, etc. The program will simply output its argument in decimal, hex and binary notation. The argument can be specified in any of the three notations by prefixing the number with 0x or 0b, or no prefix for decimal numbers. There is also support for specifying addition (+), subtraction (-) or xor (^), and then a second number to perform this operation.

The code is loosely based on the binary dumping tool presented earlier, and can be found here:

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

static int power_of_two(int n)
  if (n == 0)
    return 1;
    return 2 * power_of_two(n - 1);

static char *convert_to_bin(int integer)
  static char buffer[33]; /* Should not be longer than 32-bits + NULL. */
  int i, first_one;

  buffer[32] = '\0'; /* Always terminate. */

  first_one = 31;
  for (i = 31; i >= 0; i--) {
    if (integer - power_of_two(i) >= 0) {
      buffer[31 - i] = '1';
      if (first_one > (31 - i))
        first_one = (31 - i);
      integer -= power_of_two(i);
    } else
      buffer[31 - i] = '0';

  return &buffer[first_one];

static int convert_from_bin(char *number)
  int value, n;
  char *p;

  value = 0; 
  n = strlen(number) - 3; /* Also minus "0b". */
  p = &number[2];
  do {
    if (*p == '0') {
    } else if (*p == '1') {
      value += power_of_two(n);
  } while (*p++ != '\0');

  return value;

static int convert_to_int(char *number)
  int integer;

  if (strncmp(number, "0x", 2) == 0) {
    sscanf(number, "0x%x", &integer);
  } else if (strncmp(number, "0b", 2) == 0) { 
    integer = convert_from_bin(number);
  } else {
    integer = atoi(number);
  return integer;

static void display_int(int integer, char *prefix)
  printf("%s%-10u 0x%-10x 0b%s\n",
    prefix, integer, integer, convert_to_bin(integer));

int main(int argc, char *argv[])
  int n1, n2, result;
  char *prefix;

  if (argc == 2) { /* Just display the number in different forms. */
    n1 = convert_to_int(argv[1]);
    display_int(n1, "   ");

  } else if (argc == 4) { /* Perform extra operation. */
    n1 = convert_to_int(argv[1]);
    n2 = convert_to_int(argv[3]);
    if (argv[2][0] == '+') {
      result = n1 + n2;
      prefix = " + ";
    } else if (argv[2][0] == '-') {
      result = n1 - n2;
      prefix = " - ";
    } else if (argv[2][0] == '^') {
      result = n1 ^ n2;
      prefix = " ^ ";
    } else {
      fprintf(stderr, "%s: error: invalid operator.\n", argv[0]);
      return -1;
    display_int(n1, "   ");
    display_int(n2, prefix);
    display_int(result, " = ");

  } else {
    fprintf(stderr, "Usage: %s <number> [<+|-|^> <number>]\n", argv[0]);
    fprintf(stderr, "  number can be decimal, hex (0x) or binary (0b)\n");
    return -1;
  return 0;

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

Deus Ex vblank fix.

Deus Ex is one of those games that seems to work flawlessly under Wine in Linux, except for this issue I will mention below.

If you play Deus Ex on a fast machine, you may experience that the audio dialogs cuts off to early. According to information lying around on the Internet, this seems to be a generic bug/weakness in the Unreal engine, and apparently affects all platforms(?). But it can be fixed by forcing sync to vblank, which slows down the (too fast) frame rate. There does not seem to be any way of doing this from the menus in Deus Ex, but it can be done by configuring it in the graphics driver.

On Linux (at least with the proprietary Nvidia drivers) there is an easy way to setup sync to vblank automatically, by setting an environment variable.

You can just wrap/replace the actual binary with a small shell script like this:

export __GL_SYNC_TO_VBLANK=1
exec /path/to/deusex

Topic: Configuration, by Kjetil @ 06/03-2009, Article Link

RPN FPU Calculator

Just for fun, I wrote a RPN (Reverse Polish Notation) calculator in x86 assembler. It uses the FPU (Floating-Point Unit) for all the calculations, and some common C-library calls for input/output.

It only supports the basic arithmetic operations like addition, subtraction, multiplication and division, and only operates on integers. I have tried to emulate the basic behaviour of the common dc Unix command-line RPN calculator, so it will print the result when getting a 'p', and quit on a 'q'. Also be aware that it expects each operator or operand on a single line by itself.

It is written using AT&T syntax and will probably only assemble/link on Linux x86/i386 platforms. Check out the code:

 * Assembling and linking commands:
 * as -o fpu-rpn.o fpu-rpn.s
 * ld -dynamic-linker /lib/ -lc -o fpu-rpn fpu-rpn.o

.section .data
  .asciz "%d\n"
  .asciz "Error: Unknown input.\n"
  .asciz "Error: Stack empty.\n"

.section .bss
  .lcomm input, 16
  .lcomm buffer, 4
  .lcomm status, 2

.section .text
.globl _start

  pushl stdin
  pushl $16
  pushl $input
  call fgets
  addl $12, %esp

  /* End if EOF is reached (fgets returns NULL). */
  cmpl $0, %eax
  je end

  /* Extract first character of returned input string. */
  movl (%eax), %ebx

  /* Clear any errors on the FPU each round. */

  /* Switch-case like check on the input. */
  cmpb $0x71, %bl # 'q'
  je end
  cmpb $0x70, %bl # 'p'
  je display_result
  cmpb $0x2B, %bl # '+'
  je addition
  cmpb $0x2D, %bl # '-'
  je subtraction
  cmpb $0x2A, %bl # '*'
  je multiplication
  cmpb $0x2F, %bl # '/'
  je division

  /* Check if between 0-9 to determine if it's a number. */
  cmpb $0x30, %bl # '0'
  jl input_error
  cmpb $0x39, %bl # '9'
  jg input_error

  /* Convert number from string and push onto FPU's stack. */
  pushl %eax
  call atoi
  addl $4, %esp
  movl %eax, buffer
  filds buffer
  jmp read_input

  push stdout
  push $error_input
  call fputs
  addl $8, %esp
  jmp read_input

  jmp check_stack

  jmp check_stack

  jmp check_stack

  jmp check_stack

  fstsw status
  testw $0b1000000, status /* Test for "Stack Fault" flag. */
  jz read_input

  fdecstp /* Decrement stack to avoid displaying the fake result. */
  push stdout
  push $error_stack
  call fputs
  addl $8, %esp
  jmp read_input

  /* Display the value at the top of the FPU's stack. */
  fistpl buffer
  fstsw status
  testw $0b1000000, status
  jnz read_input /* Just do the check again to receive error messsage. */

  pushl buffer
  pushl $output_format
  call printf
  addl $8, %esp
  jmp read_input

  /* Tell the Linux kernel to end this process. */
  movl $1, %eax # exit()
  movl $0, %ebx
  int $0x80

Topic: Scripts and Code, by Kjetil @ 13/02-2009, Article Link

Byte Value Visualization

While doing cryptanalysis on some XOR-encrypted files, I came up with the idea of visualizing byte values in these files in order to discover patterns. Having used the SDL library on previous projects, this was a good candidate to draw the graphics.

Here is an output example, visualizing the source code of this application:

Visualization of a text file.

It is easy to see that this is a plain text file, since there are mostly printable ASCII characters in the 0x20 to 0x7F area. You can also see the low-value newline characters (0x0A) clearly.

Here is another example, visualizing the application in binary form after compilation:

Visualization of a binary file.

This is clearly not human-readable text, as there is a lot of NULL (0x00) bytes, and some bytes with very high values (0xFF).

I believe it's a lot easier to spot patterns this way, instead of using a hex editor. The application reads data (byte values) from standard in and stops after 1024 bytes or EOF.

But enough talk, here is the source code:

#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>

#define MAX_DATA 1024

int main(int argc, char *argv[])
  int c, n;
  unsigned char data[MAX_DATA];
  SDL_Surface *screen;
  SDL_Rect rect;
  SDL_Event event;

  n = 0;
  while ((c = fgetc(stdin)) != EOF) {
    data[n] = c;
    if (n >= MAX_DATA)

  if (SDL_Init(SDL_INIT_VIDEO) != 0) {
    fprintf(stderr, "Error: Unable to initalize SDL: %s\n", SDL_GetError());
    return 1;

  screen = SDL_SetVideoMode(n, 255, 16, SDL_DOUBLEBUF);
  if (screen == NULL) {
    fprintf(stderr, "Error: Unable to set video mode: %s\n", SDL_GetError());
    return 1;

  for (n = n - 1; n >= 0; n--) {
    rect.x = n;
    rect.y = 255 - data[n];
    rect.w = 1;
    rect.h = data[n];
    SDL_FillRect(screen, &rect, 0xffffff);

  while (1) {
    if (SDL_PollEvent(&event) == 1) {
      if (event.type == SDL_KEYDOWN) {
        if (event.key.keysym.sym == SDLK_q)
      } else if (event.type == SDL_QUIT) {

  return 0;

Topic: Scripts and Code, by Kjetil @ 24/01-2009, Article Link

MP3 Cutting Tool

It is not uncommon to have a (dumb) MP3-player that fast-forwards at a static speed, regardless of the file size/length. This is very annoying when you want to skip ahead in a pod-cast or audio-book file that lasts for over an hour. To overcome this problem, I decided to make a tool to split/cut MP3 files into smaller parts.

In order to correctly cut MP3 files, you need to know exactly where the frame boundaries are, and perform the incisions there. This tool will find the boundaries and calculate where to cut, based on the number of parts the user has specified as input.

Note that ID3 tags will not be replicated in any way, so they will probably remain in only one of the produced parts.

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

static int bitrate_matrix[16][5] = {
  {0,   0,   0,   0,   0},
  {32,  32,  32,  32,  8},
  {64,  48,  40,  48,  16},
  {96,  56,  48,  56,  24},
  {128, 64,  56,  64,  32},
  {160, 80,  64,  80,  40},
  {192, 96,  80,  96,  48},
  {224, 112, 96,  112, 56},
  {256, 128, 112, 128, 64},
  {288, 160, 128, 144, 80},
  {320, 192, 160, 160, 96},
  {352, 224, 192, 176, 112},
  {384, 256, 224, 192, 128},
  {416, 320, 256, 224, 144},
  {448, 384, 320, 256, 160},
  {0,   0,   0,   0,   0}};

static int sampling_matrix[4][3] = {
  {44100, 22050, 11025},
  {48000, 24000, 12000},
  {32000, 16000, 8000},
  {0,     0,     0}};

static int decode_header(unsigned char *header)
  int version, layer, padding;
  int bitrate_row, bitrate_col, sampling_row, sampling_col;

  version = (header[1] & 0x08) >> 3; /* MPEG version. */
  layer = (header[1] & 0x06) >> 1; /* MPEG layer. */

  bitrate_row = (header[2] & 0xf0) >> 4;
  bitrate_col = -1;
  if (version == 1) {
    if (layer == 3)      /* I */
      bitrate_col = 0;
    else if (layer == 2) /* II */
      bitrate_col = 1;
    else if (layer == 1) /* III */
      bitrate_col = 2;
  } else { /* Version 2 */
    if (layer == 3)      /* I */
      bitrate_col = 3;
    else if (layer == 2) /* II */
      bitrate_col = 4;
    else if (layer == 1) /* III */
      bitrate_col = 4;

  sampling_row = (header[2] & 0x0c) >> 2;
  sampling_col = (version == 0) ? 1 : 0;

  padding = (header[2] & 0x02) >> 1;

  if (sampling_matrix[sampling_row][sampling_col] == 0)
    return -1; /* Cannot divide by zero. */

  if (layer == 3) /* I */
    return (12 * (bitrate_matrix[bitrate_row][bitrate_col] * 1000) /
      sampling_matrix[sampling_row][sampling_col] + (padding * 4)) * 4;
  else if (layer == 2 || layer == 1) /* II or III */
    return 144 * (bitrate_matrix[bitrate_row][bitrate_col] * 1000) /
      sampling_matrix[sampling_row][sampling_col] + padding;
    return -1;

static int read_frames(FILE *src, FILE *dst, int frame_limit)
  int c, n, frame_length, no_of_frames;
  unsigned char quad[4];

  quad[0] = quad[1] = quad[2] = quad[3] = '\0';

  frame_length = n = no_of_frames = 0;
  while ((c = fgetc(src)) != EOF) {
    if (dst != NULL)
      fputc(c, dst);
    if (frame_length > 0) {

      /* While cutting the file, a frame limit is specified to stop reading. */
      if (frame_limit > 0) {
        if (frame_length == 0 && no_of_frames == frame_limit)
          return no_of_frames; /* Return early, but filehandle must be left
                                  intact by the caller to continue at the right
                                  spot! */

      /* Skip ahead in stream to avoid reading garbage. */

    /* Have a potential header ready for each read. */
    quad[0] = quad[1];
    quad[1] = quad[2];
    quad[2] = quad[3];
    quad[3] = c;

    /* Match frame sync. */
    if ((quad[0] == 0xff) && ((quad[1] & 0xf0) == 0xf0)) {
      frame_length = decode_header(quad) - 4;
      quad[0] = quad[1] = quad[2] = quad[3] = '\0';

  return no_of_frames;

int main(int argc, char *argv[])
  int i, no_of_frames, parts, limit;
  FILE *src, *dst;
  char filename[PATH_MAX]; /* POSIX limit. */

  if (argc != 3) {
    fprintf(stderr, "Usage: %s <mp3-file> <no-of-parts>\n", argv[0]);
    return 1;

  parts = atoi(argv[2]);
  if (parts == 0) {
    fprintf(stderr, "Error: Invalid number of parts specified.\n");
    return 1;

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

  no_of_frames = read_frames(src, NULL, 0);
  if (parts > no_of_frames) {
    fprintf(stderr, "Error: More parts than available frames specified.\n");
    return 1;


  for (i = 1; i <= parts; i++) {
    snprintf(filename, sizeof(filename), "%s.%02d", argv[1], i);

    dst = fopen(filename, "w");
    if (dst == NULL) {
      fprintf(stderr, "Error: Unable to open file for writing: %s\n",
      return 1;

    if (i == parts)
      limit = 0; /* Make sure all frames are read on the last part,
                    rounding errors in the formula prevents this. */
      limit = no_of_frames / parts;

    fprintf(stderr, "%02d: %s: %d\n", i, filename, 
      read_frames(src, dst, limit));


  return 0;

You will notice that the tool creates file names ending in 01, 02, etc. If this is a problem for you (or your MP3-player), simply issue a shell command like this (swaps the ".mp3" and the number):

for i in *.mp3*; do mv "$i" "${i/.mp3/}.mp3"; done

Topic: Scripts and Code, by Kjetil @ 04/01-2009, Article Link