|
From: Ben O. <ben...@us...> - 2002-02-24 00:25:27
|
Update of /cvsroot/njbfs/njbfs
In directory usw-pr-cvs1:/tmp/cvs-serv2850
Modified Files:
proc.c
Log Message:
ben: change get_attr, proc_create, add get_dirname, use SAFE_STRDUP in some places
Index: proc.c
===================================================================
RCS file: /cvsroot/njbfs/njbfs/proc.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -C2 -d -r1.6 -r1.7
*** proc.c 10 Feb 2002 21:59:23 -0000 1.6
--- proc.c 24 Feb 2002 00:25:25 -0000 1.7
***************
*** 1,2 ****
--- 1,3 ----
+
/*
* Nomad Jukebox filesystem for Linux.
***************
*** 45,76 ****
#include "njbfs.h"
#include "nomad_usb.h"
#include "track.h"
!
! /* njb_usb */
! extern int njb_usb_capture(struct nomad_usb_data *njb);
! extern int njb_usb_release(struct nomad_usb_data *njb);
! extern int njb_usb_get_first_track_tag_header(struct nomad_usb_data *njb,
! njbttaghdr_t * header);
! extern int njb_usb_get_next_track_tag_header(struct nomad_usb_data *njb,
! njbttaghdr_t * header);
! extern track_t *njb_usb_get_track_tag(struct nomad_usb_data *njb,
! njbttaghdr_t * header);
! extern int njb_usb_connect(struct nomad_usb_data *njb);
! extern int njb_usb_disconnect(struct nomad_usb_data *njb);
! extern int njb_usb_open(struct nomad_usb_data *njb,
! struct njbfs_fattr *fattr);
! extern int njb_usb_close(struct nomad_usb_data *njb,
! struct njbfs_fattr *fattr);
! extern int njb_usb_write(struct nomad_usb_data *njb, char *name,
! u_int32_t offset, u_int32_t count, void *buffer);
! extern int njb_usb_read(struct nomad_usb_data *njb, char *name,
! u_int32_t offset, u_int32_t count, void *buffer);
! extern int njb_usb_rename(struct nomad_usb_data *njb,
! struct njbfs_fattr *fattr);
! extern int njb_usb_delete(struct nomad_usb_data *njb, char *file);
! extern int njb_usb_create(struct nomad_usb_data *njb, char *file);
!
! /* forward decl */
static int njbfs_parse_filename(struct njbfs_fattr *fattr,
const char *file);
--- 46,58 ----
#include "njbfs.h"
#include "nomad_usb.h"
+ #include "njb_usb.h"
+ #include "njbfs_cache.h"
#include "track.h"
+ #include "proc.h"
+ #include "njbfs_proc.h"
! /*
! forward decl
! */
static int njbfs_parse_filename(struct njbfs_fattr *fattr,
const char *file);
***************
*** 97,121 ****
- /**
- * Initialize root directory
- */
- void njbfs_init_root_dirent(struct njbfs_sb_info *server,
- struct njbfs_fattr *fattr)
- {
- memset(fattr, 0, sizeof(*fattr));
- fattr->f_nlink = 1;
- fattr->f_uid = server->mnt.uid;
- fattr->f_gid = server->mnt.gid;
- fattr->f_blksize = NJBFS_BLOCKSIZE;
-
- fattr->f_ino = 2;
- fattr->f_mtime = CURRENT_TIME;
- fattr->f_mode =
- S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH |
- S_IFDIR | server->mnt.dir_mode;
- fattr->f_size = NJBFS_BLOCKSIZE;
- fattr->f_blocks = 0;
- }
-
int njbfs_parse_mode(char *mode)
--- 79,82 ----
***************
*** 126,152 ****
/**
! * Parse directory
*/
int njbfs_parse_options(struct njbfs_sb_info *info, void *opts)
{
! char *p;
! int optcnt = 0;
!
! strcpy(info->virtual_dir, "tracks");
!
! if (!opts)
! return 0;
! p = strtok(opts, ",");
! for (; p; p = strtok(NULL, ",")) {
! if (strncmp(p, "dir=", 5) == 0) {
! if (strlen(p + 5) > NJBFS_MAXVIRTUALDIRLEN) {
! err("ugly opts, go away!");
! return -EINVAL;
! }
! strcpy(info->virtual_dir, p + 5);
! optcnt++;
! }
! }
! return optcnt;
}
--- 87,95 ----
/**
! * Parse options. Lost the virtual directory, so nothing here for now.
*/
int njbfs_parse_options(struct njbfs_sb_info *info, void *opts)
{
! return 0;
}
***************
*** 177,180 ****
--- 120,126 ----
}
+ /*
+ TODO here: endian safe-ness
+ */
u_int32_t get_track_size(track_frame_t * frame)
***************
*** 197,372 ****
! /**
! * Get directory node from a track structure
! */
! int njbfs_get_dirlist_node_from_track(struct njbfs_dirlist_node *p,
! track_t * track)
{
! char buf[NJBFS_MAXPATHLEN];
! track_frame_t *frame;
! char *artist = NULL;
! char *title = NULL;
! char *codec = NULL;
! char *fname = NULL;
! char *ch;
! u_int32_t filesize = 0;
! int status;
!
! track->cur = track->first;
! frame = track->cur;
!
! while (frame != NULL) {
! if (!strcmp(FR_ARTIST, frame->label))
! artist = (char *) frame->data;
! else if (!strcmp(FR_TITLE, frame->label))
! title = (char *) frame->data;
! else if (!strcmp(FR_CODEC, frame->label))
! codec = (char *) frame->data;
! else if (!strcmp(FR_SIZE, frame->label))
! filesize = get_track_size(frame);
! else if (!strcmp(FR_FNAME, frame->label))
! fname = (char *) frame->data;
!
! frame = track_getframe(track);
! }
!
! /* file name */
! if (fname) {
! strcpy(buf, fname);
! }
! if ((!artist) || (!title) || (!codec))
! return -EINVAL;
!
! /* codec to lower case (MP3 ->mp3) */
! ch = codec;
! while ((*ch = tolower(*ch)) != 0)
! ch++;
!
! /*
! Replace characters like '?', '*' or '/' in
! artist and title with placeholder
! */
! ch = artist;
! while (*ch) {
! switch (*ch) {
! case '/':
! *ch = '_';
! case '*':
! *ch = '_';
! case '?':
! *ch = '_';
! }
! ch++;
! }
! ch = title;
! while (*ch) {
! switch (*ch) {
! case '/':
! *ch = '_';
! case '*':
! *ch = '_';
! case '?':
! *ch = '_';
! }
! ch++;
! }
!
! status = sprintf(buf, "%s - %s.%s", artist, title, codec);
!
! p->entry.name = (char *) kmalloc(strlen(buf) + 1, GFP_KERNEL);
! if (!p->entry.name) {
! err("kmalloc error");
! return -ENOMEM;
! }
! strcpy(p->entry.name, buf);
!
! /* attributes */
! p->entry.size = filesize;
! p->entry.blocksize = NJBFS_BLOCKSIZE;
! p->entry.blocks =
! (p->entry.size +
! (NJBFS_BLOCKSIZE - 1)) >> (NJBFS_BLOCKSIZE_BITS - 1);
!
! p->entry.mode |=
! S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |
! S_IWOTH;
! p->entry.atime = CURRENT_TIME;
!
! p->entry.fileid = track->header.trackid;
! p->entry.track = track;
!
! return 0;
}
!
!
!
! /**
! * Get directory entries from NJB
! */
! int njbfs_loaddir(struct njbfs_sb_info *info, char *name,
! struct njbfs_directory *dir)
{
! struct njbfs_dirlist_node *p;
!
! struct nomad_usb_data *njb = info->njb;
! njbttaghdr_t header;
! track_t *track;
! int status;
!
! if (strlen(info->mnt.root) + strlen(name) > NJBFS_MAXPATHLEN) {
! err("loaddir: path too long");
! return -EINVAL;
! }
!
! /* capture njb */
! status = njb_usb_capture(njb);
! if (status < 0) {
! err("loaddir: capture failed, status=%d", status);
! return -EIO;
! }
!
! status = njb_usb_get_first_track_tag_header(njb, &header);
!
! while (status != -ENOENT) {
! track = njb_usb_get_track_tag(njb, &header);
! if (track == NULL)
! break;
!
! p =
! (struct njbfs_dirlist_node *)
! kmalloc(sizeof(struct njbfs_dirlist_node), GFP_KERNEL);
! if (!p) {
! err("loaddir: kmalloc error");
! return -ENOMEM;
! }
! memset(p, 0, sizeof(struct njbfs_dirlist_node));
!
! status = njbfs_get_dirlist_node_from_track(p, track);
! if (status < 0) {
! err
! ("loaddir: unable to get dirlist node from track");
! kfree(p);
! return -EIO;
! }
!
! p->prev = NULL;
! p->next = dir->head;
! dir->head = p;
!
! status = njb_usb_get_next_track_tag_header(njb, &header);
! }
!
! /* release njb */
! status = njb_usb_release(njb);
! if (status < 0) {
! err("loaddir: release failed, status=%d", status);
! return -EIO;
! }
! return 0;
!
}
-
-
int njbfs_get_name(struct dentry *d, char *name)
{
--- 143,160 ----
! char *njbfs_strdup(char *in)
{
! char *out;
! if (!in)
! return NULL;
! out = kmalloc(strlen(in) * sizeof(char) + 1, GFP_KERNEL);
! strcpy(out, in);
! return out;
}
! int njbfs_get_dirname(struct dentry *d, char *name)
{
! return (njbfs_get_name(d->d_parent, name));
}
int njbfs_get_name(struct dentry *d, char *name)
{
***************
*** 410,421 ****
/**
* Get attributes of a dentry
*/
! int njbfs_get_attr(struct dentry *dentry, struct njbfs_fattr *fattr,
! struct njbfs_sb_info *info)
{
struct njbfs_directory *dir;
char buf[NJBFS_MAX_LINE];
struct njbfs_dirlist_node *file;
int status;
--- 198,211 ----
/**
* Get attributes of a dentry
+ *
*/
! struct njbfs_fattr *
! njbfs_get_attr(struct dentry *dentry, struct njbfs_sb_info *info)
{
struct njbfs_directory *dir;
char buf[NJBFS_MAX_LINE];
struct njbfs_dirlist_node *file;
+ struct njbfs_fattr *fattr;
int status;
***************
*** 427,431 ****
if (status < 0) {
err("njbfs_get_attr: cache get failed");
! return status;
}
--- 217,221 ----
if (status < 0) {
err("njbfs_get_attr: cache get failed");
! return NULL;
}
***************
*** 438,489 ****
if (!file) {
! fattr->f_mode =
! S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
! S_IROTH | S_IWOTH;
! fattr->f_size = NJBFS_BLOCKSIZE;
! fattr->f_blksize = NJBFS_BLOCKSIZE;
! fattr->f_blocks = 0;
! fattr->f_atime = CURRENT_TIME;
! fattr->f_mtime = CURRENT_TIME;
! fattr->f_uid = info->mnt.uid;
! fattr->f_gid = info->mnt.gid;
!
! /* NJB attributes */
! fattr->fileid = 0;
! fattr->track = NULL;
! fattr->size = 0;
!
! status = njbfs_parse_filename(fattr, dentry->d_name.name);
!
! return status;
}
!
! fattr->f_mode = file->entry.mode;
! if (file->entry.mode & S_IFDIR)
! fattr->f_mode |= info->mnt.dir_mode;
! else
! fattr->f_mode |= info->mnt.file_mode;
!
! fattr->f_size = file->entry.size;
! fattr->f_blksize = file->entry.blocksize;
! fattr->f_blocks = file->entry.blocks;
! fattr->f_nlink = file->entry.nlink;
! fattr->f_atime = file->entry.atime;
!
! /*Any way to get the real mtime, ctime? */
! fattr->f_mtime = file->entry.atime;
! fattr->f_ctime = file->entry.atime;
! fattr->f_uid = info->mnt.uid;
! fattr->f_gid = info->mnt.gid;
!
! /* get the NJB attributes */
! fattr->fileid = file->entry.fileid;
! fattr->track = NULL;
! fattr->size = 0;
!
! status = njbfs_parse_filename(fattr, dentry->d_name.name);
!
! return status;
!
}
--- 228,241 ----
if (!file) {
! return (NULL);
! } else if (!file->entry.fattr) {
! /* this shouldn't happen. */
! err("couldn't find a fattr struct!");
! return (NULL);
! } else {
! fattr = file->entry.fattr;
! return (fattr);
}
! return NULL;
}
***************
*** 531,551 ****
(struct njbfs_sb_info *) sb->u.generic_sbp;
struct dentry *dentry = f->f_dentry;
! struct njbfs_fattr fattr;
int status;
!
! if ((status = njbfs_get_attr(dentry, &fattr, info)) < 0) {
info("open: file not found");
! njbfs_free_attr(&fattr);
! return status;
}
! dbg("open: opening file, file id=%d", fattr.fileid);
!
! status = njb_usb_open(info->njb, &fattr);
! njbfs_free_attr(&fattr);
return status;
-
}
--- 283,300 ----
(struct njbfs_sb_info *) sb->u.generic_sbp;
struct dentry *dentry = f->f_dentry;
! struct njbfs_fattr *fattr;
int status;
!
! fattr = njbfs_get_attr(dentry, info);
! if (!fattr) {
info("open: file not found");
! return -ENOENT;
}
! dbg("open: opening file, file id=%d", fattr->fileid);
! status = njb_usb_open(info->njb, fattr);
return status;
}
***************
*** 559,578 ****
(struct njbfs_sb_info *) sb->u.generic_sbp;
struct dentry *dentry = f->f_dentry;
! struct njbfs_fattr fattr;
int status;
! if ((status = njbfs_get_attr(dentry, &fattr, info)) < 0) {
! err("njbfs_close: file not found, status=%d", status);
! goto out;
}
! dbg("close: closing file, file id=%d", fattr.fileid);
! status = njb_usb_close(info->njb, &fattr);
! out:
! njbfs_free_attr(&fattr);
! return status;
}
--- 308,331 ----
(struct njbfs_sb_info *) sb->u.generic_sbp;
struct dentry *dentry = f->f_dentry;
! struct njbfs_fattr *fattr;
int status;
! fattr = njbfs_get_attr(dentry, info);
! if ( !fattr ) {
! info("njbfs_close: file not found");
! return -ENOENT;
}
! dbg("close: closing file, file id=%d", fattr->fileid);
! status = njb_usb_close(info->njb, fattr);
! /* here's where we update the cache */
! if ( fattr->update_cache ) {
! njbfs_cache_empty(info);
! fattr->update_cache = 0;
! }
+ return status;
}
***************
*** 715,718 ****
--- 468,475 ----
/**
* Rename file or directory
+ *
+ * TODO: will we even allow this in the new world order?
+ * (id3 smart recognition)
+ *
*/
int
***************
*** 722,726 ****
{
char old_buf[NJBFS_MAXPATHLEN + 6], new_buf[NJBFS_MAXPATHLEN + 6];
! struct njbfs_fattr old_fattr, new_fattr;
int status;
--- 479,483 ----
{
char old_buf[NJBFS_MAXPATHLEN + 6], new_buf[NJBFS_MAXPATHLEN + 6];
! struct njbfs_fattr *old_fattr, *new_fattr;
int status;
***************
*** 728,765 ****
njbfs_get_name(new_dentry, new_buf);
! if ((status = njbfs_get_attr(old_dentry, &old_fattr, info)) < 0) {
err("rename: file '%s' not found", old_buf);
- njbfs_free_attr(&old_fattr);
return status;
}
njbfs_lock(info);
/* copy fattr */
! new_fattr.f_mode = old_fattr.f_mode;
! new_fattr.f_size = old_fattr.f_mode;
! new_fattr.f_blksize = old_fattr.f_blksize;
! new_fattr.f_blocks = old_fattr.f_blocks;
! new_fattr.f_atime = CURRENT_TIME;
! new_fattr.f_mtime = CURRENT_TIME;
! new_fattr.f_uid = old_fattr.f_uid;
! new_fattr.f_gid = old_fattr.f_gid;
! new_fattr.fileid = old_fattr.fileid;
! new_fattr.track = NULL;
! new_fattr.size = old_fattr.size;
/* default attributes */
! new_fattr.codec = old_fattr.codec;
! new_fattr.title = old_fattr.title;
! new_fattr.artist = old_fattr.artist;
! new_fattr.album = old_fattr.album;
! new_fattr.genre = old_fattr.genre;
/* parse new attributes from file name */
! status = njbfs_parse_filename(&new_fattr, new_buf);
/* rename */
!
! if ((status = njb_usb_rename(info->njb, &new_fattr)) < 0) {
err("rename: couldn't rename!");
}
njbfs_unlock(info);
--- 485,534 ----
njbfs_get_name(new_dentry, new_buf);
! old_fattr = njbfs_get_attr(old_dentry, info);
! if (!old_fattr) {
err("rename: file '%s' not found", old_buf);
return status;
}
+ new_fattr = kmalloc(sizeof(struct njbfs_fattr), GFP_KERNEL);
+ if ( !new_fattr ) {
+ err("couldn't kmalloc new_fattr");
+ return -ENOMEM;
+ }
njbfs_lock(info);
/* copy fattr */
! new_fattr->f_mode = old_fattr->f_mode;
! new_fattr->size = old_fattr->size;
! new_fattr->f_blksize = old_fattr->f_blksize;
! new_fattr->f_blocks = old_fattr->f_blocks;
! new_fattr->f_atime = CURRENT_TIME;
! new_fattr->f_mtime = CURRENT_TIME;
! new_fattr->f_uid = old_fattr->f_uid;
! new_fattr->f_gid = old_fattr->f_gid;
! new_fattr->fileid = old_fattr->fileid;
! new_fattr->track = old_fattr->track;
! new_fattr->size = old_fattr->size;
/* default attributes */
! new_fattr->codec = old_fattr->codec;
! new_fattr->title = old_fattr->title;
! new_fattr->artist = old_fattr->artist;
! new_fattr->album = old_fattr->album;
! new_fattr->genre = old_fattr->genre;
/* parse new attributes from file name */
! status = njbfs_parse_filename(new_fattr, new_buf);
/* rename */
! if ((status = njb_usb_rename(info->njb, new_fattr)) < 0) {
err("rename: couldn't rename!");
+ return status;
}
+
+ /* now remove the old fattr, add the new */
+ njbfs_remove_fattr(old_fattr);
+ njbfs_add_fattr(new_fattr);
+
+ /* a little extreme, but it works... */
+ njbfs_cache_empty(info);
njbfs_unlock(info);
***************
*** 792,801 ****
/**
! * Create a new file
! * A created file is being reopened before written to.
! * We just parse the file name and that's it.
*/
! int njbfs_proc_create(struct njbfs_sb_info *info, char *file)
{
char buf[NJBFS_MAXPATHLEN + 6];
int status;
--- 561,571 ----
/**
! * Create a new file, fill the given fattr
*/
! int njbfs_proc_create(struct njbfs_sb_info *info,
! char *file,
! struct njbfs_fattr **f)
{
+ struct njbfs_fattr *fattr = *f;
char buf[NJBFS_MAXPATHLEN + 6];
int status;
***************
*** 807,819 ****
sprintf(buf, "%s%s", info->mnt.root, file);
- njbfs_lock(info);
-
! if ((status = njb_usb_create(info->njb, buf)) < 0) {
! err("create: couldn't create file!");
! status = -EINVAL;
}
njbfs_unlock(info);
return status;
}
--- 577,625 ----
sprintf(buf, "%s%s", info->mnt.root, file);
! fattr = kmalloc(sizeof(struct njbfs_fattr), GFP_KERNEL);
! if ( !fattr ) {
! err("couldn't kmalloc fattr");
! return -ENOMEM;
}
+ memset(fattr, 0, sizeof(struct njbfs_fattr));
+ fattr->f_mode = info->mnt.file_mode;
+ fattr->f_blksize = NJBFS_BLOCKSIZE;
+ fattr->f_blocks = 0;
+ fattr->f_atime = CURRENT_TIME;
+ fattr->f_mtime = CURRENT_TIME;
+ fattr->f_uid = info->mnt.uid;
+ fattr->f_gid = info->mnt.gid;
+
+ /* NJB attributes */
+ fattr->fileid = 0;
+ fattr->track = NULL;
+ fattr->size = 0;
+
+ /* tell close() on the other end of all this rot
+ to re-init our dirlist cache */
+ fattr->update_cache = 1;
+
+ status = njbfs_parse_filename(fattr, file);
+ /* fill in any unkown info */
+ if ( !fattr->artist )
+ SAFE_STRDUP(fattr->artist, UNKNOWN_STRING);
+ if ( !fattr->title )
+ SAFE_STRDUP(fattr->title, UNKNOWN_STRING);
+ if ( !fattr->album )
+ SAFE_STRDUP(fattr->album, UNKNOWN_STRING);
+ if ( !fattr->genre )
+ SAFE_STRDUP(fattr->genre, UNKNOWN_STRING);
+ if ( !fattr->codec )
+ SAFE_STRDUP(fattr->codec, "mp3");
+
+ /* add to our linked lists */
+ njbfs_lock(info);
+
+ njbfs_add_fattr(fattr);
+
njbfs_unlock(info);
+ *f = fattr;
return status;
}
***************
*** 847,865 ****
}
! /* parse codec */
if ((strstr(buf, ".mp3") != NULL) || (strstr(buf, ".MP3") != NULL)) {
! fattr->codec =
! (char *) kmalloc(strlen(NJB_CODEC_MP3) + 1,
! GFP_KERNEL);
! strcpy(fattr->codec, NJB_CODEC_MP3);
} else {
p = k_parse_attribute(buf, "codec");
! if (p && (!strcmp(p, "mp3") || !strcmp(p, "MP3"))) {
! fattr->codec =
! (char *) kmalloc(strlen(NJB_CODEC_MP3) + 1,
! GFP_KERNEL);
! strcpy(fattr->codec, NJB_CODEC_MP3);
kfree(p);
! } else { /* default is MP3 */
fattr->codec =
(char *) kmalloc(strlen(NJB_CODEC_MP3) + 1,
--- 653,665 ----
}
! /* parse codec. */
if ((strstr(buf, ".mp3") != NULL) || (strstr(buf, ".MP3") != NULL)) {
! SAFE_STRDUP(fattr->codec, NJB_CODEC_MP3);
} else {
p = k_parse_attribute(buf, "codec");
! if (p) {
! SAFE_STRDUP(fattr->codec, p);
kfree(p);
! } else { /* Default to mp3 */
fattr->codec =
(char *) kmalloc(strlen(NJB_CODEC_MP3) + 1,
***************
*** 876,889 ****
*q = '\0';
}
! fattr->title =
! (char *) kmalloc(strlen(p + 3) + 1, GFP_KERNEL);
! strcpy(fattr->title, p + 3);
} else {
fattr->title = k_parse_attribute(buf, "title");
if (!fattr->title) {
! fattr->title =
! (char *) kmalloc(strlen("<title unknown>") + 1,
! GFP_KERNEL);
! strcpy(fattr->title, "<title unknown>");
}
}
--- 676,684 ----
*q = '\0';
}
! SAFE_STRDUP(fattr->title, p + 3);
} else {
fattr->title = k_parse_attribute(buf, "title");
if (!fattr->title) {
! SAFE_STRDUP(fattr->title, UNKNOWN_STRING);
}
}
***************
*** 894,918 ****
}
fattr->artist = k_parse_attribute(buf, "artist");
! if (!fattr->artist) {
! fattr->artist =
! (char *) kmalloc(strlen(buf) + 1, GFP_KERNEL);
! strcpy(fattr->artist, buf);
! }
/* parse album */
fattr->album = k_parse_attribute(buf, "album");
! if (!fattr->album) {
! fattr->album =
! (char *) kmalloc(strlen("<unknown>") + 1, GFP_KERNEL);
! strcpy(fattr->album, "<unknown>");
! }
/* parse genre */
fattr->genre = k_parse_attribute(buf, "genre");
! if (!fattr->genre) {
! fattr->genre =
! (char *) kmalloc(strlen("<unknown>") + 1, GFP_KERNEL);
! strcpy(fattr->genre, "<unknown>");
! }
/* parse length in milliseconds */
--- 689,704 ----
}
fattr->artist = k_parse_attribute(buf, "artist");
! if (!fattr->artist)
! SAFE_STRDUP(fattr->artist, buf);
/* parse album */
fattr->album = k_parse_attribute(buf, "album");
! if (!fattr->album)
! SAFE_STRDUP(fattr->album, UNKNOWN_STRING);
/* parse genre */
fattr->genre = k_parse_attribute(buf, "genre");
! if (!fattr->genre)
! SAFE_STRDUP(fattr->genre, UNKNOWN_STRING);
/* parse length in milliseconds */
|