/* Copyright (c) 1996, 1997, 1998 Thorsten Kukuk
   Author: Thorsten Kukuk <kukuk@vt.uni-paderborn.de>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define _GNU_SOURCE

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "lib/compat/getopt.h"
#endif
#include <locale.h>
#include <libintl.h>
#include <time.h>
#include <memory.h>
#include <rpcsvc/nis.h>

#ifndef _
#define _(String) gettext (String)
#endif

/* Print the version information.  */
static inline void
print_version (void)
{
  fprintf (stdout, "nisln (%s) %s\n", PACKAGE, VERSION);
  fprintf (stdout, gettext ("\
Copyright (C) %s Thorsten Kukuk.\n\
This is free software; see the source for copying conditions.  There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
"), "1998");
  /* fprintf (stdout, _("Written by %s.\n"), "Thorsten Kukuk"); */
}

static inline void
print_usage (void)
{
  fputs (_("nisln [-L] [-D defaults] name linkname\n"), stdout);
}

static inline  void
print_help (void)
{
  print_usage ();
  fputs (_("nisln - symbolically link NIS+ objects\n\n"),
         stdout);

  fputs (_("  -L             Follow links\n"), stdout);
  fputs (_("  -D defaults    Use this defaults values for new object\n"),
	 stdout);
  fputs (_("  --help         Give this help list\n"), stdout);
  fputs (_("  --usage        Give a short usage message\n"), stdout);
  fputs (_("  --version      Print program version\n"), stdout);
}

static inline void
print_error (void)
{
  const char *program = "nisln";

  fprintf (stderr,
           _("Try `%s --help' or `%s --usage' for more information.\n"),
           program, program);
}

/* from nis/nis_table.c from glibc snapshot 19970924 */
static void
splitname (const_nis_name name, nis_name *ibr_name, int *srch_len,
           nis_attr **srch_val)
{
  char *cptr, *key, *val, *next;
  int size;

  if (name == NULL)
    return;

  cptr = strdup (name);
  if (srch_len)
    *srch_len = 0;
  if (srch_val)
    *srch_val = NULL;
  size = 0;

  /* Not of "[key=value,key=value,...],foo.." format? */
  if (cptr[0] != '[')
    {
      *ibr_name = cptr;
      return;
    }

  *ibr_name = strchr (cptr, ']');
  if (*ibr_name == NULL || (*ibr_name)[1] != ',')
    {
      free (cptr);
      *ibr_name = NULL;
      return;
    }

  *ibr_name[0] = '\0';
  *ibr_name += 2;
  *ibr_name = strdup (*ibr_name);

  if (srch_len == NULL || srch_val == NULL)
    {
      free (cptr);
      return;
    }

  key = (cptr) + 1;
  do
    {
      next = strchr (key, ',');
      if (next)
        {
          next[0] = '\0';
          ++next;
        }

      val = strchr (key, '=');
      if (!val)
        {
          free (cptr);
          *srch_val = malloc (sizeof (nis_attr));
          if (*srch_val == NULL)
            {
              free (cptr);
              free (*ibr_name);
              *ibr_name = NULL;
              return;
            }
          (*srch_val)[0].zattr_val.zattr_val_len = 0;
          (*srch_val)[0].zattr_val.zattr_val_val = NULL;
          return;
        }

      val[0] = '\0';
      ++val;
      if ((*srch_len) + 1 >= size)
        {
          size += 10;
          if (size == 10)
            *srch_val = malloc (size * sizeof (char *));
          else
            *srch_val = realloc (val, size * sizeof (char *));
          if (*srch_val == NULL)
            {
              free (cptr);
              free (*ibr_name);
              *ibr_name = NULL;
              return;
            }
        }

      (*srch_val)[*srch_len].zattr_ndx = strdup (key);
      if (((*srch_val)[*srch_len].zattr_ndx) == NULL)
        {
          free (cptr);
          free (*ibr_name);
          *ibr_name = NULL;
          return;
        }
      (*srch_val)[*srch_len].zattr_val.zattr_val_len = strlen (val) + 1;
      (*srch_val)[*srch_len].zattr_val.zattr_val_val = strdup (val);
      if ((*srch_val)[*srch_len].zattr_val.zattr_val_val == NULL)
        {
          free (cptr);
          free (*ibr_name);
          *ibr_name = NULL;
          return;
        }
      ++(*srch_len);

      key = next;

    }
  while (next);

  free (cptr);

  return;
}

int
main (int argc, char *argv[])
{
  nis_result *res;
  nis_object obj;
  link_obj *lobj;
  unsigned long flags = MASTER_ONLY;
  char *defarg = NULL;

  setlocale (LC_MESSAGES, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  while (1)
    {
      int c;
      int option_index = 0;
      static struct option long_options[] =
      {
        {"version", no_argument, NULL, '\255'},
        {"usage", no_argument, NULL, '\254'},
        {"help", no_argument, NULL, '\253'},
        {NULL, 0, NULL, '\0'}
      };

      c = getopt_long (argc, argv, "LD:", long_options, &option_index);
      if (c == (-1))
        break;
      switch (c)
	{
	case 'L':
	  flags |= FOLLOW_LINKS;
	  break;
	case 'D':
	  defarg = optarg;
	  break;
        case '\253':
          print_help ();
          return 0;
        case '\255':
          print_version ();
          return 0;
        case '\254':
          print_usage ();
          return 0;
        default:
          print_error ();
          return 1;
	}
    }

  argc -= optind;
  argv += optind;

  if (argc < 2)
    {
      fprintf (stderr, _("%s: To few arguments\n"), "nisln");
      print_error ();
      return 1;
    }

  memset ((char *) &obj, 0, sizeof (obj));

  if (argv[0][strlen (argv[0]) - 1] != '.')
    {
      fputs (_("object name must be fully qualified.\n"), stderr);
      print_error ();
      return 1;
    }

  if (argv[1][strlen (argv[1]) - 1] != '.')
    {
      fputs (_("link name must be fully qualified.\n"), stderr);
      print_error ();
      return 1;
    }

  res = nis_lookup (nis_domain_of (argv[1]), 0);

  if (res->status != NIS_SUCCESS)
    {
      fprintf (stderr, _("bad link domain '%s': %s\n"), nis_domain_of (argv[1]),
	       nis_sperrno (res->status));
      nis_freeresult (res);
      return 1;
    }

  nis_freeresult (res);

  if (strchr (argv[0], '[') != NULL)
    res = nis_list (argv[0], flags, NULL, NULL);
  else
    res = nis_lookup (argv[0], flags);

  if (res->status != NIS_SUCCESS)
    {
      fprintf (stderr, "%s: %s\n", argv[0], nis_sperrno (res->status));
      nis_freeresult (res);
      return 1;
    }

  if (__type_of (NIS_RES_OBJECT (res)) == NIS_DIRECTORY_OBJ)
    {
      fprintf (stderr, _("%s is a directory!\n"), argv[0]);
      nis_freeresult (res);
      return 1;
    }

  obj.zo_name = strdup (nis_leaf_of (argv[1]));
  obj.zo_domain = strdup (nis_domain_of (argv[1]));
  obj.zo_owner = strdup (__nis_default_owner (defarg));
  obj.zo_group = strdup (__nis_default_group (defarg));
  obj.zo_access = __nis_default_access (defarg, 0);
  obj.zo_ttl = __nis_default_ttl (defarg);
  __type_of (&obj) = NIS_LINK_OBJ;
  lobj = &obj.LI_data;

  lobj->li_rtype = __type_of (NIS_RES_OBJECT (res));

  if (strchr (argv[0], '['))
    {
      splitname (argv[0], &lobj->li_name, &lobj->li_attrs.li_attrs_len,
		 &lobj->li_attrs.li_attrs_val);
    }
  else
    {
      lobj->li_name = argv[0];
    }

  res = nis_add (argv[1], &obj);

  if (res->status != NIS_SUCCESS)
    {
      fprintf (stderr, _("failed to add link: %s\n"),
	       nis_sperrno (res->status));
      nis_freeresult (res);
      return 1;
    }
  nis_freeresult (res);
  return 0;
}
