I've been organizing my music in a certain way for quite some time: "$ARTIST - $ALBUM/$TRACK - $TITLE.flac". Even so, I've never taken advantage of that structure, at least programmatically. Now I have, with this program:
musicdir.c
#include <glob.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <argp.h>
typedef enum {
MODE_ALL = 1
} MODE;
struct arguments {
char * artist, * album, * title, * root;
size_t artist_len, album_len, title_len, root_len;
int track;
char mode;
} argument = {NULL, NULL, NULL, "/home/music/", 0, 0, 0, 12, -1, 0};
error_t parser(int key, char * arg, struct argp_state * state) {
int track;
switch(key) {
case 'a':
if(arg) {
argument.artist = arg;
argument.artist_len = strlen(arg);
}
break;
case 'c':
if(arg) {
argument.album = arg;
argument.album_len = strlen(arg);
}
break;
case 't':
if(arg) {
argument.title = arg;
argument.title_len = strlen(arg);
}
break;
case 'r':
if(arg) {
argument.root = arg;
argument.root_len = strlen(arg);
}
break;
case 'n':
track = atoi(arg);
if(track > 0)
argument.track = track;
else
return ARGP_ERR_UNKNOWN;
break;
case 'l':
argument.mode |= MODE_ALL;
break;
case ARGP_KEY_ARG:
return ARGP_ERR_UNKNOWN;
case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
break;
}
return 0;
}
int main(int argc, char ** argv) {
static struct argp_option options[] = {
{ "artist", 'a', "artist", 0, "The artist part of the filename.", 0 },
{ "album", 'c', "album", 0, "The album part of the filename.", 0 },
{ "title", 't', "title", 0, "The title part of the filename.", 0 },
{ "track", 'n', "track", 0, "The track part of the filename, greater than zero.", 0 },
{ "root-dir", 'r', "root-dir", 0, "The root music directory. [/home/music]", 0 },
{ "list-all", 'l', NULL, 0, "List all found files rather than the default (the first file).", 0},
{ 0 }
};
static struct argp args_parsed = {
options,
&parser,
NULL,
"The music directory finder.",
NULL,
NULL
};
int arg_index;
glob_t globs;
int glob_result;
error_t result;
size_t total_len = 16, i;
char * pattern = NULL;
if((result = argp_parse(&args_parsed, argc, argv, 0, &arg_index, &argument)) == 0) {
/* printf("root=%s, artist=%s, album=%s, title=%s, track=%d\n", argument.root, argument.artist, argument.album, argument.title, argument.track); */
/* Allocate string for pattern */
if(argument.root)
total_len += argument.root_len;
if(argument.artist)
total_len += argument.artist_len;
if(argument.album)
total_len += argument.album_len;
if(argument.title)
total_len += argument.title_len;
if(argument.track >= 0)
total_len += 11;
pattern = calloc(total_len, sizeof(char));
/* Copy arguments into pattern */
strcpy(pattern, argument.root);
if(argument.artist)
sprintf(pattern + strlen(pattern), "%s - ", argument.artist);
else
strcat(pattern, "* - ");
if(argument.album)
sprintf(pattern + strlen(pattern), "%s/", argument.album);
else
strcat(pattern, "*/");
if(argument.track >= 0)
sprintf(pattern + strlen(pattern), "%d - ", argument.track);
else
strcat(pattern, "* - ");
if(argument.title)
sprintf(pattern + strlen(pattern), "%s.*", argument.title);
else
strcat(pattern, "*.*");
/* puts(pattern); */
if((glob_result = glob(pattern, GLOB_NOESCAPE, NULL, &globs)) == 0) {
if(globs.gl_pathc > 0) {
puts(*globs.gl_pathv);
if(argument.mode & MODE_ALL)
for(i = 1; i < globs.gl_pathc; i++)
puts(globs.gl_pathv[i]);
}
globfree(&globs);
}
else {
switch(glob_result) {
case GLOB_NOSPACE:
fputs("glob: Unable to allocate memory.\n", stderr);
break;
case GLOB_ABORTED:
fputs("glob: Read error.\n", stderr);
break;
case GLOB_NOMATCH:
fputs("glob: No such file or directory.\n", stderr);
break;
}
}
free(pattern);
}
else
perror("argp_parse");
return 0;
}
% ./musicdir --help
Usage: musicdir [OPTION...]
The music directory finder.
-a, --artist=artist The artist part of the filename.
-c, --album=album The album part of the filename.
-l, --list-all List all found files rather than the default (the
first file).
-n, --track=track The track part of the filename, greater than zero.
-r, --root-dir=root-dir The root music directory. [/home/music]
--root-dir=root-dir The root music directory. [/home/music]
-t, --title=title The title part of the filename.
-?, --help Give this help list
--usage Give a short usage message
Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.
% ./musicdir -a aiko
/home/music/aiko - KissHug/01 - KissHug.flac