/* * gnome-fileconvert.c: Routines for converting file formats * * Author: * Elliot Lee (sopwith@cuc.edu) * * Put in production by Miguel de Icaza. */ #include #include #include #include #include #include #include #include #include #include #include "gnome-defs.h" #include "gnome-fileconvert.h" #include "gnome-util.h" #include "libgnomeP.h" typedef gint Cost; #define UNREACHABLE 32000 /*MAXINT or something*/ #define NOCOST 0 #define MINCOST 1 #define IMPOSSIBLE_PATH NULL typedef struct _FileConverter FileConverter; typedef struct _FileType FileType; struct _FileConverter { FileType *fromtype; FileType *totype; gint cost; gchar *cmdline; }; struct _FileType { gchar *name; GList *arcs; gint cost; gint done; FileConverter *best_way_here; }; static GList *gfc_get_path(const char *fromtype, const char *totype); static gint gfc_run_pipe(gchar *acmd, gint infd); /** * gnome_file_convert: * @filename: file to convert * @fromtype: mime type of the file * @totype: target mime type we want * * Converts @filename to the @totype format. * * Returns -1 on failure, or a file descriptor to the converted * file. */ gint gnome_file_convert (const char *filename, const char *fromtype, const char *totype) { gint fd = open (filename, O_RDONLY); if (fd >= 0) return gnome_file_convert_fd (fd, fromtype, totype); else return -1; } /** * gnome_file_convert_fd: * @fd: file desciptor pointing to the file to convert. * @fromtype: mime type of the file * @totype: target mime type we want * * Converts the file opened by @fd to the @totype format. * * Returns -1 on failure, or a file descriptor to the converted * file. */ gint gnome_file_convert_fd (gint fd, const char *fromtype, const char *totype) { GList *convlist, *l; gint infd, outfd; FileConverter *converter; convlist = gfc_get_path (fromtype, totype); if (!convlist) return -1; for (l = convlist, infd = fd; l; l = l->next){ if (!(converter = l->data)){ infd = -1; break; } #ifdef GNOME_ENABLE_DEBUG g_print("%s %s: %s\n", converter->fromtype->name, converter->totype->name, converter->cmdline); #endif if (converter->cmdline == NULL) continue; outfd = gfc_run_pipe (converter->cmdline, infd); if (infd != fd) close (infd); infd = outfd; } g_list_free (convlist); return infd; } /* Probably could use gnome_config routines for this, as soon as miguel redoes it */ static void load_types_from (GHashTable *file_types, char *fname) { gchar aline[512]; gchar **parts; FILE *conffile; FileConverter *newarc; FileType *fromtype, *totype; conffile = fopen (fname, "r"); if (!conffile) return; while (fgets(aline, sizeof(aline), conffile)){ g_strchomp (aline); if (aline[0] == '#' || aline[0] == '\0') continue; parts = g_strsplit (aline, " ", 3); if (! (fromtype = g_hash_table_lookup(file_types, parts[0]))) { fromtype = g_new(FileType, 1); fromtype->name = parts[0]; fromtype->arcs = NULL; g_hash_table_insert(file_types, fromtype->name, fromtype); } if (! (totype = g_hash_table_lookup(file_types, parts[1]))) { totype = g_new(FileType, 1); totype->name = parts[1]; totype->arcs = NULL; g_hash_table_insert(file_types, totype->name, totype); } newarc = g_new(FileConverter, 1); newarc->fromtype = fromtype; newarc->totype = totype; newarc->cost = 1; /* replace this with something from the config file */ if(newarc->cost < MINCOST) /* non-positive costs cause infinite loops */ newarc->cost = MINCOST; newarc->cmdline = parts[2]; fromtype->arcs = g_list_prepend(fromtype->arcs, newarc); g_free(parts); } fclose(conffile); } static GHashTable * gfc_read_FileConverters(void) { char *file, *dirname; GHashTable *file_types; DIR *dir; struct dirent *dent; const int extlen = sizeof (".convert") - 1; file_types = g_hash_table_new(g_str_hash, g_str_equal); dirname = gnome_unconditional_datadir_file ("type-convert"); dir = opendir (dirname); if (dir){ while ((dent = readdir (dir)) != NULL){ int len = strlen (dent->d_name); if (len <= extlen) continue; if (strcmp (dent->d_name + len - extlen, ".convert")) continue; file = g_concat_dir_and_file (dirname, dent->d_name); load_types_from (file_types, file); g_free (file); } } g_free (dirname); file = g_strconcat (gnome_user_dir, "type.convert", NULL); load_types_from (file_types, file); g_free (file); return file_types; } /* Used as callback from g_hash_table_foreach */ static void gfc_reset_path(gpointer key, FileType *node, gpointer userdata) { node->best_way_here = NULL; node->cost = UNREACHABLE; } /* Calculates shortest paths from from_node using Dijkstra's algorithm. * Returns list of converters to get from from_node to to_node. * The queue is just a GList but that'll be OK for these small graphs. */ static GList * gfc_shortest_path (GHashTable *file_types, FileType *from_node, FileType *to_node) { GList *nodes_to_do, *best, *best_route=NULL, *tmp; FileType *node, *current_node; FileConverter *arc; Cost new_cost; /* Reset all the path data */ g_hash_table_foreach(file_types, (GHFunc)gfc_reset_path, NULL); /* Start with from_node */ from_node->cost = NOCOST; from_node->best_way_here = NULL; nodes_to_do = g_list_append(NULL, from_node); /* Find shortest paths */ while(nodes_to_do) { /* Find cheapest remaining reachable node */ best = NULL; for(tmp = nodes_to_do; tmp; tmp = tmp->next) if(!best || ((FileType *)tmp->data)->cost < ((FileType *)best->data)->cost) best = tmp; current_node = ((FileType *)best->data); nodes_to_do = g_list_remove_link(nodes_to_do, best); g_list_free(best); /* if to_node is found we are done, retrace the path here and return it*/ if(current_node == to_node) { for(arc = to_node->best_way_here; arc; arc = arc->fromtype->best_way_here) best_route = g_list_prepend(best_route, arc); return best_route; } /* Explore all converters available from current_node */ for(tmp = current_node->arcs; tmp; tmp = tmp->next) { arc = tmp->data; new_cost = current_node->cost + arc->cost; node = arc->totype; if(new_cost < node->cost) { if(node->cost == UNREACHABLE) nodes_to_do = g_list_prepend(nodes_to_do, node); node->cost = new_cost; node->best_way_here = arc; } } } /* no route to to_node found */ return IMPOSSIBLE_PATH; } /* Get the list of converters required to convert fromtype to totype */ static GList * gfc_get_path (const char *fromtype, const char *totype) { static GHashTable *file_types; static gboolean read_datfile = FALSE; FileType *from_node, *to_node; if(!read_datfile) { file_types = gfc_read_FileConverters(); read_datfile = TRUE; } /* Look up the start and goal types */ from_node = (FileType *) g_hash_table_lookup(file_types, fromtype); to_node = (FileType *) g_hash_table_lookup(file_types, totype); /* Give up if asked for an unknown file type */ if(from_node == NULL || to_node == NULL) return IMPOSSIBLE_PATH; return gfc_shortest_path(file_types, from_node, to_node); } static gint gfc_run_pipe (gchar *acmd, gint infd) { gchar **parts; gint childpid; gint fds[2]; if (pipe (fds)) return -1; childpid = fork (); if (childpid < 0) return -1; if (childpid){ close (fds [1]); waitpid (childpid, &childpid, 0); return fds [0]; } /* else */ parts = g_strsplit (acmd, " ", -1); dup2 (infd, 0); dup2 (fds [1], 1); dup2 (fds [1], 2); if (fds [1] > 2) close (fds [1]); if (infd > 2) close (infd); if (fork()) /* Double-forking is good for the (zombified) soul ;-) */ exit (0); else execvp (parts [0], parts); /* ERROR IF REACHED */ close (0); close (1); exit (69); }