--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <gtk/gtk.h>
+
+typedef struct sProperty
+{
+ char *name;
+ char *value;
+} Property;
+
+typedef struct sDevice
+{
+ char *node;
+ char *label;
+ char *description;
+ time_t time;
+} Device;
+
+int parse_property(char *str, int size, Property *prop)
+{
+ int equals = -1;
+ int i;
+
+ for(i=0; (equals<0 && i<size); ++i)
+ if(str[i]=='=')
+ equals = i;
+
+ if(equals<0)
+ return -1;
+
+ prop->name = malloc(equals+1);
+ strncpy(prop->name, str, equals);
+ prop->name[equals] = 0;
+
+ prop->value = malloc(size-equals);
+ strncpy(prop->value, str+equals+1, size-equals-1);
+ prop->value[size-equals-1] = 0;
+
+ return 0;
+}
+
+Property *get_device_properties(char *node)
+{
+ int pid;
+ int pipe_fd[2];
+
+ /*printf("Examining device %s\n", node);*/
+
+ pipe(pipe_fd);
+
+ pid = fork();
+ if(pid==0)
+ {
+ close(pipe_fd[0]);
+ dup2(pipe_fd[1], 1);
+
+ execl("/sbin/udevadm", "udevadm", "info", "-q", "property", "-n", node, NULL);
+ _exit(1);
+ }
+ else if(pid>0)
+ {
+ char buf[256];
+ int pos = 0;
+ int eof = 0;
+ Property *props = NULL;
+ int n_props = 0;
+
+ close(pipe_fd[1]);
+
+ while(1)
+ {
+ int newline;
+ int i;
+ Property prop;
+
+ if(!eof)
+ {
+ int len;
+
+ len = read(pipe_fd[0], buf+pos, sizeof(buf)-pos);
+ if(len==0)
+ eof = 1;
+ else if(len==-1)
+ break;
+ pos += len;
+ }
+
+ newline = -1;
+ for(i=0; (newline<0 && i<pos); ++i)
+ if(buf[i]=='\n')
+ newline = i;
+
+ if(newline<0)
+ break;
+
+ if(parse_property(buf, newline, &prop)==0)
+ {
+ /*printf("Got property '%s' = '%s'\n", prop.name, prop.value);*/
+
+ props = (Property *)realloc(props, (n_props*2)*sizeof(Property));
+ props[n_props] = prop;
+ ++n_props;
+
+ memmove(buf, buf+newline+1, pos-newline-1);
+ pos -= newline+1;
+ }
+ else
+ break;
+ }
+
+ props[n_props].name = NULL;
+ props[n_props].value = NULL;
+
+ waitpid(pid, NULL, 0);
+ close(pipe_fd[0]);
+
+ return props;
+ }
+ else
+ {
+ close(pipe_fd[0]);
+ close(pipe_fd[1]);
+
+ return NULL;
+ }
+}
+
+char *get_property_value(Property *props, char *name)
+{
+ int i;
+ for(i=0; props[i].name; ++i)
+ if(strcmp(props[i].name, name)==0)
+ return props[i].value;
+ return NULL;
+}
+
+int match_property_value(Property *props, char *name, char *value)
+{
+ char *v = get_property_value(props, name);
+ if(!v)
+ return value==NULL;
+ return strcmp(v, value)==0;
+}
+
+void free_properties(Property *props)
+{
+ int i;
+ if(!props)
+ return;
+ for(i=0; props[i].name; ++i)
+ {
+ free(props[i].name);
+ free(props[i].value);
+ }
+ free(props);
+}
+
+Device *get_devices(void)
+{
+ DIR *dir;
+ struct dirent *de;
+ char fnbuf[256];
+ Device *devices = NULL;
+ int n_devices = 0;
+
+ dir = opendir("/dev/disk/by-id");
+ while((de = readdir(dir)))
+ {
+ if(de->d_name[0]=='.' && (de->d_name[1]==0 || (de->d_name[1]=='.' && de->d_name[2]==0)))
+ continue;
+
+ snprintf(fnbuf, sizeof(fnbuf), "/dev/disk/by-id/%s", de->d_name);
+ Property *props = get_device_properties(fnbuf);
+ if(match_property_value(props, "ID_BUS", "usb") && match_property_value(props, "DEVTYPE", "partition"))
+ {
+ char *label;
+ char *vendor;
+ char *model;
+ char buf[256];
+ char pos;
+ struct stat st;
+
+ /*printf("Using device %s\n", fnbuf);*/
+
+ label = get_property_value(props, "ID_FS_LABEL");
+ if(!label)
+ label = get_property_value(props, "ID_FS_UUID");
+ if(!label)
+ {
+ char *ptr;
+
+ label = get_property_value(props, "DEVNAME");
+ for(ptr=label; *ptr; ++ptr)
+ if(*ptr=='/')
+ label = ptr+1;
+ }
+ vendor = get_property_value(props, "ID_VENDOR");
+ model = get_property_value(props, "ID_MODEL");
+
+ pos = snprintf(buf, sizeof(buf), "%s", label);
+ if(vendor && model)
+ pos += snprintf(buf+pos, sizeof(buf)-pos, " (%s %s)", vendor, model);
+
+ stat(fnbuf, &st);
+
+ devices = (Device *)realloc(devices, (n_devices+2)*sizeof(Device));
+ devices[n_devices].node = strdup(fnbuf);
+ devices[n_devices].label = strdup(label);
+ devices[n_devices].description = strdup(buf);
+ devices[n_devices].time = st.st_mtime;
+ ++n_devices;
+ }
+ free_properties(props);
+ }
+
+ devices[n_devices].node = NULL;
+ devices[n_devices].label = NULL;
+
+ return devices;
+}
+
+void free_devices(Device *devices)
+{
+ int i;
+ if(!devices)
+ return;
+ for(i=0; devices[i].node; ++i)
+ {
+ free(devices[i].node);
+ free(devices[i].label);
+ }
+ free(devices);
+}
+
+void row_activated(GtkTreeView *list, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model(list);
+
+ if(gtk_tree_model_get_iter(model, &iter, path))
+ {
+ Device *device;
+ int pid;
+
+ gtk_tree_model_get(model, &iter, 1, &device, -1);
+
+ pid = fork();
+ if(pid==0)
+ {
+ execl("/usr/bin/pmount", "pmount", device->node, device->label, NULL);
+ _exit(1);
+ }
+ else if(pid>0)
+ {
+ waitpid(pid, NULL, 0);
+ }
+ }
+
+ (void)column;
+ (void)user_data;
+
+ gtk_main_quit();
+}
+
+int main(int argc, char **argv)
+{
+ GtkWidget *window;
+ GtkWidget *viewport;
+ GtkWidget *list;
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ Device *devices;
+ int i;
+ time_t latest;
+
+ gtk_init(&argc, &argv);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 5);
+ g_signal_connect(window, "destroy", >k_main_quit, NULL);
+
+ viewport = gtk_viewport_new(NULL, NULL);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(window), viewport);
+
+ list = gtk_tree_view_new();
+ gtk_container_add(GTK_CONTAINER(viewport), list);
+ g_signal_connect(list, "row-activated", (GCallback)&row_activated, NULL);
+
+ store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list),
+ -1, "Device", gtk_cell_renderer_text_new(), "text", 0, NULL);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+
+ devices = get_devices();
+ latest = 0;
+ for(i=0; devices[i].node; ++i)
+ {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, devices[i].description, 1, &devices[i], -1);
+ if(devices[i].time>latest)
+ {
+ latest = devices[i].time;
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ }
+
+ gtk_widget_show_all(window);
+ gtk_main();
+
+ free_devices(devices);
+
+ return 0;
+}