/*
 * toolutils.{cc,hh} -- utility routines for tools
 * Eddie Kohler
 *
 * Copyright (c) 1999-2000 Massachusetts Institute of Technology
 * Copyright (c) 2000 Mazu Networks, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>

#include <click/straccum.hh>
#include <click/bitvector.hh>
#include "routert.hh"
#include "lexert.hh"
#include "toolutils.hh"
#include <click/confparse.hh>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <dirent.h>
#include <stdarg.h>

bool ignore_line_directives = false;


String
shell_command_output_string(String cmdline, const String &input, ErrorHandler *errh)
{
  FILE *f = tmpfile();
  if (!f)
    errh->fatal("cannot create temporary file: %s", strerror(errno));
  fwrite(input.data(), 1, input.length(), f);
  fflush(f);
  rewind(f);
  
  String new_cmdline = cmdline + " 0<&" + String(fileno(f));
  FILE *p = popen(new_cmdline.c_str(), "r");
  if (!p)
    errh->fatal("'%s': %s", cmdline.c_str(), strerror(errno));

  StringAccum sa;
  while (!feof(p)) {
    if (char *s = sa.reserve(2048)) {
      int x = fread(s, 1, 2048, p);
      if (x > 0)
	sa.forward(x);
    } else /* out of memory */
      break;
  }
  if (!feof(p))
    errh->warning("'%s' output too long, truncated", cmdline.c_str());

  fclose(f);
  pclose(p);
  return sa.take_string();
}


RouterT *
read_router_string(String text, const String &landmark, bool empty_ok,
		   ErrorHandler *errh)
{
  // check for archive
  Vector<ArchiveElement> archive;
  if (text.length() && text[0] == '!') {
    separate_ar_string(text, archive, errh);
    int found = -1;
    for (int i = 0; i < archive.size(); i++)
      if (archive[i].name == "config")
	found = i;
    if (found >= 0)
      text = archive[found].data;
    else {
      errh->lerror(landmark, "archive has no 'config' section");
      text = String();
    }
  }

  // read router
  if (!text.length() && !empty_ok)
    errh->lwarning(landmark, "empty configuration");
  LexerT lexer(errh, ignore_line_directives);
  lexer.reset(text, landmark);
  RouterT *router = lexer.router();
  
  // add archive bits first
  if (router && archive.size()) {
    for (int i = 0; i < archive.size(); i++)
      if (archive[i].live() && archive[i].name != "config")
	router->add_archive(archive[i]);
  }

  // read statements
  while (lexer.ystatement())
    /* nada */;

  // done
  return lexer.finish();
}

RouterT *
read_router_string(const String &text, const String &landmark, ErrorHandler *errh)
{
  return read_router_string(text, landmark, false, errh);
}

RouterT *
read_router_file(const char *filename, bool empty_ok, ErrorHandler *errh)
{
  if (!errh)
    errh = ErrorHandler::silent_handler();

  // read file string
  int old_nerrors = errh->nerrors();
  String s = file_string(filename, errh);
  if (!s && errh->nerrors() != old_nerrors)
    return 0;

  return read_router_string(s, filename_landmark(filename), empty_ok, errh);
}

RouterT *
read_router_file(const char *filename, ErrorHandler *errh)
{
  return read_router_file(filename, false, errh);
}

RouterT *
read_router(const String &whatever, bool is_expr, ErrorHandler *errh)
{
  if (is_expr)
    return read_router_string(whatever, "<expr>", errh);
  else
    return read_router_file(whatever.c_str(), false, errh);
}


void
write_router_file(RouterT *r, FILE *f, ErrorHandler *errh)
{
  if (!r)
    return;
  
  String config_str = r->configuration_string();
  
  // create archive if necessary
  const Vector<ArchiveElement> &archive = r->archive();
  if (archive.size()) {
    Vector<ArchiveElement> narchive;
    
    // add configuration
    ArchiveElement config_ae;
    config_ae.name = "config";
    config_ae.date = time(0);
    config_ae.uid = geteuid();
    config_ae.gid = getegid();
    config_ae.mode = 0644;
    config_ae.data = config_str;
    narchive.push_back(config_ae);
    
    // add other archive elements
    for (int i = 0; i < archive.size(); i++)
      if (archive[i].live() && archive[i].name != "config")
	narchive.push_back(archive[i]);

    if (narchive.size() > 1)
      config_str = create_ar_string(narchive, errh);
  }
  
  fwrite(config_str.data(), 1, config_str.length(), f);
}

int
write_router_file(RouterT *r, const char *name, ErrorHandler *errh)
{
  if (name && strcmp(name, "-") != 0) {
    FILE *f = fopen(name, "wb");
    if (!f) {
      if (errh)
	errh->error("%s: %s", name, strerror(errno));
      return -1;
    }
    write_router_file(r, f, errh);
    fclose(f);
  } else
    write_router_file(r, stdout, errh);
  return 0;
}


String
xml_quote(const String &str)
{
    const char *s = str.data();
    const char *ends = s + str.length();
    const char *first = s;
    StringAccum sa;
    for (; s < ends; s++)
	if (*s == '&' || *s == '<' || *s == '\"') {
	    sa.append(first, s - first);
	    sa << '&' << (*s == '&' ? "amp" : (*s == '<' ? "lt" : "quot")) << ';';
	    first = s + 1;
	}
    if (sa) {
	sa.append(first, s - first);
	return sa.take_string();
    } else
	return str;
}


syntax highlighted by Code2HTML, v. 0.9.1