These days however Linux GUI apps seem to require an exact installation of files in lots of different places, and don't work well if all the different pieces aren't in just the right places - for example the following sorts things that a KDE app might need:
The embedded filesystem is read-only - you can't update resources and write them back like you can on a Mac.
This release consists of the programs you can use to create a filesystem and embed it into a binary and a set of library routines you can use to access the contents.
makeresource -nocvs root_directory -o res.fsThis will take all the files and directories in the directory 'root_directory' and recursively below it and put them into the file 'res.fs'. If you specify the '-nocvs' flag then directories named 'CVS' and their contents will be ignored.
The next step is to embed the filesystem image into a linker (.o) file - you can do this easily using your friendly local GNU linker. First create a file called something like 'script' and fill it with the text:
SECTIONS { .resource (INFO) : { *(.data) } }Now link your file with the command:
ld -r -oformat elf32-i386 -o res.o -T script -b binary res.fsWhere res.o is the object file you are creating, and res.fs is the file you created in the first step. If you are using a non-x86 based system you'll have to replace the 'elf32-i386' string with something that's appropriate for your platform - 'objdump -i' will give you a listing of the file formats supported by your linker.
Finally just link the resulting resource file into your application as you would any other object file. Linking more than one resource file into a binary is not supported and will result in undefined results.
Here's a fragment of what you might toss in your Makefile:
app: .... res.o gcc .... res.o .... -lres res.fs: <list of files in root_dir> ./makeresource root_dir -o $@ %.o: %.fs ld -r -oformat elf32-i386 -o $@ -T script -b binary $<
#include "resources.h"Before you open a resource you need to choose which embedded filesystem the resource is stored in, you do this by creating a RES_ENV structure - you do this by calling res_open_env() and passing either a string which points to the code file or by passing NULL which opens the currently running binary:
#include "resource.h" RES_ENV *fs; fs = res_open_env(NULL); // opens the current code file fs = res_open_env("/usr/local/bin/app"); // opens /usr/local/bin/appres_open_env() will return NULL is the resource file doesn't exist, or has no internal resource filesystem.
When you are done with a resource filesystem close it with res_close_env(fs) - it won't actually go away until the last user of it is done with it. Note that in all the C routines below where a RES_ENV can be passed you can also pass NULL - if you do this it will create a temporary RES_ENV for the lifetime of the call - doing this a lot can be quite inefficient - it's better to call it once and destroy it when you're done.
RES_FD *res_open(RES_ENV*, const char *pathname, int flags); int res_close(RES_FD *fd); zsize_t res_read(RES_FD *fd, void *buf, size_t count); int res_rstat(RES_ENV*, const char *file_name, struct stat *buf); int res_stat(const char *file_name, struct stat *buf); int res_fstat(RES_FD *filedes, struct stat *buf); int res_lstat(const char *file_name, struct stat *buf); off_t res_lseek(RES_FD *fildes, off_t offset, int whence);res_rstat() works like res_stat - but allows you to pass in a filesystem pointer, res_stat() and res_lstat() open the current executing binary's resource filesystem and return information from it.
RES_FILE *res_fopen(RES_ENV *, const char *name, const char *mode); int res_fclose(RES_FILE *stream); size_t res_fread( void *ptr, size_t size, size_t nmemb, RES_FILE *stream); void res_clearerr(RES_FILE *stream); int res_feof(RES_FILE *stream); int res_ferror(RES_FILE *stream); RES_FD *res_fileno(RES_FILE *stream); int res_fgetc(RES_FILE *stream); char *res_fgets(char *s, int size, RES_FILE *stream); int res_getc(RES_FILE *stream); int res_ungetc(int c, RES_FILE *stream); int res_fseek(RES_FILE *stream, long offset, int whence); long res_ftell(RES_FILE *stream); void res_rewind(RES_FILE *stream); int res_fgetpos(RES_FILE *stream, fpos_t *pos); int res_fsetpos(RES_FILE *stream, fpos_t *pos);
RES_DIR *res_opendir(RES_ENV *, const char *name); int res_closedir(RES_DIR *dp); struct dirent *res_readdir(RES_DIR *dp); off_t res_telldir(RES_DIR *dp); void res_seekdir(RES_DIR *dp, off_t offset); void res_rewinddir(RES_DIR *dp);
#include "cresources.h"You start by connecting to a resource filesystem:
#include "cresources.h" res_env *fs = new res_env(NULL); // connects to the current file res_env *fs = new res_env("/another/file"); // connects to one in /another/fileWhen you're done delete the pointer.
Next you can create one of 3 types of objects:
res_env *fs = ... // as above res_fd *fd = fs->open("a/b/c"); // open a file for low level reading res_file *fl = fs->fopen("a/b/c"); // open a file for high level reading res_dir *dp = fs->opendir("a/b/c"); // open a directory for searchingDon't forget to delete these when you are done - you can happily delete the res_env after you've opened all the objects depending on it, the underlying data structures will go away when everything else has been deleted.
int res_fd::read(void *buf, size_t count); int res_fd::fstat(struct stat *buf); int res_fd::lseek(off_t offset, int whence);
int res_file::fread(void *ptr, size_t size, size_t nmemb); void res_file::clearerr(); int res_file::feof(); int res_file::ferror(); int res_file::fgetc(); int res_file::ungetc(int c); char *res_file::fgets(char *s, int size); int res_file::fseek( long offset, int whence); int res_file::ftell(); int res_file::rewind(); int res_file::fgetpos(fpos_t *pos); int res_file::fsetpos(fpos_t *pos);
struct res_dir::dirent *readdir(); off_t res_dir::telldir(); void res_dir::seekdir(off_t offset); void res_dir::rewinddir();
int res_env::stat(const char *file_name, struct stat *buf); res_fd *res_env::open(const char *name); res_file *res_env::fopen(const char *name); res_dir *res_env::opendir(const char *name);
This section is an attempt to show some possibilities and to start a dialog about how this might happen in and around the resource manager. This is not intended to replace existing functionality - but instead to allow files with the appropriate resources do the right sort of stuff.
Within KDE we could change the desktop manager to look at files to see if they have a resource file system and if they do look at a well-known place within the resource filesystem to discover prototype mimetypes and .desktop files, then unpack those to automatically allow a file to do its stuff (just as we share .desktop formats with Gnome we could also share this same info).
I expect that the following issues are likely to show up as we port it elsewhere: