Browser
/* $Id$ */
							/* Copyright (c) 2015-2022 Pierre Pronchery <khorben@defora.org> */
							/* This file is part of DeforaOS Desktop Browser */
							/* Redistribution and use in source and binary forms, with or without
							 * modification, are permitted provided that the following conditions are
							 * met:
							 *
							 * 1. Redistributions of source code must retain the above copyright notice,
							 *    this list of conditions and the following disclaimer.
							 *
							 * 2. Redistributions in binary form must reproduce the above copyright notice,
							 *    this list of conditions and the following disclaimer in the documentation
							 *    and/or other materials provided with the distribution.
							 *
							 * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
							 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
							 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
							 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
							 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
							 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
							 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
							 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
							 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
							 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
							#include <fcntl.h>
							#include <dirent.h>
							#include <unistd.h>
							#include <string.h>
							#include <System.h>
							#include <libintl.h>
							#if defined(__APPLE__)
							# include <errno.h>
							#elif defined(__linux__)
							# include <sys/syscall.h>
							# include <unistd.h>
							# include <errno.h>
							#endif
							#include "Browser.h"
							#define _(string) gettext(string)
							#define N_(string) (string)
							/* Undelete */
							/* private */
							/* types */
							#ifdef __linux__
							typedef struct _UndeleteDirent
							{
								unsigned long d_ino;
								unsigned long d_off;
								unsigned short d_reclen;
								char d_name[0];
							} UndeleteDirent;
							#else
							typedef struct dirent UndeleteDirent;
							#endif
							enum _UndeleteColumn
							{
								UC_ICON,
								UC_FILENAME
							};
							#define UC_LAST UC_FILENAME
							#define UC_COUNT (UC_LAST + 1)
							typedef struct _BrowserPlugin
							{
								BrowserPluginHelper * helper;
								guint source;
								/* refresh */
								char * selection;
								int fd;
								/* widgets */
								GtkWidget * window;
								GtkListStore * store;
								GtkWidget * view;
							} Undelete;
							/* prototypes */
							/* plug-in */
							static Undelete * _undelete_init(BrowserPluginHelper * helper);
							static void _undelete_destroy(Undelete * undelete);
							static GtkWidget * _undelete_get_widget(Undelete * undelete);
							static void _undelete_refresh(Undelete * undelete, GList * selection);
							/* useful */
							#if defined(__APPLE__) || defined(__linux__)
							static int getdents(int fd, char * buf, unsigned int count);
							#endif
							/* callbacks */
							static gboolean _undelete_on_idle(gpointer data);
							/* public */
							/* variables */
							BrowserPluginDefinition plugin =
							{
								N_("Undelete"),
								"image-missing",
								NULL,
								_undelete_init,
								_undelete_destroy,
								_undelete_get_widget,
								_undelete_refresh
							};
							/* private */
							/* functions */
							/* undelete_init */
							static Undelete * _undelete_init(BrowserPluginHelper * helper)
							{
								Undelete * undelete;
								GtkCellRenderer * renderer;
								GtkTreeViewColumn * column;
								if((undelete = object_new(sizeof(*undelete))) == NULL)
									return NULL;
								undelete->helper = helper;
								undelete->source = 0;
								undelete->selection = NULL;
								undelete->fd = -1;
								undelete->window = gtk_scrolled_window_new(NULL, NULL);
								gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(undelete->window),
										GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
								undelete->store = gtk_list_store_new(UC_COUNT,
										GDK_TYPE_PIXBUF,	/* UC_ICON */
										G_TYPE_STRING);		/* UC_FILENAME */
								undelete->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
											undelete->store));
								gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(undelete->view), TRUE);
								/* icon */
								renderer = gtk_cell_renderer_pixbuf_new();
								column = gtk_tree_view_column_new_with_attributes(NULL, renderer,
										"pixbuf", UC_ICON, NULL);
								gtk_tree_view_append_column(GTK_TREE_VIEW(undelete->view), column);
								/* filename */
								renderer = gtk_cell_renderer_text_new();
								column = gtk_tree_view_column_new_with_attributes(_("Filename"),
										renderer, "text", UC_FILENAME, NULL);
								gtk_tree_view_append_column(GTK_TREE_VIEW(undelete->view), column);
								gtk_container_add(GTK_CONTAINER(undelete->window), undelete->view);
								gtk_widget_show_all(undelete->window);
								return undelete;
							}
							/* undelete_destroy */
							static void _undelete_destroy(Undelete * undelete)
							{
								if(undelete->source != 0)
									g_source_remove(undelete->source);
								if(undelete->fd >= 0)
									close(undelete->fd);
								string_delete(undelete->selection);
								object_delete(undelete);
							}
							/* undelete_get_widget */
							static GtkWidget * _undelete_get_widget(Undelete * undelete)
							{
								return undelete->window;
							}
							/* undelete_refresh */
							static void _undelete_refresh(Undelete * undelete, GList * selection)
							{
								if(undelete->source != 0)
									g_source_remove(undelete->source);
								if(undelete->fd >= 0)
									close(undelete->fd);
								undelete->fd = -1;
								string_delete(undelete->selection);
								undelete->selection = NULL;
								if(selection == NULL
										|| selection->data == NULL
										|| (undelete->selection = string_new(selection->data))
										== NULL)
									undelete->source = 0;
								else
									undelete->source = g_idle_add(_undelete_on_idle, undelete);
							}
							/* useful */
							#if defined(__APPLE__)
							static int getdents(int fd, char * buf, unsigned int count)
							{
								(void) fd;
								(void) buf;
								(void) count;
								errno = ENOSYS;
								return -1;
							}
							#elif defined(__linux__)
							static int getdents(int fd, char * buf, unsigned int count)
							{
								return syscall(__NR_getdents64, fd, buf, count);
							}
							#endif
							/* callbacks */
							/* undelete_on_idle */
							static gboolean _undelete_on_idle(gpointer data)
							{
								Undelete * undelete = data;
								char buf[65536];
								int i;
								int j;
								UndeleteDirent de;
								size_t namlen;
								if(undelete->fd < 0)
								{
									if((undelete->fd = open(undelete->selection, O_RDONLY)) < 0)
									{
										undelete->helper->error(undelete->helper->browser,
												undelete->selection, 1);
										undelete->source = 0;
										return FALSE;
									}
									return TRUE;
								}
								if((i = getdents(undelete->fd, buf, sizeof(buf))) < 0)
									undelete->helper->error(undelete->helper->browser,
											undelete->selection, 1);
								else if(i > 0)
								{
									for(j = 0; j + (int)sizeof(de) < i; j += de.d_reclen)
									{
										memcpy(&de, &buf[j], sizeof(de));
							#ifdef __linux__
										namlen = de.d_reclen - 2
											- offsetof(UndeleteDirent, d_name);
							#else
										namlen = de.d_namlen;
							#endif
										if(strncmp(de.d_name, ".", 1) == 0 && namlen == 1)
											continue;
										if(strncmp(de.d_name, "..", 2) == 0 && namlen == 2)
											continue;
										/* FIXME implement */
									}
									return TRUE;
								}
								close(undelete->fd);
								string_delete(undelete->selection);
								undelete->selection = NULL;
								undelete->source = 0;
								return FALSE;
							}
							