sanename: Remap unusual characters in filenames

The other day at work, I was having a hard time running a bunch of *NIX scripts on filenames because the filenames had spaces and other unusual characters in them. This C program is simple: It, starting at the current directory, recursively renames every file in the current directory and all subdirectories to only have letters, numbers, the ., the /, the -, and the _ character.

This program is dangerous; don't run this program from a directory where there are files whose names you don't want changed. I use this program when my boss gives me a bunch of filenames with spaces and other strange characters that make it difficult for me to run scripts on the files.

This program is a quick and dirty hack; in particular, people using scripts with a lot of non-ASCII characters will have anything not ASCII converted in to the _ character. This hack only works for English or languages where the majority of characters are ASCII letters or numbers.

Since the program is so small, I am including its source below.

Without further ado, the code:

/* Sam Trenholme 2010 Public domain */

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

char sanechr(char in) {
	if(in >= '0' && in <= '9') {
		return in;
	}
	if(in >= 'A' && in <= 'Z') {
		return in;
	}
	if(in == '.' || in == '_' || in == '-' || in == '/') {
		return in;
	}
	if(in >= 'a' && in <= 'z') {
		return in;
	}
	return '_';
}

/* Convert anything in a filename that is not [A-Za-z0-9\-\.] into a _ */
void c_dir(char *path, int depth) {
	DIR *dir;
	struct dirent *rd;
	struct stat stats;
	char fullname[1024];
	char sanename[1024];
	int a;

	if(depth > 256) {
		return;
	}

	dir = opendir(path);
	if(dir == NULL) {
		return; 
	}

	for(;;) {
		rd = readdir(dir);
		if(rd == NULL || rd->d_name == NULL) {
			return;
		}
		/* Skip "." and ".."; this is imperfect because it 
                 * also skips .{anything} */
		if(strlen(rd->d_name) <= 2 && *(rd->d_name) == '.') {
			continue;
		}
		snprintf(fullname,1022,"%s/%s",path,rd->d_name);
		strncpy(sanename,fullname,1023);
		for(a = 0 ; a < 1023 ; a++) {
			if(*(sanename + a) == 0) {
				break;
			}
			*(sanename + a) = sanechr(*(sanename + a));
		}
		if(lstat(fullname,&stats) != 0) { /* Error */
			continue;
		}
		rename(fullname,sanename);
		if(S_ISDIR(stats.st_mode)) {
			c_dir(sanename,depth + 1);
		} 
	}
}	

main() {
	c_dir(".",1);
}