/* nis_tcl.c
 *
 * Copyright (C) 1994 Matthew R. Wette -- All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies.  Matthew R. Wette
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * notes -
 *  1.	standalone compile ...
 *	  gcc -o nistcl -I/usr/local/include -Dnis_tcl_init=Tcl_AppInit \ 
 *	      nis_tcl.o -L/usr/local/lib -ltcl -lm
 *  2.	types
 *	  nis_result: just a list of objects 
 *	  nis_object: {name owner group domain access? ttl? .objdata}
 *	  objdata: {type data}
 *	  directory_obj:
 *	  group_obj:
 *	  table_obj: {type maxcol sep @cols path}
 *	  table_col: {name {flags} {rights}}
 *	  entry_obj: {type @vals}
 *	  entry_col: {flags vals}
 *	  link_obj:
 *	  po_data:
 *
 * history -
 *	30Jan94	M.Wette: created.
 *
 * version -
 *	$Id: nistcl.c,v 1.2 1994/04/19 17:28:04 mwette Exp $
 */

#include <rpcsvc/nis.h>
#include <tcl.h>

#ifndef NIS_LIBRARY
#define NIS_LIBRARY ".";
#endif
char nis_library[64] = NIS_LIBRARY;
char nis_library_cmd[64] = "source $nis_library/nis.tcl";

static int nis_list_callback(nis_name, nis_object*, void*);
static int nis_print_result(nis_result *result);
static int expand_result(Tcl_DString* dstring, nis_result *result);
static int expand_object(Tcl_DString* dstring, nis_object *object);
static int expand_objdata(Tcl_DString* dstring, objdata *data);
static int expand_table_obj(Tcl_DString* dstring, table_obj *ta_obj);
static int expand_table_col(Tcl_DString* dstring, table_col *ta_col);
static int expand_entry_obj(Tcl_DString* dstring, entry_obj *en_obj);
static int expand_entry_col(Tcl_DString* dstring, entry_col *en_col);


static int hash_table_init_done = 0;
static Tcl_HashTable hash_table;

static void
hello_hash_table(void)
{
  if (hash_table_init_done) return;
  Tcl_InitHashTable(&hash_table, TCL_STRING_KEYS);
  hash_table_init_done = 1;
  return;
}


static char *
alloc_result(Tcl_Interp* interp, nis_result *result)
{
  Tcl_HashEntry *hash_entry;
  int is_new;
  char name[12];
  static int nres = 0;

  sprintf(name, "nis_result%03d", ++nres);
  if (nres == 999) nres = 0;
  hello_hash_table();
  hash_entry = Tcl_CreateHashEntry(&hash_table, name, &is_new);
  if (!is_new) { /*nis_freeresult(...);*/ }
  Tcl_SetHashValue(hash_entry, (ClientData)result);
  return Tcl_GetHashKey(&hash_table, hash_entry);
}

static char *
delete_result(ClientData clientData, Tcl_Interp* interp, char* name1,
    char *name2, int flags)
{
  /* Hash_delete ... */
  nis_freeresult((nis_result*)clientData);
}

static nis_result *
find_result(char *key)
{
  Tcl_HashEntry *hash_entry;

  hash_entry = Tcl_FindHashEntry(&hash_table, key);
  return (nis_result*)Tcl_GetHashValue(hash_entry);
}

int
result_is_error(nis_result* result, Tcl_Interp* interp)
{
  if (result->status == 0) return 0;
  Tcl_SetResult(interp, nis_sperrno(result->status), TCL_STATIC);
  nis_freeresult(result);
  return 1;
}


static char *
alloc_object(Tcl_Interp* interp, nis_object *object)
{
  Tcl_HashEntry *hash_entry;
  int is_new;
  char name[12];
  static int nobj = 0;

  sprintf(name, "nis_object%03d", ++nobj);
  if (nobj == 999) nobj = 0;
  hello_hash_table();
  hash_entry = Tcl_CreateHashEntry(&hash_table, name, &is_new);
  if (!is_new) { /*nis_freeresult(...);*/ }
  Tcl_SetHashValue(hash_entry, (ClientData)object);
  return Tcl_GetHashKey(&hash_table, hash_entry);
}

static char *
delete_object(ClientData clientData, Tcl_Interp* interp, char* name1,
    char *name2, int flags)
{
  nis_destroy_object((nis_object*)clientData);
}

static nis_object *
find_object(char *key)
{
  Tcl_HashEntry *hash_entry;

  hash_entry = Tcl_FindHashEntry(&hash_table, key);
  return (nis_object*)Tcl_GetHashValue(hash_entry);
}

struct list_handle {
  char *command;
  Tcl_Interp *interp;
};

int
nis_list_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_result *result;
  int flags = 0;
  char *callback;
  struct list_handle handle;

/*
 *   nis_result *nis_list(const nis_name  name, const u_long flags,
 *	  int (*callback)(const nis_name name, const nis_object *object,
 *	  const void *userdata), const void *userdata));
 */
  callback = argc > 2? argv[2]: 0;
  handle.interp = interp;
  handle.command = callback;
  result = nis_list(argv[1], flags, callback? nis_list_callback: 0, &handle);
  if (result->status != 0 && result->status != NIS_CBRESULTS) {
    Tcl_SetResult(interp, nis_sperrno(result->status), TCL_STATIC);
    nis_freeresult(result);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp, alloc_result(interp, result), TCL_STATIC);
  return TCL_OK;
}

/* userdata is a string in which %n will be replaced by name and %o object */
static int
nis_list_callback(nis_name name, nis_object *object, void *userdata)
{
  int len, status;
  char *tmpl, *p, *q, *np, *op;
  struct list_handle *handle = (struct list_handle*) userdata;
  char bufr[128];

  tmpl = handle->command;
  len = strlen(tmpl) + strlen(name) + 15;
  np = name;
  op = alloc_object(handle->interp, object);
  p = tmpl; q = bufr;
  while (*p) {
    if (*p == '%') {
      if (p[1] == 'n') {
        p += 2;
        while (*np) *q++ = *np++;
      } else if (p[1] == 'o') {
        p += 2;
        while (*op) *q++ = *op++;
      } else {
        *q++ = *p++;
      }
    } else {
      *q++ = *p++;
    }
  }
  *q = '\0';
  return Tcl_Eval(handle->interp, bufr);
}


int
nis_add_entry_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_result *result;
/*
 *   nis_result *nis_add_entry(const nis_name name, const nis_object *object,
 *	  const u_long flags);
 */
  Tcl_SetResult(interp, "not implemented yet", TCL_STATIC);
  return TCL_ERROR;
}

int
nis_remove_entry_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
/*
 *   nis_result *nis_remove_entry(const nis_name name,
 *	  const nis_object *object, const u_long flags);
 */
  Tcl_SetResult(interp, "not implemented yet", TCL_STATIC);
  return TCL_ERROR;
}

int
nis_modify_entry_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
/*
 *   nis_result *nis_modify_entry(const nis_name name,
 *	  const nis_object *object, const u_long flags);
 */
  Tcl_SetResult(interp, "not implemented yet", TCL_STATIC);
  return TCL_ERROR;
}

/* nis_result *nis_first_entry(const nis_name name);
 */
int
nis_first_entry_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_result *result;

  if (argc < 2) {
    Tcl_SetResult(interp, "usage: nis_first_entry <nis_name>", TCL_STATIC);
    return TCL_ERROR;
  }
  result = nis_first_entry(argv[1]);
  if (result->status != 0) {
    Tcl_SetResult(interp, nis_sperrno(result->status), TCL_STATIC);
    nis_freeresult(result);
    return TCL_ERROR;
  }
  if (result == 0) { return TCL_ERROR; }
  Tcl_SetResult(interp, alloc_result(interp, result), TCL_STATIC);
  return TCL_OK;
}

int
nis_next_entry_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_result *result;
  /*
   * nis_result *nis_next_entry(const nis_name name, const netobj *cookie);
   */
  result = find_result(argv[2]);
  if (result == 0) {
    Tcl_SetResult(interp, "nis_next_entry_tcl: bad arg", TCL_STATIC);
    return TCL_ERROR;
  }
  result = nis_next_entry(argv[1], &(result->cookie));
  if (result->status != 0) {
    Tcl_SetResult(interp, nis_sperrno(result->status), TCL_STATIC);
    nis_freeresult(result);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp, alloc_result(interp, result), TCL_STATIC);
  return TCL_OK;
}

/* nis_name nis_local_directory(void); */
int
nis_local_directory_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_SetResult(interp, nis_local_directory(), TCL_VOLATILE);
  return TCL_OK;
}

/* nis_name nis_local_host(void); */
int
nis_local_host_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_SetResult(interp, nis_local_host(), TCL_VOLATILE);
  return TCL_OK;
}

/* nis_name nis_local_group(void); */
int
nis_local_group_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_SetResult(interp, nis_local_group(), TCL_VOLATILE);
  return TCL_OK;
}

/* nis_name nis_local_principal(void); */
int
nis_local_principal_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_SetResult(interp, nis_local_principal(), TCL_VOLATILE);
  return TCL_OK;
}


int
nis_lookup_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_result *result;

  result = nis_lookup(argv[1], FOLLOW_LINKS|EXPAND_NAME);
  if (result->status != 0) {
    Tcl_SetResult(interp, nis_sperrno(result->status), TCL_STATIC);
    nis_freeresult(result);
    return TCL_ERROR;
  }
  Tcl_SetResult(interp, alloc_result(interp, result), TCL_STATIC);
  return TCL_OK;
}

int
nis_add_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_SetResult(interp, "NOT IMPLEMENTED", TCL_STATIC);
  return TCL_ERROR;
}

int
nis_remove_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_SetResult(interp, "NOT IMPLEMENTED", TCL_STATIC);
  return TCL_ERROR;
}

int
nis_modify_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  Tcl_SetResult(interp, "NOT IMPLEMENTED", TCL_STATIC);
  return TCL_ERROR;
}


int
nis_print_result_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_result *result;

  result = (nis_result*) find_result(argv[1]);
  if (result == 0) return TCL_ERROR;
  nis_print_result(result);
  return TCL_OK;
}

int
nis_result_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_result *result;
  nis_object *object;
  Tcl_DString dstring;
  int i;
  char bufr[16];

  if (argc < 3) {
    Tcl_SetResult(interp, "usage: nis_result <result> <command>", TCL_STATIC);
    return TCL_ERROR;
  }
  result = find_result(argv[1]);
  if (result == 0) return TCL_ERROR;
  if (strcmp(argv[2], "expand") == 0) {
    Tcl_DStringInit(&dstring);
    expand_result(&dstring, result);
    Tcl_DStringResult(interp, &dstring);
  } else if (strcmp(argv[2], "nobject") == 0) {
    sprintf(bufr, "%d", result->objects.objects_len);
    Tcl_SetResult(interp, bufr, TCL_VOLATILE);
  } else if (strcmp(argv[2], "object") == 0) {
    i = 0;
    object = &(result->objects.objects_val[i]);
    Tcl_SetResult(interp, alloc_object(interp, object), TCL_STATIC);
  } else {
    Tcl_SetResult(interp, "nis_result: bad arg", TCL_STATIC);
  }
  return TCL_OK;
}

int
nis_object_tcl(
    ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
{
  nis_object *object;
  Tcl_DString dstring;
  int i;
  char *p, bufr[16];

  if (argc < 3) {
    Tcl_SetResult(interp, "usage: nis_object <object> <command>", TCL_STATIC);
    return TCL_ERROR;
  }
  object = find_object(argv[1]);
  if (object == 0) return TCL_ERROR;
  if (strcmp(argv[2], "expand") == 0) {
    Tcl_DStringInit(&dstring);
    expand_object(&dstring, object);
    Tcl_DStringResult(interp, &dstring);
  } else if (strcmp(argv[2], "type") == 0) {
    switch (object->zo_data.zo_type) {
    case BOGUS_OBJ: p = "BOGUS_OBJ"; break;
    case NO_OBJ: p = "NO_OBJ"; break;
    case DIRECTORY_OBJ: p = "DIRECTORY_OBJ"; break;
    case GROUP_OBJ: p = "GROUP_OBJ"; break;
    case TABLE_OBJ: p = "TABLE_OBJ"; break;
    case ENTRY_OBJ: p = "ENTRY_OBJ"; break;
    case LINK_OBJ: p = "LINK_OBJ"; break;
    case PRIVATE_OBJ: p = "PRIVATE_OBJ"; break;
    default: p = "UNKNOWN_OBJ"; break;
    }
    Tcl_SetResult(interp, p, TCL_STATIC);
  } else {
    Tcl_SetResult(interp, "nis_object: bad arg", TCL_STATIC);
  }
  return TCL_OK;
}



nis_tcl_init(Tcl_Interp *interp)
{
  char buf[23];
  int i, lev;

  Tcl_CreateCommand(interp, "nis_list", nis_list_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_add_entry", nis_add_entry_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_remove_entry", nis_remove_entry_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_modify_entry", nis_modify_entry_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_first_entry", nis_first_entry_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_next_entry", nis_next_entry_tcl, 0, 0);

  Tcl_CreateCommand(interp, "nis_local_directory", nis_local_directory_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_local_host", nis_local_host_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_local_group", nis_local_group_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_local_principal", nis_local_principal_tcl, 0, 0);

  Tcl_CreateCommand(interp, "nis_lookup", nis_lookup_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_add", nis_add_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_remove", nis_remove_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_modify", nis_modify_tcl, 0, 0);

  Tcl_CreateCommand(interp, "nis_result", nis_result_tcl, 0, 0);
  Tcl_CreateCommand(interp, "nis_object", nis_object_tcl, 0, 0);

  Tcl_CreateCommand(interp, "nis_print_result", nis_print_result_tcl, 0, 0);

  Tcl_SetVar(interp, "nis_library", nis_library, TCL_GLOBAL_ONLY);
  Tcl_Eval(interp, nis_library_cmd);

  return TCL_OK;
}



/* not in Sun nsl */
static int
nis_print_result(nis_result *result)
{
  int i;
  printf("Status=%s\n", nis_sperrno(result->status));
  printf("Number of Objects=%d\n", result->objects.objects_len);
  for (i = 0; i < result->objects.objects_len; i++) {
    nis_print_object(&(result->objects.objects_val[i]));
  }
  printf("\n");
}


static int
expand_result(Tcl_DString* dstring, nis_result *result)
{
  int i, status;
  nis_object *object;

  for (i = 0; i < result->objects.objects_len; i++) {
    status = expand_object(dstring, &(result->objects.objects_val[i]));
    if (status == -1) return -1;
  }
  return 0;
}

static int
expand_object(Tcl_DString* dstring, nis_object* object)
{
  expand_objdata(dstring, &(object->zo_data));
}

static int
expand_objdata(Tcl_DString* dstring, objdata* data)
{
  switch (data->zo_type) {
  case BOGUS_OBJ:
    break;
  case NO_OBJ:
    break;
  case DIRECTORY_OBJ:
    return -1;
    /*expand_dir_obj(dstring, &(data->objdata_u.di_data));*/
    break;
  case GROUP_OBJ:
    return -1;
    /*expand_group_obj(dstring, &(data->objdata_u.gr_data));*/
    break;
  case TABLE_OBJ:
    expand_table_obj(dstring, &(data->objdata_u.ta_data));
    break;
  case ENTRY_OBJ:
    expand_entry_obj(dstring, &(data->objdata_u.en_data));
    break;
  case LINK_OBJ:
    return -1;
    break;
  case PRIVATE_OBJ:
    return -1;
    break;
  default:
    return -1;
    break;
  }
  return 0;
}

static int
expand_table_obj(Tcl_DString* dstring, table_obj *ta_obj)
{
  int i, status;
  char bufr[8];

  Tcl_DStringAppendElement(dstring, ta_obj->ta_type);
  sprintf(bufr, "%d", ta_obj->ta_maxcol);
  Tcl_DStringAppendElement(dstring, bufr);
  sprintf(bufr, "%c", ta_obj->ta_sep);
  Tcl_DStringAppendElement(dstring, bufr);
  Tcl_DStringStartSublist(dstring);
  for (i = 0; i < ta_obj->ta_cols.ta_cols_len; i++) {
    status = expand_table_col(dstring, &(ta_obj->ta_cols.ta_cols_val[i]));
    if (status < 0) return status;
  }
  Tcl_DStringEndSublist(dstring);
  Tcl_DStringAppendElement(dstring, ta_obj->ta_path);
  return 0;
}

static int
expand_table_col(Tcl_DString* dstring, table_col *ta_col)
{
  Tcl_DStringAppendElement(dstring, ta_col->tc_name);
  return 0;
}

static int
expand_entry_obj(Tcl_DString* dstring, entry_obj *en_obj)
{
  int i;
  char bufr[32];

#if 0
  Tcl_DStringStartSublist(dstring);
  if (ta_col->tc_flags & TA_BINARY)
    Tcl_DStringAppendElement(dstring, "BINARY");
  if (ta_col->tc_flags & TA_CRYPT)
    Tcl_DStringAppendElement(dstring, "CRYPT");
  if (ta_col->tc_flags & TA_XDR)
    Tcl_DStringAppendElement(dstring, "XDR");
  if (ta_col->tc_flags & TA_SEARCHABLE)
    Tcl_DStringAppendElement(dstring, "SEARCHABLE");
  if (ta_col->tc_flags & TA_CASE)
    Tcl_DStringAppendElement(dstring, "CASE");
  if (ta_col->tc_flags & TA_MODIFIED)
    Tcl_DStringAppendElement(dstring, "MODIFIED");
  if (ta_col->tc_flags & TA_ASN1)
    Tcl_DStringAppendElement(dstring, "ASN1");
  Tcl_DStringEndSublist(dstring);
#endif
#if 0
  Tcl_DStringAppendElement(dstring, en_obj->en_type);
#endif
  for (i = 0; i < en_obj->en_cols.en_cols_len; i++) {
    expand_entry_col(dstring, &(en_obj->en_cols.en_cols_val[i]));
  }
  return 0;
}

static int
expand_entry_col(Tcl_DString* dstring, entry_col *en_col)
{
  Tcl_DStringAppendElement(dstring, en_col->ec_value.ec_value_val);
  return 0;
}

/* --- last line of nis_tcl.c --- */

/*
 *
 *
 * NIS types -
 *    typedef char* nis_name
 *    nis_result   (a big structure)
 *
 for nis_lists ...
FOLLOW_LINKS
FOLLOW_PATH
HARD_LOOKUP
ALL_RESULTS  
NO_CACHE
MASTER_ONLY
EXPAND_NAME
RETURN_RESULT

 *  nis_admin -
 *   void nis_ping(const nis_name dirname, const u_long utime,
 *	  const nis_object *dirobj);
 *   nis_result *nis_checkpoint(const nis_name dirname);
 *
 *  nis_db -
 *   bool db_initialize(const char *dictionary_pathname);
 *   bool db_create_table(const char *table_name, const table_obj *table);
 *   bool db_destroy_table(const char *table_name);
 *   db_result *db_first_entry(const char *table_name, const int numattrs,
 *	  const nis_attr *attrs);
 *   db_result *db_next_entry(const char *table_name,
 *	  const db_next_desc *next_handle);
 *   db_result *db_reset_next_entry(const char *table_name,
 *   db_result *db_list_entries(const char *table_name,
 *	  const int numattrs, const nis_attr *attrs);
 *   db_result *db_remove_entry(const char *table_name,
 *	  const int numattrs, const nis_attr *attrs);
 *   db_result *db_add_entry(const char *table_name, const int numattrs,
 *	  const nis_attr *attrs, const entry_obj *entry);
 *   db_status db_checkpoint(const char *table_name);
 *   db_status db_standby(const char *table_name );
 *
 * nis_error -
 *   char *nis_sperrno(const nis_error status);
 *   void nis_perror(const nis_error status, const char *label);
 *   void nis_lerror(const nis_error status, const char *label);
 *   char *nis_sperror(const nis_error status, const char *label);
 *   char *nis_sperror_r(nis_error status, char *label, char * buf);
 *
 * nis_groups -
 *   bool_t nis_ismember(const nis_name principal, const nis_name group);
 *   nis_error nis_addmember(const nis_name member, const nis_name group);
 *   nis_error nis_removemember(const nis_name member, const nis_name group);
 *   nis_error nis_creategroup(const nis_name group, const u_long flags);
 *   nis_error nis_destroygroup(const nis_name group);
 *   void nis_print_group_entry(const nis_name group);
 *   nis_error nis_verifygroup(const nis_name group);
 *
 * nis_server -
 *   nis_error nis_mkdir(const nis_name dirname,
 *        const nis_server *machine);
 *   nis_error nis_rmdir(const nis_name dirname,
 *        const nis_server *machine);
 *   nis_error nis_servstate(const nis_server *machine,
 *        const nis_tag *tags,
 *        const int numtags, nis_tag **result);
 *   nis_error nis_stats(const nis_server *machine,
 *        const nis_tag *tags, const int numtags,
 *        nis_tag **result);
 *   void nis_freetags(nis_tag *tags, const int numtags);
 *   nis_server **nis_getservlist(const nis_name dirname);
 *   void nis_freeservlist(nis_server **machines);
 *
 * nis_subr -
 *   nis_name nis_leaf_of(const nis_name name);
 *   nis_name nis_name_of(const nis_name name);
 *   nis_name nis_domain_of(const nis_name name);
 *   nis_name *nis_getnames(const nis_name name);
 *   void nis_freenames(nis_name *namelist);
 *   name_pos nis_dir_cmp(const nis_name n1, const nis_name n2);
 *   nis_object *nis_clone_object(const nis_object *src, nis_object *dest);
 *   void nis_destroy_object(nis_object *obj);
 *   void nis_print_object(const nis_object *obj);
 *
 * nis_names -
 * nis_tables -
 */
