Skip to content

Instantly share code, notes, and snippets.

@stbuehler
Created April 10, 2011 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stbuehler/912353 to your computer and use it in GitHub Desktop.
Save stbuehler/912353 to your computer and use it in GitHub Desktop.
Torrent Announce Info extractor
/*
Torrent Announce Info extractor
lists all announce urls from torrents on stdout
Build with ragel (http://www.complang.org/ragel/) and a c compiler:
ragel -T1 torrent-announce-info.rl && CFLAGS='-O2' make torrent-announce-info
list torrent files on stdin:
ls -1 /path/to/torrents/*.torrent | torrent-announce-info
or
find /path/to/torrents/ -name '*.torrent' | torrent-announce-info
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define MAXSTRLEN 1024
/*
static char tempbuf[MAXSTRLEN+1];
char* tostr(const char* str, int len) {
memcpy(tempbuf, str, len);
tempbuf[len] = '\0';
return tempbuf;
}
*/
void print_announce(const char* str, int len) {
write(1, str, len);
write(1, "\n", 1);
}
%%{
machine torrent_parser;
prepush {
if (top >= 31) {
fprintf(stderr, "stack overflow\n");
return;
}
}
action string_start { slen = 0; }
action string_len {
slen = 10*slen + (fc - '0');
if (slen > MAXSTRLEN) {
fprintf(stderr, "string overflow\n");
return;
}
}
action string_read { sstart = fpc+1; fpc += slen; }
dict = 'd' >{ fcall dict_start; };
list = 'l' >{ fcall list_start; };
num = 'i' ('-'|'') [0-9]* 'e';
string = ([0-9]+) >string_start $string_len ':'>string_read;
value = dict | list | num | string;
dict_start := ( string value )* 'e' @{ fret; };
list_start := ( value )* 'e' @{ fret; };
action torrententry {
if (8 == slen && 0 == strncmp("announce", sstart, 8)) {
fcall torrentannounce;
} else if (13 == slen && 0 == strncmp("announce-list", sstart, 13)) {
fcall torrentannouncelist;
} else if (strncmp("announce-list", sstart, slen) < 0) {
/* fprintf(stderr, "stopping on '%s'\n", tostr(sstart, slen)); */
return; /* done with announces, key too "big" */
} else {
/* fprintf(stderr, "skipping '%s'\n", tostr(sstart, slen)); */
fcall torrentdict_skipvalue; /* skip value */
}
}
action announce {
print_announce(sstart, slen);
}
torrentannounce := string @announce @{ fret; };
torrentannouncelist := 'l' ( string @announce | 'l' (string @announce)* 'e')* 'e' @{ fret; };
torrentdict_skipvalue := value ( string @torrententry)* 'e';
torrentdict := 'd' ( string @torrententry)* 'e';
}%%
%% write data;
#define BLOCK_SIZE 4096
#define INIT_READ BLOCK_SIZE
typedef struct tfile tfile;
struct tfile {
int fd;
const char *filename;
char data[2*BLOCK_SIZE];
ssize_t len;
int eof;
};
#ifndef O_NOATIME
# define O_NOATIME 0
#endif
static void file_start(tfile *f, const char *filename) {
ssize_t r;
f->filename = filename;
f->fd = open(filename, O_RDONLY | O_NOATIME);
if (-1 == f->fd) {
fprintf(stderr, "couldn't open '%s': %s\n", filename, strerror(errno));
f->len = 0;
f->eof = 1;
return;
}
r = read(f->fd, f->data, INIT_READ);
if (r <= 0) {
if (r < 0) {
fprintf(stderr, "couldn't read from '%s': %s\n", f->filename, strerror(errno));
}
f->len = 0;
f->eof = 1;
return;
}
if (r < INIT_READ) f->eof = 1;
f->len = r;
}
static void file_forward(tfile *f, const char **sstart) {
ssize_t r;
memmove(f->data, f->data + BLOCK_SIZE, BLOCK_SIZE);
r = read(f->fd, f->data + BLOCK_SIZE, BLOCK_SIZE);
if (r <= 0) {
if (r < 0) {
fprintf(stderr, "couldn't read from '%s': %s\n", f->filename, strerror(errno));
}
f->len = 0;
f->eof = 1;
return;
}
if (r < BLOCK_SIZE) f->eof = 1;
f->len = BLOCK_SIZE + r;
if (NULL != *sstart && (*sstart - f->data > BLOCK_SIZE)) {
*sstart -= BLOCK_SIZE;
} else {
*sstart = NULL;
}
}
static void file_close(tfile *f) {
close(f->fd);
}
static void do_parse_torrent(tfile *f) {
int stack[32], top = 0, cs;
%% write init;
const char *sstart = NULL;
int slen = 0;
for ( ; f->len > 0; ) {
const char *p = f->data, *pe = p+f->len, *eof = f->eof ? pe : NULL;
%% write exec;
/* fprintf(stderr, "cs: %i @pos: %i '%c'\n", cs, p - f->data, *p); */
if (f->eof || cs == torrent_parser_error || cs >= torrent_parser_first_final) return;
file_forward(f, &sstart);
}
}
void parse_torrent(const char *filename) {
tfile f;
file_start(&f, filename);
do_parse_torrent(&f);
file_close(&f);
}
int main() {
char *filename = NULL;
size_t len = 0;
while (-1 != getline(&filename, &len, stdin)) {
char *delim;
if (0 == len) continue;
if (NULL != (delim = strchr(filename, '\n'))) *delim = '\0';
parse_torrent(filename);
}
free(filename);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment