source: trunk/third/nautilus-cd-burner/make-iso.c @ 21565

Revision 21565, 17.6 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21564, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3   make-iso.c: code to generate iso files
4 
5   Copyright (C) 2002-2004 Red Hat, Inc.
6 
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   General Public License for more details.
16 
17   You should have received a copy of the GNU General Public
18   License along with this program; if not, write to the
19   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.
21 
22   Authors: Alexander Larsson <alexl@redhat.com>
23*/
24
25#include "config.h"
26
27#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
28#include <sys/param.h>
29#include <sys/mount.h>
30#else
31#include <sys/vfs.h>
32#endif /* __FreeBSD__ || __NetBSD__ || __OpenBSD__ */
33#include <sys/types.h>
34#include <sys/stat.h>
35#ifdef HAVE_STATVFS
36#include <sys/statvfs.h>
37#endif
38#include <time.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43#include <fcntl.h>
44#include <signal.h>
45#include <libgnomevfs/gnome-vfs.h>
46#include <gtk/gtkmessagedialog.h>
47
48#include "nautilus-cd-burner.h"
49#include "make-iso.h"
50
51#ifndef HAVE_MKDTEMP
52#include "mkdtemp.h"
53#endif
54
55struct mkisofs_output {
56        GMainLoop *loop;
57        int result;
58        int pid;
59        const char *filename;
60        gboolean debug;
61};
62
63static struct mkisofs_output *mkisofs_output_ptr;
64
65void
66make_iso_cancel (void)
67{
68        if (mkisofs_output_ptr)
69        {
70                kill (mkisofs_output_ptr->pid, SIGINT);
71                unlink (mkisofs_output_ptr->filename);
72
73                g_main_loop_quit (mkisofs_output_ptr->loop);
74        }
75}
76
77static void
78write_all (int fd, char *buf, int len)
79{
80        int bytes;
81        int res;
82       
83        bytes = 0;
84        while (bytes < len) {
85                res = write (fd, buf + bytes, len - bytes);
86                if (res <= 0) {
87                        return;
88                }
89                bytes += res;
90        }
91        return;
92}
93
94static void
95copy_file (const char *source, const char *dest)
96{
97        int sfd, dfd;
98        struct stat stat_buf;
99        char buffer[1024*8];
100        ssize_t len;
101
102        if (link (source, dest) == 0) {
103                return;
104        }
105       
106        if (stat (source, &stat_buf) != 0) {
107                g_warning ("Trying to copy nonexisting file\n");
108                return;
109        }
110
111        sfd = open (source, O_RDONLY);
112        if (sfd == -1) {
113                g_warning ("Can't copy file (open source failed)\n");
114                return;
115        }
116       
117        dfd = open (dest, O_WRONLY | O_CREAT, stat_buf.st_mode);
118        if (dfd == -1) {
119                close (sfd);
120                g_warning ("Can't copy file (open dest '%s' failed)\n", dest);
121                perror ("error:");
122                return;
123        }
124
125        while ( (len = read (sfd, &buffer, sizeof (buffer))) > 0) {
126                write_all (dfd, buffer, len);
127        }
128        close (dfd);
129        close (sfd);
130}
131
132static char *
133escape_path (const char *str)
134{
135        char *escaped, *d;
136        const char *s;
137        int len;
138       
139        s = str;
140        len = 1;
141        while (*s != 0) {
142                if (*s == '\\' ||
143                    *s == '=') {
144                        len++;
145                }
146               
147                len++;
148                s++;
149        }
150       
151        escaped = g_malloc (len);
152       
153        s = str;
154        d = escaped;
155        while (*s != 0) {
156                if (*s == '\\' ||
157                    *s == '=') {
158                        *d++ = '\\';
159                }
160               
161                *d++ = *s++;
162        }
163        *d = 0;
164       
165        return escaped;
166}
167
168static gboolean
169dir_is_empty (const char *virtual_path)
170{
171        GnomeVFSFileInfo *info;
172        GnomeVFSDirectoryHandle *handle;
173        GnomeVFSResult result;
174        char *escaped_path, *uri;
175        gboolean found_file;
176       
177        escaped_path = gnome_vfs_escape_path_string (virtual_path);
178        uri = g_strconcat ("burn:///", escaped_path, NULL);
179        g_free (escaped_path);
180       
181        result = gnome_vfs_directory_open (&handle, uri, GNOME_VFS_FILE_INFO_DEFAULT);
182        if (result != GNOME_VFS_OK) {
183                g_free (uri);
184                return TRUE;
185        }
186       
187        info = gnome_vfs_file_info_new ();
188
189        found_file = FALSE;
190       
191        while (TRUE) {
192                result = gnome_vfs_directory_read_next (handle, info);
193                if (result != GNOME_VFS_OK)
194                        break;
195
196                /* Skip "." and "..".  */
197                if (info->name[0] == '.'
198                    && (info->name[1] == 0
199                        || (info->name[1] == '.' && info->name[2] == 0))) {
200                        gnome_vfs_file_info_clear (info);
201                        continue;
202                }
203               
204                found_file = TRUE;
205                break;
206        }
207
208        gnome_vfs_directory_close (handle);
209        gnome_vfs_file_info_unref (info);
210
211        return !found_file;
212}
213
214static char *
215get_backing_file (const char *virtual_path)
216{
217        GnomeVFSHandle *handle;
218        GnomeVFSResult res;
219        char *escaped_path, *uri;
220        char *mapping;
221
222        escaped_path = gnome_vfs_escape_path_string (virtual_path);
223        uri = g_strconcat ("burn:///", escaped_path, NULL);
224        g_free (escaped_path);
225        res = gnome_vfs_open (&handle,
226                              uri,
227                              GNOME_VFS_OPEN_READ);
228        g_free (uri);
229        if (res == GNOME_VFS_OK) {
230                res =  gnome_vfs_file_control (handle,
231                                               "mapping:get_mapping",
232                                               &mapping);
233                gnome_vfs_close (handle);
234                if (res == GNOME_VFS_OK) {
235                        return mapping;
236                }
237        }
238        return NULL;
239}
240
241static gboolean
242ask_disable_joliet (GtkWindow *parent)
243{
244        GtkWidget *dialog;
245        int res;
246
247        dialog = gtk_message_dialog_new (parent,
248                                         GTK_DIALOG_DESTROY_WITH_PARENT,
249                                         GTK_MESSAGE_QUESTION,
250                                         GTK_BUTTONS_OK_CANCEL,
251                                         _("Some files don't have a suitable name for a Windows-compatible CD.\nDo you want to continue with Windows compatibility disabled?"));
252        gtk_window_set_title (GTK_WINDOW (dialog), _("Windows compatibility"));
253        res = gtk_dialog_run (GTK_DIALOG (dialog));
254        gtk_widget_destroy (dialog);
255        return (res == GTK_RESPONSE_OK);
256}
257
258struct mkisofs_state {
259        FILE *graft_file;
260        char *emptydir;
261        int depth;
262        char *tmpdir;
263        char *copy_to_dir;
264        int copy_depth;
265        GList *remove_files;
266        gboolean found_file;
267};
268
269static void
270graft_file_end_dir_visitor (struct mkisofs_state *state)
271{
272        char *last_slash;
273       
274        if (state->copy_depth > 0) {
275                state->copy_depth--;
276                if (state->copy_depth == 0) {
277                        g_free (state->copy_to_dir);
278                        state->copy_to_dir = NULL;
279                } else {
280                        last_slash = strrchr (state->copy_to_dir, '/');
281                        if (last_slash != NULL) {
282                                *last_slash = 0;
283                        }
284                }
285        }
286}
287
288static gboolean
289graft_file_visitor (const gchar *rel_path,
290                    GnomeVFSFileInfo *info,
291                    struct mkisofs_state *state,
292                    gboolean *recurse)
293{
294        char *mapping, *path1, *path2;
295        char *new_copy_dir;
296        char *copy_path;
297
298        *recurse = TRUE;
299       
300        if (state->copy_to_dir != NULL) {
301                if (info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
302                        new_copy_dir = g_build_filename (state->copy_to_dir,
303                                                         info->name,
304                                                         NULL);
305                        g_free (state->copy_to_dir);
306                        state->copy_to_dir = new_copy_dir;
307                        mkdir (state->copy_to_dir, 0777);
308                        state->remove_files = g_list_prepend (state->remove_files, g_strdup (state->copy_to_dir));
309                        state->copy_depth++;
310                } else {
311                        copy_path = g_build_filename (state->copy_to_dir, info->name, NULL);
312                        mapping = get_backing_file (rel_path);
313                        if (mapping != NULL) {
314                                copy_file (mapping, copy_path);
315                                state->remove_files = g_list_prepend (state->remove_files, g_strdup (copy_path));
316                        }
317                }
318                return TRUE;
319        }
320       
321        if (info->type != GNOME_VFS_FILE_TYPE_DIRECTORY) {
322                mapping = get_backing_file (rel_path);
323                if (mapping != NULL) {
324                        path1 = escape_path (rel_path);
325                        path2 = escape_path (mapping);
326                        state->found_file = TRUE;
327                        fprintf (state->graft_file, "%s=%s\n", path1, path2);
328                        g_free (path1);
329                        g_free (path2);
330                        g_free (mapping);
331                }
332        } else {
333                if (dir_is_empty (rel_path)) {
334                        path1 = escape_path (rel_path);
335                        path2 = escape_path (state->emptydir);
336                        state->found_file = TRUE;
337                        fprintf (state->graft_file, "%s/=%s\n", path1, path2);
338                        g_free (path1);
339                        g_free (path2);
340                } else if (state->depth >= 6) {
341                        new_copy_dir = g_build_filename (state->tmpdir, "subdir.XXXXXX", NULL);
342                        copy_path = mkdtemp (new_copy_dir);
343                        if (copy_path != NULL) {
344                                state->remove_files = g_list_prepend (state->remove_files, g_strdup (copy_path));
345                                state->copy_depth = 1;
346                                state->copy_to_dir = copy_path;
347                                path1 = escape_path (rel_path);
348                                path2 = escape_path (copy_path);
349                                state->found_file = TRUE;
350                                fprintf (state->graft_file, "%s/=%s\n", path1, path2);
351                                g_free (path1);
352                                g_free (path2);
353                        } else {
354                                g_free (new_copy_dir);
355                                g_warning ("Couldn't create temp subdir\n");
356                                *recurse = FALSE;
357                        }
358                }
359        }
360
361        return TRUE;
362}
363       
364static void
365create_graft_file (GnomeVFSURI *uri,
366                   const gchar *prefix,
367                   struct mkisofs_state *state)
368{
369        GnomeVFSFileInfo *info;
370        GnomeVFSDirectoryHandle *handle;
371        GnomeVFSResult result;
372        gboolean stop;
373
374        result = gnome_vfs_directory_open_from_uri (&handle, uri, GNOME_VFS_FILE_INFO_DEFAULT);
375        if (result != GNOME_VFS_OK)
376                return;
377
378        info = gnome_vfs_file_info_new ();
379
380        stop = FALSE;
381        while (! stop) {
382                gchar *rel_path;
383                gboolean recurse;
384
385                result = gnome_vfs_directory_read_next (handle, info);
386                if (result != GNOME_VFS_OK)
387                        break;
388
389                /* Skip "." and "..".  */
390                if (info->name[0] == '.'
391                    && (info->name[1] == 0
392                        || (info->name[1] == '.' && info->name[2] == 0))) {
393                        gnome_vfs_file_info_clear (info);
394                        continue;
395                }
396
397                if (prefix == NULL)
398                        rel_path = g_strdup (info->name);
399                else
400                        rel_path = g_strconcat (prefix, info->name, NULL);
401
402                recurse = FALSE;
403                stop = ! graft_file_visitor (rel_path, info, state, &recurse);
404               
405                if (! stop
406                    && recurse
407                    && info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
408                        GnomeVFSURI *new_uri;
409                        gchar *new_prefix;
410
411                        if (prefix == NULL)
412                                new_prefix = g_strconcat (info->name, "/",
413                                                          NULL);
414                        else
415                                new_prefix = g_strconcat (prefix, info->name,
416                                                          "/", NULL);
417
418                        new_uri = gnome_vfs_uri_append_file_name (uri, info->name);
419
420                        state->depth++;
421                        create_graft_file (new_uri,
422                                           new_prefix,
423                                           state);
424                        state->depth--;
425                        graft_file_end_dir_visitor (state);
426
427                        gnome_vfs_uri_unref (new_uri);
428                        g_free (new_prefix);
429                }
430
431                g_free (rel_path);
432
433                gnome_vfs_file_info_clear (info);
434
435                if (stop)
436                        break;
437        }
438
439        gnome_vfs_directory_close (handle);
440        gnome_vfs_file_info_unref (info);
441}
442
443static gboolean 
444mkisofs_stdout_read (GIOChannel   *source,
445                     GIOCondition  condition,
446                     gpointer      data)
447{
448        struct mkisofs_output *mkisofs_output = data;
449        char *line;
450        GIOStatus status;
451       
452        status = g_io_channel_read_line (source,
453                                         &line, NULL, NULL, NULL);
454
455        if (line && mkisofs_output->debug) {
456                g_print ("make_iso stdout: %s", line);
457        }
458
459        if (status == G_IO_STATUS_NORMAL) {
460                g_free (line);
461        }
462        return TRUE;
463}
464
465static gboolean 
466mkisofs_stderr_read (GIOChannel   *source,
467                     GIOCondition  condition,
468                     gpointer      data)
469{
470        struct mkisofs_output *mkisofs_output = data;
471        char *line;
472        char fraction_str[7];
473        double fraction;
474        GIOStatus status;
475
476        status = g_io_channel_read_line (source,
477                                         &line, NULL, NULL, NULL);
478
479        if (line && mkisofs_output->debug) {
480                g_print ("make_iso stderr: %s", line);
481        }
482
483        if (status == G_IO_STATUS_NORMAL) {
484                if (strncmp (line, "Total translation table size", 28) == 0) {
485                        cd_progress_set_fraction (1.0);
486                        g_main_loop_quit (mkisofs_output->loop);
487                        mkisofs_output->result = RESULT_FINISHED;
488                }
489
490                if (strstr (line, "estimate finish")) {
491                        if (sscanf (line, "%6c%% done, estimate finish",
492                                                fraction_str) == 1) {
493                                fraction_str[6] = 0;
494                                fraction = g_strtod (fraction_str, NULL);
495                                cd_progress_set_fraction (fraction/100.0);
496                        }
497                }
498                if (strstr (line, "Incorrectly encoded string")) {
499                        cd_progress_set_fraction (1.0);
500                        g_main_loop_quit (mkisofs_output->loop);
501                        mkisofs_output->result = RESULT_ERROR;
502                }
503
504                g_free (line);
505        }
506        return TRUE;
507}
508
509/**
510 * Create an ISO image in filename from the data files in burn:///
511 */
512int
513make_iso (const char *filename, const char *label,
514                gboolean warn_low_space, gboolean use_joliet, gboolean debug)
515{
516        GnomeVFSURI *uri;
517        char *filelist = NULL;
518        struct mkisofs_state state = {NULL};
519        char *tempdir;
520        GList *l;
521        int stdout_pipe, stderr_pipe;
522        int i;
523        GError *error;
524        const char *argv[20]; /* Shouldn't need more than 20 arguments */
525        struct mkisofs_output mkisofs_output;
526        GIOChannel *channel;
527        guint stdout_tag, stderr_tag;
528        char *stdout_data, *stderr_data;
529        char *dirname;
530        int exit_status, res;
531        unsigned long iso_size;
532#ifdef HAVE_STATVFS
533        struct statvfs statfs_buf;
534#else
535        struct statfs statfs_buf;
536#endif
537        GtkWidget *dialog;
538
539        if (label) {
540                g_return_val_if_fail (strlen (label) < 32, RESULT_ERROR);
541        }
542
543        mkisofs_output.debug = debug;
544
545        dirname = g_strdup_printf ("iso-%s.XXXXXX", g_get_user_name ());
546        tempdir = g_build_filename (g_get_tmp_dir (), dirname, NULL);
547        g_free (dirname);
548        state.tmpdir = mkdtemp (tempdir);
549
550        mkisofs_output.result = RESULT_ERROR;
551
552        if (state.tmpdir == 0) {
553                g_warning ("Unable to create temp dir\n");
554                goto cleanup;
555        }
556       
557        state.emptydir = g_build_filename (state.tmpdir, "emptydir", NULL);
558        mkdir (state.emptydir, 0777);
559        state.remove_files = g_list_prepend (state.remove_files, g_strdup (state.emptydir));
560
561        filelist = g_build_filename (state.tmpdir, "filelist", NULL);
562
563        state.graft_file = fopen (filelist, "w");
564        if (state.graft_file == NULL) {
565                goto cleanup;
566        }
567       
568        uri = gnome_vfs_uri_new ("burn:///");
569        state.found_file = FALSE;
570        create_graft_file (uri, NULL, &state);
571        gnome_vfs_uri_unref (uri);
572
573        fclose (state.graft_file);
574        state.remove_files = g_list_prepend (state.remove_files, g_strdup (filelist));
575
576        if (!state.found_file) {
577                dialog = gtk_message_dialog_new (cd_progress_get_window (),
578                                                 GTK_DIALOG_DESTROY_WITH_PARENT,
579                                                 GTK_MESSAGE_ERROR,
580                                                 GTK_BUTTONS_CLOSE,
581                                                 _("No files selected to write to CD."));
582                gtk_window_set_title (GTK_WINDOW (dialog),
583                                      _("No files selected"));
584                gtk_dialog_run (GTK_DIALOG (dialog));
585                gtk_widget_destroy (dialog);
586                mkisofs_output.result = RESULT_ERROR;
587                goto cleanup;
588        }
589
590retry:
591        i = 0;
592        argv[i++] = "mkisofs";
593        argv[i++] = "-r";
594        if (use_joliet) {
595                argv[i++] = "-J";
596        }
597        /* Undocumented -input-charset option */
598        argv[i++] = "-input-charset";
599        argv[i++] = "utf8";
600        argv[i++] = "-q";
601        argv[i++] = "-graft-points";
602        argv[i++] = "-path-list";
603        argv[i++] = filelist;
604        argv[i++] = "-print-size";
605        argv[i++] = NULL;
606
607        error = NULL;
608
609        if (debug) {
610                g_print ("launching command: ");
611                for (i = 0; argv[i] != NULL; i++) {
612                        g_print ("%s ", argv[i]);
613                }
614                g_print ("\n");
615        }
616
617        if (!g_spawn_sync (NULL,
618                           (char **)argv,
619                           NULL,
620                           G_SPAWN_SEARCH_PATH,
621                           NULL, NULL,
622                           &stdout_data,
623                           &stderr_data,
624                           &exit_status,
625                           &error)) {
626                g_warning ("mkisofs command failed: %s\n", error->message);
627                g_error_free (error);
628                /* TODO: Better error handling */
629                mkisofs_output.result = RESULT_ERROR;
630                goto cleanup;
631        }
632
633        if (exit_status != 0 && use_joliet) {
634                if (strstr (stderr_data, "Joliet tree sort failed.") != NULL) {
635                        g_free (stdout_data);
636                        g_free (stderr_data);
637                        if (ask_disable_joliet (cd_progress_get_window ())) {
638                                use_joliet = FALSE;
639                                goto retry;
640                        } else {
641                                mkisofs_output.result = RESULT_ERROR;
642                                goto cleanup;
643                        }
644                }
645        }
646
647        g_free (stderr_data);
648        iso_size = atol (stdout_data); /* blocks of 2048 bytes */
649        g_free (stdout_data);
650
651        dirname = g_path_get_dirname (filename);
652#ifdef HAVE_STATVFS
653        res = statvfs (dirname, &statfs_buf);
654#else
655        res = statfs (dirname, &statfs_buf);
656#endif
657        if (res == -1) {
658                g_warning ("Cannot get free space at %s\n", dirname);
659                g_free (dirname);
660        } else if (iso_size / statfs_buf.f_bsize >= statfs_buf.f_bavail / 2048) {
661                g_free (dirname);
662                if (warn_low_space) {
663                        dialog = gtk_message_dialog_new (cd_progress_get_window (),
664                                                         GTK_DIALOG_DESTROY_WITH_PARENT,
665                                                         GTK_MESSAGE_ERROR,
666                                                         GTK_BUTTONS_CLOSE,
667                                                         ngettext("Not enough space to store CD image (%ld Megabyte needed)",
668                                                                  "Not enough space to store CD image (%ld Megabytes needed)",
669                                                                  iso_size / 512),
670                                                         iso_size / 512);
671                        gtk_window_set_title (GTK_WINDOW (dialog),
672                                              _("Not enough space"));
673                        gtk_dialog_run (GTK_DIALOG (dialog));
674                        gtk_widget_destroy (dialog);
675                        mkisofs_output.result = RESULT_ERROR;
676                } else {
677                        mkisofs_output.result = RESULT_RETRY;
678                }
679
680                goto cleanup;
681        }
682
683        i = 0;
684        argv[i++] = "mkisofs";
685        argv[i++] = "-r";
686        if (use_joliet) {
687                argv[i++] = "-J";
688        }
689        argv[i++] = "-input-charset";
690        argv[i++] = "utf8";
691        argv[i++] = "-graft-points";
692        argv[i++] = "-path-list";
693        argv[i++] = filelist;
694        if (label) {
695                argv[i++] = "-V";
696                argv[i++] = label;
697        }
698        argv[i++] = "-o";
699        argv[i++] = filename;
700        argv[i++] = NULL;
701
702        if (debug) {
703                g_print ("launching command: ");
704                for (i = 0; argv[i] != NULL; i++) {
705                        g_print ("%s ", argv[i]);
706                }
707                g_print ("\n");
708        }
709
710        cd_progress_set_text (_("Creating CD image"));
711        cd_progress_set_fraction (0.0);
712        cd_progress_set_image_spinning (TRUE);
713        error = NULL;
714        if (!g_spawn_async_with_pipes  (NULL,
715                                        (char **)argv,
716                                        NULL,
717                                        G_SPAWN_SEARCH_PATH,
718                                        NULL, NULL,
719                                        &mkisofs_output.pid,
720                                        /*stdin*/NULL,
721                                        &stdout_pipe,
722                                        &stderr_pipe,
723                                        &error)) {
724                g_warning ("mkisofs command failed: %s\n", error->message);
725                g_error_free (error);
726                mkisofs_output.result = RESULT_ERROR;
727                goto cleanup;
728        } else {
729                mkisofs_output.loop = g_main_loop_new (NULL, FALSE);
730       
731                channel = g_io_channel_unix_new (stdout_pipe);
732                g_io_channel_set_encoding (channel, NULL, NULL);
733                stdout_tag = g_io_add_watch (channel,
734                                             (G_IO_IN | G_IO_HUP | G_IO_ERR),
735                                             mkisofs_stdout_read,
736                                             &mkisofs_output);
737                g_io_channel_unref (channel);
738                channel = g_io_channel_unix_new (stderr_pipe);
739                g_io_channel_set_encoding (channel, NULL, NULL);
740                stderr_tag = g_io_add_watch (channel,
741                                             (G_IO_IN | G_IO_HUP | G_IO_ERR),
742                                             mkisofs_stderr_read,
743                                             &mkisofs_output);
744                g_io_channel_unref (channel);
745
746                mkisofs_output_ptr = &mkisofs_output;
747                mkisofs_output.filename = filename;
748               
749                g_main_loop_run (mkisofs_output.loop);
750                g_main_loop_unref (mkisofs_output.loop);
751               
752                g_source_remove (stdout_tag);
753                g_source_remove (stderr_tag);
754        }
755        mkisofs_output_ptr = NULL;
756
757 cleanup:
758        for (l = state.remove_files; l != NULL; l = l->next) {
759                remove ((char *)l->data);
760                g_free (l->data);
761        }
762        g_list_free (state.remove_files);
763       
764        g_free (filelist);
765        g_free (state.emptydir);
766        rmdir (tempdir);
767        g_free (tempdir);
768        cd_progress_set_image_spinning (FALSE);
769
770        return mkisofs_output.result;
771}
772
Note: See TracBrowser for help on using the repository browser.