/* $Id: playlist.c,v 1.11 2006/09/15 14:51:24 toad32767 Exp $ */ /** ** 2006 by Marco Trillo ** This file is part of UModPlayer, and is released by ** its autors to the Public Domain. ** In case it's not legally possible, its autors grant ** anyone the right to use, redistribute and modify ** this software for any purpose, without any conditions, ** unless such conditions are required by law. ** ** THIS FILE COMES WITHOUT ANY WARRANTY. THE AUTHORS ** SHALL NOT BE LIABLE FOR ANY DAMAGE RESULTING BY THE ** USE OR MISUSE OF THIS SOFTWARE. **/ #include #include #include #include LOCAL struct playlist *phead = NULL; LOCAL struct playlist *ptop = NULL; /* * Create a new playlist item and insert it * in the top of the list */ EXPORT struct playlist * new_playlist() { struct playlist *p; p = (struct playlist *) malloc(sizeof(struct playlist)); if (p == NULL) return NULL; p->name = NULL; p->path = NULL; p->type = 0; p->prev = ptop; p->next = NULL; /* insert this playlist at the end (top) of the list */ if (ptop != NULL) ptop->next = p; ptop = p; if (phead == NULL) phead = p; return p; } /* * Find the playlist item at the given position `pos' */ EXPORT struct playlist * find_playlist(int pos) { int i; struct playlist *p = phead; for (i = 0; i < pos; ++i) { if (p == NULL) return NULL; p = p->next; } return p; } /* * Remove the playlist item `p' from the list */ EXPORT void remove_from_list(struct playlist * p) { struct playlist *v, *n; if (p == NULL) return; v = p->prev; n = p->next; /* * Delete `p' from the playlist list. * If `p' is the first item (`phead'), * update `phead' to be `p->next'. * * If `p' is the last item (`ptop'), * update `ptop' to be `p->prev'. */ if (v != NULL) v->next = n; else phead = n; if (n != NULL) n->prev = v; else ptop = v; } /* * Delete the playlist item `p' */ EXPORT void delete_playlist(struct playlist * p) { remove_from_list(p); free((void *) (p->path)); free((void *) (p->name)); free((void *) p); } /* * Move the playlist item from position `from' to position `to' */ EXPORT int move_playlist(int from, int to) { struct playlist *p, *h; /* * Find the playlist item at `from' */ p = find_playlist(from); if (p == NULL) return UM_ERR_NOITEM; /* * Find the playlist item already at `pos' */ h = find_playlist(to); if (h == NULL) return UM_ERR_NOITEM; /* do we really need to do anything? */ if (from == to) return UM_OK; /* * Remove the item `p' from the list */ remove_from_list(p); if (to > from) { /* move upwards */ struct playlist *hn; /* * Now insert `p' just after `h' */ hn = h->next; h->next = p; p->prev = h; if (hn != NULL) { hn->prev = p; p->next = hn; } else { /* `h' was the last item on the list! */ p->next = NULL; ptop = p; } } else if (to < from) { /* move downwards */ struct playlist *hv; /* * insert `p' just before `h' */ hv = h->prev; h->prev = p; p->next = h; if (hv != NULL) { hv->next = p; p->prev = hv; } else { /* `h' was the first item on the list! */ p->prev = NULL; phead = p; } } return UM_OK; } /* * Find the next item in the list to `p'. * * If `p' is NULL; give the first item (phead). * If `p' is the last item (ptop), return NULL. */ EXPORT struct playlist * next_playlist(struct playlist * p) { if (p == NULL) return phead; return p->next; } /* * Find the previous item in the list to `p'. * * If `p' is NULL; give the last item (ptop). * If `p' is the first item (phead), return NULL. */ EXPORT struct playlist * prev_playlist(struct playlist * p) { if (p == NULL) return ptop; return p->prev; } /* * Print the playlist list. */ EXPORT void print_list() { int i; struct playlist *p; for (p = phead, i = 0; p != NULL; p = p->next, ++i) { printf("%4d: %s [%s]\n", i, p->name, module_typestr(p->type) ); } } /* * Delete the playlist list */ EXPORT void delete_list() { struct playlist *p, *v; p = ptop; while (p != NULL) { v = p->prev; delete_playlist(p); p = v; } } /* * Function to fill `p' with approppriate file info. * `name' is the file name, used only if no info can be grabbed from `p' * (i.e., if no song name can be provided, use the file name instead) * * The work buffer `buf' should be provided by the caller. */ EXPORT int item_get_info(struct playlist * p, char *name, char *buf, int buflen) { int fd, len, err; MODULE m; struct stat st; if (stat(p->path, &st) == -1) { err = UM_ERR_IO; cleanup: free((void *) (p->path)); remove_from_list(p); free((void *) p); return err; } if (S_ISDIR(st.st_mode)) { goto nomodule; } fd = open(p->path, O_RDONLY); if (fd < 0) { err = UM_ERR_IO; goto cleanup; } len = (int) read(fd, buf, buflen); close(fd); if (load_module(&m, buf, buflen) == 0) { char nam[32]; /* this is enough */ module_name(&m, nam); p->name = strcopyof(nam); if (p->name == NULL) { err = UM_ERR_MEMORY; goto cleanup; } p->type = module_type(&m); return UM_OK; } else { nomodule: /* * Use the file name if no song name * can be detected. */ p->name = strcopyof(name); if (p->name == NULL) { err = UM_ERR_MEMORY; goto cleanup; } p->type = 0; return UM_OK; } /* NOTREACHED */ return UM_OK; } /* * Create a playlist list from the given directory name `dir'. */ EXPORT int list_from_dir(char *dir) { DIR *d; struct dirent *e; struct playlist *p; int ret; char buf[512]; delete_list(); d = opendir(dir); if (d == NULL) return UM_ERR_IO; while ((e = readdir(d)) != NULL) { /* * Skip hidden files, as well as * special files `.' & `..' */ if (e->d_name[0] == '.') continue; p = new_playlist(); if (p == NULL) { return UM_ERR_MEMORY; } snprintf(buf, 512, "%s/%s", dir, e->d_name); p->path = strcopyof(buf); if (p->path == NULL) { remove_from_list(p); free((void *) p); return UM_ERR_MEMORY; } ret = item_get_info(p, e->d_name, buf, 512); if (ret != UM_OK) return ret; } return UM_OK; } /* * Save the playlist list to a file */ EXPORT int save_list(char *file) /* note that this shadows the 'file' global variable */ { FILE *fp; struct playlist *p; if (file == NULL) return UM_ERR_INTERNAL; fp = fopen(file, "w+"); if (fp == NULL) { return UM_ERR_IO; } for (p = phead; p != NULL; p = p->next) { /* * XXX -- this will break when `p->path' * contain a new line character. */ fwrite(p->path, 1, strlen(p->path), fp); putc('\n', fp); } fclose(fp); return UM_OK; } /* * Extract the file-name of a full path. * Return value must be free()'d */ EXPORT char * grab_name(char *path) { char *name, *t; name = strcopyof(path); if (name == NULL) return NULL; t = strrchr(name, '/'); if (t != NULL) { int l, rl; l = strlen(name); rl = l - (int) (t - name); /* length of the name + 1 (for * '/') */ memmove(name, t + 1, rl); /* includes NUL, because rl = * length of the name + 1 */ } return name; } /* * Load a saved playlist */ EXPORT int load_list(char *file) { FILE *fp; struct playlist *p; char *name; int ret; char buf[512]; delete_list(); if (file == NULL) { return UM_ERR_INTERNAL; } fp = fopen(file, "r"); if (fp == NULL) { return UM_ERR_IO; } while (!feof(fp)) { p = new_playlist(); if (p == NULL) { fclose(fp); return UM_ERR_MEMORY; } p->path = ReadStringFromFile(fp); if (p->path == NULL) { fclose(fp); free((void *) (p->path)); remove_from_list(p); return UM_ERR_MEMORY; } if (p->path[0] == '\0') { free((void *) (p->path)); remove_from_list(p); break; } /* * Extract the file name from the path */ name = grab_name(p->path); if (name == NULL) { fclose(fp); free((void *) (p->path)); remove_from_list(p); return UM_ERR_MEMORY; } ret = item_get_info(p, name, buf, 512); free((void *) name); if (ret != UM_OK) { fclose(fp); return ret; } } fclose(fp); return UM_OK; }