/*
 * iso9660.h
 *
 * Routines and data structures to support reading and writing
 * ISO 9660 CD images. See the ISO 9660 or ECMA 119 standards.
 *
 * Also supports Rock Ridge extensions for long file names and Unix stuff.
 * Also supports Microsoft's Joliet extensions for Unicode and long file names.
 * Also supports El Torito bootable CD spec.
 */

typedef struct Cdimg Cdimg;
typedef struct Cdinfo Cdinfo;
typedef struct Conform Conform;
typedef struct Direc Direc;
typedef struct Dumproot Dumproot;
typedef struct Voldesc Voldesc;
typedef struct XDir XDir;

#ifndef CHLINK
#define CHLINK 0
#endif

struct XDir {
	char	*name;
	char	*uid;
	char	*gid;
	char	*symlink;
	ulong   uidno;   /* Numeric uid */
	ulong   gidno;   /* Numeric gid */

	ulong	mode;
	ulong	atime;
	ulong	mtime;
	ulong   ctime;

        vlong   length;
};

/*
 * A directory entry in a ISO9660 tree.
 * The extra data (uid, etc.) here is put into the system use areas.
 */
struct Direc {
	char *name;	/* real name */
	char *confname;	/* conformant name */
	char *srcfile;	/* file to copy onto the image */

	ulong block;
	ulong length;
	int flags;

	char *uid;
	char *gid;
	char *symlink;
	ulong mode;
	long atime;
	long ctime;
	long mtime;

	ulong uidno;
	ulong gidno;

	Direc *child;
	int nchild;
};
enum {  /* Direc flags */
	Dbadname = 1<<0  /* Non-conformant name     */
};

/*
 * Data found in a volume descriptor.
 */
struct Voldesc {
	char *systemid;
	char *volumeset;
	char *publisher;
	char *preparer;
	char *application;

	/* file names for various parameters */
	char *abstract;
	char *biblio;
	char *notice;

	/* path table */
	ulong pathsize;
	ulong lpathloc;
	ulong mpathloc;

	/* root of file tree */
	Direc root;	
};

/*
 * An ISO9660 CD image.  Various parameters are kept in memory but the
 * real image file is opened for reading and writing on fd.
 *
 * The bio buffers brd and bwr moderate reading and writing to the image.
 * The routines we use are careful to flush one before or after using the other,
 * as necessary.
 */
struct Cdimg {
	char *file;
	int fd;
	ulong dumpblock;
	ulong nextblock;
	ulong iso9660pvd;
	ulong jolietsvd;
	ulong pathblock;
	ulong rrcontin; /* rock ridge continuation offset */
	ulong nulldump;	/* next dump block */
	ulong nconform;	/* number of conform entries written already */
	ulong bootcatptr;
	ulong bootcatblock;
	ulong bootimageptr;
	Direc *bootdirec;
	char *bootimage;
	
	Biobuf brd;
	Biobuf bwr;

	int flags;

	Voldesc iso;
	Voldesc joliet;
};
enum {	/* Cdimg->flags, Cdinfo->flags */
	CDjoliet = 1<<0,
	CDplan9 = 1<<1,
	CDconform = 1<<2,
	CDrockridge = 1<<3,
	CDnew = 1<<4,
	CDdump = 1<<5,
	CDbootable = 1<<6
};

typedef struct Tx Tx;
struct Tx {
	char *bad;	/* atoms */
	char *good;
};

struct Conform {
	Tx *t;
	int nt;	/* delta = 32 */
};

struct Cdinfo {
	int flags;

	char *volumename;

	char *volumeset;
	char *publisher;
	char *preparer;
	char *application;
	char *bootimage;
};

enum {
	Blocklen = 2048
};

/*
 * This is a doubly binary tree.
 * We have a tree keyed on the MD5 values
 * as well as a tree keyed on the block numbers.
 */
typedef struct Dump Dump;
typedef struct Dumpdir Dumpdir;

struct Dump {
	Cdimg *cd;
	Dumpdir *md5root;
	Dumpdir *blockroot;
};

struct Dumpdir {
	char *name;
	uchar md5[MD5dlen];
	ulong block;
	ulong length;
	Dumpdir *md5left;
	Dumpdir *md5right;
	Dumpdir *blockleft;
	Dumpdir *blockright;
};

struct Dumproot {
	char *name;
	int nkid;
	Dumproot *kid;
	Direc root;
	Direc jroot;
};

/*
 * ISO9660 on-CD structures.
 */
typedef struct Cdir Cdir;
typedef struct Cpath Cpath;
typedef struct Cvoldesc Cvoldesc;

/* a volume descriptor block */
struct Cvoldesc {
	uchar	magic[8];	/* 0x01, "CD001", 0x01, 0x00 */
	uchar	systemid[32];	/* system identifier */
	uchar	volumeid[32];	/* volume identifier */
	uchar	unused[8];	/* character set in secondary desc */
	uchar	volsize[8];	/* volume size */
	uchar	charset[32];
	uchar	volsetsize[4];	/* volume set size = 1 */
	uchar	volseqnum[4];	/* volume sequence number = 1 */
	uchar	blocksize[4];	/* logical block size */
	uchar	pathsize[8];	/* path table size */
	uchar	lpathloc[4];	/* Lpath */
	uchar	olpathloc[4];	/* optional Lpath */
	uchar	mpathloc[4];	/* Mpath */
	uchar	ompathloc[4];	/* optional Mpath */
	uchar	rootdir[34];	/* directory entry for root */
	uchar	volumeset[128];	/* volume set identifier */
	uchar	publisher[128];
	uchar	preparer[128];	/* data preparer identifier */
	uchar	application[128];	/* application identifier */
	uchar	notice[37];	/* copyright notice file */
	uchar	abstract[37];	/* abstract file */
	uchar	biblio[37];	/* bibliographic file */
	uchar	cdate[17];	/* creation date */
	uchar	mdate[17];	/* modification date */
	uchar	xdate[17];	/* expiration date */
	uchar	edate[17];	/* effective date */
	uchar	fsvers;		/* file system version = 1 */
};

/* a directory entry */
struct Cdir {
	uchar	len;
	uchar	xlen;
	uchar	dloc[8];
	uchar	dlen[8];
	uchar	date[7];
	uchar	flags;
	uchar	unitsize;
	uchar	gapsize;
	uchar	volseqnum[4];
	uchar	namelen;
	uchar	name[1];	/* chumminess */
};

/* a path table entry */
struct Cpath {
	uchar   namelen;
	uchar   xlen;
	uchar   dloc[4];
	uchar   parent[2];
	uchar   name[1];        /* chumminess */
};

enum { /* Rockridge flags */
	RR_PX = 1<<0,
	RR_PN = 1<<1,
	RR_SL = 1<<2,
	RR_NM = 1<<3,
	RR_CL = 1<<4,
	RR_PL = 1<<5,
	RR_RE = 1<<6,
	RR_TF = 1<<7
};

enum { /* CputrripTF type argument */
	TFcreation = 1<<0,
	TFmodify = 1<<1,
	TFaccess = 1<<2,
	TFattributes = 1<<3,
	TFbackup = 1<<4,
	TFexpiration = 1<<5,
	TFeffective = 1<<6,
	TFlongform = 1<<7
};

enum { /* CputrripNM flag types */
	NMcontinue = 1<<0,
	NMcurrent = 1<<1,
	NMparent = 1<<2,
	NMroot = 1<<3,
	NMvolroot = 1<<4,
	NMhost = 1<<5
};

/* boot.c */
void Cputbootvol(Cdimg*);
void Cputbootcat(Cdimg*);
void Cupdatebootvol(Cdimg*);
void Cupdatebootcat(Cdimg*);
void findbootimage(Cdimg*, Direc*);

/* cdrdwr.c */
Cdimg *createcd(char*, Cdinfo);
Cdimg *opencd(char*, Cdinfo);
void Creadblock(Cdimg*, void*, ulong, ulong);
ulong big(void*, int);
ulong little(void*, int);
int parsedir(Cdimg*, Direc*, uchar*, int, char *(*)(uchar*, int));
void setroot(Cdimg*, ulong, ulong, ulong);
void setvolsize(Cdimg*, ulong, ulong);
void setpathtable(Cdimg*, ulong, ulong, ulong, ulong);
void Cputc(Cdimg*, int);
void Cputnl(Cdimg*, ulong, int);
void Cputnm(Cdimg*, ulong, int);
void Cputn(Cdimg*, long, int);
void Crepeat(Cdimg*, int, int);
void Cputs(Cdimg*, char*, int);
void Cwrite(Cdimg*, void*, int);
void Cputr(Cdimg*, Rune);
void Crepeatr(Cdimg*, Rune, int);
void Cputrs(Cdimg*, Rune*, int);
void Cputrscvt(Cdimg*, char*, int);
void Cpadblock(Cdimg*);
void Cputdate(Cdimg*, ulong);
void Cputdate1(Cdimg*, ulong);
void Cread(Cdimg*, void*, int);
void Cwflush(Cdimg*);
void Cwseek(Cdimg*, ulong);
ulong Cwoffset(Cdimg*);
ulong Croffset(Cdimg*);
int Cgetc(Cdimg*);
void Crseek(Cdimg*, ulong);
char *Crdline(Cdimg*, int);
int Clinelen(Cdimg*);

/* conform.c */
void rdconform(Cdimg*);
char *conform(char*, int);
void wrconform(Cdimg*, int, ulong*, ulong*);

/* direc.c */
void mkdirec(Direc*, XDir*);
Direc *walkdirec(Direc*, char*);
Direc *adddirec(Direc*, char*, XDir*);
void copydirec(Direc*, Direc*);
void checknames(Direc*, int (*)(char*));
void convertnames(Direc*, char* (*)(char*, char*));
void dsort(Direc*, int (*)(const void*, const void*));
void setparents(Direc*);

/* dump.c */
ulong Cputdumpblock(Cdimg*);
int hasdump(Cdimg*);
Dump *dumpcd(Cdimg*, Direc*);
Dumpdir *lookupmd5(Dump*, uchar*);
void insertmd5(Dump*, char*, uchar*, ulong, ulong);

Direc readdumpdirs(Cdimg*, XDir*, char*(*)(uchar*,int));
char *adddumpdir(Direc*, ulong, XDir*);
void copybutname(Direc*, Direc*);

void readkids(Cdimg*, Direc*, char*(*)(uchar*,int));
void freekids(Direc*);
void readdumpconform(Cdimg*);
void rmdumpdir(Direc*, char*);

/* ichar.c */
char *isostring(uchar*, int);
int isbadiso9660(char*);
int isocmp(const void*, const void*);
int isisofrog(char);
void Cputisopvd(Cdimg*, Cdinfo);

/* jchar.c */
char *jolietstring(uchar*, int);
int isbadjoliet(char*);
int jolietcmp(const void*, const void*);
int isjolietfrog(Rune);
void Cputjolietsvd(Cdimg*, Cdinfo);

/* path.c */
void writepathtables(Cdimg*);

/* util.c */
void *emalloc(ulong);
void *erealloc(void*, ulong);
char *atom(char*);
char *struprcpy(char*, char*);
int chat(char*, ...);

/* unix.c, plan9.c */
void dirtoxdir(XDir*, Dir*);
void fdtruncate(int, ulong);
long uidno(char*);
long gidno(char*);

/* rune.c */
Rune *strtorune(Rune*, char*);
Rune *runechr(Rune*, Rune);
int runecmp(Rune*, Rune*);

/* sysuse.c */
int Cputsysuse(Cdimg*, Direc*, int, int, int);

/* write.c */
void writefiles(Dump*, Cdimg*, Direc*);
void writedirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int));
void writedumpdirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int));
int Cputisodir(Cdimg*, Direc*, int, int, int);
int Cputjolietdir(Cdimg*, Direc*, int, int, int);
void Cputendvd(Cdimg*);

enum { 
	Blocksize = 2048,
	Ndirblock = 16,		/* directory blocks allocated at once */

	DTdot = 0,
	DTdotdot,
	DTiden,
	DTroot,
	DTrootdot
};

extern ulong now;
extern Conform *map;
extern int chatty;
extern int docolon;
extern int mk9660;
