Some updated to bundle/settings to integrate the two. Settings can now be seamlessly loaded from bundle/share/settings directory. This is useful to opentv module but may be useful elsewhere in the future.
This commit is contained in:
parent
d1e94d8a46
commit
a8096d4ea8
12 changed files with 432 additions and 488 deletions
2
Makefile
2
Makefile
|
@ -169,7 +169,7 @@ CFLAGS_com = -g -funsigned-char -O2
|
|||
CFLAGS_com += -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS_com += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR}
|
||||
|
||||
MKBUNDLE = $(CURDIR)/support/mkbundle.py
|
||||
MKBUNDLE = $(CURDIR)/support/mkbundle
|
||||
|
||||
ifndef V
|
||||
ECHO = printf "$(1)\t\t%s\n" $(2)
|
||||
|
|
|
@ -32,13 +32,6 @@
|
|||
* Opaque data types
|
||||
* *************************************************************************/
|
||||
|
||||
/* Bundle or Direct */
|
||||
typedef enum filebundle_handle_type
|
||||
{
|
||||
FB_BUNDLE,
|
||||
FB_DIRECT
|
||||
} fb_type;
|
||||
|
||||
/* File bundle dir handle */
|
||||
typedef struct filebundle_dir
|
||||
{
|
||||
|
@ -142,6 +135,42 @@ static uint8_t *_fb_deflate ( const uint8_t *data, size_t orig, size_t *size )
|
|||
return bufout;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Miscellanous
|
||||
* *************************************************************************/
|
||||
|
||||
/* Get path stats */
|
||||
// TODO: this could do with being more efficient
|
||||
int fb_stat ( const char *path, struct filebundle_stat *st )
|
||||
{
|
||||
int ret = 1;
|
||||
fb_dir *dir;
|
||||
fb_file *fp;
|
||||
|
||||
if (*path == '/') {
|
||||
struct stat _st;
|
||||
if (!lstat(path, &_st)) {
|
||||
st->type = FB_DIRECT;
|
||||
st->is_dir = S_ISDIR(_st.st_mode) ? 1 : 0;
|
||||
st->size = _st.st_size;
|
||||
ret = 0;
|
||||
}
|
||||
} else if ((dir = fb_opendir(path))) {
|
||||
st->type = dir->type;
|
||||
st->is_dir = 1;
|
||||
st->size = 0;
|
||||
ret = 0;
|
||||
fb_closedir(dir);
|
||||
} else if ((fp = fb_open(path, 0, 0))) {
|
||||
st->type = fp->type;
|
||||
st->is_dir = 0;
|
||||
st->size = fp->size;
|
||||
ret = 0;
|
||||
fb_close(fp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Directory processing
|
||||
* *************************************************************************/
|
||||
|
@ -213,7 +242,7 @@ fb_dirent *fb_readdir ( fb_dir *dir )
|
|||
fb_dirent *ret = NULL;
|
||||
if (dir->type == FB_BUNDLE) {
|
||||
if (dir->b.cur) {
|
||||
dir->dirent.name = dir->b.cur->name;
|
||||
strcpy(dir->dirent.name, dir->b.cur->name);
|
||||
dir->dirent.type = dir->b.cur->type;
|
||||
dir->b.cur = dir->b.cur->next;
|
||||
ret = &dir->dirent;
|
||||
|
@ -225,7 +254,7 @@ fb_dirent *fb_readdir ( fb_dir *dir )
|
|||
struct stat st;
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), "%s/%s", dir->d.root, de->d_name);
|
||||
dir->dirent.name = de->d_name;
|
||||
strcpy(dir->dirent.name, de->d_name);
|
||||
dir->dirent.type = FB_UNKNOWN;
|
||||
if (!lstat(buf, &st))
|
||||
dir->dirent.type = S_ISDIR(st.st_mode) ? FB_DIR : FB_FILE;
|
||||
|
@ -235,6 +264,50 @@ fb_dirent *fb_readdir ( fb_dir *dir )
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Get entry list */
|
||||
int fb_scandir ( const char *path, fb_dirent ***list )
|
||||
{
|
||||
int i, ret = -1;
|
||||
struct dirent **de;
|
||||
struct filebundle_stat st;
|
||||
if (fb_stat(path, &st)) return -1;
|
||||
if (!st.is_dir) return -1;
|
||||
|
||||
/* Direct */
|
||||
if (st.type == FB_DIRECT) {
|
||||
if ((ret = scandir(path, &de, NULL, NULL)) != -1) {
|
||||
if (ret == 0) return 0;
|
||||
*list = malloc(sizeof(fb_dirent*)*ret);
|
||||
for (i = 0; i < ret; i++) {
|
||||
(*list)[i] = calloc(1, sizeof(fb_dirent));
|
||||
strcpy((*list)[i]->name, de[i]->d_name);
|
||||
(*list)[i]->type = FB_DIRECT;
|
||||
free(de[i]);
|
||||
}
|
||||
free(de);
|
||||
}
|
||||
|
||||
/* Bundle */
|
||||
} else {
|
||||
fb_dir *dir;
|
||||
if ((dir = fb_opendir(path))) {
|
||||
const filebundle_entry_t *fb;
|
||||
ret = dir->b.root->d.count;
|
||||
fb = dir->b.root->d.child;
|
||||
*list = malloc(ret * sizeof(fb_dirent));
|
||||
i = 0;
|
||||
while (fb) {
|
||||
(*list)[i] = calloc(1, sizeof(fb_dirent));
|
||||
strcpy((*list)[i]->name, fb->name);
|
||||
fb = fb->next;
|
||||
i++;
|
||||
}
|
||||
fb_closedir(dir);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Directory processing
|
||||
* *************************************************************************/
|
||||
|
@ -344,6 +417,7 @@ fb_file *fb_open ( const char *path, int decompress, int compress )
|
|||
|
||||
/* Open */
|
||||
ret = fb_open2(dir, pos+1, decompress, compress);
|
||||
fb_closedir(dir);
|
||||
free(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
@ -351,8 +425,9 @@ fb_file *fb_open ( const char *path, int decompress, int compress )
|
|||
/* Close file */
|
||||
void fb_close ( fb_file *fp )
|
||||
{
|
||||
if (fp->type == FB_DIRECT && fp->d.cur)
|
||||
if (fp->type == FB_DIRECT && fp->d.cur) {
|
||||
fclose(fp->d.cur);
|
||||
}
|
||||
if (fp->buf)
|
||||
free(fp->buf);
|
||||
free(fp);
|
||||
|
@ -387,7 +462,6 @@ ssize_t fb_read ( fb_file *fp, void *buf, size_t count )
|
|||
fp->pos += count;
|
||||
} else if (fp->type == FB_DIRECT) {
|
||||
fp->pos += fread(buf, 1, count, fp->d.cur);
|
||||
return -1;
|
||||
} else {
|
||||
count = MIN(count, fp->b.root->f.size - fp->pos);
|
||||
memcpy(buf, fp->b.root->f.data + fp->pos, count);
|
||||
|
|
|
@ -24,6 +24,13 @@
|
|||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Bundle or Direct */
|
||||
typedef enum filebundle_handle_type
|
||||
{
|
||||
FB_BUNDLE,
|
||||
FB_DIRECT
|
||||
} fb_type;
|
||||
|
||||
/* File bundle entry type */
|
||||
enum filebundle_type
|
||||
{
|
||||
|
@ -39,6 +46,7 @@ typedef struct filebundle_entry
|
|||
const char *name;
|
||||
union {
|
||||
struct {
|
||||
size_t count;
|
||||
struct filebundle_entry *child;
|
||||
} d;
|
||||
struct {
|
||||
|
@ -53,10 +61,18 @@ typedef struct filebundle_entry
|
|||
/* File bundle directory entry */
|
||||
typedef struct filebundle_dirent
|
||||
{
|
||||
const char *name;
|
||||
enum filebundle_type type;
|
||||
char name[256];
|
||||
enum filebundle_type type;
|
||||
} fb_dirent;
|
||||
|
||||
/* File bundle stat */
|
||||
struct filebundle_stat
|
||||
{
|
||||
fb_type type;
|
||||
uint8_t is_dir;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* Opaque types */
|
||||
typedef struct filebundle_dir fb_dir;
|
||||
typedef struct filebundle_file fb_file;
|
||||
|
@ -64,10 +80,14 @@ typedef struct filebundle_file fb_file;
|
|||
/* Root of bundle */
|
||||
extern filebundle_entry_t *filebundle_root;
|
||||
|
||||
/* Miscellaneous */
|
||||
int fb_stat ( const char *path, struct filebundle_stat *st );
|
||||
|
||||
/* Directory processing wrappers */
|
||||
fb_dir *fb_opendir ( const char *path );
|
||||
fb_dirent *fb_readdir ( fb_dir *fb );
|
||||
void fb_closedir ( fb_dir *fb );
|
||||
int fb_scandir ( const char *path, fb_dirent ***list );
|
||||
|
||||
/* File processing wrappers */
|
||||
// Note: all access is read-only
|
||||
|
|
396
src/settings.c
396
src/settings.c
|
@ -31,6 +31,7 @@
|
|||
#include "htsmsg_json.h"
|
||||
#include "settings.h"
|
||||
#include "tvheadend.h"
|
||||
#include "filebundle.h"
|
||||
|
||||
static char *settingspath;
|
||||
|
||||
|
@ -43,7 +44,6 @@ hts_settings_get_root(void)
|
|||
return settingspath ?: "No settings dir";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -56,17 +56,12 @@ hts_settings_init(const char *confpath)
|
|||
|
||||
if(confpath != NULL) {
|
||||
settingspath = strdup(confpath);
|
||||
} else {
|
||||
|
||||
if(homedir != NULL) {
|
||||
snprintf(buf, sizeof(buf), "%s/.hts", homedir);
|
||||
if(stat(buf, &st) == 0 || mkdir(buf, 0700) == 0) {
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s/.hts/tvheadend", homedir);
|
||||
|
||||
if(stat(buf, &st) == 0 || mkdir(buf, 0700) == 0)
|
||||
settingspath = strdup(buf);
|
||||
}
|
||||
} else if(homedir != NULL) {
|
||||
snprintf(buf, sizeof(buf), "%s/.hts", homedir);
|
||||
if(stat(buf, &st) == 0 || mkdir(buf, 0700) == 0) {
|
||||
snprintf(buf, sizeof(buf), "%s/.hts/tvheadend", homedir);
|
||||
if(stat(buf, &st) == 0 || mkdir(buf, 0700) == 0)
|
||||
settingspath = strdup(buf);
|
||||
}
|
||||
}
|
||||
if(settingspath == NULL) {
|
||||
|
@ -82,139 +77,42 @@ hts_settings_init(const char *confpath)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
hts_settings_save(htsmsg_t *record, const char *pathfmt, ...)
|
||||
{
|
||||
char path[256];
|
||||
char fullpath[256];
|
||||
char fullpath2[256];
|
||||
int x, l, fd;
|
||||
va_list ap;
|
||||
struct stat st;
|
||||
htsbuf_queue_t hq;
|
||||
htsbuf_data_t *hd;
|
||||
char *n;
|
||||
int ok;
|
||||
|
||||
if(settingspath == NULL)
|
||||
return;
|
||||
|
||||
va_start(ap, pathfmt);
|
||||
vsnprintf(path, sizeof(path), pathfmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
n = path;
|
||||
|
||||
while(*n) {
|
||||
if(*n == ':' || *n == '?' || *n == '*' || *n > 127 || *n < 32)
|
||||
*n = '_';
|
||||
n++;
|
||||
}
|
||||
|
||||
l = strlen(path);
|
||||
|
||||
for(x = 0; x < l; x++) {
|
||||
if(path[x] == '/') {
|
||||
/* It's a directory here */
|
||||
|
||||
path[x] = 0;
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s", settingspath, path);
|
||||
|
||||
if(stat(fullpath, &st) && mkdir(fullpath, 0700)) {
|
||||
tvhlog(LOG_ALERT, "settings", "Unable to create dir \"%s\": %s",
|
||||
fullpath, strerror(errno));
|
||||
return;
|
||||
}
|
||||
path[x] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s.tmp", settingspath, path);
|
||||
|
||||
if((fd = tvh_open(fullpath, O_CREAT | O_TRUNC | O_RDWR, 0700)) < 0) {
|
||||
tvhlog(LOG_ALERT, "settings", "Unable to create \"%s\" - %s",
|
||||
fullpath, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
ok = 1;
|
||||
|
||||
htsbuf_queue_init(&hq, 0);
|
||||
htsmsg_json_serialize(record, &hq, 1);
|
||||
|
||||
TAILQ_FOREACH(hd, &hq.hq_q, hd_link)
|
||||
if(write(fd, hd->hd_data + hd->hd_data_off, hd->hd_data_len) !=
|
||||
hd->hd_data_len) {
|
||||
tvhlog(LOG_ALERT, "settings", "Failed to write file \"%s\" - %s",
|
||||
fullpath, strerror(errno));
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
snprintf(fullpath2, sizeof(fullpath2), "%s/%s", settingspath, path);
|
||||
|
||||
if(ok)
|
||||
rename(fullpath, fullpath2);
|
||||
else
|
||||
unlink(fullpath);
|
||||
|
||||
htsbuf_queue_flush(&hq);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
hts_settings_load_one(const char *filename)
|
||||
{
|
||||
struct stat st;
|
||||
int fd;
|
||||
char *mem;
|
||||
htsmsg_t *r;
|
||||
int n;
|
||||
|
||||
if(stat(filename, &st) < 0)
|
||||
return NULL;
|
||||
|
||||
if((fd = tvh_open(filename, O_RDONLY, 0)) < 0)
|
||||
return NULL;
|
||||
|
||||
mem = malloc(st.st_size + 1);
|
||||
mem[st.st_size] = 0;
|
||||
|
||||
n = read(fd, mem, st.st_size);
|
||||
close(fd);
|
||||
if(n == st.st_size)
|
||||
r = htsmsg_json_deserialize(mem);
|
||||
else
|
||||
r = NULL;
|
||||
|
||||
free(mem);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static int
|
||||
hts_settings_buildpath(char *dst, size_t dstsize, const char *fmt, va_list ap)
|
||||
hts_settings_makedirs ( char *path )
|
||||
{
|
||||
size_t x;
|
||||
struct stat st;
|
||||
size_t l = strlen(path);
|
||||
for(x = 0; x < l; x++) {
|
||||
if(path[x] == '/' && x != 0) {
|
||||
path[x] = 0;
|
||||
if(stat(path, &st) && mkdir(path, 0700)) {
|
||||
tvhlog(LOG_ALERT, "settings", "Unable to create dir \"%s\": %s",
|
||||
path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
path[x] = '/';
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static void
|
||||
hts_settings_buildpath
|
||||
(char *dst, size_t dstsize, const char *fmt, va_list ap, const char *prefix)
|
||||
{
|
||||
char tmp[256];
|
||||
char *n = dst;
|
||||
|
||||
if(settingspath == NULL)
|
||||
return -1;
|
||||
|
||||
vsnprintf(tmp, sizeof(tmp), fmt, ap);
|
||||
if (*tmp != '/')
|
||||
snprintf(dst, dstsize, "%s/%s", settingspath, tmp);
|
||||
if (*tmp != '/' && prefix)
|
||||
snprintf(dst, dstsize, "%s/%s", prefix, tmp);
|
||||
else
|
||||
strncpy(dst, tmp, dstsize);
|
||||
|
||||
|
@ -223,7 +121,134 @@ hts_settings_buildpath(char *dst, size_t dstsize, const char *fmt, va_list ap)
|
|||
*n = '_';
|
||||
n++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
hts_settings_save(htsmsg_t *record, const char *pathfmt, ...)
|
||||
{
|
||||
char path[256];
|
||||
char tmppath[256];
|
||||
int fd;
|
||||
va_list ap;
|
||||
htsbuf_queue_t hq;
|
||||
htsbuf_data_t *hd;
|
||||
int ok;
|
||||
|
||||
if(settingspath == NULL)
|
||||
return;
|
||||
|
||||
/* Clean the path */
|
||||
va_start(ap, pathfmt);
|
||||
hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
|
||||
va_end(ap);
|
||||
|
||||
/* Create directories */
|
||||
if (hts_settings_makedirs(path)) return;
|
||||
|
||||
/* Create tmp file */
|
||||
snprintf(tmppath, sizeof(tmppath), "%s.tmp", path);
|
||||
if((fd = tvh_open(tmppath, O_CREAT | O_TRUNC | O_RDWR, 0700)) < 0) {
|
||||
tvhlog(LOG_ALERT, "settings", "Unable to create \"%s\" - %s",
|
||||
tmppath, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store data */
|
||||
ok = 1;
|
||||
htsbuf_queue_init(&hq, 0);
|
||||
htsmsg_json_serialize(record, &hq, 1);
|
||||
TAILQ_FOREACH(hd, &hq.hq_q, hd_link)
|
||||
if(write(fd, hd->hd_data + hd->hd_data_off, hd->hd_data_len) !=
|
||||
hd->hd_data_len) {
|
||||
tvhlog(LOG_ALERT, "settings", "Failed to write file \"%s\" - %s",
|
||||
tmppath, strerror(errno));
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
htsbuf_queue_flush(&hq);
|
||||
|
||||
/* Move */
|
||||
if(ok) {
|
||||
rename(tmppath, path);
|
||||
|
||||
/* Delete tmp */
|
||||
} else
|
||||
unlink(tmppath);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
hts_settings_load_one(const char *filename)
|
||||
{
|
||||
ssize_t n;
|
||||
char *mem;
|
||||
fb_file *fp;
|
||||
htsmsg_t *r = NULL;
|
||||
|
||||
/* Open */
|
||||
if (!(fp = fb_open(filename, 1, 0))) return NULL;
|
||||
|
||||
/* Load data */
|
||||
mem = malloc(fb_size(fp)+1);
|
||||
n = fb_read(fp, mem, fb_size(fp));
|
||||
if (n >= 0) mem[n] = 0;
|
||||
fb_close(fp);
|
||||
|
||||
/* Decode */
|
||||
if(n == fb_size(fp))
|
||||
r = htsmsg_json_deserialize(mem);
|
||||
free(mem);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
static htsmsg_t *
|
||||
_hts_settings_load(const char *fullpath)
|
||||
{
|
||||
char child[256];
|
||||
struct filebundle_stat st;
|
||||
fb_dirent **namelist, *d;
|
||||
htsmsg_t *r, *c;
|
||||
int n, i;
|
||||
|
||||
/* Invalid */
|
||||
if (fb_stat(fullpath, &st)) return NULL;
|
||||
|
||||
/* Directory */
|
||||
if (st.is_dir) {
|
||||
|
||||
/* Get file list */
|
||||
if((n = fb_scandir(fullpath, &namelist)) < 0)
|
||||
return NULL;
|
||||
|
||||
/* Read files */
|
||||
r = htsmsg_create_map();
|
||||
for(i = 0; i < n; i++) {
|
||||
d = namelist[i];
|
||||
if(d->name[0] != '.') {
|
||||
snprintf(child, sizeof(child), "%s/%s", fullpath, d->name);
|
||||
if ((c = hts_settings_load_one(child)))
|
||||
htsmsg_add_msg(r, d->name, c);
|
||||
}
|
||||
free(d);
|
||||
}
|
||||
free(namelist);
|
||||
|
||||
/* File */
|
||||
} else {
|
||||
r = hts_settings_load_one(fullpath);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -232,45 +257,27 @@ hts_settings_buildpath(char *dst, size_t dstsize, const char *fmt, va_list ap)
|
|||
htsmsg_t *
|
||||
hts_settings_load(const char *pathfmt, ...)
|
||||
{
|
||||
htsmsg_t *ret = NULL;
|
||||
char fullpath[256];
|
||||
char child[256];
|
||||
va_list ap;
|
||||
struct stat st;
|
||||
struct dirent **namelist, *d;
|
||||
htsmsg_t *r, *c;
|
||||
int n, i;
|
||||
|
||||
/* Try normal path */
|
||||
va_start(ap, pathfmt);
|
||||
if(hts_settings_buildpath(fullpath, sizeof(fullpath), pathfmt, ap) < 0)
|
||||
return NULL;
|
||||
hts_settings_buildpath(fullpath, sizeof(fullpath),
|
||||
pathfmt, ap, settingspath);
|
||||
va_end(ap);
|
||||
ret = _hts_settings_load(fullpath);
|
||||
|
||||
if(stat(fullpath, &st) != 0)
|
||||
return NULL;
|
||||
|
||||
if(S_ISDIR(st.st_mode)) {
|
||||
|
||||
if((n = scandir(fullpath, &namelist, NULL, NULL)) < 0)
|
||||
return NULL;
|
||||
|
||||
r = htsmsg_create_map();
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
d = namelist[i];
|
||||
if(d->d_name[0] != '.') {
|
||||
snprintf(child, sizeof(child), "%s/%s", fullpath, d->d_name);
|
||||
c = hts_settings_load_one(child);
|
||||
if(c != NULL)
|
||||
htsmsg_add_msg(r, d->d_name, c);
|
||||
}
|
||||
free(d);
|
||||
}
|
||||
free(namelist);
|
||||
|
||||
} else {
|
||||
r = hts_settings_load_one(fullpath);
|
||||
/* Try bundle path */
|
||||
if (!ret && *pathfmt != '/') {
|
||||
va_start(ap, pathfmt);
|
||||
hts_settings_buildpath(fullpath, sizeof(fullpath),
|
||||
pathfmt, ap, "data/conf");
|
||||
va_end(ap);
|
||||
ret = _hts_settings_load(fullpath);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -283,12 +290,12 @@ hts_settings_remove(const char *pathfmt, ...)
|
|||
va_list ap;
|
||||
|
||||
va_start(ap, pathfmt);
|
||||
if(hts_settings_buildpath(fullpath, sizeof(fullpath), pathfmt, ap) < 0)
|
||||
return;
|
||||
hts_settings_buildpath(fullpath, sizeof(fullpath),
|
||||
pathfmt, ap, settingspath);
|
||||
va_end(ap);
|
||||
unlink(fullpath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -296,48 +303,19 @@ int
|
|||
hts_settings_open_file(int for_write, const char *pathfmt, ...)
|
||||
{
|
||||
char path[256];
|
||||
char fullpath[256];
|
||||
int x, l;
|
||||
va_list ap;
|
||||
struct stat st;
|
||||
char *n;
|
||||
|
||||
if(settingspath == NULL)
|
||||
return -1;
|
||||
|
||||
/* Build path */
|
||||
va_start(ap, pathfmt);
|
||||
vsnprintf(path, sizeof(path), pathfmt, ap);
|
||||
hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
|
||||
va_end(ap);
|
||||
|
||||
n = path;
|
||||
|
||||
while(*n) {
|
||||
if(*n == ':' || *n == '?' || *n == '*' || *n > 127 || *n < 32)
|
||||
*n = '_';
|
||||
n++;
|
||||
}
|
||||
|
||||
l = strlen(path);
|
||||
|
||||
for(x = 0; x < l; x++) {
|
||||
if(path[x] == '/') {
|
||||
/* It's a directory here */
|
||||
|
||||
path[x] = 0;
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s", settingspath, path);
|
||||
|
||||
if(stat(fullpath, &st) && mkdir(fullpath, 0700)) {
|
||||
tvhlog(LOG_ALERT, "settings", "Unable to create dir \"%s\": %s",
|
||||
fullpath, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
path[x] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s", settingspath, path);
|
||||
/* Create directories */
|
||||
if (for_write)
|
||||
if (hts_settings_makedirs(path)) return -1;
|
||||
|
||||
/* Open file */
|
||||
int flags = for_write ? O_CREAT | O_TRUNC | O_WRONLY : O_RDONLY;
|
||||
|
||||
return tvh_open(fullpath, flags, 0700);
|
||||
return tvh_open(path, flags, 0700);
|
||||
}
|
||||
|
|
252
support/mkbundle
252
support/mkbundle
|
@ -1,133 +1,155 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Filebundle generator
|
||||
# Copyright (C) 2009 Andreas Öman
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# Replacement for old mkbundle script that creates a full file heirarchy
|
||||
#
|
||||
|
||||
set -u # Fail on undefined vars
|
||||
import os, sys, re
|
||||
import gzip, cStringIO
|
||||
from optparse import OptionParser
|
||||
|
||||
READER="cat"
|
||||
SOURCE=
|
||||
DEPFILE=
|
||||
OUTPUT=
|
||||
PREFIX=
|
||||
COMPRESS=false
|
||||
usage()
|
||||
{
|
||||
cat << EOF
|
||||
usage: $0 options
|
||||
# Add reverse path split
|
||||
def rsplit ( p ):
|
||||
i = p.find(os.path.sep)
|
||||
if i != -1:
|
||||
return (p[:i], p[i+1:])
|
||||
else:
|
||||
return (p, '')
|
||||
|
||||
Generate a filebundle .c-file from a directory
|
||||
# Process command line
|
||||
optp = OptionParser()
|
||||
optp.add_option('-z', '--gzip', action='store_true',
|
||||
help='Compress the files with gzip')
|
||||
optp.add_option('-l', '--gzlevel', type='int', default=9,
|
||||
help='Specify compression level if using gzip')
|
||||
optp.add_option('-o', '--output', default=None,
|
||||
help='Specify output file (default /dev/stdout)')
|
||||
optp.add_option('-d', '--deps', default=None,
|
||||
help='Specify deps file to update during run')
|
||||
(opts, args) = optp.parse_args()
|
||||
|
||||
OPTIONS:
|
||||
-h Show this message
|
||||
-s Source path
|
||||
-d Dependency file (for use with make)
|
||||
-o Output file (.c file)
|
||||
-z Compress individual files using gzip
|
||||
-p Filebundle prefix
|
||||
EOF
|
||||
}
|
||||
# Setup
|
||||
outf = sys.stdout
|
||||
if opts.output:
|
||||
outf = open(opts.output, 'w')
|
||||
depf = None
|
||||
if opts.deps:
|
||||
depf = open(opts.deps, 'w')
|
||||
print >>depf, '%s: \\' % opts.output
|
||||
|
||||
# Build heirarchy
|
||||
root = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..'))
|
||||
ents = {}
|
||||
for path in args:
|
||||
for (p, ds, fs) in os.walk(path):
|
||||
p = os.path.abspath(p)
|
||||
n = p.replace(root+'/', '')
|
||||
t = ents
|
||||
while True:
|
||||
(d,n) = rsplit(n)
|
||||
if d not in t:
|
||||
t[d] = {}
|
||||
t = t[d]
|
||||
if not n: break
|
||||
for f in fs:
|
||||
t[f] = None
|
||||
|
||||
while getopts "s:d:o:zhp:" OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
h)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
z)
|
||||
READER="gzip -9"
|
||||
COMPRESS=true
|
||||
;;
|
||||
d)
|
||||
DEPFILE="$OPTARG"
|
||||
;;
|
||||
s)
|
||||
SOURCE="$OPTARG"
|
||||
;;
|
||||
o)
|
||||
OUTPUT="$OPTARG"
|
||||
;;
|
||||
p)
|
||||
PREFIX="$OPTARG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# Output a file
|
||||
def output_file ( path, name, idx, next = -1 ):
|
||||
n = 'NULL'
|
||||
if next >= 0: n = '&filebundle_entry_%06d' % next
|
||||
p = os.path.join(root, path, name);
|
||||
|
||||
# Dep file
|
||||
if depf: print >>depf, ' %s\\' % p
|
||||
|
||||
if [[ -z $SOURCE ]] || [[ -z $OUTPUT ]] || [[ -z $PREFIX ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
# First the data
|
||||
print >>outf, '// FILE : %s %s %d %d' % (path, name, idx, next)
|
||||
print >>outf, 'static const uint8_t filebundle_data_%06d[] = {' % idx,
|
||||
o = -1
|
||||
d = open(p, 'rb').read()
|
||||
if opts.gzip:
|
||||
o = len(d)
|
||||
l = opts.gzlevel
|
||||
t = cStringIO.StringIO()
|
||||
if l < 0: l = 1
|
||||
if l > 9: l = 9
|
||||
z = gzip.GzipFile(filename=name, mode='w', compresslevel=l, fileobj=t)
|
||||
z.write(d)
|
||||
z.close()
|
||||
d = t.getvalue()
|
||||
t.close()
|
||||
i = 0
|
||||
for b in d:
|
||||
if not (i % 12): print >>outf, '\n ',
|
||||
print >>outf, '0x%02x,' % ord(b),
|
||||
i = i + 1
|
||||
print >>outf, ''
|
||||
print >>outf, '};'
|
||||
|
||||
print >>outf, 'static filebundle_entry_t filebundle_entry_%06d = {' % idx
|
||||
print >>outf, ' .type = FB_FILE,'
|
||||
print >>outf, ' .name = "%s",' % name
|
||||
print >>outf, ' .next = %s,' % n
|
||||
print >>outf, ' .f.size = %d,' % len(d)
|
||||
print >>outf, ' .f.orig = %d,' % o
|
||||
print >>outf, ' .f.data = filebundle_data_%06d,' % idx
|
||||
print >>outf, '};'
|
||||
print >>outf, ''
|
||||
|
||||
FILES=`find "${SOURCE}" \( \( -name .svn -or -name *~ \) -and -prune \) -or -printf "%P "`
|
||||
# Output a directory
|
||||
def output_dir ( path, name, idx, child, count, next = -1 ):
|
||||
n = 'NULL'
|
||||
if next >= 0: n = '&filebundle_entry_%06d' % next
|
||||
print >>outf, '// DIR: %s %s %d %d %d %d'\
|
||||
% (path, name, idx, child, count, next)
|
||||
print >>outf, 'static filebundle_entry_t filebundle_entry_%06d = {' % idx
|
||||
print >>outf, ' .type = FB_DIR,'
|
||||
print >>outf, ' .name = "%s",' % name
|
||||
print >>outf, ' .next = %s,' % n
|
||||
print >>outf, ' .d.count = %d,' % count
|
||||
print >>outf, ' .d.child = &filebundle_entry_%06d,' % child
|
||||
print >>outf, '};'
|
||||
print >>outf, ''
|
||||
|
||||
echo >${OUTPUT} "// auto-generated by $0"
|
||||
# Create output
|
||||
def add_entry ( ents, path = "", name = "", idx = -1, next = -1 ):
|
||||
|
||||
for file in $FILES; do
|
||||
# Add children
|
||||
d = os.path.join(path, name)
|
||||
p = -1
|
||||
c = 0
|
||||
for k in ents:
|
||||
|
||||
# File
|
||||
if not ents[k]:
|
||||
output_file(d, k, idx+1, p)
|
||||
p = idx = idx + 1
|
||||
c = c + 1
|
||||
|
||||
# Directory
|
||||
else:
|
||||
tmp = add_entry(ents[k], d, k, idx, p)
|
||||
if tmp != idx:
|
||||
p = idx = tmp
|
||||
c = c + 1
|
||||
|
||||
if [ -f ${SOURCE}/$file ]; then
|
||||
# Add directory
|
||||
if p >= 0:
|
||||
if name:
|
||||
output_dir(path, name, idx+1, p, c, next)
|
||||
idx = idx + 1
|
||||
|
||||
name=`echo $file | perl -pe s#[^a-z0-9A-Z]#_#g`
|
||||
|
||||
echo >>${OUTPUT} "// ${SOURCE}/$file"
|
||||
echo >>${OUTPUT} "static const unsigned char embedded_$name[]={"
|
||||
$READER <${SOURCE}/$file | od -v -An -b | sed s/^\ */0/ | sed s/\ *$$/,/| sed s/\ /,\ 0/g|sed s/$/,/ >>${OUTPUT}
|
||||
echo >>${OUTPUT} "};"
|
||||
fi
|
||||
done
|
||||
return idx
|
||||
|
||||
echo >>${OUTPUT} "#include <filebundle.h>"
|
||||
echo >>${OUTPUT} "static const struct filebundle_entry filebundle_entries[] = {"
|
||||
[[ -z $DEPFILE ]] || echo >${DEPFILE} -n "${OUTPUT}: "
|
||||
# Output header
|
||||
print >>outf, '// Auto-generated - DO NOT EDIT'
|
||||
print >>outf, '// COMMAND: [%s]' % (' '.join(sys.argv))
|
||||
print >>outf, ''
|
||||
print >>outf, '#include "filebundle.h"'
|
||||
print >>outf, ''
|
||||
|
||||
for file in $FILES; do
|
||||
[[ -z $DEPFILE ]] || echo >>${DEPFILE} -n "${SOURCE}/$file "
|
||||
# Output entries
|
||||
idx = add_entry(ents)
|
||||
|
||||
if [ -f ${SOURCE}/$file ]; then
|
||||
|
||||
if $COMPRESS; then
|
||||
ORIGINAL_SIZE=`stat -c "%s" ${SOURCE}/$file`
|
||||
else
|
||||
ORIGINAL_SIZE="-1"
|
||||
fi
|
||||
|
||||
N=`echo $file | perl -pe s#[^a-z0-9A-Z]#_#g`
|
||||
echo >>${OUTPUT} "{\"$file\", embedded_$N, sizeof(embedded_$N),${ORIGINAL_SIZE}},"
|
||||
fi
|
||||
done
|
||||
|
||||
echo >>${OUTPUT} "{(void *)0, 0, 0, 0}};"
|
||||
[[ -z $DEPFILE ]] || echo >>${DEPFILE} ""
|
||||
|
||||
cat >>${OUTPUT} <<EOF
|
||||
|
||||
static struct filebundle fb = {
|
||||
.entries = &filebundle_entries[0],
|
||||
.prefix = "${PREFIX}"
|
||||
};
|
||||
|
||||
static void __attribute__((constructor)) filebundle_init(void)
|
||||
{
|
||||
extern struct filebundle *filebundles;
|
||||
|
||||
fb.next = filebundles;
|
||||
filebundles = &fb;
|
||||
}
|
||||
|
||||
EOF
|
||||
# Output top link
|
||||
print >>outf, 'filebundle_entry_t *filebundle_root = &filebundle_entry_%06d;' % idx
|
||||
|
|
|
@ -1,150 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Replacement for old mkbundle script that creates a full file heirarchy
|
||||
#
|
||||
|
||||
import os, sys, re
|
||||
import gzip, cStringIO
|
||||
from optparse import OptionParser
|
||||
|
||||
# Add reverse path split
|
||||
def rsplit ( p ):
|
||||
i = p.find(os.path.sep)
|
||||
if i != -1:
|
||||
return (p[:i], p[i+1:])
|
||||
else:
|
||||
return (p, '')
|
||||
|
||||
# Process command line
|
||||
optp = OptionParser()
|
||||
optp.add_option('-z', '--gzip', action='store_true',
|
||||
help='Compress the files with gzip')
|
||||
optp.add_option('-l', '--gzlevel', type='int', default=9,
|
||||
help='Specify compression level if using gzip')
|
||||
optp.add_option('-o', '--output', default=None,
|
||||
help='Specify output file (default /dev/stdout)')
|
||||
optp.add_option('-d', '--deps', default=None,
|
||||
help='Specify deps file to update during run')
|
||||
(opts, args) = optp.parse_args()
|
||||
|
||||
# Setup
|
||||
outf = sys.stdout
|
||||
if opts.output:
|
||||
outf = open(opts.output, 'w')
|
||||
depf = None
|
||||
if opts.deps:
|
||||
depf = open(opts.deps, 'w')
|
||||
print >>depf, '%s: \\' % opts.output
|
||||
|
||||
# Build heirarchy
|
||||
root = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..'))
|
||||
ents = {}
|
||||
for path in args:
|
||||
for (p, ds, fs) in os.walk(path):
|
||||
p = os.path.abspath(p)
|
||||
n = p.replace(root+'/', '')
|
||||
t = ents
|
||||
while True:
|
||||
(d,n) = rsplit(n)
|
||||
if d not in t:
|
||||
t[d] = {}
|
||||
t = t[d]
|
||||
if not n: break
|
||||
for f in fs:
|
||||
t[f] = None
|
||||
|
||||
# Output a file
|
||||
def output_file ( path, name, idx, next = -1 ):
|
||||
n = 'NULL'
|
||||
if next >= 0: n = '&filebundle_entry_%06d' % next
|
||||
p = os.path.join(root, path, name);
|
||||
|
||||
# Dep file
|
||||
if depf: print >>depf, ' %s\\' % p
|
||||
|
||||
# First the data
|
||||
print >>outf, '// FILE : %s %s %d %d' % (path, name, idx, next)
|
||||
print >>outf, 'static const uint8_t filebundle_data_%06d[] = {' % idx,
|
||||
o = -1
|
||||
d = open(p, 'rb').read()
|
||||
if opts.gzip:
|
||||
o = len(d)
|
||||
l = opts.gzlevel
|
||||
t = cStringIO.StringIO()
|
||||
if l < 0: l = 1
|
||||
if l > 9: l = 9
|
||||
z = gzip.GzipFile(filename=name, mode='w', compresslevel=l, fileobj=t)
|
||||
z.write(d)
|
||||
z.close()
|
||||
d = t.getvalue()
|
||||
t.close()
|
||||
i = 0
|
||||
for b in d:
|
||||
if not (i % 12): print >>outf, '\n ',
|
||||
print >>outf, '0x%02x,' % ord(b),
|
||||
i = i + 1
|
||||
print >>outf, ''
|
||||
print >>outf, '};'
|
||||
|
||||
print >>outf, 'static filebundle_entry_t filebundle_entry_%06d = {' % idx
|
||||
print >>outf, ' .type = FB_FILE,'
|
||||
print >>outf, ' .name = "%s",' % name
|
||||
print >>outf, ' .next = %s,' % n
|
||||
print >>outf, ' .f.size = %d,' % len(d)
|
||||
print >>outf, ' .f.orig = %d,' % o
|
||||
print >>outf, ' .f.data = filebundle_data_%06d,' % idx
|
||||
print >>outf, '};'
|
||||
print >>outf, ''
|
||||
|
||||
# Output a directory
|
||||
def output_dir ( path, name, idx, child, next = -1 ):
|
||||
n = 'NULL'
|
||||
if next >= 0: n = '&filebundle_entry_%06d' % next
|
||||
print >>outf, '// DIR: %s %s %d %d %d' % (path, name, idx, child, next)
|
||||
print >>outf, 'static filebundle_entry_t filebundle_entry_%06d = {' % idx
|
||||
print >>outf, ' .type = FB_DIR,'
|
||||
print >>outf, ' .name = "%s",' % name
|
||||
print >>outf, ' .next = %s,' % n
|
||||
print >>outf, ' .d.child = &filebundle_entry_%06d,' % child
|
||||
print >>outf, '};'
|
||||
print >>outf, ''
|
||||
|
||||
# Create output
|
||||
def add_entry ( ents, path = "", name = "", idx = -1, next = -1 ):
|
||||
|
||||
# Add children
|
||||
d = os.path.join(path, name)
|
||||
p = -1
|
||||
for k in ents:
|
||||
|
||||
# File
|
||||
if not ents[k]:
|
||||
output_file(d, k, idx+1, p)
|
||||
p = idx = idx + 1
|
||||
|
||||
# Directory
|
||||
else:
|
||||
tmp = add_entry(ents[k], d, k, idx, p)
|
||||
if tmp != idx:
|
||||
p = idx = tmp
|
||||
|
||||
# Add directory
|
||||
if p >= 0:
|
||||
if name:
|
||||
output_dir(path, name, idx+1, p, next)
|
||||
idx = idx + 1
|
||||
|
||||
return idx
|
||||
|
||||
# Output header
|
||||
print >>outf, '// Auto-generated - DO NOT EDIT'
|
||||
print >>outf, '// COMMAND: [%s]' % (' '.join(sys.argv))
|
||||
print >>outf, ''
|
||||
print >>outf, '#include "filebundle.h"'
|
||||
print >>outf, ''
|
||||
|
||||
# Output entries
|
||||
idx = add_entry(ents)
|
||||
|
||||
# Output top link
|
||||
print >>outf, 'filebundle_entry_t *filebundle_root = &filebundle_entry_%06d;' % idx
|
Loading…
Add table
Reference in a new issue