/* map.c 05-Apr-96 */

/****************************************************************
 * (C)opyright (C) 1993,1996  Martin Stover, Marburg, Germany   *
 ****************************************************************/

#include "net.h"

typedef struct {
  uint8 connection;
  uint8 volume;
  uint8 buff[512];  /* complete path  */
  uint8 *path;      /* points to path */
} NWPATH;

static void show_map(uint8 *drvstr)
{
  int j;
  for (j=0; j < 32; j++){
    uint8 connid;
    uint8 dhandle;
    uint8 flags;
    if (*drvstr && (j + 'A' != *drvstr)) continue;
    if ((!get_drive_info(j, &connid, &dhandle, &flags)) && flags){
      char  servern[52];
      char  path[256];
      servern[0]='\0';
      if (flags & 0x80) { /* lokal DRIVE */
        path[0]= '\\';
        if (j < 2){
          strcpy(path, "maps to a local disk.");
        } else if (getcurdir(j+1, path+1)) {
          strcpy(path, "maps to a local disk.");
        }
      } else {
        if (get_dir_path(dhandle, path)) {
          strcpy(path, "DHANDLE !OK");
        }
        if (connid) {
          get_fs_name(connid, servern);
          strcat(servern, "\\");
        } else servern[0]='\0';
      }
      if (flags & 0x80) printf("Drive %c:  %s\n", (char)j+'A', path); else printf("Drive %c: = %s%s\n", (char)j+'A', servern, path);
    }
  }
}
#if 0
static void do_map(int drive, NWPATH *nwp)
{
  if (drive > -1 && drive < 32) {
    uint8 connid;
    uint8 dhandle;
    uint8 flags;
    if ((!get_drive_info(drive, &connid, &dhandle, &flags)) && flags){
      char  servern[52];
      char  path[256];
      if (flags & 0x80) { /* lokal DRIVE */
        path[0]= '\\';
        if (drive < 2){
          strcpy(path, "maps to a local disk.");
        } else if (getcurdir(drive+1, path+1)) {
          strcpy(path, "maps to a local disk.");
        }
      } else {
        if (get_dir_path(dhandle, path)) {
          strcpy(path, "DHANDLE !OK");
        }
      }
      if (connid) {
        get_fs_name(connid, servern);
        strcat(servern, "\\");
      } else servern[0]='\0';
      printf("DOMAP %c: = %s%s\n", (char)drive+'A', servern, path);
    }
  }
}
#endif

static int do_map(int drive, NWPATH *nwp, int delete)
{
  int result = -1;

  if (drive > -1 && drive < 32) {
    uint8 connid = 0;
    uint8 dhandle = 0;
    uint8 flags = 0;

    if (delete) {
      if (!get_drive_info(drive, &connid, &dhandle, &flags)
          && flags && !(flags & 0x80) && connid) {
        result = dealloc_dir_handle(dhandle);
      }
    } else {
      /*
       * The old mars-dosutils MAP used DOS redirector INT 21h AX=5F03h.
       * NetWare Client32 returns AX=0001 for that call, while Novell MAP
       * works.  Use the NetWare shell/NCP "Allocate Permanent Directory
       * Handle" path instead; this is what the surrounding code already
       * provides via alloc_permanent_dir_handle().
       */
      if (!get_drive_info(drive, &connid, &dhandle, &flags)
          && flags && !(flags & 0x80) && connid) {
        (void)dealloc_dir_handle(dhandle);
      }

      result = alloc_permanent_dir_handle(0, nwp->path, drive + 'A', NULL);

      /*
       * Some requesters historically accepted lowercase drive letters in
       * this call.  Keep this only as compatibility fallback.
       */
      if (result < 0)
        result = alloc_permanent_dir_handle(0, nwp->path, drive + 'a', NULL);
    }
  }

  return(result);
}

static int parse_argv(uint8 *drvstr, NWPATH *nwpath,
                  int argc, char *argv[], int smode, int argvmode)
{
  int k     = 0;
  int mode  = 0;
  uint8 *pd = drvstr;
  *drvstr   = '\0';
  memset(nwpath, 0, sizeof(NWPATH));
  *(nwpath->buff) = '\0';
  nwpath->path = nwpath->buff;

  while (++k < argc && mode > -1) {
    uint8 *p  = argv[k];
    while (*p && mode > -1) {
      if (!mode) {
        if (*p == ':') mode = -1;
        else if (smode && *p != 's' && *p  != 'S') mode = -1;
      }
      if (mode < 0) break;
      else if (mode < 20) {
        if (*p == ':') {
          if (!mode || (mode > 1 && (*drvstr != 'S' || !smode)))
            mode = -1;
          else {
            *pd = '\0';
            if (mode > 1) {
              *drvstr='s';
              *(drvstr+1)=(uint8) atoi((char*)drvstr+1);
            }
            mode = 20;
            pd   = nwpath->buff;
          }
        } else {
          if (++mode == 20) mode = -1;
          else {
            if (*p > 0x60 && *p < 0x7b)
              *pd++ = *p - 0x20;  /* upshift */
            else
              *pd++ = *p;
          }
        }
      } else if (mode == 20) {
        if (*p == '=') mode = 30;
        else if (*p != ' ' && *p != '\t') mode = -2;
      } else if (mode == 30) {
        if (*p != ' ' && *p != '\t') {
          mode = 40;
          continue;
        }
      } else if (mode == 40) {
        if (*p > 0x60 && *p < 0x7b)
          *pd++ = *p - 0x20;  /* upshift */
        else
          *pd++ = *p;
      }
      p++;
    } /* while *p */
  } /* while k */
  if (mode == 30) {
    if (argvmode != 1)
       getcwd((char *)nwpath->buff, sizeof(nwpath->buff));
    mode = 40;
  }
  if (mode && mode != 20 && mode != 40) {
    fprintf(stderr, "Cannot interpret line. errcode=%d\n", mode);
    return(mode < 0 ? mode : -3);
  }
  return(0);
}

int func_map(int argc, char *argv[], int mode)
{
  uint8  drvstr[22];
  NWPATH nwpath;
  if (!ipx_init()) argc = 1;
  if (!parse_argv(drvstr, &nwpath, argc, argv, 0, mode)) {
    if (*(nwpath.path) || mode==1) {
      if (do_map(*drvstr - 'A', &nwpath, mode)< 0)
        fprintf(stderr, "MAP Error\n");
    }
    if (mode != 1)
      show_map(drvstr);
    return(0);
  }
  return(1);
}


/* ------------------------------------------------- */
static int show_search(uint8 *drvstr)
{
  SEARCH_VECTOR          drives;
  SEARCH_VECTOR_ENTRY *p=drives;
  int j=0;
  get_search_drive_vektor(drives);
  while (p->drivenummer != 0xff && j++ < 16) {
    char  path[256];
    char  nwname_path[300];


    if ( !*drvstr || j == *(drvstr+1)) {

      if (p->drivenummer == 0xfe){
        strcpy(path, p->dospath);
      } else {
        *path      = p->drivenummer+'A';
        *(path+1)  = ':';
        strcpy(path+2, p->dospath);
      }

      if (p->flags && !(p->flags & 0x80)){
        char *pp=nwname_path;
        *pp++ = '[';
        get_fs_name(p->connid, pp);
        pp   +=strlen(pp);
        *pp++='\\';
        if (get_dir_path(p->dhandle, pp)) {
          strcpy(pp, "ERROR NW");
        }
        pp   += strlen(pp);
        *pp ++= ']';
        *pp   = '\0';
      } else {
        *nwname_path  = '\0';
      }
      printf("SEARCH%2d = %s %s\n", j,  path, nwname_path);
    }
    p++;
  }
  return(0);
}

static int set_search(uint8 *drvstr, NWPATH *nwp, int pathmode)
{
  int result=-1;
  SEARCH_VECTOR          drives;
  SEARCH_VECTOR_ENTRY *p=drives;
  int j=0;
  int entry = (*drvstr=='s') ? *(drvstr+1) : 0;
  get_search_drive_vektor(drives);

  while (p->drivenummer != 0xff && j++ < 16) {
    if (!entry && (p->drivenummer + 'A' == *drvstr)) entry=j;
    if (p->drivenummer + 'A' == nwp->path[0] && nwp->path[1] == ':'
         && !strcmp(nwp->path+2, p->dospath)) {
      p->drivenummer=0xfe;
      *(p->dospath) = '\0';
    }
    p++;
  }

  if (entry > 0) {
    if (entry > 16)  entry = 16;
    if (pathmode == 2 && entry <= j && entry < 16) {  /* insert modus */
      int k=j+1-entry;
      if (j < 16) {
        p++;
        k++;
        j++;
      }
      while (k--) {
        memcpy(p, p-1, sizeof(SEARCH_VECTOR_ENTRY));
        --p;
      }
    }
    if (--entry < j)
      p = drives+entry;
    else (p+1)->drivenummer = 0xff;
    p->flags       = 0;
    p->drivenummer = 0xfe;
    if (pathmode==1)
      *(p->dospath) = '\0';
    else
      strcpy(p->dospath, nwp->path);
    result = set_search_drive_vektor(drives);
  }
  return(result);
}


static int path_is_drive_path(uint8 *path)
{
  if (!path || !path[0] || path[1] != ':') return(0);
  if (path[0] >= 'A' && path[0] <= 'Z') return(1);
  if (path[0] >= 'a' && path[0] <= 'z') return(1);
  return(0);
}

static void upstr_local(uint8 *s)
{
  while (*s) {
    if (*s >= 'a' && *s <= 'z') *s -= 0x20;
    s++;
  }
}

static int parse_pathins_arg(uint8 *drvstr, NWPATH *nwp, int argc, char *argv[], int mode)
{
  char joined[512];
  char *p;
  char *q;
  int slot = 0;
  int k;

  *drvstr = '\0';
  memset(nwp, 0, sizeof(NWPATH));
  nwp->path = nwp->buff;
  *(nwp->buff) = '\0';

  if (argc < 2) return(1);

  joined[0] = '\0';
  for (k = 1; k < argc; k++) {
    if (k > 1) strcat(joined, " ");
    strncat(joined, argv[k], sizeof(joined) - strlen(joined) - 1);
  }

  p = joined;
  while (*p == ' ' || *p == '\t') p++;

  if (*p != 'S' && *p != 's') return(-1);
  p++;

  while (*p >= '0' && *p <= '9') {
    slot = slot * 10 + (*p - '0');
    p++;
  }

  if (slot < 1 || slot > 16) return(-1);
  if (*p != ':') return(-1);
  p++;

  drvstr[0] = 's';
  drvstr[1] = (uint8)slot;
  drvstr[2] = '\0';

  while (*p == ' ' || *p == '\t') p++;

  if (mode == 1) {
    /* PATHDEL S1: */
    return(0);
  }

  if (*p == '=') p++;
  while (*p == ' ' || *p == '\t') p++;

  if (!*p) return(-1);

  q = nwp->buff;
  while (*p && (q - nwp->buff) < (int)sizeof(nwp->buff) - 1) {
    *q++ = *p++;
  }
  *q = '\0';

  upstr_local(nwp->buff);
  nwp->path = nwp->buff;

  return(0);
}

static int set_search_native(uint8 *drvstr, NWPATH *nwp, int pathmode)
{
  int result=-1;
  SEARCH_VECTOR          drives;
  SEARCH_VECTOR_ENTRY *p=drives;
  int j=0;
  int entry = (*drvstr=='s') ? *(drvstr+1) : 0;

  get_search_drive_vektor(drives);

  while (p->drivenummer != 0xff && j++ < 16) {
    if (!entry && path_is_drive_path(nwp->path)
        && (p->drivenummer + 'A' == nwp->path[0])) entry=j;

    if (path_is_drive_path(nwp->path)
        && p->drivenummer + 'A' == nwp->path[0]
        && !strcmp(nwp->path+2, p->dospath)) {
      p->drivenummer=0xfe;
      *(p->dospath) = '\0';
      p->flags = 0;
    }
    p++;
  }

  if (entry > 0) {
    if (entry > 16)  entry = 16;

    if (pathmode == 2 && entry <= j && entry < 16) {  /* insert modus */
      int k=j+1-entry;
      if (j < 16) {
        p++;
        k++;
        j++;
      }
      while (k--) {
        memcpy(p, p-1, sizeof(SEARCH_VECTOR_ENTRY));
        --p;
      }
    }

    if (--entry < j)
      p = drives+entry;
    else
      (p+1)->drivenummer = 0xff;

    memset(p, 0, sizeof(SEARCH_VECTOR_ENTRY));

    if (pathmode==1) {
      p->drivenummer = 0xfe;
      *(p->dospath) = '\0';
      result = set_search_drive_vektor(drives);
    } else if (path_is_drive_path(nwp->path)) {
      p->flags       = 0;
      p->drivenummer = (uint8)(nwp->path[0] - 'A');
      if (nwp->path[0] >= 'a' && nwp->path[0] <= 'z')
        p->drivenummer = (uint8)(nwp->path[0] - 'a');
      strmaxcpy(p->dospath, nwp->path+2, sizeof(p->dospath)-1);
      result = set_search_drive_vektor(drives);
    } else {
      /*
       * Search path entries are not drive mappings.  The original code stores
       * the NetWare path text directly in dospath with drivenummer=0xfe.
       * Client32 keeps/prints these entries correctly; allocating a permanent
       * directory handle here made set_search_drive_vektor() return success,
       * but the entry did not actually replace SEARCH1.
       */
      p->flags       = 0;
      p->drivenummer = 0xfe;
      strmaxcpy(p->dospath, nwp->path, sizeof(p->dospath)-1);
      result = set_search_drive_vektor(drives);
    }
  }

  return(result);
}


int func_path(int argc, char *argv[], int mode)
{
  uint8  drvstr[22];
  NWPATH nwpath;
  int rc;

  /*
   * PATH/PATHINS/PATHDEL need their own parser.  The old parse_argv()
   * rejects common login-script syntax such as:
   *   PATHINS S1:=SYS:PUBLIC
   *   MAP INS S1:=SYS:PUBLIC
   */
  if (argc < 2) {
    show_search("");
    return(0);
  }

  rc = parse_pathins_arg(drvstr, &nwpath, argc, argv, mode);
  if (!rc) {
    int result=0;
    if (*(nwpath.path) || mode==1)
       result=set_search_native(drvstr, &nwpath, mode);
    if (mode != 1)
      show_search(drvstr);
    return(result);
  }

  fprintf(stderr, "Cannot interpret line. errcode=-1\n");
  return(1);
}

void remove_nwpathes(void)
{
  SEARCH_VECTOR          drives;
  SEARCH_VECTOR_ENTRY *p=drives;
  int j=0;
  get_search_drive_vektor(drives);
  while (p->drivenummer != 0xff && j++ < 16) {
    if (p->flags && !(p->flags & 0x80)){
      p->flags=0;
      p->drivenummer=0xfe;
      *(p->dospath) ='\0';
    }
    ++p;
  }
  set_search_drive_vektor(drives);
}





