int64_t library_track_add(track_t *track, int64_t directory) { static const char *sql = "INSERT INTO tracks (fileid, file, cuefileid, cuefile, track, title, artistid, artist, albumid, album, start, duration, trackindex) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; sqlite3_stmt *query; if (!prepare_query(sql, &query)) { return -1; } track->fileid = library_file(track->file, directory); if (track->cuefile) { track->cuefileid = library_file(track->cuefile, directory); } if (track->artist) { track->artistid = field_rowid_create("artists", "name", track->artist); } if (track->album) { track->albumid = field_rowid_create("albums", "name", track->album); } sqlite3_bind_int64(query, 1, track->fileid); sqlite3_bind_text(query, 2, track->file, -1, NULL); sqlite3_bind_int64(query, 3, track->cuefileid); sqlite3_bind_text(query, 4, track->cuefile, -1, NULL); sqlite3_bind_int(query, 5, track->track); sqlite3_bind_text(query, 6, track->title, -1, NULL); sqlite3_bind_int64(query, 7, track->artistid); sqlite3_bind_text(query, 8, track->artist, -1, NULL); sqlite3_bind_int64(query, 9, track->albumid); sqlite3_bind_text(query, 10, track->album, -1, NULL); sqlite3_bind_double(query, 11, track->start); sqlite3_bind_double(query, 12, track->duration); sqlite3_bind_double(query, 13, track->trackindex); if (!execute(query)) { return -1; } if (track->album) { increment_album_tracks(track->albumid); } return sqlite3_last_insert_rowid(db_handle()); }
static int64_t scan_file(const char *path, int64_t directory) { const char *extension; int64_t file = 0; track_t **tracks; int i; for (extension = path + strlen(path); *(extension) != '.' && extension != path; --extension) { } ++extension; if (!strcasecmp(extension, "cue")) { /* CUE sheet */ musicd_log(LOG_DEBUG, "scan", "cue: %s", path); cue_read(path, directory); } else if(FreeImage_GetFIFFromFilename(path) != FIF_UNKNOWN) { /* Image file */ if (FreeImage_GetFileType(path, 0) != FIF_UNKNOWN) { musicd_log(LOG_DEBUG, "scan", "image: %s", path); file = library_file(path, directory); library_image_add(file); } } else { tracks = tracks_from_path(path); /* Try tracks */ if (!tracks) { return file; } for (i = 0; tracks[i]; ++i) { musicd_log(LOG_DEBUG, "scan", "track: %s", path); library_track_add(tracks[i], directory); scan_track_added(); file = library_file(path, 0); } tracks_free(tracks); } return file; }
static void import_unix_dlopen(environment_t* r) { /* * This is the only library we don't load dynamically from a scheme * file. But we should load it dynamically from HERE via dlopen. * * Just ignore dlclose for now, the OS will take care of that as well * when process exit (we'll deal with resource handling like that later) * (TODO) */ static void *lib = dlopen(library_file("libunix-dlopen.so").c_str(), RTLD_LAZY | RTLD_GLOBAL); if ( lib == NULL ) raise(runtime_exception(format("(unix dlopen): %s", dlerror()))); /* * Exported name on left, dlsym name on right */ const char* sym[] = { "dlclose", "proc_dlclose", "dlerror", "proc_dlerror", "dlopen", "proc_dlopen", "dlopen-internal", "proc_dlopen_internal", "dlopen-self", "proc_dlopen_self", "dlsym", "proc_dlsym", "dlsym-syntax", "proc_dlsym_syntax", NULL}; for ( const char** s = sym; *s; s += 2 ) { void *f = dlsym(lib, *(s+1)); if ( f == NULL ) raise(runtime_exception(format("(unix dlopen): %s", dlerror()))); r->define(*s, reinterpret_cast<lambda_t>(f)); } }
/** * @todo FIXME Multiple files in same cue sheet * @todo FIXME Rewrite this garbage */ bool cue_read(const char *cuepath, int64_t directory) { bool result = true; FILE *file; char *directory_path, *path, *path2; bool header_read = false; char album[512], albumartist[512]; char line[1024], instr[16], string1[512], *ptr; int64_t track_file; struct stat status; //char file[strlen(path) + 1024 + 2], cuefile[strlen(path) + 1024 + 2]; int i; int index, mins, secs, frames; /* Track is stored in prev_track until index of the following track is known. * This is mandatory for figuring out the track's length. Last track's * length is calculated from file's total length. */ track_t *prev_track = NULL, *track = NULL, *file_track = NULL; file = fopen(cuepath, "r"); if (!file) { musicd_perror(LOG_ERROR, "cue", "can't open file %s", cuepath); return false; } directory_path = malloc(strlen(cuepath)); /* Extract directory path. */ for (i = strlen(cuepath) - 1; i > 0 && cuepath[i] != '/'; --i) { } strncpy(directory_path, cuepath, i); directory_path[i] = '\0'; /* Directory + 256 4-byte UTF-8 characters + '/' + '\0', more than needed. */ path = malloc(strlen(directory_path) + 1024 + 2); path2 = malloc(strlen(directory_path) + 1024 + 2); album[0] = '\0'; albumartist[0] = '\0'; /* Check for BOM, seek back if not found. */ fread(line, 1, 3, file); if (line[0] != (char)0xef || line[1] != (char)0xbb || line[2] != (char)0xbf) { fseek(file, 0, SEEK_SET); } while (read_line(file, line, sizeof(line))) { /* Read instruction, up to 15 characters. */ if (sscanf(line, "%15s", instr) < 1) { continue; } /* Skip comments. */ if (!strcmp(instr, "REM")) { continue; } ptr = line + strlen(instr) + 1; if (ptr[0] == '"') { ptr += read_string(ptr, string1) + 1; } if (!strcmp(instr, "PERFORMER")) { /*musicd_log(LOG_DEBUG, "cue", "performer: %s", string1);*/ if (!header_read) { strcpy(albumartist, string1); } else if (track) { free(track->artist); track->artist = strcopy(string1); } } else if (!strcmp(instr, "TITLE")) { /*musicd_log(LOG_DEBUG, "cue", "title: %s", string1);*/ if (!header_read) { strcpy(album, string1); } else if (track) { free(track->title); track->title = strcopy(string1); } } else if (!strcmp(instr, "FILE")) { if (file_track) { musicd_log(LOG_WARNING, "cue", "multiple FILEs in a single cue sheet" "(%s) is currently unsupported, sorry", cuepath); break; } header_read = true; sprintf(path, "%s/%s", directory_path, string1); if (stat(path, &status)) { result = false; break; } /* Prioritizing: if there are multiple cue sheets and a cue sheet with * same base name as the track file exists, it is used for the track. * otherwise, sheet with highest mtime will result to be selected. */ for (i = strlen(path) - 1; i > 0 && path[i] != '.'; --i) { } strncpy(path2, path, i); strcpy(path2 + i, ".cue"); if (strcmp(path2, cuepath) && stat(path2, &status) == 0) { musicd_log(LOG_DEBUG, "cue", "multiple cue sheets for '%s', trying '%s'", path, path2); if (cue_read(path2, directory)) { break; } } file_track = track_from_path(path); if (!file_track) { break; } track_file = library_file(path, 0); if (track_file > 0) { /* File already exists, clear associated tracks. */ library_file_clear(track_file); } else { track_file = library_file(path, directory); if (track_file <= 0) { /* Some error... */ break; } } library_file_mtime_set(track_file, status.st_mtime); musicd_log(LOG_DEBUG, "cue", "audio: %s", path); continue; } if (!file_track) { continue; } if (!strcmp(instr, "TRACK")) { sscanf(ptr, "%d %s", &index, string1); /*musicd_log(LOG_DEBUG, "cue", "track: %d '%s'", index, string1);*/ if (track) { if (!prev_track) { prev_track = track; } else { prev_track->duration = track->start - prev_track->start; library_track_add(prev_track, directory); scan_track_added(); track_free(prev_track); prev_track = track; } } track = track_new(); track->cuefile = strcopy(cuepath); track->file = strcopy(path); track->track = index; /* Set artist same as the album artist and replace if track spefific * artist is later defined. */ track->artist = strcopy(albumartist); track->album = strcopy(album); track->albumartist = strcopy(albumartist); } if (!strcmp(instr, "INDEX")) { sscanf(ptr, "%d %d:%d:%d", &index, &mins, &secs, &frames); /*musicd_log(LOG_DEBUG, "cue", "index: %d %2.2d:%2.2d.%2.2d", index, mins, secs, frames);*/ if (index == 1) { /* One frame is 1/75 seconds */ track->start = mins * 60.0 + secs + frames / 75.0; } } } if (prev_track) { prev_track->duration = track->start - prev_track->start; library_track_add(prev_track, directory); scan_track_added(); track_free(prev_track); } if (track) { track->duration = file_track->duration - track->start; library_track_add(track, directory); scan_track_added(); track_free(track); } track_free(file_track); fclose(file); free(directory_path); free(path); free(path2); return result; }
/** * Iterates through directory. Subdirectories will be scanned using * scan_directory. */ static void iterate_directory(const char *dirpath, int dir_id) { struct stat status; DIR *dir; struct dirent *entry; int64_t file; time_t file_mtime; char *path; if (!(dir = opendir(dirpath))) { /* Probably no read access - ok, we just omit. */ musicd_perror(LOG_WARNING, "scan", "could not open directory %s", dirpath); return; } /* + 256 4-bit UTF-8 characters + / and \0 * More than enough on every platform really in use. */ path = malloc(strlen(dirpath) + 1024 + 2); errno = 0; while ((entry = readdir(dir))) { if (interrupted) { break; } /* Omit hidden files and most importantly . and .. */ if (entry->d_name[0] == '.') { goto next; } sprintf(path, "%s/%s", dirpath, entry->d_name); if (stat(path, &status)) { goto next; } if (S_ISDIR(status.st_mode)) { scan_directory(path, dir_id); goto next; } if (!S_ISREG(status.st_mode)) { goto next; } file = library_file(path, 0); if (file > 0) { file_mtime = library_file_mtime(file); if (file_mtime == status.st_mtime) { goto next; } } file = scan_file(path, dir_id); if (file) { library_file_mtime_set(file, status.st_mtime); } next: errno = 0; } closedir(dir); if (errno) { /* It was possible to open the directory but we can't iterate it anymore? * Something's fishy. */ musicd_perror(LOG_ERROR, "scan", "could not iterate directory %s", path); } free(path); }
static void import_scheme_file(environment_t *r, const char* file) { import(r, exports_import); // add (import) import(r, library_file(file)); }
void load_library_index() { if ( library_map != NULL ) return; std::string filename = library_file(library_index_file); environment_t *env = null_environment(); program_t *p = parse(slurp(open_file(filename)), env); cons_t *index = p->root; if ( !pairp(index) || !symbolp(caar(index)) ) invalid_index_format(filename + ": no list with symbols"); for ( ; !nullp(index); index = cdr(index) ) { if ( symbol_name(caar(index)) == "define-library-index" ) { if ( library_map != NULL ) invalid_index_format(filename + ": only one define-library-index allowed"); if ( !listp(cdar(index)) ) { invalid_index_format(filename + ": define-library-index is not a list"); } size_t len = length(cdar(index)); library_map = (library_map_t*) malloc((1+len)*sizeof(library_map_t)); size_t i = 0; for ( cons_t *lib = cdar(index); !nullp(lib); lib = cdr(lib), ++i ) { cons_t *name = caar(lib); cons_t *file = cadar(lib); if ( !listp(name) || !stringp(file) ) invalid_index_format(filename + ": not list/string pair"); library_map[i].library_name = strdup(sprint(name).c_str()); library_map[i].source_file = strdup(file->string); } // important to signal end of list: library_map[i].library_name = NULL; library_map[i].source_file = NULL; continue; } else if ( symbol_name(caar(index)) == "define-repl-imports" ) { if ( repl_libraries != NULL ) invalid_index_format(filename + ": only one define-repl-imports allowed"); if ( !listp(cdar(index)) ) { invalid_index_format(filename + ": define-repl-imports is not a list"); } size_t len = length(cdar(index)); repl_libraries = (const char**) malloc((1+len)*sizeof(char*)); const char **s = repl_libraries; for ( cons_t *lib = cdar(index); !nullp(lib); lib = cdr(lib), ++s ) { cons_t *name = car(lib); *s = strdup(sprint(name).c_str()); } *s = NULL; continue; } else invalid_index_format(filename + ": unknown label " + sprint(caar(index))); } }