/*************************************************************************** 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 #include #include #include #include #include #include #include #include #include #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; id_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; id_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); }