/* Copyright (c) 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.  */

#if defined(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 <stdio.h>
#include <getopt.h>
#include <string.h>
#include <locale.h>
#include <libintl.h>
#include <rpcsvc/nis.h>

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

static int raw_binary = 0;
static int cnt_only = 0;
static int print_object = 0;
static unsigned long cnt_match = 0;

static int
print_obj (const_nis_name name __attribute__ ((unused)),
	   const nis_object *obj, const void *udata)
{
  const char *separator = (const char *)udata;
  u_int j;

  ++cnt_match;
  if (cnt_only)
    return 0;
  else
    if (print_object)
      nis_print_object (obj);
    else
      {
	for (j = 0; j < obj->EN_data.en_cols.en_cols_len; ++j)
	  {
	    if (ENTRY_VAL (obj, j) != NULL)
	      {
		if ((obj->EN_data.en_cols.en_cols_val->ec_flags & EN_BINARY)
		    && !raw_binary)
		  fputs ("*BINARY*", stdout);
		else
		  {
		    fprintf (stdout, "%.*s", (int)ENTRY_LEN (obj,j),
			     ENTRY_VAL (obj, j));
		  }
	      }
	    if (j + 1 < obj->EN_data.en_cols.en_cols_len)
	      fprintf (stdout, "%c", *separator);
	  }
	fputs ("\n", stdout);
      }
  return 0;
}

/* Print the version information.  */
static inline void
print_version (void)
{
  fprintf (stdout, "nismatch (%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 (_("Usage: nismatch [-AMPchov] [-s sep] key tablename\n"), stdout);
  fputs (_("       nismatch [-AMPchov] [-s sep] colname=key ... tablename\n"),
	 stdout);
  fputs (_("       nismatch [-AMPchov] [-s sep] indexedname\n"), stdout);
}

static void
print_help (void)
{
  print_usage ();
  fputs (_("nismatch - searching in NIS+ tables\n\n"),
         stdout);
  fputs (_("  -A             Use ALL_RESULTS flag for lookups\n"), stdout);
  fputs (_("  -M             Query master server only\n"), stdout);
  fputs (_("  -P             Follow concatenation path\n"), stdout);
  fputs (_("  -c             Print only count of found matches\n"), stdout);
  fputs (_("  -h             Print names of columns\n"), stdout);
  fputs (_("  -o             Display internal representation of object\n"),
         stdout);
  fputs (_("  -s sep         Use \"sep\" to separate the table columns\n"),
         stdout);
  fputs (_("  -v             Display binary data directly\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 = "nismatch";

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

int
main (int argc, char *argv[])
{
  nis_result *res = NULL;
  char separator = '\t';
  int new_sep = 0;
  int display_header = 0;
  int flags = EXPAND_NAME;
  char *buf;

  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, "AMPchos:uv", long_options, &option_index);
      if (c == (-1))
	break;
      switch (c)
	{
	case 'o':
	  if (display_header)
	    {
	      fputs ("You could not use -h and -o !\n", stderr);
	      print_error ();
	      return 1;
	    }
	  print_object = 1;
	  break;
	case 'h':
	  if (print_object)
	    {
	      fputs ("You could not use -h and -o !\n", stderr);
	      print_error ();
	      return 1;
	    }
	  display_header = 1;
	  break;
        case 's':
          new_sep = 1;
          separator = optarg[0];
          break;
	case 'A': /* ALL_RESULTS implies FOLLOW_PATH */
	  flags |= ALL_RESULTS;
	case 'P':
	  flags |= FOLLOW_PATH;
	  break;
	case 'M':
	  flags |= MASTER_ONLY;
	  break;
	case 'v':
	  ++raw_binary;
	  break;
	case 'c':
	  cnt_only = 1;
	  break;
        case '\253':
          print_help ();
          return 0;
        case '\255':
          print_version ();
          return 0;
        case '\254':
          print_usage ();
          return 0;
        default:
          print_error ();
          return 2;
	}
    }

  argc -= optind;
  argv += optind;

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

  if (argv[0][0] == '[')	/* Indexed names! */
    {
      char *p;

      p = strchr (argv[0], ']');
      if (p == NULL)
	{
	  fputs (_("badly formatted index name\n"), stderr);
	  return 2;
	}
      ++p;
      if (p == NULL)
	{
	  fputs (_("tablename is missing\n"), stderr);
	  print_error ();
	  return 2;
	}
      if (*p == ',')
	++p;
      res = nis_lookup (p, flags);
    }
  else
    {
      if (argc == 1)
	{
	  print_error ();
	  return 2;
	}
      if ((strchr(argv[0], '=') == NULL) && (argc > 2))
	{
	  /* If we have no "=", we could only have one key,
	     but make a sanity check first. */
	  fputs (_("only one implicit column name allowed.\n"), stderr);
	  print_error ();
	  return 2;
	}
      res = nis_lookup (argv[argc - 1], flags);
    }

  if (res == NULL ||
      (res->status != NIS_SUCCESS && res->status != NIS_S_SUCCESS))
    {
      if (res == NULL)
	fputs (_("Out of memory!\n"), stderr);
      else
	nis_perror (res->status, "nismatch");
      nis_freeresult (res);
      return 2;
    }

  if (__type_of(NIS_RES_OBJECT(res)) != NIS_TABLE_OBJ)
    {
      fprintf (stderr, _("%s.%s is not a table!\n"),
	       NIS_RES_OBJECT (res)->zo_name,
	       NIS_RES_OBJECT (res)->zo_domain);
      nis_freeresult(res);
      return 2;
    }

  /* Get the contents of the table. We have 3 cases:
     1: indexed names, very nice.
     2: only one key, nice, too
     3: Not indexed, but we could generate an indexed name */
  if (argv[0][0] == '[')	/* Indexed names! */
    {
      buf = alloca (strlen (argv[0]) + 1);
      if (buf == NULL)
	{
	  fputs (_("Out of memory!\n"), stderr);
	  return 2;
	}
      strcpy (buf, argv[0]);
    }
  else
    {
      if (strchr (argv[0], '=') == NULL) /* case 2 */
	{
	  if (!(NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_val[0].tc_flags
		& TA_SEARCHABLE))
	    {
	      fputs (_("table column is not searchable\n"), stderr);
	      nis_freeresult(res);
	      return 2;
	    }
	  buf = alloca (strlen (NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_val[0].tc_name) + strlen (argv[0]) + strlen (argv[1]) + 20);
	  if (buf == NULL)
	    {
	      fputs (_("Out of memory!\n"), stderr);
	      return 2;
	    }
	  sprintf (buf, "[%s=%s],%s",
		   NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_val[0].tc_name,
		   argv[0], argv[1]);
	}
      else /* case 3, not so easy */
	{
	  long i;

	  buf = alloca (NIS_MAXNAMELEN + 1); /* XXX make it better here ! */
	  if (buf == NULL)
	    {
	      fputs (_("Out of memory!\n"), stderr);
	      return 2;
	    }
	  strcpy (buf, "[");
	  for(i = 0; i < argc - 1; ++i)
	    {
	      unsigned int j;
	      char *cp = strchr (argv[i], '=');
	      if (cp  == NULL)
		{
		  fprintf (stderr, _("%s: badly formatted search pattern\n"),
			   argv[i]);
		  print_error ();
		  return 2;
		}
	      for (j = 0; j < NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_len;
		   ++j)
		{
		  if (strncasecmp (NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_val[j].tc_name,
				   argv[i], cp - argv[i]) == 0)
		    {
		      if (NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_val[j].tc_flags & TA_SEARCHABLE)
			{
			  strncat (buf, argv[i],  NIS_MAXNAMELEN - strlen (buf));
			  strncat (buf, ",", NIS_MAXNAMELEN - strlen (buf));
			  break;
			}
		      else
			{
			  *cp = '\0';
			  fprintf (stderr,
				   _("table column '%s' is not searchable\n"),
				   argv[i]);
			  return 2;
			}
		    }
		}
	      if (j == NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_len)
		{
		  *cp = '\0';
		  fprintf (stderr, _("table '%s.%s' has no such column '%s'\n"),
			   NIS_RES_OBJECT (res)->zo_name,
			   NIS_RES_OBJECT (res)->zo_domain, argv[i]);
		  return 2;
		}
	    }
	  buf[strlen (buf) - 1] = '\0';
	  strncat (buf, "],", NIS_MAXNAMELEN - strlen (buf));
	  strncat (buf, argv[argc - 1], NIS_MAXNAMELEN - strlen (buf));
	}
    }
  if (!new_sep)
    {
      /* Get the separator character */
      separator = res->objects.objects_val->TA_data.ta_sep;
    }
  /* Should we print the header line with the name of the columns ? */
  if (display_header && !print_object && !cnt_only)
    {
      unsigned int i;

      fprintf (stdout, "# ");
      for (i = 0;
	   i < NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_len;
	   ++i)
	{
	  fprintf (stdout, "%s", NIS_RES_OBJECT(res)->TA_data.ta_cols.ta_cols_val[i].tc_name);
	  if (i == NIS_RES_OBJECT (res)->TA_data.ta_cols.ta_cols_len - 1)
	    fputs ("\n", stdout);
	  else
	    fprintf (stdout, "%c", separator);
	}
    }

  /* Now we try to get the contents of the table */
  res = nis_list (buf, flags, print_obj, (void *)&separator);
  if (res == NULL || (res->status != NIS_SUCCESS && res->status != NIS_CBRESULTS))
    {
      if (res == NULL)
	{
	  fputs (_("Out of memory!\n"), stderr);
	  return 2;
	}
      else
	{
	  if (res->status != NIS_PARTIAL)
	    {
	      nis_freeresult (res);
	      return 2;
	    }
	}
    }
  nis_freeresult (res);

  if (cnt_only)
    fprintf (stdout, "%lu\n", cnt_match);

  if (cnt_match)
    return 0;
  else
    return 1;
}
