/*
   GSPLUS - Advanced Apple IIGS Emulator Environment
   Based on the KEGS emulator written by Kent Dickey
   See COPYRIGHT.txt for Copyright information
   See LICENSE.txt for license (GPL v2)
 */

#include "defc.h"
#include <ctype.h>

#include "prodos.h"

#include <errno.h>

#if defined(__APPLE__)
#include <sys/xattr.h>
#endif

#ifdef _MSC_VER
#define strcasecmp stricmp
#define strncasecmp strnicmp
#endif

#define DEF_DISK_SIZE   (800*1024)
#define MAX_FILE_NAMES  51

char out_name[] = "POOF1";

int g_def_file_type = 4;
int g_def_aux_type = 0;
int g_crlf = 0;

void make_legal_prodos_name(char *new_name, char *old_name);

static int hex(byte c) {
  if (c >= '0' && c <= '9') return c - '0';
  if (c >= 'a' && c <= 'f') return c + 10 - 'a';
  if (c >= 'A' && c <= 'F') return c + 10 - 'A';
  return 0;
}

void get_file_type(const char *name, int *file_type, int *aux_type) {
#if defined(__APPLE__)
  unsigned char buffer[32];
  ssize_t size = getxattr(name, XATTR_FINDERINFO_NAME, buffer, 32, 0, 0);
  if (size >= 16) {

    if (!memcmp("pdos", buffer + 4, 4))
    {
      if (buffer[0] == 'p')
      {
        *file_type = buffer[1];
        *aux_type = (buffer[2] << 8) | buffer[3];
        return;
      }
      if (!memcmp("PSYS", buffer, 4))
      {
        *file_type = 0xff;
        *aux_type = 0x0000;
        return;
      }
      if (!memcmp("PS16", buffer, 4))
      {
        *file_type = 0xb3;
        *aux_type = 0x0000;
        return;
      }

      if (!isxdigit(buffer[0]) && isxdigit(buffer[1]) && buffer[2] == ' ' && buffer[3] == ' ')
      {
        *file_type = (hex(buffer[0]) << 8) | hex(buffer[1]);
        *aux_type = 0;
        return;
      }
    }
    if (!memcmp("TEXT", buffer, 4))
    {
      *file_type = 0x04;
      *aux_type = 0x0000;
      return;
    }
  }

#endif


  char *cp = strrchr(name, '.');
  if (cp) {
    if (strcasecmp(cp, ".SDK") == 0) {
      *file_type = 0xe0;
      *aux_type = 0x8002;
      return;
    }
    if (!strcasecmp(cp, ".SHK")) {
      *file_type = 0xe0;
      *aux_type = 0x8002;
      return;
    }
  }
  *file_type = g_def_file_type;
  *aux_type = g_def_aux_type;
}

int main(int argc, char **argv)     {
  char    *files[MAX_FILE_NAMES];
  char    *name;
  struct stat stat_buf;
  int in;
  int in_size;
  int ret;
  ProDisk *disk;
  char new_name[128];
  byte in_buf[1024];
  int disk_size;
  int i;
  int done;
  int pos;
  int size;
  int num_files;
  int file_type;
  int aux_type;

  disk_size = DEF_DISK_SIZE;
  if(argc < 2) {
    fprintf(stderr, "%s: {-[size in K]} {unix_files}\n",
            argv[0]);
    exit(1);
  }

  num_files = 0;
  for(i = 1; i < argc; i++) {
    if(argv[i][0] == '-') {

      if(!strcmp(argv[i], "-cr")) {
        g_crlf = 1;
      } else if(!strcmp(argv[i], "-txt")) {
        g_def_file_type = 4;
        g_def_aux_type = 0;
      } else if(!strcmp(argv[i], "-bin")) {
        g_def_file_type = 6;
        g_def_aux_type = 0;
      } else {
        /* I smell a -size_in_K */
        char *end = NULL;
        disk_size = strtoul(&(argv[i][1]), &end, 10) * 1024;
        if (*end) {
          printf("Invalid size: %s\n", argv[i]+1);
          exit(1);
        }
        printf("disk_size: %d, 0x%x\n", disk_size, disk_size);
        if(disk_size < 40*1024) {
          printf("Too small!\n");
          exit(1);
        }
        if(disk_size > 32 * 1024 * 1024) {
          printf("Too big!\n");
          exit(1);
        }
      }
    } else {
      files[num_files] = argv[i];
      num_files++;
      if(num_files >= MAX_FILE_NAMES) {
        printf("Too many filenames: %d\n", num_files);
        exit(2);
      }
    }
  }

  disk = allocate_memdisk(out_name, disk_size);
  format_memdisk(disk, out_name);

  for(i = 0; i < num_files; i++) {
    name = files[i];
    in = open(name, O_RDONLY | O_BINARY);
    if(in < 0) {
      fprintf(stderr, "opening %s returned %d, errno: %d\n",
              name, in, errno);
      exit(1);
    }

    ret = fstat(in, &stat_buf);
    if(ret != 0) {
      fprintf(stderr, "fstat returned %d, errno: %d\n",
              ret, errno);
    }

    in_size = stat_buf.st_size;
    printf("in size: %d\n", in_size);

    if(in_size > disk->disk_bytes_left) {
      printf("File bigger than %d, too big!\n", disk_size);
      exit(2);
    }

    make_legal_prodos_name(new_name, name);



    file_type = 0;
    aux_type = 0;
    get_file_type(name, &file_type, &aux_type);

    create_new_file(disk, 2, 1, new_name, file_type,
                    0, 0, 0, 0xc3, aux_type, 0, in_size);


    done = 0;
    pos = 0;
    while(pos < in_size) {
      size = 512;
      if(pos + size > in_size) {
        size = in_size - pos;
      }
      ret = read(in, in_buf, size);
      if(ret != size || ret <= 0) {
        fprintf(stderr, "read returned %d, errno: %d\n",
                ret, errno);
        exit(2);
      }
      if (file_type == 4 && g_crlf) {
        int i;
        for(i = 0; i < size; ++i) {
          if(in_buf[i] == '\n') in_buf[i] = '\r';
        }
      }
      ret = pro_write_file(disk, in_buf, pos, size);
      if(ret != 0) {
        printf("pro_write returned %d!\n", ret);
        exit(3);
      }
      pos += size;
    }

    close_file(disk);

    close(in);
  }

  flush_disk(disk);
  return 0;
}

void make_legal_prodos_name(char *new_name, char *old_name)      {
  char    *ptr;
  int start_len, start_char;
  int len;
  int pos;
  int c;
  int j;

  for(j = 0; j < 16; j++) {
    /* make sure it ends with null == 15 + 1 */
    new_name[j] = 0;
  }

  start_char = 0;
  start_len = strlen(old_name);
  len = 0;
  ptr = &old_name[start_len - 1];
  for(j = start_len - 1; j >= 0; j--) {
    if(*ptr == '/' || *ptr == ':') {
      break;
    }
    ptr--;
    len++;
  }
  ptr++;

  if(len <= 0) {
    printf("Filename: %s has len: %d!\n", old_name, len);
    exit(1);
  }

  printf("mid_name: %s, len:%d\n", ptr, len);

  pos = 0;
  for(j = 0; j < 15; j++) {
    c = ptr[pos];
    if(isalnum(c)) {
      c = toupper(c);
    } else if(c != 0) {
      c = '.';
    }

    if(j == 0 && !isalpha(c)) {
      c = 'A';
    }

    new_name[j] = c;

    pos++;
    if(pos == 7 && len > 15) {
      pos = len - 8;
    }
  }

  printf("new_name: %s\n", new_name);
}

void flush_disk(ProDisk *disk)      {
  disk_write_data(disk, 6, disk->bitmap_ptr,
                  disk->size_bitmap_blocks * 512);
  close(disk->fd);
  disk->fd = -1;
}

void close_file(ProDisk *disk)      {
  write_ind_block(disk);
  write_master_ind_block(disk);
  disk_write_data(disk, disk->dir_blk_num, &(disk->dir_blk_data[0]), 512);
  disk->file_ptr = 0;
}

ProDisk *allocate_memdisk(char *out_name, int size)          {
  ProDisk *disk;
  int out;

  out = open(out_name, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0x1b6);
  if(out < 0) {
    fprintf(stderr, "opening %s returned %d, errno: %d\n",
            out_name, out, errno);
    exit(1);
  }

  disk = (ProDisk *)malloc(sizeof(ProDisk));
  if(disk == 0) {
    printf("allocate_memdisk failed, errno: %d\n", errno);
  }

  disk->fd = out;
  disk->total_blocks = (size + 511) / 512;
  disk->bitmap_ptr = 0;
  disk->disk_bytes_left = 0;
  disk->size_bitmap_blocks = 0;
  disk->dir_blk_num = -1;
  disk->ind_blk_num = -1;
  disk->master_ind_blk_num = -1;

  return disk;
}

void format_memdisk(ProDisk *disk, char *name)      {
  byte zero_buf[1024];
  int total_blocks;
  byte    *bitmap_ptr;
  Vol_hdr *vol_hdr;
  Directory *dir;
  int size_bitmap_bytes;
  int size_bitmap_blocks;
  int disk_blocks_left;
  int i, j;

  total_blocks = disk->total_blocks;

  /* Zero out blocks 0 and 1 */
  for(i = 0; i < 2*512; i++) {
    zero_buf[i] = 0;
  }
  disk_write_data(disk, 0x00000, zero_buf, 2*512);

  /* and make the image the right size */
  disk_write_data(disk, total_blocks - 1, zero_buf, 512);

  dir = disk_read_dir(disk, 2);
  set_l2byte(&(dir->prev_blk), 0);
  set_l2byte(&(dir->next_blk), 3);
  vol_hdr = (Vol_hdr *)&(dir->file_entries[0]);
  vol_hdr->storage_type_name_len = 0xf0 + strlen(name);
  strncpy((char *)vol_hdr->vol_name, name, strlen(name));
  vol_hdr->version = 0;
  vol_hdr->min_version = 0;
  vol_hdr->access = 0xc3;
  vol_hdr->entry_length = 0x27;
  vol_hdr->entries_per_block = 0x0d;
  set_l2byte(&(vol_hdr->file_count), 0);
  vol_hdr->entries_per_block = 0x0d;
  set_l2byte(&(vol_hdr->bit_map), 6);
  set_l2byte(&(vol_hdr->total_blocks), total_blocks);
  for(i = 1; i < 13; i++) {
    set_file_entry(&(dir->file_entries[i]), 0, "", 0, 0, 0,
                   0, 0, 0, 0, 0, 0, 0, 0);
  }
  disk_write_dir(disk, 2);


  for(i = 3; i < 6; i++) {
    dir = disk_read_dir(disk, i);
    set_l2byte(&(dir->prev_blk), i - 1);
    set_l2byte(&(dir->next_blk), i + 1);
    if(i == 5) {
      set_l2byte(&(dir->next_blk), 0);
    }
    for(j = 0; j < 13; j++) {
      set_file_entry(&(dir->file_entries[j]), 0, "", 0, 0, 0,
                     0, 0, 0, 0, 0, 0, 0, 0);
    }

    disk_write_dir(disk, i);
  }

  size_bitmap_bytes = (total_blocks + 7)/ 8;
  size_bitmap_blocks = (size_bitmap_bytes + 511)/ 512;
  bitmap_ptr = (byte *)malloc(size_bitmap_blocks * 512);
  for(i = 0; i < 6+size_bitmap_blocks; i++) {
    set_bitmap_used(bitmap_ptr, i);
  }
  for(i = 6+size_bitmap_blocks; i < total_blocks; i++) {
    set_bitmap_free(bitmap_ptr, i);
  }
  for(i = total_blocks; i < size_bitmap_blocks*512*8; i++) {
    set_bitmap_used(bitmap_ptr, i);
  }

  disk_write_data(disk, 6, bitmap_ptr, size_bitmap_blocks * 512);
  disk->bitmap_ptr = bitmap_ptr;
  disk->size_bitmap_blocks = size_bitmap_blocks;
  disk->bitmap_bytes = size_bitmap_blocks * 512;
  disk->bitmap_cur_pos = 0;

  disk_blocks_left = total_blocks - 6 - size_bitmap_blocks;
  disk->disk_bytes_left = disk_blocks_left * 512;
}

void disk_write_data(ProDisk *disk, int blk_num, byte *buf, int size)      {
  int size_in_blocks;
  int ret;

#if 0
  printf("Writing blk %04x from buf: %08x, %03x\n", blk_num, buf, size);
#endif

  size_in_blocks = size >> 9;
  if(size_in_blocks * 512 != size) {
    printf("disk_write: blk: %04x, buf: %08x, size: %08x\n",
           blk_num, (word32)buf, size);
    exit(1);
  }

  ret = lseek(disk->fd, 512*blk_num, SEEK_SET);
  if(ret != 512*blk_num) {
    printf("disk_write: seek: %d, errno: %d, blk: %04x, buf: "
           "%08x, sz: %08x\n", ret, errno, blk_num,
           (word32)buf, size);
    exit(1);
  }

  ret = write(disk->fd, buf, size);
  if(ret != size) {
    printf("disk_write: write: %d, errno: %d, blk: %04x, buf: "
           "%08x, sz: %08x\n", ret, errno, blk_num,
           (word32)buf, size);
    exit(1);
  }
}

void disk_read_data(ProDisk *disk, int blk_num, byte *buf, int size)      {
  int size_in_blocks;
  int ret;
  int i;

  size_in_blocks = size >> 9;
  if(size_in_blocks * 512 != size) {
    printf("disk_read: blk: %04x, buf: %08x, size: %08x\n",
           blk_num, (word32)buf, size);
    exit(1);
  }

  ret = lseek(disk->fd, 512*blk_num, SEEK_SET);
  if(ret != 512*blk_num) {
    printf("disk_read: seek: %d, errno: %d, blk: %04x, buf: "
           "%08x, sz: %08x\n", ret, errno, blk_num,
           (word32)buf, size);
    exit(1);
  }

  ret = read(disk->fd, buf, size);
  if(ret != size) {
    printf("disk_read: read: %d, errno: %d, blk: %04x, buf: "
           "%08x, sz: %08x\n", ret, errno, blk_num,
           (word32)buf, size);
    for(i = 0; i < size; i++) {
      buf[i] = 0;
    }
  }
}

Directory *disk_read_dir(ProDisk *disk, int blk_num)            {
  disk_write_dir(disk, blk_num);

  disk->dir_blk_num = blk_num;
  disk_read_data(disk, blk_num, &(disk->dir_blk_data[0]), 512);

  return (Directory *)&(disk->dir_blk_data[0]);
}

void disk_write_dir(ProDisk *disk, int blk_num)      {
  if(disk->dir_blk_num >= 0) {
    if(disk->dir_blk_num != blk_num) {
      printf("disk_write_dir: %04x != %04x\n",
             disk->dir_blk_num, blk_num);
    }
    disk_write_data(disk, disk->dir_blk_num,
                    &(disk->dir_blk_data[0]), 512);
    disk->dir_blk_num = -1;
  }
}

void create_new_file(ProDisk *disk, int dir_block, int storage_type, char *name,
                     int file_type, word32 creation_time, int version, int min_version,
                     int access, int aux_type, word32 last_mod, word32 eof) {
  Vol_hdr *vol_ptr;
  int val;
  Directory *dir_ptr;
  File_entry *file_ptr;
  int file_count;
  int pos;
  int last_pos;
  int done;
  int next_blk;
  int name_len;

  name_len = strlen(name);
  dir_ptr = disk_read_dir(disk, dir_block);
  next_blk = dir_block;
  val = dir_ptr->file_entries[0].storage_type_name_len;
  last_pos = 13;
  pos = 0;
  if(((val & 0xf0) == 0xf0) || ((val & 0xf0) == 0xe0)) {
    /* vol dir or subdir header */
    vol_ptr = (Vol_hdr *)&(dir_ptr->file_entries[0]);
    file_count = get_l2byte(&(vol_ptr->file_count));
    pos = 1;
    last_pos = vol_ptr->entries_per_block;
  } else {
    printf("dir_block: %04x not subdir or voldir\n", dir_block);
    exit(6);
  }

  vol_ptr = 0;

  done = 0;
  while(!done) {
    file_ptr = &(dir_ptr->file_entries[pos]);
    if(((file_ptr->storage_type_name_len) & 0xf0) == 0) {
      /* Got it! */
      file_ptr->storage_type_name_len =
        (storage_type << 4) | name_len;
      strncpy((char *)file_ptr->file_name, name, 15);
      file_ptr->file_type = file_type;
      set_l2byte(&(file_ptr->key_pointer),
                 find_next_free_block(disk));
      set_l2byte(&(file_ptr->blocks_used), 0);
      set_pro_time(&(file_ptr->creation_time),
                   creation_time);
      file_ptr->version = version;
      file_ptr->min_version = min_version;
      file_ptr->access = access;
      set_l2byte(&(file_ptr->aux_type), aux_type);
      set_pro_time(&(file_ptr->last_mod), last_mod);
      set_l2byte(&(file_ptr->header_pointer),
                 dir_block);
      set_l3byte(&(file_ptr->eof), eof);
      disk_write_dir(disk, next_blk);

      dir_ptr = disk_read_dir(disk, dir_block);
      vol_ptr = (Vol_hdr *)&(dir_ptr->file_entries[0]);
      set_l2byte(&(vol_ptr->file_count), file_count+1);
      disk_write_dir(disk, dir_block);

      disk_read_dir(disk, next_blk);
      /* re-read dir so that ptrs are set up right */
      disk->file_ptr = file_ptr;
      disk->file_open = 1;
      disk->ind_blk_num = -1;
      disk->master_ind_blk_num = -1;
      done = 1;
      break;
    } else {
      /* check to make sure name is unique */
      if((file_ptr->storage_type_name_len & 0x0f)== name_len) {
        if(!memcmp(file_ptr->file_name, name,name_len)) {
          printf("Name %s already on disk!\n",
                 name);
          exit(8);
        }
      }
      pos++;
      if(pos >= last_pos) {
        /* Go to next block */
        next_blk = get_l2byte(&(dir_ptr->next_blk));
        if(next_blk) {
          dir_ptr = disk_read_dir(disk, next_blk);
          pos = 0;
        } else {
          printf("Top directory full!\n");
          exit(2);
        }
      }
    }
  }
}

int pro_write_file(ProDisk *disk, byte *in_buf, int pos, int size)     {
  int block;
  int i;

  block = get_disk_block(disk, pos, 1);
  if(block < 7) {
    printf("pro_write_file, get_disk_block: %d\n", block);
    exit(3);
  }
  if(size < 512) {
    for(i = size; i < 512; i++) {
      in_buf[i] = 0;
    }
  } else if(size > 512) {
    printf("error, pro_write_file size: %d too big\n", size);
    exit(4);
  }

  disk_write_data(disk, block, in_buf, 512);

  return 0;
}



int get_disk_block(ProDisk *disk, int pos, int create)     {
  File_entry *file_ptr;
  int storage_type;
  word32 eof;
  int lo, hi;
  int offset;
  int master_ind_block, ind_block;
  int ret_block;
  int key_block;

  if(pos >= 128*256*512) {
    printf("offset too big\n");
    exit(3);
  }

  file_ptr = disk->file_ptr;

  eof = get_l3byte(&(file_ptr->eof));
  storage_type = (file_ptr->storage_type_name_len) >> 4;

  key_block = get_l2byte(&(file_ptr->key_pointer));

  if(storage_type == 1 && pos >= 512) {
    /* make it sapling */
    get_new_ind_block(disk);
    inc_l2byte(&(file_ptr->blocks_used));
    disk->ind_blk_data[0] = key_block & 0xff;
    disk->ind_blk_data[0x100] = key_block >> 8;
    key_block = disk->ind_blk_num;
    set_l2byte(&(file_ptr->key_pointer), key_block);
    file_ptr->storage_type_name_len += 0x10;
    storage_type++;
  }
  if(storage_type == 2 && pos >= 256*512) {
    /* make it tree */
    get_new_master_ind_block(disk);
    inc_l2byte(&(file_ptr->blocks_used));
    disk->master_ind_blk_data[0] = key_block & 0xff;
    disk->master_ind_blk_data[0x100] = key_block >> 8;
    key_block = disk->master_ind_blk_num;
    set_l2byte(&(file_ptr->key_pointer), key_block);
    file_ptr->storage_type_name_len += 0x10;
    storage_type++;
  }

  switch(storage_type) {
    case 1:
      if(pos >= 512) {
        printf("Error1!\n");
        exit(3);
      }
      ret_block = key_block;
      if(ret_block == 0) {
        ret_block = find_next_free_block(disk);
        inc_l2byte(&(file_ptr->blocks_used));
        set_l2byte(&(file_ptr->key_pointer), ret_block);
      }
      return ret_block;
    case 2:
      ind_block = key_block;
      if(ind_block <= 0) {
        printf("write failure, ind_block: %d!\n", ind_block);
        exit(3);
      }
      offset = pos >> 9;
      if(offset >= 256) {
        printf("pos too big!\n");
        exit(3);
      }

      lo = disk->ind_blk_data[offset];
      hi = disk->ind_blk_data[offset + 0x100];
      ret_block = hi*256 + lo;
      if(ret_block == 0) {
        /* Need to alloc a block for this guy */
        ret_block = find_next_free_block(disk);
        inc_l2byte(&(file_ptr->blocks_used));
        disk->ind_blk_data[offset] = ret_block & 0xff;
        disk->ind_blk_data[offset + 0x100] =
          ((ret_block >> 8) & 0xff);
      }
      return ret_block;
    case 3:
      /* tree */
      master_ind_block = key_block;
      if(master_ind_block <= 0) {
        printf("write failure, master_ind_block: %d!\n",
               master_ind_block);
        exit(3);
      }
      offset = pos >> 17;
      if(offset >= 128) {
        printf("master too big!\n");
        exit(4);
      }
      lo = disk->master_ind_blk_data[offset];
      hi = disk->master_ind_blk_data[offset + 0x100];
      ind_block = hi*256 + lo;
      if(ind_block == 0) {
        /* Need to alloc an ind block */
        get_new_ind_block(disk);
        ind_block = disk->ind_blk_num;
        inc_l2byte(&(file_ptr->blocks_used));
        disk->master_ind_blk_data[offset] = ind_block & 0xff;
        disk->master_ind_blk_data[offset + 0x100] =
          ((ind_block >> 8) & 0xff);
      }

      offset = (pos >> 9) & 0xff;
      lo = disk->ind_blk_data[offset];
      hi = disk->ind_blk_data[offset + 0x100];
      ret_block = hi*256 + lo;

      if(ret_block == 0) {
        /* Need to alloc a block for this guy */
        ret_block = find_next_free_block(disk);
        inc_l2byte(&(file_ptr->blocks_used));
        disk->ind_blk_data[offset] = ret_block & 0xff;
        disk->ind_blk_data[offset + 0x100] =
          ((ret_block >> 8) & 0xff);
      }
      return ret_block;
    default:
      printf("unknown storage type: %d\n", storage_type);
      exit(4);
  }

  printf("Can't get here!\n");
  exit(5);
}

void get_new_ind_block(ProDisk *disk)      {
  int ind_blk_num;
  int i;

  write_ind_block(disk);

  ind_blk_num = find_next_free_block(disk);
  for(i = 0; i < 512; i++) {
    disk->ind_blk_data[i] = 0;
  }

  disk->ind_blk_num = ind_blk_num;
}

void write_ind_block(ProDisk *disk)      {
  int ind_blk_num;

  ind_blk_num = disk->ind_blk_num;
  if(ind_blk_num > 0) {
    printf("Write ind block: %04x\n", ind_blk_num);
    disk_write_data(disk, ind_blk_num, &(disk->ind_blk_data[0]),
                    512);
    disk->ind_blk_num = -1;
  }
}

void get_new_master_ind_block(ProDisk *disk)      {
  int master_ind_blk_num;
  int i;

  write_master_ind_block(disk);

  master_ind_blk_num = find_next_free_block(disk);
  for(i = 0; i < 512; i++) {
    disk->master_ind_blk_data[i] = 0;
  }

  disk->master_ind_blk_num = master_ind_blk_num;
}

void write_master_ind_block(ProDisk *disk)      {
  int master_ind_blk_num;

  master_ind_blk_num = disk->master_ind_blk_num;
  if(master_ind_blk_num > 0) {
    printf("Write master_ind block: %04x\n", master_ind_blk_num);
    disk_write_data(disk, master_ind_blk_num,
                    &(disk->master_ind_blk_data[0]), 512);
    disk->master_ind_blk_num = -1;
  }
}

int find_next_free_block(ProDisk *disk)     {
  byte    *bitmap_ptr;
  int pos;
  int bitmap_bytes;
  int i, j;
  word32 val;

  bitmap_ptr = disk->bitmap_ptr;
  bitmap_bytes = disk->bitmap_bytes;
  pos = disk->bitmap_cur_pos;

  for(i = pos; i < bitmap_bytes; i++) {
    val = bitmap_ptr[i];
    if(val == 0) {
      continue;
    }
    for(j = 0; j < 8; j++) {
      if(val & (0x80 >> j)) {
        set_bitmap_used(bitmap_ptr, 8*i+j);
        disk->bitmap_cur_pos = i;
        return 8*i + j;
      }
    }
    return -1;
  }
  return -1;
}



void set_bitmap_used(byte *ptr, int i)      {
  word32 offset, bit;
  word32 val;

  offset = i >> 3;
  bit = i & 7;

  val = ~(0x80 >> bit);
  ptr[offset] &= val;
}

void set_bitmap_free(byte *ptr, int i)      {
  int offset, bit;
  int val;

  offset = i >> 3;
  bit = i & 7;

  val = (0x80 >> bit);
  ptr[offset] |= val;
}

void set_file_entry(File_entry *entry, int storage_type_name_len, char *file_name,
                    int file_type, int key_pointer, int blocks_used, int eof,
                    word32 creation_time, int version, int min_version, int access,
                    int aux_type, word32 last_mod, int header_pointer) {

  entry->storage_type_name_len = storage_type_name_len;
  strncpy((char *)entry->file_name, file_name, 15);
  entry->file_type = file_type;
  set_l2byte(&(entry->key_pointer), key_pointer);
  set_l2byte(&(entry->blocks_used), blocks_used);
  set_l3byte(&(entry->eof), eof);
  set_pro_time(&(entry->creation_time), creation_time);
  entry->version = version;
  entry->min_version = min_version;
  entry->access = access;
  set_l2byte(&(entry->aux_type), aux_type);
  set_pro_time(&(entry->last_mod), last_mod);
  set_l2byte(&(entry->aux_type), header_pointer);

}

void set_l2byte(L2byte *ptr, int val)      {
  ptr->low = (val & 0xff);
  ptr->hi = ((val >> 8) & 0xff);

}

void set_l3byte(L3byte *ptr, int val)      {
  ptr->low = (val & 0xff);
  ptr->hi = ((val >> 8) & 0xff);
  ptr->higher = ((val >> 16) & 0xff);

}

void set_pro_time(Pro_time *ptr, word32 val)      {
  ptr->times[0] = ((val >> 24) & 0xff);
  ptr->times[1] = ((val >> 16) & 0xff);
  ptr->times[2] = ((val >> 8) & 0xff);
  ptr->times[3] = ((val) & 0xff);
}

int get_l2byte(L2byte *ptr)     {
  int val;

  val = ((ptr->hi) * 256) + ptr->low;
  return val;
}

int get_l3byte(L3byte *ptr)     {
  int val;

  val = ((ptr->higher) * 65536) + ((ptr->hi) * 256) + ptr->low;
  return val;
}

void inc_l2byte(L2byte *ptr)      {
  set_l2byte(ptr, get_l2byte(ptr) + 1);
}
