#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "kernel.h"
#include "swis.h"
#include "flex.h"

#include "main.h"
#include "msgtrans.h"
#include "path.h"

#include <ctype.h>

// find out if a given path is writeable
// by enquiring about the FS status

_kernel_oserror *path_canon(char *filename, char **path, int extra)
{
  _kernel_oserror *e;
  int size;

  *path=NULL;

  e=_swix(OS_FSControl,_INR(0,5)|_OUT(5),37,filename,NULL,0,0,0,&size);
  if (e) return e;

  if (!flex_alloc((flex_ptr)path,1-size+extra)) {
    return msgtrans_errorlookup("NoMem");
  }

  e=_swix(OS_FSControl,_INR(0,5),37,filename,*path,0,0,1-size);
  if (e) {
    flex_free((flex_ptr)path);
    *path=NULL;
  }
  return e;
}

int path_readonly(char *filename)
{
  // get FS information block
  int *fscb;
  char *prefix=NULL;
  char *fullname;
  int size;
  _kernel_oserror *e;

  if (*(filename+strlen(filename)-1)=='.') {
    if (!flex_alloc((flex_ptr)&prefix,strlen(filename))) {
      msgtrans_error("NoMem");
      return 1;
    }
    strncpy(prefix,filename,strlen(filename)-1);
    *(prefix+strlen(filename)-1)=0;
  }

  e=path_canon(prefix?prefix:filename,&fullname,0);
  if (prefix) flex_free((flex_ptr)&prefix);

  if (e) {
    error(e,FALSE);
    return 1;
  }

  e=_swix(OS_FSControl,_INR(0,2)|_OUT(2),13,fullname,0,&fscb);
  flex_free((flex_ptr)&fullname);
  if (e) {
    error(e,FALSE);
    return 1;
  }
  
  return (*(fscb+8) & (1<<16))!=0; // test readonly flag
}

static int path_dirmatch(char *a, char *b,int *index)
{
  int offset;

  for (offset=*index;toupper(*(a+offset))==toupper(*(b+offset));offset++) {
    if (*(a+offset)=='.' || *(a+offset)==0) {
      *index=offset+1;
      return 1;
    }
  }

  return 0;
}

void path_make_boot_relative(char **source, char **dest)
{
  // see if we can make this relative to the boot path
  int root_index,dest_index;
  char *bootpath=getenv("Boot$Path");
  char *bp_canon=NULL;
  char *sp_canon=NULL;
  _kernel_oserror *e;
  int nodot_length;

  if (bootpath==NULL) {
    *dest=NULL;
    return;
  }

  nodot_length=1+strlen(*source)>1+strlen(bootpath) ? 1+strlen(*source) : 1+strlen(bootpath);
  char nodot[nodot_length];

  // make sure both paths are canonical
  strcpy(nodot,*source);
  e=path_canon(nodot,&sp_canon,1);
  if (e) {
    error(e,FALSE);
    *dest=NULL;
    return;
  }

  memcpy(nodot,bootpath,strlen(bootpath)-1);
  *(nodot+strlen(bootpath)-1)=0;

  e=path_canon(nodot,&bp_canon,1);
  if (e) {
    flex_free((flex_ptr)&sp_canon);
    error(e,FALSE);
    *dest=NULL;
    return;
  }

  // see if the root parts match
  for (root_index=0; *(bp_canon+root_index)>0 && *(sp_canon+root_index)>0 && *(bp_canon+root_index)!='$'; root_index++) {
    if (toupper(*(bp_canon+root_index))!=toupper(*(sp_canon+root_index))) {
      flex_free((flex_ptr)&sp_canon);
      flex_free((flex_ptr)&bp_canon);
      *dest=NULL;
      return;
    }
  }

  if (!(*(bp_canon+root_index)=='$' && *(sp_canon+root_index)=='$')) {
    flex_free((flex_ptr)&sp_canon);
    flex_free((flex_ptr)&bp_canon);
    *dest=NULL;
    return;
  }

  strcpy(sp_canon+strlen(sp_canon),".");
  strcpy(bp_canon+strlen(bp_canon),".");

  // the paths are on the same disk.  Good.
  // now we need to see if we can make them relative.
  // copy over all matching bits
  if (!flex_alloc((flex_ptr)dest,1+strlen(*source))) {
    flex_free((flex_ptr)&sp_canon);
    flex_free((flex_ptr)&bp_canon);
    *dest=NULL;
    return;
  }

  strcpy(*dest,"Boot:");
  dest_index=5;

  if (memcmp(sp_canon+root_index,bp_canon+root_index,2)!=0) {
    // odd.  bail out
    flex_free((flex_ptr)&sp_canon);
    flex_free((flex_ptr)&bp_canon);
    *dest=NULL;
    return;
  }
  root_index+=2; // skip the . after $

  // see how many dirs match
  while (*(bp_canon+root_index) && *(sp_canon+root_index)) {
    // test it dir at a time
    if (!path_dirmatch(sp_canon,bp_canon,&root_index)) {
      // mismatch here.
      // add ^s for the rest of the boot path
      int bp_index=root_index;
      while (*(bp_canon+bp_index)) {
        if (*(bp_canon+bp_index)=='.') {
          strcpy(*dest+dest_index,"^.");
          dest_index+=2;
        }
        bp_index++;
      }

      // tack on rest of path
      strcpy(*dest+dest_index,sp_canon+root_index);

      // and finish
      flex_free((flex_ptr)&bp_canon);
      flex_free((flex_ptr)&sp_canon);
      return;
    }
  }

  // ran out of one or the other
  // out of boot path?  tack on the rest
  if (*(bp_canon+root_index)==0) {
    strcpy(*dest+dest_index,sp_canon+root_index);
  } else {
    // out of dest, so add ^s
    while (*(bp_canon+root_index)) {
      if (*(bp_canon+root_index)=='.') {
        strcpy(*dest+dest_index,"^.");
        dest_index+=2;
      }
      root_index++;
    }
  }

  flex_free((flex_ptr)&bp_canon);
  flex_free((flex_ptr)&sp_canon);

}

