/*
 * Program mkisofs.c - generate iso9660 filesystem  based upon directory
 * tree on hard disk.

   Written by Eric Youngdale (1993).

   Copyright 1993 Yggdrasil Computing, Incorporated

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "mkisofs.h"

#ifdef linux
#include <getopt.h>
#endif

#include "iso9660.h"
#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#ifndef AMIGA
#include <sys/stat.h>
#endif

#ifndef VMS
#ifndef AMIGA
#include <unistd.h>
#endif
#endif

#ifdef AMIGA
#ifdef LATTICE
#include <proto/exec.h>
#include <proto/utility.h>
#endif
#if defined(__GNUC__) || defined(AZTEC_C)
#include <clib/exec_protos.h>
#include <clib/utility_protos.h>
#endif
#endif

struct Library *UtilityBase = NULL;

#include "exclude.h"

struct directory * root = NULL;

static char version_string[] = "$VER: mkisofs-1.00-Amiga 1.5 (23.06.94)";

FILE * discimage;
unsigned int next_extent = 0;
unsigned int last_extent = 0;
unsigned int path_table_size = 0;
unsigned int path_table[4] = {0,};
unsigned int path_blocks = 0;
struct iso_directory_record root_record;
int use_RockRidge = 0;
int verbose = 0;
int all_files  = 0;
int follow_links = 0;
int generate_tables = 0;
static int timezone_offset;
char * extension_record = NULL;
int extension_record_extent = 0;
static  int extension_record_size = 0;
int cdtv_trademark_file = 0;

int convert_filenames = 1;
int inhibit_relocation = 0;
int sort_extents = 0;
int progress_indicator = 1;
int short_filenames = 1;
int map_filenames = 1;
int preallocate = 0;

char * preparer = NULL;
char * publisher = NULL;
char * system_identifier = NULL;


char * path_table_l = NULL;
char * path_table_m = NULL;
int goof = 0;

void usage(void){
  fprintf(stderr,
    "Usage:\n"
    "mkisofs [-RvaTcres12AB] [-o outfile] [-V volid] [-p preparer]\n"
    "        [-P publisher] [-S systemid] [-D path -D path ...]\n"
    "        [-x path -x path ...] [--cdtv] path\n"
    );
  exit(1);
}

int get_iso9660_timezone_offset(void){
#ifdef AMIGA
  return 0;
#else
  struct tm gm;
  struct tm * pt;
  time_t ctime;
  int local_min, gmt_min;

  time(&ctime);
  pt = gmtime(&ctime);
  gm = *pt;
  pt = localtime(&ctime);

  if(gm.tm_year < pt->tm_year)
    gm.tm_yday = -1;

  if(gm.tm_year > pt->tm_year)
    pt->tm_yday = -1;

  gmt_min = gm.tm_min + 60*(gm.tm_hour + 24*gm.tm_yday);
  local_min = pt->tm_min + 60*(pt->tm_hour + 24*pt->tm_yday);
  return (gmt_min - local_min)/15;
#endif
}


/* Fill in date in the iso9660 format */
int FDECL2(iso9660_date,char *, result, time_t, ctime){
  struct tm *local;
#ifdef AMIGA
  local = gmtime(&ctime);
#else
  local = localtime(&ctime);
#endif
  result[0] = local->tm_year;
  result[1] = local->tm_mon + 1;
  result[2] = local->tm_mday;
  result[3] = local->tm_hour;
  result[4] = local->tm_min;
  result[5] = local->tm_sec;
  result[6] = timezone_offset;
  return 0;
}

int FDECL3(iso9660_file_length,const char*, name, struct directory_entry *, sresult, 
			int, dirflag){
  char buf[33];
  char *result = buf;
  const char * pnt;
#if 0
  int seen_dot = 0;
  int priority = 32767;
  int tildes = 0;
  int ignore = 0;
  int extra = 0;
  int chars_after_dot = 0;
  int chars_before_dot = 0;
#endif

  if(strcmp(name,".") == 0){
    sresult->isorec.iso_name = "";
    return 1;
  };

  if(strcmp(name,"..") == 0){
    sresult->isorec.iso_name = "\1";
    return 1;
  };

  if (!convert_filenames) {
    int i;
    for (pnt=name, i=0; *pnt && i < 30; i++)
      *result++ = *pnt++;
    if (!dirflag) {
      *result++ = ';';
      *result++ = '1';
    }
    *result++ = 0;
    sresult->isorec.iso_name = strdup_forever (buf);
    return i+(dirflag?0:2);
  }

  sresult->isorec.iso_name =
    strdup_forever (dirflag ? convert_dirname (name) :
                    convert_filename (name));
  sresult->priority = 32767;
  return (int) strlen (sresult->isorec.iso_name);

#if 0

  pnt = name;
  while(*pnt){
    if(*pnt == '#') {priority = 1; pnt++; continue; };
    if(*pnt == '~') {priority = 1; tildes++; pnt++; continue;};
    if(ignore) {pnt++; continue;};
    if(*pnt == '.') {
      if (seen_dot) {ignore++; continue;}
      *result++ = '.';
      seen_dot++;
    } else if (seen_dot) {
      if(chars_after_dot < 3) {
	chars_after_dot++;
	*result++ = toupper(*pnt);
      }
    } else {
      if(chars_before_dot < 8) {
	chars_before_dot++;
	*result++ = toupper(*pnt);
      };
    };
    pnt++;
  };
  
  if(tildes == 2){
    int prio1 = 0;
    pnt = name;
    while (*pnt && *pnt != '~') pnt++;
    if (*pnt) pnt++;
    while(*pnt && *pnt != '~'){
      prio1 = 10*prio1 + *pnt - '0';
      pnt++;
    };
    priority = prio1;
  };
    
  if (!dirflag){
    if (!seen_dot) {
      *result++ = '.'; 
      extra++;
    };
    *result++ = ';';
    *result++ = '1';
    extra += 2;
  };
		    
  *result++ = 0;
  sresult->priority = priority;
  sresult->isorec.iso_name = strdup_forever (buf);
  return chars_before_dot + chars_after_dot + seen_dot + extra;
#endif
}

#ifdef AMIGA
static void Cleanup (void)
{
  if (UtilityBase)
    CloseLibrary (UtilityBase);
}
#endif

int FDECL2(main, int, argc, char **, argv){
  char * outfile, *volid;
  struct directory_entry de;
#ifndef AMIGA
  unsigned int mem_start;
  struct stat statbuf;
#endif
  char * scan_tree;
  int c;
  char * trademark_filename;

  if (argc < 2)
    usage();

#ifdef AMIGA
  UtilityBase = OpenLibrary ((UBYTE*) "utility.library", 0);
  if (!UtilityBase) {
    fprintf (stderr, "cannot open utility.library!\n");
    exit (10);
  }
  atexit (Cleanup);
#endif

  outfile = NULL;
  volid = "CDROM";
  while ((c = getopt(argc, argv, "acefo:p:rsvx:ABCD:P:RS:TV:-:12")) != EOF)
                                 
    switch (c)
      {
      case '-':
        if (strcmp (optarg, "cdtv") == 0) {
	  convert_filenames = 0;
	  cdtv_trademark_file = 1;
	  inhibit_relocation = 1;
	  system_identifier = "CDTV";
	  all_files++;
	} else {
	  fprintf (stderr, "unknown option --%s\n", optarg);
	  exit (1);
	}
	break;
      case 'c':
        convert_filenames = 0;
        break;
      case 'C':
        cdtv_trademark_file = 1;
        break;
      case 'D':
        convert_filenames = 0;
	include_conv (optarg);
        break;
      case 'r':
        inhibit_relocation = 1;
	break;
      case 'e':
        sort_extents = 1;
        break;
      case 's':
        preallocate = 1;
        break;
      case 'p':
	preparer = optarg;
	if(strlen(preparer) > 128) {
		fprintf(stderr,"Preparer string too long\n");
		exit(1);
	};
	break;
      case 'P':
	publisher = optarg;
	if(strlen(publisher) > 128) {
		fprintf(stderr,"Publisher string too long\n");
		exit(1);
	};
	break;
      case 'S':
        system_identifier = optarg;
	if(strlen(system_identifier) > 32) {
		fprintf(stderr,"System identifier string too long\n");
		exit (1);
	}
	break;
      case 'o':
	outfile = optarg;
	break;
      case 'f':
	follow_links++;
	break;
      case 'R':
	use_RockRidge++;
	break;
      case 'V':
#ifdef AMIGA
	if (strlen (optarg) > 30) {
	  fprintf (stderr, "volume name too long (max. 30 characters)\n");
	  exit (1);
	}
#endif
	volid = optarg;
	break;
      case 'v':
	verbose++;
	progress_indicator=0;
	break;
      case 'a':
	all_files++;
	break;
      case 'T':
	generate_tables++;
	break;
      case 'x':
        exclude(optarg);
	break;
      case '1':
        short_filenames = 1;
	break;
      case '2':
        short_filenames = 0;
	break;
      case 'A':
        map_filenames = 1;
	break;
      case 'B':
        map_filenames = 0;
	break;
      default:
	usage();
	exit(1);
      }

#ifndef AMIGA
  mem_start = (unsigned int) sbrk(0);
#endif

  if (volid && convert_filenames && map_filenames &&
      strspn (volid, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_") !=
      strlen (volid))
    fprintf (stderr, "WARNING: volume id contains illegal characters!\n");

  if(verbose) fprintf(stderr,"%s\n", version_string + 6);
  /* Now find the timezone offset */

  timezone_offset = get_iso9660_timezone_offset();

  /*  The first step is to scan the directory tree, and take some notes */

  scan_tree = argv[optind];

  if(!scan_tree){
	  usage();
	  exit(1);
  }

  if(scan_tree[strlen(scan_tree)-1] != '/'
#ifdef AMIGA
     && scan_tree[strlen(scan_tree)-1] != ':'
#endif
    ) {
    scan_tree = (char *) malloc(strlen(argv[optind])+2);
    strcpy(scan_tree, argv[optind]);
    strcat(scan_tree, "/");
  }

  /* Test if trademark file is present: */
  if (cdtv_trademark_file) {
    struct stat sbuf;
    trademark_filename = (char *) malloc (strlen (scan_tree) + 10);
    strcpy (trademark_filename, scan_tree);
    strcat (trademark_filename, "cdtv.tm");
    if (stat (trademark_filename, &sbuf) == -1) {
      fprintf (stderr, "ERROR: file %s missing\n", trademark_filename);
      exit (1);
    }
    free (trademark_filename);
  }

  if(use_RockRidge){
	extension_record = generate_rr_extension_record("RRIP_1991A",
				       "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS",
				       "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE.  SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION.", &extension_record_size);
  }

#ifndef AMIGA
  stat(argv[optind], &statbuf);
  add_directory_hash(statbuf.st_dev, statbuf.st_ino);
#endif

  de.filedir = root;  /* We need this to bootstrap */
  if (!scan_directory_tree(argv[optind], &de))
    exit (1);
  if (progress_indicator)
    putchar ('\n');
  root->self = root->contents;  /* Fix this up so that the path tables get done right */

  if(reloc_dir) sort_n_finish(reloc_dir);

  if (goof) exit(1);
  
  if (outfile){
	  discimage = fopen(outfile, "w");
	  if (!discimage){
		  fprintf(stderr,"Unable to open disc image file\n");
		  exit(1);

	  }
  } else
	  discimage =  stdout;

  /* Now assign addresses on the disc for the path table. */

  path_blocks = (path_table_size + (SECTOR_SIZE - 1)) >> 11;
  if (path_blocks & 1) path_blocks++;
  path_table[0] = 0x14;
  path_table[1] = path_table[0] + path_blocks;
  path_table[2] = path_table[1] + path_blocks;
  path_table[3] = path_table[2] + path_blocks;

  last_extent = path_table[3] + path_blocks;  /* The next free block */

  /* The next step is to go through the directory tree and assign extent
     numbers for all of the directories */

  if (verbose)
    printf ("assigning directory addresses\n");

  assign_directory_addresses(root);

  if (progress_indicator)
    putchar ('\n');

  if(extension_record) {
	  struct directory_entry * s_entry;
	  extension_record_extent = last_extent++;
	  s_entry = root->contents;
	  set_733(s_entry->rr_attributes + s_entry->rr_attr_size - 24,
		  extension_record_extent);
	  set_733(s_entry->rr_attributes + s_entry->rr_attr_size - 8,
		  extension_record_size);
  };

  if (use_RockRidge && reloc_dir)
	  finish_cl_pl_entries();

  if (verbose)
    printf ("generating path tables\n");

  /* Now we generate the path tables that are used by DOS to improve directory
     access times. */
  generate_path_tables();

  /* Generate root record for volume descriptor. */
  generate_root_record();

  if (verbose)
    dump_tree(root);

  iso_write(discimage, volid);

#ifndef AMIGA
  fprintf(stderr,"Max brk space used %x\n", 
	  ((unsigned int)sbrk(0)) - mem_start);
#endif
  fprintf(stderr,"%d extents written (%d Mb)\n", last_extent, last_extent >> 9);

  return 0;
}

