/******************************* LICENCE **************************************
* Any code in this file may be redistributed or modified under the terms of
* the GNU General Public Licence as published by the Free Software 
* Foundation; version 2 of the licence.
****************************** END LICENCE ***********************************/

/******************************************************************************
* Author:
* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php
*
* Contributors:
* 
******************************************************************************/

/********************************* PURPOSE ************************************
* bk.h
* This header file is the public interface to bkisofs.
******************************** END PURPOSE *********************************/

#ifndef bk_h
#define bk_h

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <limits.h>
#include <sys/timeb.h>
#include <stdio.h>

#include "bkError.h"

#ifdef WINDOWS_BUILD
    /* on windows i can't get an off_t to be 64 bits */
    typedef long long bk_off_t;
    #define bk_lseek _lseeki64
    typedef struct _stati64 BkStatStruct;
#else
    typedef off_t bk_off_t;
    #define bk_lseek lseek
    typedef struct stat BkStatStruct;
#endif

/* can be |ed */
#define FNTYPE_9660 1
#define FNTYPE_ROCKRIDGE 2
#define FNTYPE_JOLIET 4

/* many library functions rely on this being at least 256 */
#define NCHARS_FILE_ID_MAX_STORE 256

/* maximum length of the target of a symbolic link
* !! this is used for both the number of characters in the path and the number
* of bytes in the SL record, that should probably be fixed */
#define NCHARS_SYMLINK_TARGET_MAX 251

/* maximum number of bytes to read from a file for comparing it quickly
* with others (is it likely to be a hard link or not) */
#define MAX_NBYTES_HARDLINK_HEAD 32

/* options for VolInfo.bootMediaType */
#define BOOT_MEDIA_NONE 0
#define BOOT_MEDIA_NO_EMULATION 1
#define BOOT_MEDIA_1_2_FLOPPY 2 /* 1228800 byte floppy disk image */
#define BOOT_MEDIA_1_44_FLOPPY 3 /* 1474560 byte floppy disk image */
#define BOOT_MEDIA_2_88_FLOPPY 4 /* 2949120 byte floppy disk image */
#define BOOT_MEDIA_HARD_DISK 5

#define READ_WRITE_BUFFER_SIZE 102400

/* warning message string lengths in VolInfo */
#define BK_WARNING_MAX_LEN 512

#define IS_DIR(posix)      ((posix & 0770000) == 0040000)
#define IS_REG_FILE(posix) ((posix & 0770000) == 0100000)
#define IS_SYMLINK(posix)  ((posix & 0770000) == 0120000)

#define BK_BASE_PTR(item) ((BkFileBase*)(item))
#define BK_DIR_PTR(item) ((BkDir*)(item))
#define BK_FILE_PTR(item) ((BkFile*)(item))
#define BK_SYMLINK_PTR(item) ((BkSymLink*)(item))

/*******************************************************************************
* BkFileBase
* Linked list node.
* All files, directories, links need this. */
typedef struct BkFileBase
{
    char original9660name[15]; /* 8.3 + ";1" max */
    char name[NCHARS_FILE_ID_MAX_STORE]; /* '\0' terminated */
    unsigned posixFileMode; /* file type and permissions */
    
    struct BkFileBase* next;
    
} BkFileBase;

/*******************************************************************************
* BkDir
* Linked list node.
* Information about a directory and it's contents. */
typedef struct BkDir
{
    BkFileBase base; /* intended to be accessed using a cast */
    
    BkFileBase* children; /* child directories, files, etc. */
    
} BkDir;

/*******************************************************************************
* BkHardLink
* Linked list node.
* Information about a hard link (where to find a certain file).
* This is for internal use but is defined here because BkFile references it.
* You don't need to use this structure, please ignore it. */
typedef struct BkHardLink
{
    bool onImage;
    bk_off_t position; /* if on image */
    char* pathAndName; /* if on filesystem, full path + filename
                       * is to be freed whenever the BkHardLink is freed */
    unsigned size; /* size of the file being pointed to */
    int headSize;
    unsigned char head[MAX_NBYTES_HARDLINK_HEAD];
    bool alreadyCounted; /* for estimateIsoSize() */
    
    unsigned extentNumberWrittenTo; /* only set once one file is written */
    
    struct BkHardLink* next;
    
} BkHardLink;

/*******************************************************************************
* BkFile
* Linked list node.
* Information about a file, whether on the image or on the filesystem. */
typedef struct BkFile
{
    BkFileBase base; /* intended to be accessed using a cast */
    
    unsigned size; /* in bytes, don't need bk_off_t because it's stored 
                   * in a 32bit unsigned int on the iso */
    BkHardLink* location; /* basically a copy of the following variables */
    bool onImage;
    bk_off_t position; /* if on image, in bytes */
    char* pathAndName; /* if on filesystem, full path + filename
                       * is to be freed whenever the File is freed */
    
} BkFile;

/*******************************************************************************
* BkSymLink
* Linked list node.
* Information about a symbolic link. */
typedef struct BkSymLink
{
    BkFileBase base; /* intended to be accessed using a cast */
    
    char target[NCHARS_SYMLINK_TARGET_MAX];
    
} BkSymLink;

/*******************************************************************************
* VolInfo
* Information about a volume (one image).
* Strings are '\0' terminated. */
typedef struct VolInfo
{
    /* private bk use  */
    unsigned filenameTypes;
    bk_off_t pRootDrOffset; /* primary (9660 and maybe rockridge) */
    bk_off_t sRootDrOffset; /* secondary (joliet), 0 if does not exist */
    bk_off_t bootRecordSectorNumberOffset;
    int imageForReading;
    ino_t imageForReadingInode; /* to know which file was open for reading
                                * (filename is not reliable) */
    const BkFile* bootRecordOnImage; /* if visible, pointer to the file in the 
                                     *  directory tree */
    char warningMessage[BK_WARNING_MAX_LEN];
    bool rootRead; /* did i read the root record inside volume descriptor? */
    bool stopOperation; /* cancel current opertion */
    int imageForWriting;
    void(*progressFunction)(struct VolInfo*);
    void(*writeProgressFunction)(struct VolInfo*, double);
    struct timeb lastTimeCalledProgress;
    bk_off_t estimatedIsoSize;
    BkHardLink* fileLocations; /* list of where to find regular files */
    char readWriteBuffer[READ_WRITE_BUFFER_SIZE];
    char readWriteBuffer2[READ_WRITE_BUFFER_SIZE];
    
    /* public use, read only */
    time_t creationTime;
    BkDir dirTree;
    unsigned char bootMediaType;
    unsigned bootRecordSize;       /* in bytes */
    bool bootRecordIsOnImage;      /* unused if visible (flag below) */
    bk_off_t bootRecordOffset;     /* if on image */
    char* bootRecordPathAndName;   /* if on filesystem */
    bool bootRecordIsVisible;      /* whether boot record is a visible file 
                                   *  on the image */
    bool scanForDuplicateFiles;    /* whether to check every file for uniqueness
                                   * to decide is it a hard link or not */
    
    /* public use, read/write */
    char volId[33];
    char publisher[129];
    char dataPreparer[129];
    unsigned posixFileDefaults;    /* for extracting */
    unsigned posixDirDefaults;     /* for extracting or creating on iso */
    bool(*warningCbk)(const char*);
    bool followSymLinks;           /* whether to stat the link itself rather 
                                   *  than the file it's pointing to */
    
} VolInfo;

/* public bkisofs functions */

/* adding */
int bk_add_boot_record(VolInfo* volInfo, const char* srcPathAndName, 
                       int bootMediaType);
int bk_add(VolInfo* volInfo, const char* srcPathAndName, 
           const char* destPathStr, void(*progressFunction)(VolInfo*));
int bk_add_as(VolInfo* volInfo, const char* srcPathAndName, 
              const char* destPathStr, const char* nameToUse, 
              void(*progressFunction)(VolInfo*));
int bk_create_dir(VolInfo* volInfo, const char* destPathStr, 
                  const char* newDirName);

/* deleting */
void bk_delete_boot_record(VolInfo* volInfo);
int bk_delete(VolInfo* volInfo, const char* pathAndName);

/* extracting */
int bk_extract_boot_record(VolInfo* volInfo, const char* destPathAndName,
                           unsigned destFilePerms);
int bk_extract(VolInfo* volInfo, const char* srcPathAndName, 
               const char* destDir, bool keepPermissions, 
               void(*progressFunction)(VolInfo*));
int bk_extract_as(VolInfo* volInfo, const char* srcPathAndName, 
                  const char* destDir, const char* nameToUse,
                  bool keepPermissions, void(*progressFunction)(VolInfo*));

/* getters */
bk_off_t bk_estimate_iso_size(const VolInfo* volInfo, int filenameTypes);
time_t bk_get_creation_time(const VolInfo* volInfo);
int bk_get_dir_from_string(const VolInfo* volInfo, const char* pathStr, 
                           BkDir** dirFoundPtr);
int bk_get_permissions(VolInfo* volInfo, const char* pathAndName, 
                       mode_t* permissions);
const char* bk_get_publisher(const VolInfo* volInfo);
const char* bk_get_volume_name(const VolInfo* volInfo);
const char* bk_get_error_string(int errorId);

/* setters */
void bk_cancel_operation(VolInfo* volInfo);
void bk_destroy_vol_info(VolInfo* volInfo);
int bk_init_vol_info(VolInfo* volInfo, bool scanForDuplicateFiles);
int bk_rename(VolInfo* volInfo, const char* srcPathAndName, 
              const char* newName);
int bk_set_boot_file(VolInfo* volInfo, const char* srcPathAndName);
void bk_set_follow_symlinks(VolInfo* volInfo, bool doFollow);
int bk_set_permissions(VolInfo* volInfo, const char* pathAndName, 
                       mode_t permissions);
int bk_set_publisher(VolInfo* volInfo, const char* publisher);
int bk_set_vol_name(VolInfo* volInfo, const char* volName);

/* reading */
int bk_open_image(VolInfo* volInfo, const char* filename);
int bk_read_dir_tree(VolInfo* volInfo, int filenameType, 
                     bool keepPosixPermissions, 
                     void(*progressFunction)(VolInfo*));
int bk_read_vol_info(VolInfo* volInfo);

/* writing */
int bk_write_image(const char* newImagePathAndName, VolInfo* volInfo, 
                   time_t creationTime, int filenameTypes, 
                   void(*progressFunction)(VolInfo*, double));

#ifdef __cplusplus
}
#endif

#endif
