/***************************************************************************
filebrowser.c - eboxy file browser plugin
-------------------
begin : Sun Oct 13 2002
copyright : (C) 2002 by Paul Eggleton
email : bluelightning@bluelightning.org
***************************************************************************/
/***************************************************************************
* *
* 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <pwd.h>
#include <unistd.h>
#include <regex.h>
#include "eboxyplugin.h"
#include "pluginconstants.h"
/* Constants */
/* Sort order */
static const int SORT_FORWARD = 0; /* sort in forward */
static const int SORT_REVERSE = 1; /* sort in reverse order */
/* Sort item */
static const int SORT_NAME = 0; /* sort by file name */
static const int SORT_SIZE = 1; /* sort by file size */
static const int SORT_ATIME = 2; /* sort by last access time */
static const int SORT_CTIME = 3; /* sort by last change time */
static const int SORT_MTIME = 4; /* sort by last modification time */
static const int SORT_VERSION = 5; /* sort by version */
static const int SORT_EXT = 6; /* sort by file name extension */
static const int SORT_DIR = 7; /* sort by file or directory */
/* Globals */
char *listboxname;
char *dirpath;
char *rootpath;
char *pattern;
int showhidden;
int sort_opts;
int sort_order;
/* Function prototypes */
char *fbp_setup(const char *sender, int numargs, const char *args[]);
char *fbp_refresh(const char *sender, int numargs, const char *args[]);
char *fbp_getpath(const char *sender);
int fbp_setpath(const char *sender, const char *value);
char *fbp_getroot(const char *sender);
int fbp_setroot(const char *sender, const char *value);
char *fbp_getpattern(const char *sender);
int fbp_setpattern(const char *sender, const char *value);
char *fbp_getshowhidden(const char *sender);
int fbp_setshowhidden(const char *sender, const char *value);
char *fbp_getfilename(const char *sender);
char *fbp_listchoose(const char *sender);
int fbp_setsort(const char *sender, const char *value);
char *fbp_getsort(const char *sender);
int fbp_setsortdirection(const char *sender, const char *value);
char *fbp_getsortdirection(const char *sender);
int refresh_dir(void);
char *expandPath(const char *filename);
int strtobool(const char *str);
char *patternToRegEx(const char *pattern);
static int sortcmp(const void * a, const void * b);
/* Implementation */
int ebplugin_init(void) {
int rc = 0;
/* Set version info for the plugin */
setPluginInfo("FileBrowser", "0.4");
/* Register an object to be used in scripts */
rc = registerObject("filebrowser");
if(rc)
return rc;
rc = registerMethodDL("filebrowser", "setup", 1, "fbp_setup");
if(rc)
return rc;
rc = registerMethodDL("filebrowser", "refresh", 0, "fbp_refresh");
if(rc)
return rc;
rc = registerPropertyDL("filebrowser", "path", "fbp_getpath", "fbp_setpath");
if(rc)
return rc;
rc = registerPropertyDL("filebrowser", "rootpath", "fbp_getroot", "fbp_setroot");
if(rc)
return rc;
rc = registerPropertyDL("filebrowser", "pattern", "fbp_getpattern", "fbp_setpattern");
if(rc)
return rc;
rc = registerPropertyDL("filebrowser", "showhidden", "fbp_getshowhidden", "fbp_setshowhidden");
if(rc)
return rc;
rc = registerPropertyDL("filebrowser", "filename", "fbp_getfilename", NULL);
if(rc)
return rc;
rc = registerPropertyDL("filebrowser","sort","fbp_getsort", "fbp_setsort");
if(rc)
return rc;
rc = registerPropertyDL("filebrowser","sortdirection","fbp_getsortdirection", "fbp_setsortdirection");
if(rc)
return rc;
listboxname = NULL;
rootpath = strdup("/");
dirpath = expandPath(".");
pattern = strdup("*");
showhidden = 0;
sort_opts = SORT_DIR;
sort_order = SORT_FORWARD;
/* Success */
return 0;
}
int ebplugin_message(int msgcode, void *msgdata) {
if(msgcode == PLMSG_BEFOREPAGECHANGE) {
/* Page is changing */
if(listboxname) {
free(listboxname);
listboxname = NULL;
}
}
return 0; /* return 0 here for future compatibility */
}
void ebplugin_deinit(void) {
free(dirpath);
free(pattern);
free(rootpath);
if(listboxname)
free(listboxname);
}
char *fbp_setup(const char *sender, int numargs, const char *args[]) {
listboxname = strdup(args[0]);
refresh_dir();
registerEventHandlerDL(listboxname, "OnChoose", "fbp_listchoose");
return NULL;
}
char *fbp_refresh(const char *sender, int numargs, const char *args[]) {
refresh_dir();
return NULL;
}
char *fbp_getpath(const char *sender) {
return strdup(dirpath);
}
int fbp_setpath(const char *sender, const char *value) {
char *newpath;
int rc;
newpath = expandPath(value);
if(!strncmp(rootpath, newpath, strlen(rootpath))) {
free(dirpath);
dirpath = newpath;
rc = refresh_dir();
}
else {
fprintf(stderr, "filebrowser: new path %s is outside root path %s", newpath, rootpath);
free(newpath);
rc = -1;
}
return rc;
}
char *fbp_getroot(const char *sender) {
return strdup(rootpath);
}
int fbp_setroot(const char *sender, const char *value) {
char *newroot;
struct stat statbuf;
int rc;
newroot = expandPath(value);
rc = lstat(newroot,&statbuf);
if(rc != 0 || !S_ISDIR(statbuf.st_mode)) {
fprintf(stderr, "filebrowser: specified root path %s could not be accessed\n", newroot);
free(newroot);
return -2;
}
/* Make sure path has a trailing slash */
free(rootpath);
if(newroot[strlen(newroot)-1] != '/') {
rootpath = malloc(strlen(newroot)+2);
strcpy(rootpath, newroot);
strcat(rootpath, "/");
free(newroot);
}
else
rootpath = newroot;
newroot = NULL;
rc = 0;
if(strncmp(rootpath, dirpath, strlen(rootpath))) {
/* Current path is outside root path */
free(dirpath);
dirpath = strdup(rootpath);
rc = refresh_dir();
}
return rc;
}
char *fbp_getpattern(const char *sender) {
return strdup(pattern);
}
int fbp_setpattern(const char *sender, const char *value) {
int rc;
free(pattern);
pattern = strdup(value);
rc = refresh_dir();
return rc;
}
char *fbp_getshowhidden(const char *sender) {
if(showhidden)
return strdup("true");
else
return strdup("false");
}
int fbp_setshowhidden(const char *sender, const char *value) {
showhidden = strtobool(value);
return 0;
}
char *fbp_getfilename(const char *sender) {
char *fullname;
const char *selectedtext;
if(listboxname && dirpath) {
selectedtext = getPropertyAsString(listboxname, "selectedtext");
fullname = malloc(PATH_MAX);
/* Build full name of file */
strcpy(fullname, dirpath);
if(dirpath[strlen(dirpath)-1] != '/')
strcat(fullname, "/");
strcat(fullname, selectedtext);
return fullname;
}
return NULL;
}
char *fbp_getsort(const char *sender) {
char *ret;
if(sort_opts == SORT_NAME)
ret = "name";
else if(sort_opts == SORT_SIZE)
ret = "size";
else if(sort_opts == SORT_ATIME)
ret = "atime";
else if(sort_opts == SORT_CTIME)
ret = "ctime";
else if(sort_opts == SORT_MTIME)
ret = "mtime";
else if(sort_opts == SORT_VERSION)
ret = "version";
else if(sort_opts == SORT_EXT)
ret = "ext";
else if(sort_opts == SORT_DIR)
ret = "dir";
else
ret = "none";
return strdup(ret);
}
int fbp_setsort(const char *sender, const char *value) {
int rc;
if(strcasecmp(value, "name")==0)
sort_opts = SORT_NAME;
else if(strcasecmp(value, "size")==0)
sort_opts = SORT_SIZE;
else if(strcasecmp(value, "atime")==0)
sort_opts = SORT_ATIME;
else if(strcasecmp(value, "ctime")==0)
sort_opts = SORT_CTIME;
else if(strcasecmp(value, "mtime")==0)
sort_opts = SORT_MTIME;
else if(strcasecmp(value, "version")==0)
sort_opts = SORT_VERSION;
else if(strcasecmp(value, "ext")==0)
sort_opts = SORT_EXT;
else if(strcasecmp(value, "dir")==0)
sort_opts = SORT_DIR;
rc = refresh_dir();
return rc;
}
char *fbp_getsortdirection(const char *sender) {
if(sort_order==SORT_FORWARD)
return strdup("forward");
else
return strdup("reverse");
}
int fbp_setsortdirection(const char *sender, const char *value) {
int rc;
if(strcasecmp(value, "forward")==0)
sort_order = SORT_FORWARD;
else
sort_order = SORT_REVERSE;
rc = refresh_dir();
return rc;
}
int refresh_dir(void) {
DIR *dp;
struct dirent *entry;
struct stat statbuf;
const char *margs[1];
char *fullname;
char *listitem;
int rc;
int count;
int i;
regex_t *regex = NULL;
struct dirent **files;
if(listboxname) {
if(strcmp(pattern, "*")) {
int rc;
char *regstr;
/* Make space for the regular expression */
regex = (regex_t *) malloc(sizeof(regex_t));
memset(regex, 0, sizeof(regex_t));
/* Convert pattern to regular expression */
regstr = patternToRegEx(pattern);
if(!regstr) {
/* Malloc error */
return 1;
}
/* Compile regular expression */
if((rc=regcomp(regex, regstr, REG_EXTENDED))!=0) {
/* An error occurred, get and print the error */
size_t length;
char *buffer;
length = regerror(rc, regex, NULL, 0);
buffer = malloc(length);
regerror(rc, regex, buffer, length);
fprintf(stderr, "%s\n", buffer);
free(buffer);
regfree(regex);
free(regstr);
return 1;
}
free(regstr);
}
/* Clear listbox */
callMethod(listboxname, "clear", 0, NULL);
fullname = malloc(PATH_MAX);
/* Get list of files, sorted using sortcmp() function */
count = scandir(dirpath, &files, NULL, sortcmp);
/* Test for further requirements and display file */
for(i=0; i<count; ++i) {
int showentry = 0;
if(!strcmp(files[i]->d_name, "..")) /* Only show .. if we aren't at the root */
showentry = !(!strcmp(dirpath, rootpath));
else {
showentry = !(!strcmp(files[i]->d_name, ".")) && (showhidden || files[i]->d_name[0] != '.');
}
if(showentry) {
/* Build full name of file */
strcpy(fullname, dirpath);
if(dirpath[strlen(dirpath)-1] != '/')
strcat(fullname, "/");
strcat(fullname, files[i]->d_name);
/* Get info on file entry */
rc = lstat(fullname, &statbuf);
if(rc == 0) {
if(S_ISDIR(statbuf.st_mode)) {
/* Directory - append a / onto the name */
listitem = malloc(strlen(files[i]->d_name)+2);
strcpy(listitem, files[i]->d_name);
strcat(listitem, "/");
}
else {
/* Normal file */
if(regex == NULL || regexec(regex, files[i]->d_name, 0, NULL, REG_NOSUB)==0)
listitem = strdup(files[i]->d_name);
else
listitem = NULL;
}
if(listitem) {
margs[0] = listitem;
callMethod(listboxname, "additem", 1, margs);
free(listitem);
}
}
else {
perror("filebrowser");
printf("filebrowser: file was %s\n", fullname);
}
}
}
free(fullname);
if(regex)
regfree(regex);
}
return 0;
}
/* Called whenever the user chooses an item from the list */
char *fbp_listchoose(const char *sender) {
const char *selectedtext;
char *newpath;
char *oldpath;
char *backpos;
if(!strcmp(sender, listboxname)) {
selectedtext = getPropertyAsString(sender, "selectedtext");
if(strlen(selectedtext)>0) {
if(selectedtext[strlen(selectedtext)-1] == '/') {
/* Change directory */
if(!strcmp(selectedtext, "../")) {
/* Up to parent */
if(dirpath[strlen(dirpath)-1] == '/')
dirpath[strlen(dirpath)-1] = '\0';
backpos = strrchr(dirpath, '/');
if(backpos) {
newpath = malloc((backpos-dirpath) + 2);
strncpy(newpath, dirpath, (backpos-dirpath));
newpath[backpos-dirpath] = '/';
newpath[backpos-dirpath+1] = '\0';
}
else
newpath = strdup(dirpath);
}
else {
/* Enter directory */
newpath = malloc(strlen(selectedtext) + strlen(dirpath) + 2);
strcpy(newpath, dirpath);
if(dirpath[strlen(dirpath)-1] != '/')
strcat(newpath, "/");
strcat(newpath, selectedtext);
}
oldpath = dirpath;
dirpath = newpath;
if(refresh_dir() == 0) {
/* Change of path succeeded */
free(oldpath);
fireEvent("filebrowser", "OnPathChange");
}
else {
dirpath = oldpath;
free(newpath);
}
}
else {
/* A file was chosen */
fireEvent("filebrowser", "OnChooseFile");
}
}
}
return NULL;
}
/* Expands any environment variables in the specified path
Note: you should free what this returns when you're done with it */
char *expandPath(const char *filename) {
const int bufsize = PATH_MAX;
char *path = NULL;
char *buf = NULL;
char *bufptr = NULL;
char *expandbuf = NULL;
int i=0, j=0, k=0;
int expandstart = 0;
int expanding = 0;
int length = 0;
struct passwd *userinfo;
if(filename == NULL)
return NULL;
path = strdup(filename);
if(strlen(path) == 0)
return path;
buf = malloc(bufsize+1);
expandbuf = malloc(bufsize+1);
while(1) {
if(expanding) {
if(i >= strlen(path) ||
k >= bufsize ||
!(isalnum(path[i]) || path[i] == '_')) {
/* End of variable, try to dereference it */
if(path[expandstart] == '$') {
if(k==0 && i < strlen(path) && path[i] == '$') {
/* Special variable (process ID) */
bufptr = malloc(11);
snprintf(bufptr, 10, "%d", getpid());
length = strlen(bufptr);
if(bufsize-j < length)
length = bufsize-j;
strncat(buf, bufptr, length);
j += length;
free(bufptr);
i++; /* need to skip second $ */
}
else {
/* Get value of specified environment variable */
expandbuf[k] = '\0';
bufptr = getenv(expandbuf);
if(bufptr) {
/* getenv returned something, add it to the string */
int length = strlen(bufptr);
if(bufsize-j < length)
length = bufsize-j;
buf[j] = '\0';
strncat(buf, bufptr, length);
j += length;
/* we DO NOT free bufptr here because it's a pointer into the user's env table */
}
}
}
else if(path[expandstart] == '~') {
/* Note that we always expect j==0 here because anything else is not allowed */
if(i >= strlen(path) || path[i] == '/') {
/* Home directory */
if(k==0) {
/* Current user's home directory */
bufptr = getenv("HOME");
if(bufptr) {
strncpy(buf, bufptr, bufsize);
j = strlen(bufptr);
/* we DO NOT free bufptr here because it's a pointer into the user's env table */
}
}
else {
/* Some other user's home directory */
expandbuf[k] = '\0';
userinfo = getpwnam(expandbuf);
if(userinfo) {
strncpy(buf, userinfo->pw_dir, bufsize);
j = strlen(userinfo->pw_dir);
}
else {
j = i;
strncpy(buf, path, j);
}
}
}
else {
j = i;
strncpy(buf, path, j);
}
}
expanding = 0;
}
else
expandbuf[k++] = path[i];
}
if(i >= strlen(path))
break;
if(!expanding) {
if(path[i] == '$' || (i==0 && path[i] == '~')) {
/* Start of something to expand */
k = 0;
expandstart = i;
expanding = 1;
}
else {
/* FIXME: this needs work (.. (parent), /./ in middle of path, etc.) */
if(path[i] == '.' && path[i+1] != '.' && (i==0 || path[i] == '/') && (path[i+1] == '\0' || path[i+1] == '/')) {
if(i == 0) {
/* Get current directory */
if(getcwd(buf, bufsize))
j+=strlen(buf);
}
}
else {
buf[j] = path[i];
j++;
}
}
}
i++;
}
/* Cleanup */
free(expandbuf);
free(path);
buf[j] = '\0';
return buf;
}
/* Convert a string value into a boolean */
int strtobool(const char *str) {
if(strcasecmp(str, "true") == 0 || strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0 || atoi(str) != 0)
return 1;
else
return 0;
}
/* Convert a simple file pattern into a basic regular expression */
char *patternToRegEx(const char *pattern) {
int i, j=0;
char *buf;
buf = malloc(strlen(pattern) * 2);
if(!buf) {
perror("filebrowser");
return NULL;
}
for(i=0; i<strlen(pattern); i++) {
if(pattern[i] == '?') {
buf[j++] = '.';
}
else if(pattern[i] == '*') {
strcpy(buf+j, ".*");
j+=2;
}
else if(pattern[i] == '.') {
strcpy(buf+j, "\\.");
j+=2;
}
else if(pattern[i] == ';') { // Multiple patterns
buf[j++] = '|';
}
else {
buf[j++] = pattern[i];
}
}
buf[j] = '\0';
return buf;
}
/* Custom comparison function to use with scandir() */
static int sortcmp(const void *a, const void *b) {
int cmp, dif;
struct stat stat_a;
struct stat stat_b;
char *fullname_a;
char *fullname_b;
/* Get full name for both files */
fullname_a = malloc(PATH_MAX);
fullname_b = malloc(PATH_MAX);
strcpy(fullname_a, dirpath);
if(dirpath[strlen(dirpath)-1] != '/')
strcat(fullname_a, "/");
strcat(fullname_a, (*(const struct dirent **) a)->d_name);
strcpy(fullname_b, dirpath);
if(dirpath[strlen(dirpath)-1] != '/')
strcat(fullname_b, "/");
strcat(fullname_b, (*(const struct dirent **) b)->d_name);
/* Obtain file details */
lstat(fullname_a, &stat_a);
lstat(fullname_b, &stat_b);
cmp = 0;
dif = 0;
if (sort_opts == SORT_SIZE) {
dif = (int)(stat_a.st_size - stat_b.st_size);
}
else if (sort_opts == SORT_ATIME) {
dif = (int)(stat_a.st_atime - stat_b.st_atime);
}
else if (sort_opts == SORT_CTIME) {
dif = (int)(stat_a.st_ctime - stat_b.st_ctime);
}
else if (sort_opts == SORT_MTIME) {
dif = (int)(stat_a.st_mtime - stat_b.st_mtime);
}
else if (sort_opts == SORT_DIR) {
if(S_ISDIR(stat_a.st_mode) && ! S_ISREG(stat_a.st_mode))
dif = 1;
if(S_ISDIR(stat_b.st_mode) && ! S_ISREG(stat_b.st_mode))
dif -= 1;
/* FIXME: implement these or remove them */
/* } else if (sort_opts == SORT_VERSION) { */
/* } else if (sort_opts == SORT_EXT) { */
} else { /* assume SORT_NAME */
dif = 0;
}
if (dif > 0) cmp = -1;
if (dif < 0) cmp = 1;
if (dif == 0) {
/* sort by name- may be a tie_breaker for time or size cmp */
dif = strcasecmp((*(const struct dirent **) a)->d_name, (*(const struct dirent **) b)->d_name);
if (dif > 0) cmp = 1;
if (dif < 0) cmp = -1;
}
if (sort_order == SORT_REVERSE) {
cmp = -1 * cmp;
}
free(fullname_a);
free(fullname_b);
return(cmp);
}
syntax highlighted by Code2HTML, v. 0.9.1