diff --git a/Makefile.am b/Makefile.am index f653092..d72ae55 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ bin_PROGRAMS=tcfs tcfs_SOURCES=src/tcfs.c -tcfs_LDADD = $(LIBOBJS) $(FUSE_LIBS) -tcfs_CFLAGS = $(FUSE_CFLAGS) -DFUSE_USE_VERSION=28 \ No newline at end of file +tcfs_LDADD = $(LIBOBJS) $(FUSE_LIBS) $(libavcore_LIBS) $(libavutil_LIBS) $(libavformat_LIBS) $(libavcodec_LIBS) +tcfs_CFLAGS = $(FUSE_CFLAGS) $(libavcore_CFLAGS) $(libavutil_CFLAGS) $(libavformat_CFLAGS) $(libavcodec_CFLAGS) -DFUSE_USE_VERSION=28 \ No newline at end of file diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..9ea4546 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +aclocal && autoconf && autoheader && automake --add-missing \ No newline at end of file diff --git a/configure.ac b/configure.ac index 9011124..12a1164 100644 --- a/configure.ac +++ b/configure.ac @@ -9,18 +9,24 @@ AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CC -AM_PROG_CC_C_O # Checks for libraries. PKG_CHECK_MODULES([FUSE], [fuse >= 2.8]) +PKG_CHECK_MODULES([libavcodec], [libavcodec >= 52.94]) +PKG_CHECK_MODULES([libavformat], [libavformat >= 52.84]) +PKG_CHECK_MODULES([libavcore], [libavcore >= 0.12]) +PKG_CHECK_MODULES([libavutil], [libavutil >= 50.32]) # Checks for header files. -AC_CHECK_HEADERS([fcntl.h stddef.h stdlib.h string.h]) +AC_CHECK_HEADERS([fcntl.h stddef.h stdlib.h string.h errno.h dirent.h]) # Checks for typedefs, structures, and compiler characteristics. +AC_PROG_CC_C99 +AM_PROG_CC_C_O # Checks for library functions. AC_CHECK_FUNCS([memset strerror]) +AC_CHECK_FUNCS([asprintf], [], [AC_MSG_ERROR([asprintf emulation for non-GNU systems NYI])]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/run-autotools.sh b/run-autotools.sh deleted file mode 100755 index 10ad5ca..0000000 --- a/run-autotools.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -aclocal && autoconf && automake --add-missing \ No newline at end of file diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..d7aa301 --- /dev/null +++ b/src/common.h @@ -0,0 +1,61 @@ +#ifndef TCFS_COMMON_H +#define TCFS_COMMON_H 1 + +#if HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_ASPRINTF +# define _GNU_SOURCE +#endif + +#include +#if HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif + +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif + +#ifdef HAVE_STRINGS_H +# include +#endif + +#ifdef HAVE_INTTYPES_H +# include +#endif + +#ifdef HAVE_STDINT_H +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_ERRNO_H +# include +#endif + +#ifdef HAVE_DIRENT_H +# include +#endif + +#endif diff --git a/src/tcfs.c b/src/tcfs.c index 150c83d..750bb43 100644 --- a/src/tcfs.c +++ b/src/tcfs.c @@ -4,31 +4,300 @@ This work is licensed under the Open Software License ("OSL") v. 3.0. */ -#include "config.h" +#include "common.h" -#include -#include #include #include -#include -#include -#include -#include -#include struct options { char *base; char *tfmt; } options; -int is_audio_file(const char *path) +// Get the filename from a path, i.e. a pointer to the character after the last / in path. +// If path contains no /, returns path. +char *get_file_from_path(const char *path) { + if(!path) + return NULL; + + char *ret = strchr(path, '/'); + if(ret == NULL) + return path; + + return ret+1; +} + +// Get the extension from the file name +char *get_ext_from_file(const char *file) +{ + if(!file) + return NULL; + + char *ret = strchr(file, '.'); + if(!ret) + return file+strlen(file); + if(ret == file) + return file+strlen(file); // return pointer to '\0'; + + return ret+1; +} + +char *strip_ext_from_path(const char *path) +{ + if(!path) + return NULL; + + char *ret = strdup(path); + + char *ext = strchr(ret, '.'); + char *file = strchr(ret, '/'); + if(!ext || (extst_size = 0; + if(ret == -1) { + free(base_path); + return -errno; + } else + return 0; + } else { + free(base_path); + return -errno; + } + } else { + free(base_path); + return -errno; + } + } + return 0; +} + +int tcfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + char *base_path = path_cat(path); + if(!base_path) + return -ENOMEM; + + struct stat st; + int ret = stat(base_path, &st); + if(ret == -1) { + free(base_path); + return -errno; + } + if(!S_ISDIR(st.st_mode)) { + free(base_path); + return -ENOTDIR; + } + + DIR *dir = opendir(base_path); + if(!dir) { + free(base_path); + return -errno; + } + + union { + struct dirent de; + char b[offsetof(struct dirent, d_name) + NAME_MAX + 1]; + } u; + struct dirent *dep; + while(readdir_r(dir, &u.de, &dep) == 0) { + if(!dep) { + free(base_path); + closedir(dir); + return 0; + } + char *full_file_path; + if(base_path[strlen(base_path)-1] == '/') { + if(asprintf(&full_file_path, "%s%s", base_path, dep->d_name) < 0) { + free(base_path); + return -ENOMEM; + } + } else { + if(asprintf(&full_file_path, "%s/%s", base_path, dep->d_name) < 0) { + free(base_path); + return -ENOMEM; + } + } + + ret = is_tc_audio_file(full_file_path); + free(full_file_path); + if(ret < 0) { + free(base_path); + return ret; + } + + if(ret == 0) + filler(buf, dep->d_name, NULL, 0); + else { + char *ext = strchr(dep->d_name, '.'); + char *file_new_ext; + if(ext == NULL) { + if(asprintf(&file_new_ext, "%s.%s", dep->d_name, options.tfmt) < 0) { + free(base_path); + return -ENOMEM; + } + } else { + *(ext+1) = '\0'; + if(asprintf(&file_new_ext, "%s%s", dep->d_name, options.tfmt) < 0) { + free(base_path); + return -ENOMEM; + } + } + filler(buf, file_new_ext, NULL, 0); + } + } + + free(base_path); + closedir(dir); + return 0; +} + +struct tcfs_file_info { + enum { + DIRECT, + TRANSCODE + } type; + FILE *fd; +}; + +int tcfs_open(const char *path, struct fuse_file_info *fi) +{ + char *base_path = path_cat(path); + if(!base_path) + return -ENOMEM; + + struct tcfs_file_info *tcfs_fi = (struct tcfs_file_info *)malloc(sizeof(struct tcfs_file_info)); + if(!tcfs_fi) { + free(base_path); + return -ENOMEM; + } + + tcfs_fi->fd = fopen(base_path, "r"); + + if(!tcfs_fi->fd) { + char *src_name = get_tc_source_name(base_path); + if(src_name) { + tcfs_fi->fd = fopen(src_name, "r"); + if(!tcfs_fi->fd) { + return -errno; + } + } else { + return -errno; + } + } + + fi->fh = (uint64_t)tcfs_fi; + fi->direct_io = 1; + return 0; +} + +int tcfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) +{ + struct tcfs_file_info *tcfs_fi = (struct tcfs_file_info*)fi->fh; + if(!tcfs_fi) + return -EINVAL; + + errno = 0; + int ret = fread(buf, 1, size, tcfs_fi->fd); + if((ret == 0) && (errno != 0)) + return -errno; + + return ret; } void *tcfs_init(struct fuse_conn_info *conn) @@ -40,7 +309,11 @@ void *tcfs_init(struct fuse_conn_info *conn) } static struct fuse_operations tcfs_oper = { - .init = tcfs_init + .init = tcfs_init, + .getattr = tcfs_getattr, + .readdir = tcfs_readdir, + .open = tcfs_open, + .read = tcfs_read }; enum { @@ -72,6 +345,9 @@ int main(int argc, char *argv[]) return -1; } + if(options.base[strlen(options.base)-1] == '/') + options.base[strlen(options.base)-1] = '\0'; + struct stat st; if(stat(options.base, &st) != 0) { fprintf(stderr, "Could not stat '%s': %s\n", options.base, strerror(errno));