//
// Copyright (c) 1997 Jean-Marc Zucconi
// All rights reserved.
//
// 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
//    in this position and unchanged.
// 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.
// 3. The name of the author may not be used to endorse or promote products
//    derived from this software without specific prior written permission
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sys/filio.h>
#include <signal.h>

#include <c++tk.h>

#include "cd-write.h"
#include "iso9660.h"

static  ButtonBox bbox;

#define R "-relief flat -anchor w "
static int a_opt = ISO9660_a, d_opt = ISO9660_d, D_opt = ISO9660_D ,
    f_opt = ISO9660_f, l_opt = ISO9660_l, L_opt = ISO9660_L, 
    N_opt = ISO9660_N, R_opt = ISO9660_R, r_opt = ISO9660_r, T_opt = ISO9660_T;

static LabelEntry E_out, E_tree, E_BootImage, E_BootCatalog;
static LabelEntry E_app, E_vol, E_prep, E_pub, E_excl;
static ComboBox C_exclude;

static DirSelectDialog dirsel;
static FileSelectDialog filesel;
static char *output_file = 0, *tree = 0;
static char *app_id = 0, *vol_id = 0, *preparer = 0, *publisher = 0;
static char *boot_image = 0, *boot_catalog = 0;

static int building = 0;

static char mkisofs_program[1024] = {0};
static int button;

static void
mkisofs ()
{
#define MAXARGS 1200
    char *a[MAXARGS]; 
    int i = 0, p[2];
    int pid;
    static char *s1 = 0, *s2 = 0;
    char *q, *qq;

    if (a_opt)
	a[i++] = "-a";
    if (d_opt)
	a[i++] = "-d";
    if (D_opt)
	a[i++] = "-D";
    if (f_opt)
	a[i++] = "-f";
    if (L_opt)
	a[i++] = "-L";
    if (l_opt)
	a[i++] = "-l";
    if (N_opt)
	a[i++] = "-N";
    if (R_opt)
	a[i++] = "-R";
    if (r_opt)
	a[i++] = "-r";
    if (T_opt)
	a[i++] = "-T";
    if (preparer && *preparer) {
	a[i++] = "-p";
	a[i++] = preparer;
    }
    if (app_id && *app_id) {
	a[i++] = "-A";
	a[i++] = app_id;
    }
    if (vol_id && *vol_id) {
	a[i++] = "-V";
	a[i++] = vol_id;
    }
    if (publisher && *publisher) {
	a[i++] = "-P";
	a[i++] = publisher;
    }
    if (s1)
	free (s1);
    s1 = q = strdup (E_excl.subwidget ("entry", "get"));
    while (qq = strtok (q, " \t")) {
	q = 0;
	a[i++] = "-m";
	a[i++] = qq;
    }

    if (boot_image) {
	a[i++] = "-b";
	a[i++] = boot_image;
    }
    if (boot_catalog) {
	a[i++] = "-c";
	a[i++] = boot_catalog;
    }

    if (s2)
	free (s2);
    s2 = q = strdup (C_exclude.subwidget ("listbox", "get 0 end"));
    while (qq = strtok (q, " \t")) {
	q = 0;
	a[i++] = "-x";
	a[i++] = qq;
    }

    a[i++] = "-o";
    a[i++] = output_file;
    a[i++] = "-v";
    a[i++] = tree;
    a[i] = 0;

    if (pipe (p) < 0) {
	perror ("pipe");
	exit (1);
    }
    start_info ();
    pid = fork ();
    if (pid < 0) {
	perror ("fork");
	exit (1);
    }
   
    w_active |= ISO_ACTIVE;
    bbox.subwidget ("cancel", "configure -text Cancel");
    if (pid == 0) {
	close (p[0]);
	dup2 (p[1], 2);
	execv (mkisofs_program, a);
    } else {
	char buf[1000];
	fd_set s;
	close (p[1]);
	i = 1;
	if (ioctl (p[0], FIONBIO, &i) < 0) {
	    perror ("ioctl (FIONBIO)");
	    exit (1);
	}
	FD_ZERO (&s);
	FD_SET (p[0], &s);
	while (1) {
	    if (select (p[0]+1, &s, 0, 0, 0) < 0) {
		perror ("select");
		exit (1);
	    }
	    i = read (p[0], buf, sizeof(buf));
	    if (i <= 0) 
		break;
	    Info (buf, i);
	    TkUpdate ();
	    if (building > 1) {
		kill(pid, SIGKILL);
		break;
	    }
	}
	close (p[0]);
	if (wait (&i) < 0) {
	    perror ("wait");
	    exit (1);
	}
	Info ("\n", 1);
	if (WIFEXITED (i)) {
	    i = WEXITSTATUS (i);
	    if (i)
		sprintf (buf, "Process exited with status code %d", i);
	    else
		sprintf (buf, "Process exited normally");
	} else if (WIFSIGNALED (i)) {
	    sprintf (buf, "Process terminated by signal %d", WTERMSIG (i));
	    unlink (output_file);
	} else 
	    sprintf (buf, "Process stopped by signal %d", WIFSTOPPED (i));
	Info (buf, strlen (buf));
    }
    end_info ();
    bbox.subwidget ("cancel", "configure -text Dismiss");
    w_active &= ~ISO_ACTIVE;
}
static int
relpath (char **p)
{
    char *s = *p;
    int l = strlen (tree);
    while (*s && (*s == ' ' || *s == '\t'))
	s++;
    if (!*s) {
	free (*p);
	*p = 0;
	return 0;
    }
    if (strncmp (tree, s, l))
	return 1;
    strcpy (*p, s+l+1);
    return 0;
}
static int
too_long (char *s, int l, char *p)
{
    char m[100];

    if (strlen (s) <= l)
	return 0;
    sprintf (m, "String too long (%s)\nThe length is limited to %d chars", p, l);
    Error (m);
    return 1;
}
static void
ret (int argc, char **argv)
{
    char *p;
    if (**argv == '0') {
	if (building) {
	    building++;
	    return;
	}
	TkDestroy (".iso9660");
	return;
    }
    if (building)
	return;
    if (output_file)
	free (output_file);
    p = output_file = strdup (E_out.subwidget ("entry", "get"));
    while (*p && (*p == ' '))
	p++;
    if (!*p) {
	Error ("No output file specified");
	return;
    }
    if (tree)
	free (tree);
    p = tree = strdup (E_tree.subwidget ("entry", "get"));
    while (*p && (*p == ' '))
	p++;
    if (!*p) {
	Error ("No path specified");
	return;
    }
    if (preparer)
	free (preparer);
    preparer = strdup (E_prep.subwidget ("entry", "get"));
    if (too_long (preparer, 128, "Preparer"))
	return;

    if (app_id)
	free (app_id);
    app_id = strdup (E_app.subwidget ("entry", "get"));
    if (too_long (app_id, 128, "Application ID"))
	return;

    if (vol_id)
	free (vol_id);
    vol_id = strdup (E_vol.subwidget ("entry", "get"));
    if (too_long (vol_id, 32, "Volume ID"))
	return;

    if (publisher)
	free (publisher);
    publisher = strdup (E_pub.subwidget ("entry", "get"));
    if (too_long (publisher, 128, "Publisher"))
	return;

    if (boot_image)
	free (boot_image);
    p = boot_image = strdup (E_BootImage.subwidget ("entry", "get"));
    if (relpath (&boot_image)) {
	Error ("Invalid path for boot image");
	return;
    }

    if (boot_catalog)
	free (boot_catalog);
    p = boot_catalog = strdup (E_BootCatalog.subwidget ("entry", "get"));
    if (relpath (&boot_catalog)) {
	Error ("Invalid path for boot catalog");
	return;
    }
    if ((boot_catalog && !boot_image) || (!boot_catalog && boot_image)) {
	Error ("You must specify a boot image AND\n\
a boot catalog to make a bootable CD.");
	return;
    }

    building = 1;
    mkisofs ();
    building = 0;
}
static void
select_dir (int argc, char **argv)
{
    char *s = strdup (*argv);
    E_tree.subwidget ("entry", "delete 0 end");
    E_tree.subwidget ("entry insert end", s);
    free (s);
}
static void
select_file (int argc, char **argv)
{
    char c = **argv;
    char *s = strdup (*++argv);
    switch (c) {
    case 'I':
	E_BootImage.subwidget ("entry", "delete 0 end");
	E_BootImage.subwidget ("entry insert end", s);
	break;
    case 'C':
	E_BootCatalog.subwidget ("entry", "delete 0 end");
	E_BootCatalog.subwidget ("entry insert end", s);
	break;
    default:
	E_out.subwidget ("entry", "delete 0 end");
	E_out.subwidget ("entry insert end", s);
    }
    free (s);
}
static void
browse (int argc, char **argv)
{
    if (**argv == 'D') {
	if (*TkWinfo ("exists", ".dirsel") == '0')
	    dirsel = tixDirSelectDialog (".dirsel");
	dirsel.command (select_dir);
	dirsel.popup ();
    } else {
	if (*TkWinfo ("exists", ".filesel") == '0')
	    filesel = tixFileSelectDialog (".filesel");
	filesel.command (select_file, *argv);
	filesel.popup ();
    }
}
static void
fileselect (int argc, char **argv)
{
    strcpy (mkisofs_program, argv[0]);
}
static void
setbutton (int argc, char **argv)
{
    button = argv[0][0] - '0';
}
static int 
mkisofs_find ()
{
    button = -1;

    TkToplevel (".mkisofs");
    TkLabel (".mkisofs.l", "-wraplength 6i -justify left -text {\n\
I don't know where is ``mkisofs'' and MKISOFS is not set in your environment.\n\
}");
    ExFileSelectBox2 B = tixExFileSelectBox2 (".mkisofs.sel");
    B.command (fileselect);
    bbox = tixButtonBox (".mkisofs.b -orientation horizontal");
    bbox.add ("ok",  "-text Select");
    bbox.add ("cancel",  "-text Cancel");
    bbox.subwidget ("ok")->command (setbutton, "1");
    bbox.subwidget ("cancel")->command (setbutton, "0");

    TkPack (".mkisofs.l .mkisofs.sel .mkisofs.b ", "-side top -pady 2m -fill x");

    TkWait (&button);
    TkDestroy (".mkisofs");
    if (!*mkisofs_program || !button) {
	*mkisofs_program = 0;
	return 0;
    }
    return 1;
}
void
create_iso9660 (int argc, char **argv)
{

    if (*TkWinfo ("exists", ".iso9660") == '1')
	return;

    if (!*mkisofs_program) {
	struct stat st;
	char *p = getenv ("MKISOFS");
	if (p)
	    strcpy (mkisofs_program, p);
	else
	    strcpy (mkisofs_program, MKISOFS);
	if (stat (mkisofs_program, &st) < 0)
	    if (!mkisofs_find ())
		return;
    }

    TkToplevel (".iso9660");

    Balloon balloon = tixBalloon (".iso9660.balloon");
    TkFrame (".iso9660.d -border 1 -relief raised");
    E_out = tixLabelEntry (".iso9660.d.e", "-label {Output file:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    Button B = TkButton (".iso9660.d.browse", "-text {Browse...}");
    B.command (browse, "F");
    TkPack (".iso9660.d.e -side left -expand yes -fill x -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.d.browse -side left -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.d", "-side top -fill x -pady 8");

    TkFrame (".iso9660.t -border 1 -relief raised");
    E_tree = tixLabelEntry (".iso9660.t.e", "-label {Path:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    B = TkButton (".iso9660.t.browse", "-text {Browse ...}");
    B.command (browse, "D");
    TkPack (".iso9660.t.e -side left -expand yes -fill x -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.t.browse -side left -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.t", "-side top -fill x -pady 8");

    E_app = tixLabelEntry (".iso9660.app", "-label {Application ID:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    balloon.bind (".iso9660.app -balloonmsg", app_help);
    TkPack (".iso9660.app", "-side top -fill x");

    E_vol = tixLabelEntry (".iso9660.vol", "-label {Volume ID:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    balloon.bind (".iso9660.vol -balloonmsg", vol_help);
    TkPack (".iso9660.vol", "-side top -fill x");

    E_prep = tixLabelEntry (".iso9660.prep", "-label {Preparer:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    balloon.bind (".iso9660.prep -balloonmsg", prep_help);
    TkPack (".iso9660.prep", "-side top -fill x");

    E_pub = tixLabelEntry (".iso9660.pub", "-label {Publisher:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    balloon.bind (".iso9660.pub -balloonmsg", pub_help);
    TkPack (".iso9660.pub", "-side top -fill x");

    C_exclude = tixComboBox (".iso9660.excl", "-label {Exclude paths:}\
                  -dropdown true -editable 1 -labelside top -history 1 \
                   -historylimit 1000");
    balloon.bind (".iso9660.excl -balloonmsg", x_help);
    TkPack (".iso9660.excl", "-side top -fill x");

    E_excl = tixLabelEntry (".iso9660.glob", "-label {Exclude patterns:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    balloon.bind (".iso9660.glob -balloonmsg", m_help);
    TkPack (".iso9660.glob", "-side top -fill x");

    TkFrame (".iso9660.bopt -border 1 -relief raised");
    E_BootImage = tixLabelEntry (".iso9660.bopt.e", "-label {Boot image:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    B = TkButton (".iso9660.bopt.browse", "-text {Browse ...}");
    B.command (browse, "I");
    TkPack (".iso9660.bopt.e -side left -expand yes -fill x -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.bopt.browse -side left -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.bopt", "-side top -fill x -pady 8");
    balloon.bind (".iso9660.bopt.e -balloonmsg", b_help);

    TkFrame (".iso9660.c -border 1 -relief raised");
    E_BootCatalog = tixLabelEntry (".iso9660.c.e", "-label {Boot catalog:} -labelside top -options {\n\
	    entry.width 55\n\
	    label.anchor w }");
    B = TkButton (".iso9660.c.browse", "-text {Browse ...}");
    B.command (browse, "C");
    TkPack (".iso9660.c.e -side left -expand yes -fill x -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.c.browse -side left -anchor s -padx 4 -pady 1");
    TkPack (".iso9660.c", "-side top -fill x -pady 8");
    balloon.bind (".iso9660.c.e -balloonmsg", c_help);

    Checkbutton b = TkCheckbutton (".iso9660.a_opt", R"-text {include all files}");
    b.variable (&a_opt);
    balloon.bind (".iso9660.a_opt -balloonmsg", a_help);

    b = TkCheckbutton (".iso9660.d_opt", R"-text {omit trailing period}");
    b.variable (&d_opt);
    balloon.bind (".iso9660.d_opt -balloonmsg", d_help);

    b = TkCheckbutton (".iso9660.dd_opt", R"-text {do not use deep directory relocation}");
    b.variable (&D_opt);
    balloon.bind (".iso9660.dd_opt -balloonmsg", D_help);

    b = TkCheckbutton (".iso9660.f_opt", R"-text {follow  symbolic links}");
    b.variable (&f_opt);
    balloon.bind (".iso9660.f_opt -balloonmsg", f_help);

    b = TkCheckbutton (".iso9660.l_opt", R"-text {allow full 32 character  filenames}");
    b.variable (&l_opt);
    balloon.bind (".iso9660.l_opt -balloonmsg", l_help);

    b = TkCheckbutton (".iso9660.ll_opt", R"-text {allow  filenames to begin with a period}");
    b.variable (&L_opt);
    balloon.bind (".iso9660.ll_opt -balloonmsg", L_help);

    b = TkCheckbutton (".iso9660.nn_opt", R"-text {omit version numbers}");
    b.variable (&N_opt);
    balloon.bind (".iso9660.nn_opt -balloonmsg", N_help);

    b = TkCheckbutton (".iso9660.rr_opt", R"-text {use Rock Ridge extensions}");
    b.variable (&R_opt);
    balloon.bind (".iso9660.rr_opt -balloonmsg", R_help);

    b = TkCheckbutton (".iso9660.r_opt", R"-text {reset ownership and file mode}");
    b.variable (&R_opt);
    balloon.bind (".iso9660.r_opt -balloonmsg", r_help);

    b = TkCheckbutton (".iso9660.tt_opt", R"-text {generate TRANS.TBL files}");
    b.variable (&T_opt);
    balloon.bind (".iso9660.tt_opt -balloonmsg", T_help);

    TkPack (".iso9660.a_opt .iso9660.d_opt \
             .iso9660.dd_opt .iso9660.f_opt .iso9660.l_opt .iso9660.ll_opt \
             .iso9660.nn_opt .iso9660.rr_opt .iso9660.r_opt .iso9660.tt_opt",
	    "-side top -pady 2 -fill x");

    bbox = tixButtonBox (".iso9660.b -orientation horizontal");
    bbox.add ("go",  "-text GO");
    bbox.add ("cancel",  "-text Dismiss");
    bbox.subwidget ("go")->command (ret, "1");
    bbox.subwidget ("cancel")->command (ret, "0");
    TkPack (" .iso9660.b", "-side top -fill x");
}
 
