code:
/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010
 * Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "libguile/__scm.h"
#include "libguile/_scm.h"
#include "libguile/continuations.h"
#include "libguile/eq.h"
#include "libguile/expand.h"
#include "libguile/list.h"
#include "libguile/macros.h"
#include "libguile/memoize.h"
#include "libguile/modules.h"
#include "libguile/srcprop.h"
#include "libguile/ports.h"
#include "libguile/print.h"
#include "libguile/strings.h"
#include "libguile/throw.h"
#include "libguile/validate.h"

#define CAR(x)   SCM_CAR(x)
#define CDR(x)   SCM_CDR(x)
#define CAAR(x)  SCM_CAAR(x)
#define CADR(x)  SCM_CADR(x)
#define CDAR(x)  SCM_CDAR(x)
#define CDDR(x)  SCM_CDDR(x)
#define CADDR(x) SCM_CADDR(x)
#define CDDDR(x) SCM_CDDDR(x)
#define CADDDR(x) SCM_CADDDR(x)

SCM_SYMBOL (sym_case_lambda_star, "case-lambda*");

/* {Evaluator memoized expressions}
 */

scm_t_bits scm_tc16_memoized;

#define MAKMEMO(n, args) 	(scm_cell (scm_tc16_memoized | ((n) << 16), (scm_t_bits)(args)))

#define MAKMEMO_BEGIN(exps) \
  MAKMEMO (SCM_M_BEGIN, exps)
#define MAKMEMO_IF(test, then, else_) \
  MAKMEMO (SCM_M_IF, scm_cons (test, scm_cons (then, else_)))
#define FIXED_ARITY(nreq) \
  scm_list_1 (SCM_I_MAKINUM (nreq))
#define REST_ARITY(nreq, rest) \
  scm_list_2 (SCM_I_MAKINUM (nreq), rest)
#define FULL_ARITY(nreq, rest, nopt, kw, inits, alt) \
  scm_list_n (SCM_I_MAKINUM (nreq), rest, SCM_I_MAKINUM (nopt), kw, inits, \
              alt, SCM_UNDEFINED)
#define MAKMEMO_LAMBDA(body, arity) \
  MAKMEMO (SCM_M_LAMBDA, (scm_cons (body, arity)))
#define MAKMEMO_LET(inits, body) \
  MAKMEMO (SCM_M_LET, scm_cons (inits, body))
#define MAKMEMO_QUOTE(exp) \
  MAKMEMO (SCM_M_QUOTE, exp)
#define MAKMEMO_DEFINE(var, val) \
  MAKMEMO (SCM_M_DEFINE, scm_cons (var, val))
#define MAKMEMO_DYNWIND(in, expr, out) \
  MAKMEMO (SCM_M_DYNWIND, scm_cons (in, scm_cons (expr, out)))
#define MAKMEMO_WITH_FLUIDS(fluids, vals, expr) \
  MAKMEMO (SCM_M_WITH_FLUIDS, scm_cons (fluids, scm_cons (vals, expr)))
#define MAKMEMO_APPLY(proc, args)\
  MAKMEMO (SCM_M_APPLY, scm_list_2 (proc, args))
#define MAKMEMO_CONT(proc) \
  MAKMEMO (SCM_M_CONT, proc)
#define MAKMEMO_CALL_WITH_VALUES(prod, cons) \
  MAKMEMO (SCM_M_CALL_WITH_VALUES, scm_cons (prod, cons))
#define MAKMEMO_CALL(proc, nargs, args) \
  MAKMEMO (SCM_M_CALL, scm_cons (proc, scm_cons (SCM_I_MAKINUM (nargs), args)))
#define MAKMEMO_LEX_REF(n) \
  MAKMEMO (SCM_M_LEXICAL_REF, SCM_I_MAKINUM (n))
#define MAKMEMO_LEX_SET(n, val) \
  MAKMEMO (SCM_M_LEXICAL_SET, scm_cons (SCM_I_MAKINUM (n), val))
#define MAKMEMO_TOP_REF(var) \
  MAKMEMO (SCM_M_TOPLEVEL_REF, var)
#define MAKMEMO_TOP_SET(var, val) \
  MAKMEMO (SCM_M_TOPLEVEL_SET, scm_cons (var, val))
#define MAKMEMO_MOD_REF(mod, var, public) \
  MAKMEMO (SCM_M_MODULE_REF, scm_cons (mod, scm_cons (var, public)))
#define MAKMEMO_MOD_SET(val, mod, var, public) \
  MAKMEMO (SCM_M_MODULE_SET, scm_cons (val, scm_cons (mod, scm_cons (var, public))))
#define MAKMEMO_PROMPT(tag, exp, handler) \
  MAKMEMO (SCM_M_PROMPT, scm_cons (tag, scm_cons (exp, handler)))

/* Primitives for the evaluator */
scm_t_bits scm_tc16_memoizer;
#define SCM_MEMOIZER_P(x) (SCM_SMOB_PREDICATE (scm_tc16_memoizer, (x)))
#define SCM_MEMOIZER(M) (SCM_SMOB_OBJECT_1 (M))

/* This table must agree with the list of M_ constants in memoize.h */
static const char *const memoized_tags[] =
{
  "begin",
  "if",
  "lambda",
  "let",
  "quote",
  "define",
  "dynwind",
  "with-fluids",
  "apply",
  "call/cc",
  "call-with-values",
  "call",
  "lexical-ref",
  "lexical-set!",
  "toplevel-ref",
  "toplevel-set!",
  "module-ref",
  "module-set!",
  "prompt",
};

static int
scm_print_memoized (SCM memoized, SCM port, scm_print_state *pstate)
{
  scm_puts ("#<memoized ", port);
  scm_write (scm_unmemoize_expression (memoized), port);
  scm_puts (">", port);
  return 1;
}

static int
lookup (SCM x, SCM env)
{
  int i = 0;
  for (; scm_is_pair (env); env = CDR (env), i++)
    if (scm_is_eq (x, CAR (env)))
      return i; /* bound */
  abort ();
}

/* Abbreviate SCM_EXPANDED_REF. Copied because I'm not sure about symbol pasting */
#define REF(x,type,field) \
  (scm_struct_ref (x, SCM_I_MAKINUM (SCM_EXPANDED_##type##_##field)))

static SCM list_of_guile = SCM_BOOL_F;

static SCM memoize (SCM exp, SCM env);

static SCM
memoize_exps (SCM exps, SCM env)
{
  SCM ret;
  for (ret = SCM_EOL; scm_is_pair (exps); exps = CDR (exps))
    ret = scm_cons (memoize (CAR (exps), env), ret);
  return scm_reverse_x (ret, SCM_UNDEFINED);
}

static SCM
memoize (SCM exp, SCM env)
{
  if (!SCM_EXPANDED_P (exp))
    abort ();

  switch (SCM_EXPANDED_TYPE (exp))
    {
    case SCM_EXPANDED_VOID:
      return MAKMEMO_QUOTE (SCM_UNSPECIFIED);

    case SCM_EXPANDED_CONST:
      return MAKMEMO_QUOTE (REF (exp, CONST, EXP));

    case SCM_EXPANDED_PRIMITIVE_REF:
      if (scm_is_eq (scm_current_module (), scm_the_root_module ()))
        return MAKMEMO_TOP_REF (REF (exp, PRIMITIVE_REF, NAME));
      else
        return MAKMEMO_MOD_REF (list_of_guile, REF (exp, PRIMITIVE_REF, NAME),
                                SCM_BOOL_F);

    case SCM_EXPANDED_LEXICAL_REF:
      return MAKMEMO_LEX_REF (lookup (REF (exp, LEXICAL_REF, GENSYM), env));

    case SCM_EXPANDED_LEXICAL_SET:
      return MAKMEMO_LEX_SET (lookup (REF (exp, LEXICAL_SET, GENSYM), env),
                              memoize (REF (exp, LEXICAL_SET, EXP), env));

    case SCM_EXPANDED_MODULE_REF:
      return MAKMEMO_MOD_REF (REF (exp, MODULE_REF, MOD),
                              REF (exp, MODULE_REF, NAME),
                              REF (exp, MODULE_REF, PUBLIC));

    case SCM_EXPANDED_MODULE_SET:
      return MAKMEMO_MOD_SET (memoize (REF (exp, MODULE_SET, EXP), env),
                              REF (exp, MODULE_SET, MOD),
                              REF (exp, MODULE_SET, NAME),
                              REF (exp, MODULE_SET, PUBLIC));

    case SCM_EXPANDED_TOPLEVEL_REF:
      return MAKMEMO_TOP_REF (REF (exp, TOPLEVEL_REF, NAME));

    case SCM_EXPANDED_TOPLEVEL_SET:
      return MAKMEMO_TOP_SET (REF (exp, TOPLEVEL_SET, NAME),
                              memoize (REF (exp, TOPLEVEL_SET, EXP), env));

    case SCM_EXPANDED_TOPLEVEL_DEFINE:
      return MAKMEMO_DEFINE (REF (exp, TOPLEVEL_DEFINE, NAME),
                             memoize (REF (exp, TOPLEVEL_DEFINE, EXP), env));

    case SCM_EXPANDED_CONDITIONAL:
      return MAKMEMO_IF (memoize (REF (exp, CONDITIONAL, TEST), env),
                         memoize (REF (exp, CONDITIONAL, CONSEQUENT), env),
                         memoize (REF (exp, CONDITIONAL, ALTERNATE), env));

    case SCM_EXPANDED_APPLICATION:
      {
        SCM proc, args;

        proc = REF (exp, APPLICATION, PROC);
        args = memoize_exps (REF (exp, APPLICATION, ARGS), env);

        if (SCM_EXPANDED_TYPE (proc) == SCM_EXPANDED_TOPLEVEL_REF)
          {
            SCM var = scm_module_variable (scm_current_module (),
                                           REF (proc, TOPLEVEL_REF, NAME));
            if (SCM_VARIABLEP (var))
              {
                SCM val = SCM_VARIABLE_REF (var);
                if (SCM_MEMOIZER_P (val))
                  return scm_apply (SCM_SMOB_OBJECT_1 (val), args, SCM_EOL);
              }
          }
        /* otherwise we all fall down here */
        return MAKMEMO_CALL (memoize (proc, env), scm_ilength (args), args);
      }

    case SCM_EXPANDED_SEQUENCE:
      return MAKMEMO_BEGIN (memoize_exps (REF (exp, SEQUENCE, EXPS), env));

    case SCM_EXPANDED_LAMBDA:
      /* The body will be a lambda-case. */
      return memoize (REF (exp, LAMBDA, BODY), env);

    case SCM_EXPANDED_LAMBDA_CASE:
      {
        SCM req, rest, opt, kw, inits, vars, body, alt;
        SCM walk, minits, arity, new_env;
        int nreq, nopt, ntotal;

        req = REF (exp, LAMBDA_CASE, REQ);
        rest = REF (exp, LAMBDA_CASE, REST);
        opt = REF (exp, LAMBDA_CASE, OPT);
        kw = REF (exp, LAMBDA_CASE, KW);
        inits = REF (exp, LAMBDA_CASE, INITS);
        vars = REF (exp, LAMBDA_CASE, GENSYMS);
        body = REF (exp, LAMBDA_CASE, BODY);
        alt = REF (exp, LAMBDA_CASE, ALTERNATE);

        nreq = scm_ilength (req);
        nopt = scm_is_pair (opt) ? scm_ilength (opt) : 0;
        ntotal = scm_ilength (vars);

        /* The vars are the gensyms, according to the divine plan. But we need
           to memoize the inits within their appropriate environment,
           complicating things. */
        new_env = env;
        for (walk = req; scm_is_pair (walk);
             walk = CDR (walk), vars = CDR (vars))
          new_env = scm_cons (CAR (vars), new_env);

        minits = SCM_EOL;
        for (walk = opt; scm_is_pair (walk);
             walk = CDR (walk), vars = CDR (vars), inits = CDR (inits))
          {
            minits = scm_cons (memoize (CAR (inits), new_env), minits);
            new_env = scm_cons (CAR (vars), new_env);
          }

        if (scm_is_true (rest))
          {
            new_env = scm_cons (CAR (vars), new_env);
            vars = CDR (vars);
          }

        for (; scm_is_pair (inits); vars = CDR (vars), inits = CDR (inits))
          {
            minits = scm_cons (memoize (CAR (inits), new_env), minits);
            new_env = scm_cons (CAR (vars), new_env);
          }
        if (!scm_is_null (vars))
          abort ();

        minits = scm_reverse_x (minits, SCM_UNDEFINED);

        if (scm_is_true (kw))
          {
            /* (aok? (kw name sym) ...) -> (aok? (kw . index) ...) */
            SCM aok = CAR (kw), indices = SCM_EOL;
            for (kw = CDR (kw); scm_is_pair (kw); kw = CDR (kw))
              {
                SCM k;
                int idx;

                k = CAR (CAR (kw));
                idx = ntotal - 1 - lookup (CADDR (CAR (kw)), new_env);
                indices = scm_acons (k, SCM_I_MAKINUM (idx), indices);
              }
            kw = scm_cons (aok, scm_reverse_x (indices, SCM_UNDEFINED));
          }

        if (scm_is_false (alt) && scm_is_false (kw) && scm_is_false (opt))
          {
            if (scm_is_false (rest))
              arity = FIXED_ARITY (nreq);
            else
              arity = REST_ARITY (nreq, SCM_BOOL_T);
          }
        else if (scm_is_true (alt))
          arity = FULL_ARITY (nreq, rest, nopt, kw, minits,
                              SCM_MEMOIZED_ARGS (memoize (alt, env)));
        else
          arity = FULL_ARITY (nreq, rest, nopt, kw, minits, SCM_BOOL_F);

        return MAKMEMO_LAMBDA (memoize (body, new_env), arity);
      }

    case SCM_EXPANDED_LET:
      {
        SCM vars, exps, body, inits, new_env;

        vars = REF (exp, LET, GENSYMS);
        exps = REF (exp, LET, VALS);
        body = REF (exp, LET, BODY);

        inits = SCM_EOL;
        new_env = env;
        for (; scm_is_pair (vars); vars = CDR (vars), exps = CDR (exps))
          {
            new_env = scm_cons (CAR (vars), new_env);
            inits = scm_cons (memoize (CAR (exps), env), inits);
          }

        return MAKMEMO_LET (scm_reverse_x (inits, SCM_UNDEFINED),
                            memoize (body, new_env));
      }

    case SCM_EXPANDED_LETREC:
      {
        SCM vars, exps, body, undefs, new_env;
        int i, nvars, in_order_p;

        vars = REF (exp, LETREC, GENSYMS);
        exps = REF (exp, LETREC, VALS);
        body = REF (exp, LETREC, BODY);
        in_order_p = scm_is_true (REF (exp, LETREC, IN_ORDER_P));
        nvars = i = scm_ilength (vars);
        undefs = SCM_EOL;
        new_env = env;

        for (; scm_is_pair (vars); vars = CDR (vars))
          {
            new_env = scm_cons (CAR (vars), new_env);
            undefs = scm_cons (MAKMEMO_QUOTE (SCM_UNDEFINED), undefs);
          }

        if (in_order_p)
          {
            SCM body_exps = SCM_EOL;
            for (; scm_is_pair (exps); exps = CDR (exps), i--)
              body_exps = scm_cons (MAKMEMO_LEX_SET (i-1,
                                                     memoize (CAR (exps), new_env)),
                                    body_exps);
            body_exps = scm_cons (memoize (body, new_env), body_exps);
            body_exps = scm_reverse_x (body_exps, SCM_UNDEFINED);
            return MAKMEMO_LET (undefs, MAKMEMO_BEGIN (body_exps));
          }
        else
          {
            SCM sets = SCM_EOL, inits = SCM_EOL;
            for (; scm_is_pair (exps); exps = CDR (exps), i--)
              {
                sets = scm_cons (MAKMEMO_LEX_SET ((i-1) + nvars,
                                                  MAKMEMO_LEX_REF (i-1)),
                                 sets);
                inits = scm_cons (memoize (CAR (exps), new_env), inits);
              }
            inits = scm_reverse_x (inits, SCM_UNDEFINED);
            return MAKMEMO_LET
              (undefs,
               MAKMEMO_BEGIN (scm_list_2 (MAKMEMO_LET (inits, MAKMEMO_BEGIN (sets)),
                                          memoize (body, new_env))));
          }
      }

    case SCM_EXPANDED_DYNLET:
      return MAKMEMO_WITH_FLUIDS (memoize_exps (REF (exp, DYNLET, FLUIDS), env),
                                  memoize_exps (REF (exp, DYNLET, VALS), env),
                                  memoize (REF (exp, DYNLET, BODY), env));

    default:
      abort ();
    }
}

SCM_DEFINE (scm_memoize_expression, "memoize-expression", 1, 0, 0,
            (SCM exp),
	    "Memoize the expression @var{exp}.")
#define FUNC_NAME s_scm_memoize_expression
{
  SCM_ASSERT_TYPE (SCM_EXPANDED_P (exp), exp, 1, FUNC_NAME, "expanded");
  return memoize (exp, scm_current_module ());
}
#undef FUNC_NAME

#define SCM_MAKE_MEMOIZER(STR, MEMOIZER, N)                             \
  (scm_cell (scm_tc16_memoizer,                                         \
             (scm_t_bits)(scm_c_make_gsubr (STR, N, 0, 0, MEMOIZER))))
#define SCM_DEFINE_MEMOIZER(STR, MEMOIZER, N)                           \
SCM_SNARF_INIT(scm_c_define (STR, SCM_MAKE_MEMOIZER (STR, MEMOIZER, N)))

static SCM m_apply (SCM proc, SCM args);
static SCM m_call_cc (SCM proc);
static SCM m_call_values (SCM prod, SCM cons);
static SCM m_dynamic_wind (SCM pre, SCM exp, SCM post);
static SCM m_prompt (SCM tag, SCM exp, SCM handler);

SCM_DEFINE_MEMOIZER ("@apply", m_apply, 2);
SCM_DEFINE_MEMOIZER ("@call-with-current-continuation", m_call_cc, 1);
SCM_DEFINE_MEMOIZER ("@call-with-values", m_call_values, 2);
SCM_DEFINE_MEMOIZER ("@dynamic-wind", m_dynamic_wind, 3);
SCM_DEFINE_MEMOIZER ("@prompt", m_prompt, 3);

static SCM m_apply (SCM proc, SCM args)
#define FUNC_NAME "@apply"
{
  SCM_VALIDATE_MEMOIZED (1, proc);
  SCM_VALIDATE_MEMOIZED (2, args);
  return MAKMEMO_APPLY (proc, args);
}
#undef FUNC_NAME

static SCM m_call_cc (SCM proc)
#define FUNC_NAME "@call-with-current-continuation"
{
  SCM_VALIDATE_MEMOIZED (1, proc);
  return MAKMEMO_CONT (proc);
}
#undef FUNC_NAME

static SCM m_call_values (SCM prod, SCM cons)
#define FUNC_NAME "@call-with-values"
{
  SCM_VALIDATE_MEMOIZED (1, prod);
  SCM_VALIDATE_MEMOIZED (2, cons);
  return MAKMEMO_CALL_WITH_VALUES (prod, cons);
}
#undef FUNC_NAME

static SCM m_dynamic_wind (SCM in, SCM expr, SCM out)
#define FUNC_NAME "memoize-dynwind"
{
  SCM_VALIDATE_MEMOIZED (1, in);
  SCM_VALIDATE_MEMOIZED (2, expr);
  SCM_VALIDATE_MEMOIZED (3, out);
  return MAKMEMO_DYNWIND (in, expr, out);
}
#undef FUNC_NAME

static SCM m_prompt (SCM tag, SCM exp, SCM handler)
#define FUNC_NAME "@prompt"
{
  SCM_VALIDATE_MEMOIZED (1, tag);
  SCM_VALIDATE_MEMOIZED (2, exp);
  SCM_VALIDATE_MEMOIZED (3, handler);
  return MAKMEMO_PROMPT (tag, exp, handler);
}
#undef FUNC_NAME

SCM_SYMBOL (sym_placeholder, "_");

static SCM unmemoize (SCM expr);

static SCM
unmemoize_exprs (SCM exprs)
{
  SCM ret, tail;
  if (scm_is_null (exprs))
    return SCM_EOL;
  ret = scm_list_1 (unmemoize (CAR (exprs)));
  tail = ret;
  for (exprs = CDR (exprs); !scm_is_null (exprs); exprs = CDR (exprs))
    {
      SCM_SETCDR (tail, scm_list_1 (unmemoize (CAR (exprs))));
      tail = CDR (tail);
    }
  return ret;
}

static SCM
unmemoize_bindings (SCM inits)
{
  SCM ret, tail;
  if (scm_is_null (inits))
    return SCM_EOL;
  ret = scm_list_1 (scm_list_2 (sym_placeholder, unmemoize (CAR (inits))));
  tail = ret;
  for (inits = CDR (inits); !scm_is_null (inits); inits = CDR (inits))
    {
      SCM_SETCDR (tail, scm_list_1 (scm_list_2 (sym_placeholder,
                                                unmemoize (CAR (inits)))));
      tail = CDR (tail);
    }
  return ret;
}

static SCM
unmemoize_lexical (SCM n)
{
  char buf[16];
  buf[15] = 0;
  snprintf (buf, 15, "<%u>", scm_to_uint32 (n));
  return scm_from_locale_symbol (buf);
}

static SCM
unmemoize (const SCM expr)
{
  SCM args;

  if (!SCM_MEMOIZED_P (expr))
    abort ();

  args = SCM_MEMOIZED_ARGS (expr);
  switch (SCM_MEMOIZED_TAG (expr))
    {
    case SCM_M_APPLY:
      return scm_cons (scm_sym_atapply, unmemoize_exprs (args));
    case SCM_M_BEGIN:
      return scm_cons (scm_sym_begin, unmemoize_exprs (args));
    case SCM_M_CALL:
      return scm_cons (unmemoize (CAR (args)), unmemoize_exprs (CDDR (args)));
    case SCM_M_CONT:
      return scm_list_2 (scm_sym_atcall_cc, unmemoize (args));
    case SCM_M_CALL_WITH_VALUES:
      return scm_list_3 (scm_sym_at_call_with_values,
                         unmemoize (CAR (args)), unmemoize (CDR (args)));
    case SCM_M_DEFINE:
      return scm_list_3 (scm_sym_define, CAR (args), unmemoize (CDR (args)));
    case SCM_M_DYNWIND:
      return scm_list_4 (scm_sym_at_dynamic_wind,
                         unmemoize (CAR (args)),
                         unmemoize (CADR (args)),
                         unmemoize (CDDR (args)));
    case SCM_M_WITH_FLUIDS:
      {
        SCM binds = SCM_EOL, fluids, vals;
        for (fluids = CAR (args), vals = CADR (args); scm_is_pair (fluids);
             fluids = CDR (fluids), vals = CDR (vals))
          binds = scm_cons (scm_list_2 (unmemoize (CAR (fluids)),
                                        unmemoize (CAR (vals))),
                            binds);
        return scm_list_3 (scm_sym_with_fluids,
                           scm_reverse_x (binds, SCM_UNDEFINED),
                           unmemoize (CDDR (args)));
      }
    case SCM_M_IF:
      return scm_list_4 (scm_sym_if, unmemoize (scm_car (args)),
                         unmemoize (scm_cadr (args)), unmemoize (scm_cddr (args)));
    case SCM_M_LAMBDA:
      if (scm_is_null (CDDR (args)))
        return scm_list_3 (scm_sym_lambda,
                           scm_make_list (CADR (args), sym_placeholder),
                           unmemoize (CAR (args)));
      else if (scm_is_null (CDDDR (args)))
        {
          SCM formals = scm_make_list (CADR (args), sym_placeholder);
          return scm_list_3 (scm_sym_lambda,
                             scm_is_true (CADDR (args))
                             ? scm_cons_star (sym_placeholder, formals)
                             : formals,
                             unmemoize (CAR (args)));
        }
      else
        {
          SCM body = CAR (args), spec = CDR (args), alt, tail;

          alt = CADDR (CDDDR (spec));
          if (scm_is_true (alt))
            tail = CDR (unmemoize (alt));
          else
            tail = SCM_EOL;

          return scm_cons
            (sym_case_lambda_star,
             scm_cons (scm_list_2 (scm_list_5 (CAR (spec),
                                               CADR (spec),
                                               CADDR (spec),
                                               CADDDR (spec),
                                               unmemoize_exprs (CADR (CDDDR (spec)))),
                                   unmemoize (body)),
                       tail));
        }
    case SCM_M_LET:
      return scm_list_3 (scm_sym_let,
                         unmemoize_bindings (CAR (args)),
                         unmemoize (CDR (args)));
    case SCM_M_QUOTE:
      return scm_list_2 (scm_sym_quote, args);
    case SCM_M_LEXICAL_REF:
      return unmemoize_lexical (args);
    case SCM_M_LEXICAL_SET:
      return scm_list_3 (scm_sym_set_x, unmemoize_lexical (CAR (args)),
                         unmemoize (CDR (args)));
    case SCM_M_TOPLEVEL_REF:
      return args;
    case SCM_M_TOPLEVEL_SET:
      return scm_list_3 (scm_sym_set_x, CAR (args), unmemoize (CDR (args)));
    case SCM_M_MODULE_REF:
      return SCM_VARIABLEP (args) ? args
        : scm_list_3 (scm_is_true (CDDR (args)) ? scm_sym_at : scm_sym_atat,
                      scm_i_finite_list_copy (CAR (args)),
                      CADR (args));
    case SCM_M_MODULE_SET:
      return scm_list_3 (scm_sym_set_x,
                         SCM_VARIABLEP (CDR (args)) ? CDR (args)
                         : scm_list_3 (scm_is_true (CDDDR (args))
                                       ? scm_sym_at : scm_sym_atat,
                                       scm_i_finite_list_copy (CADR (args)),
                                       CADDR (args)),
                         unmemoize (CAR (args)));
    case SCM_M_PROMPT:
      return scm_list_4 (scm_sym_at_prompt,
                         unmemoize (CAR (args)),
                         unmemoize (CADR (args)),
                         unmemoize (CDDR (args)));
    default:
      abort ();
    }
}

SCM_DEFINE (scm_memoized_p, "memoized?", 1, 0, 0,
            (SCM obj),
	    "Return @code{#t} if @var{obj} is memoized.")
#define FUNC_NAME s_scm_memoized_p
{
  return scm_from_bool (SCM_MEMOIZED_P (obj));
}
#undef FUNC_NAME

SCM_DEFINE (scm_unmemoize_expression, "unmemoize-expression", 1, 0, 0,
            (SCM m),
	    "Unmemoize the memoized expression @var{m}.")
#define FUNC_NAME s_scm_unmemoize_expression
{
  SCM_VALIDATE_MEMOIZED (1, m);
  return unmemoize (m);
}
#undef FUNC_NAME

SCM_DEFINE (scm_memoized_expression_typecode, "memoized-expression-typecode", 1, 0, 0,
            (SCM m),
	    "Return the typecode from the memoized expression @var{m}.")
#define FUNC_NAME s_scm_memoized_expression_typecode
{
  SCM_VALIDATE_MEMOIZED (1, m);

  /* The tag is a 16-bit integer so it fits in an inum.  */
  return SCM_I_MAKINUM (SCM_MEMOIZED_TAG (m));
}
#undef FUNC_NAME

SCM_DEFINE (scm_memoized_expression_data, "memoized-expression-data", 1, 0, 0,
            (SCM m),
	    "Return the data from the memoized expression @var{m}.")
#define FUNC_NAME s_scm_memoized_expression_data
{
  SCM_VALIDATE_MEMOIZED (1, m);
  return SCM_MEMOIZED_ARGS (m);
}
#undef FUNC_NAME

SCM_DEFINE (scm_memoized_typecode, "memoized-typecode", 1, 0, 0,
            (SCM sym),
	    "Return the memoized typecode corresponding to the symbol @var{sym}.")
#define FUNC_NAME s_scm_memoized_typecode
{
  int i;

  SCM_VALIDATE_SYMBOL (1, sym);

  for (i = 0; i < sizeof(memoized_tags)/sizeof(const char*); i++)
    if (strcmp (scm_i_symbol_chars (sym), memoized_tags[i]) == 0)
      return scm_from_int32 (i);

  return SCM_BOOL_F;
}
#undef FUNC_NAME

SCM_SYMBOL (scm_unbound_variable_key, "unbound-variable");
static void error_unbound_variable (SCM symbol) SCM_NORETURN;
static void error_unbound_variable (SCM symbol)
{
  scm_error (scm_unbound_variable_key, NULL, "Unbound variable: ~S",
	     scm_list_1 (symbol), SCM_BOOL_F);
}

SCM_DEFINE (scm_memoize_variable_access_x, "memoize-variable-access!", 2, 0, 0,
            (SCM m, SCM mod),
	    "Look up and cache the variable that @var{m} will access, returning the variable.")
#define FUNC_NAME s_scm_memoize_variable_access_x
{
  SCM mx;
  SCM_VALIDATE_MEMOIZED (1, m);
  mx = SCM_MEMOIZED_ARGS (m);
  switch (SCM_MEMOIZED_TAG (m))
    {
    case SCM_M_TOPLEVEL_REF:
      if (SCM_VARIABLEP (mx))
        return mx;
      else
        {
          SCM var = scm_module_variable (mod, mx);
          if (scm_is_false (var) || scm_is_false (scm_variable_bound_p (var)))
            error_unbound_variable (mx);
          SCM_SET_SMOB_OBJECT (m, var);
          return var;
        }

    case SCM_M_TOPLEVEL_SET:
      {
        SCM var = CAR (mx);
        if (SCM_VARIABLEP (var))
          return var;
        else
          {
            var = scm_module_variable (mod, var);
            if (scm_is_false (var))
              error_unbound_variable (CAR (mx));
            SCM_SETCAR (mx, var);
            return var;
          }
      }

    case SCM_M_MODULE_REF:
      if (SCM_VARIABLEP (mx))
        return mx;
      else
        {
          SCM var;
          mod = scm_resolve_module (CAR (mx));
          if (scm_is_true (CDDR (mx)))
            mod = scm_module_public_interface (mod);
          var = scm_module_lookup (mod, CADR (mx));
          if (scm_is_false (scm_variable_bound_p (var)))
            error_unbound_variable (CADR (mx));
          SCM_SET_SMOB_OBJECT (m, var);
          return var;
        }

    case SCM_M_MODULE_SET:
      /* FIXME: not quite threadsafe */
      if (SCM_VARIABLEP (CDR (mx)))
        return CDR (mx);
      else
        {
          SCM var;
          mod = scm_resolve_module (CADR (mx));
          if (scm_is_true (CDDDR (mx)))
            mod = scm_module_public_interface (mod);
          var = scm_module_lookup (mod, CADDR (mx));
          SCM_SETCDR (mx, var);
          return var;
        }

    default:
      scm_wrong_type_arg (FUNC_NAME, 1, m);
      return SCM_BOOL_F;
    }
}
#undef FUNC_NAME

void
scm_init_memoize ()
{
  scm_tc16_memoized = scm_make_smob_type ("%memoized", 0);
  scm_set_smob_mark (scm_tc16_memoized, scm_markcdr);
  scm_set_smob_print (scm_tc16_memoized, scm_print_memoized);

  scm_tc16_memoizer = scm_make_smob_type ("memoizer", 0);

#include "libguile/memoize.x"

  list_of_guile = scm_list_1 (scm_from_locale_symbol ("guile"));
}

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/


code:
/* classes: h_files */

#ifndef SCM_MEMOIZE_H
#define SCM_MEMOIZE_H

/* Copyright (C) 1995,1996,1998,1999,2000,2001,2002,2004,2008,2009,2010
 * Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "libguile/__scm.h"

SCM_API SCM scm_sym_and;
SCM_API SCM scm_sym_begin;
SCM_API SCM scm_sym_case;
SCM_API SCM scm_sym_cond;
SCM_API SCM scm_sym_define;
SCM_API SCM scm_sym_do;
SCM_API SCM scm_sym_if;
SCM_API SCM scm_sym_lambda;
SCM_API SCM scm_sym_let;
SCM_API SCM scm_sym_letstar;
SCM_API SCM scm_sym_letrec;
SCM_API SCM scm_sym_quote;
SCM_API SCM scm_sym_quasiquote;
SCM_API SCM scm_sym_unquote;
SCM_API SCM scm_sym_uq_splicing;
SCM_API SCM scm_sym_with_fluids;

SCM_API SCM scm_sym_at;
SCM_API SCM scm_sym_atat;
SCM_API SCM scm_sym_atapply;
SCM_API SCM scm_sym_atcall_cc;
SCM_API SCM scm_sym_at_call_with_values;
SCM_API SCM scm_sym_at_prompt;
SCM_API SCM scm_sym_delay;
SCM_API SCM scm_sym_at_dynamic_wind;
SCM_API SCM scm_sym_eval_when;
SCM_API SCM scm_sym_arrow;
SCM_API SCM scm_sym_else;
SCM_API SCM scm_sym_apply;
SCM_API SCM scm_sym_set_x;
SCM_API SCM scm_sym_args;

/* {Memoized Source}
 */

SCM_INTERNAL scm_t_bits scm_tc16_memoized;

#define SCM_MEMOIZED_P(x) 	(SCM_SMOB_PREDICATE (scm_tc16_memoized, (x)))
#define SCM_MEMOIZED_TAG(x) 	(SCM_SMOB_FLAGS (x))
#define SCM_MEMOIZED_ARGS(x) 	(SCM_SMOB_OBJECT (x))

enum
  {
    SCM_M_BEGIN,
    SCM_M_IF,
    SCM_M_LAMBDA,
    SCM_M_LET,
    SCM_M_QUOTE,
    SCM_M_DEFINE,
    SCM_M_DYNWIND,
    SCM_M_WITH_FLUIDS,
    SCM_M_APPLY,
    SCM_M_CONT,
    SCM_M_CALL_WITH_VALUES,
    SCM_M_CALL,
    SCM_M_LEXICAL_REF,
    SCM_M_LEXICAL_SET,
    SCM_M_TOPLEVEL_REF,
    SCM_M_TOPLEVEL_SET,
    SCM_M_MODULE_REF,
    SCM_M_MODULE_SET,
    SCM_M_PROMPT
  };

SCM_INTERNAL SCM scm_memoize_expression (SCM exp);
SCM_INTERNAL SCM scm_unmemoize_expression (SCM memoized);
SCM_INTERNAL SCM scm_memoized_expression_typecode (SCM memoized);
SCM_INTERNAL SCM scm_memoized_expression_data (SCM memoized);
SCM_INTERNAL SCM scm_memoized_typecode (SCM sym);
SCM_INTERNAL SCM scm_memoize_variable_access_x (SCM memoized, SCM module);
SCM_API SCM scm_memoized_p (SCM obj);

SCM_INTERNAL void scm_init_memoize (void);

#endif  /* SCM_MEMOIZE_H */

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/

license: mit


code:
/* Copyright (C) 1991, 1992, 1996, 1998, 2001, 2006 Free Software Foundation, Inc.
   This file is derived from mkstemps.c from the GNU Libiberty Library
   which in turn is derived from the GNU C Library.

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

   The GNU C Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include "libguile/__scm.h"

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef __MINGW32__
#include <process.h>
#endif

#ifndef TMP_MAX
#define TMP_MAX 16384
#endif

/* We provide this prototype to avoid compiler warnings.  If this ever
   conflicts with a declaration in a system header file, we'll find
   out, because we should include that header file here.  */
int mkstemp (char *);

/* Generate a unique temporary file name from TEMPLATE.

   TEMPLATE has the form:

   <path>/ccXXXXXX

   The last six characters of TEMPLATE must be "XXXXXX"; they are
   replaced with a string that makes the filename unique.

   Returns a file descriptor open on the file for reading and writing.  */
int
mkstemp (template)
     char *template;
{
  static const char letters[]
    = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  static scm_t_uint64 value;
#ifdef HAVE_GETTIMEOFDAY
  struct timeval tv;
#endif
  char *XXXXXX;
  size_t len;
  int count;

  len = strlen (template);

  if ((int) len < 6
      || strncmp (&template[len - 6], "XXXXXX", 6))
    {
      return -1;
    }

  XXXXXX = &template[len - 6];

#ifdef HAVE_GETTIMEOFDAY
  /* Get some more or less random data.  */
  gettimeofday (&tv, NULL);
  value += ((scm_t_uint64) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid ();
#else
  value += getpid ();
#endif

  for (count = 0; count < TMP_MAX; ++count)
    {
      scm_t_uint64 v = value;
      int fd;

      /* Fill in the random bits.  */
      XXXXXX[0] = letters[v % 62];
      v /= 62;
      XXXXXX[1] = letters[v % 62];
      v /= 62;
      XXXXXX[2] = letters[v % 62];
      v /= 62;
      XXXXXX[3] = letters[v % 62];
      v /= 62;
      XXXXXX[4] = letters[v % 62];
      v /= 62;
      XXXXXX[5] = letters[v % 62];

      fd = open (template, O_RDWR|O_CREAT|O_EXCL, 0600);
      if (fd >= 0)
	/* The file does not exist.  */
	return fd;

      /* This is a random value.  It is only necessary that the next
	 TMP_MAX values generated by adding 7777 to VALUE are different
	 with (module 2^32).  */
      value += 7777;
    }

  /* We return the null string if we can't find a unique file name.  */
  template[0] = '\0';
  return -1;
}


code:
/* Copyright (C) 1998,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdarg.h>

#include "libguile/_scm.h"

#include "libguile/eval.h"
#include "libguile/smob.h"
#include "libguile/procprop.h"
#include "libguile/vectors.h"
#include "libguile/hashtab.h"
#include "libguile/struct.h"
#include "libguile/variable.h"
#include "libguile/fluids.h"
#include "libguile/deprecation.h"

#include "libguile/modules.h"

int scm_module_system_booted_p = 0;

scm_t_bits scm_module_tag;

/* The current module, a fluid. */
static SCM the_module;

/* Most of the module system is implemented in Scheme. These bindings from
   boot-9 are needed to provide the Scheme interface. */
static SCM the_root_module_var;
static SCM module_make_local_var_x_var;
static SCM process_define_module_var;
static SCM process_use_modules_var;
static SCM resolve_module_var;
static SCM module_public_interface_var;
static SCM module_export_x_var;
static SCM default_duplicate_binding_procedures_var;

static SCM unbound_variable (const char *func, SCM sym)
{
  scm_error (scm_from_locale_symbol ("unbound-variable"), func,
             "Unbound variable: ~S", scm_list_1 (sym), SCM_BOOL_F);
}

SCM
scm_the_root_module (void)
{
  if (scm_module_system_booted_p)
    return SCM_VARIABLE_REF (the_root_module_var);
  else
    return SCM_BOOL_F;
}

SCM_DEFINE (scm_current_module, "current-module", 0, 0, 0,
	    (),
	    "Return the current module.")
#define FUNC_NAME s_scm_current_module
{
  SCM curr = scm_fluid_ref (the_module);

  return scm_is_true (curr) ? curr : scm_the_root_module ();
}
#undef FUNC_NAME

static void scm_post_boot_init_modules (void);

SCM_DEFINE (scm_set_current_module, "set-current-module", 1, 0, 0,
	    (SCM module),
	    "Set the current module to @var{module} and return\n"
	    "the previous current module.")
#define FUNC_NAME s_scm_set_current_module
{
  SCM old;

  if (!scm_module_system_booted_p)
    scm_post_boot_init_modules ();

  SCM_VALIDATE_MODULE (SCM_ARG1, module);

  old = scm_current_module ();
  scm_fluid_set_x (the_module, module);

  return old;
}
#undef FUNC_NAME

SCM_DEFINE (scm_interaction_environment, "interaction-environment", 0, 0, 0,
	    (),
	    "Return a specifier for the environment that contains\n"
	    "implementation--defined bindings, typically a superset of those\n"
	    "listed in the report.  The intent is that this procedure will\n"
	    "return the environment in which the implementation would\n"
	    "evaluate expressions dynamically typed by the user.")
#define FUNC_NAME s_scm_interaction_environment
{
  return scm_current_module ();
}
#undef FUNC_NAME

SCM
scm_c_call_with_current_module (SCM module,
				SCM (*func)(void *), void *data)
{
  return scm_c_with_fluid (the_module, module, func, data);
}

void
scm_dynwind_current_module (SCM module)
{
  scm_dynwind_fluid (the_module, module);
}

/*
  convert "A B C" to scheme list (A B C)
 */
static SCM
convert_module_name (const char *name)
{
  SCM list = SCM_EOL;
  SCM *tail = &list;

  const char *ptr;
  while (*name)
    {
      while (*name == ' ')
	name++;
      ptr = name;
      while (*ptr && *ptr != ' ')
	ptr++;
      if (ptr > name)
	{
	  SCM sym = scm_from_locale_symboln (name, ptr-name);
	  *tail = scm_cons (sym, SCM_EOL);
	  tail = SCM_CDRLOC (*tail);
	}
      name = ptr;
    }

  return list;
}

SCM
scm_c_resolve_module (const char *name)
{
  return scm_resolve_module (convert_module_name (name));
}

SCM
scm_resolve_module (SCM name)
{
  return scm_call_1 (SCM_VARIABLE_REF (resolve_module_var), name);
}

SCM
scm_c_define_module (const char *name,
		     void (*init)(void *), void *data)
{
  SCM module = scm_call_1 (SCM_VARIABLE_REF (process_define_module_var),
			   scm_list_1 (convert_module_name (name)));
  if (init)
    scm_c_call_with_current_module (module, (SCM (*)(void*))init, data);
  return module;
}

void
scm_c_use_module (const char *name)
{
  scm_call_1 (SCM_VARIABLE_REF (process_use_modules_var),
	      scm_list_1 (scm_list_1 (convert_module_name (name))));
}

SCM
scm_module_export (SCM module, SCM namelist)
{
  return scm_call_2 (SCM_VARIABLE_REF (module_export_x_var),
		     module, namelist);
}

/*
  @code{scm_c_export}(@var{name-list})

  @code{scm_c_export} exports the named bindings from the current
  module, making them visible to users of the module. This function
  takes a list of string arguments, terminated by NULL, e.g.

  @example
    scm_c_export ("add-double-record", "bamboozle-money", NULL);
  @end example
*/
void
scm_c_export (const char *name, ...)
{
  if (name)
    {
      va_list ap;
      SCM names = scm_cons (scm_from_locale_symbol (name), SCM_EOL);
      SCM *tail = SCM_CDRLOC (names);
      va_start (ap, name);
      while (1)
	{
	  const char *n = va_arg (ap, const char *);
	  if (n == NULL)
	    break;
	  *tail = scm_cons (scm_from_locale_symbol (n), SCM_EOL);
	  tail = SCM_CDRLOC (*tail);
	}
      va_end (ap);
      scm_module_export (scm_current_module (), names);
    }
}

/* Environments */

SCM_SYMBOL (sym_module, "module");

SCM
scm_lookup_closure_module (SCM proc)
{
  if (scm_is_false (proc))
    return scm_the_root_module ();
  else if (SCM_EVAL_CLOSURE_P (proc))
    return SCM_PACK (SCM_SMOB_DATA (proc));
  else
    {
      SCM mod;

      /* FIXME: The `module' property is no longer set on eval closures, as it
	 introduced a circular reference that precludes garbage collection of
	 modules with the current weak hash table semantics (see
	 http://lists.gnu.org/archive/html/guile-devel/2009-01/msg00102.html and
	 http://thread.gmane.org/gmane.comp.programming.garbage-collection.boehmgc/2465
	 for details). Since it doesn't appear to be used (only in this
	 function, which has 1 caller), we no longer extend
	 `set-module-eval-closure!' to set the `module' property. */
      abort ();

      mod = scm_procedure_property (proc, sym_module);
      if (scm_is_false (mod))
	mod = scm_the_root_module ();
      return mod;
    }
}

/*
 * C level implementation of the standard eval closure
 *
 * This increases loading speed substantially.  The code may be
 * replaced by something based on environments.[ch], in a future
 * release.
 */

/* Return the list of default duplicate binding handlers (procedures).  */
static inline SCM
default_duplicate_binding_handlers (void)
{
  SCM get_handlers;

  get_handlers = SCM_VARIABLE_REF (default_duplicate_binding_procedures_var);

  return (scm_call_0 (get_handlers));
}

/* Resolve the import of SYM in MODULE, where SYM is currently provided by
   both IFACE1 as VAR1 and IFACE2 as VAR2.  Return the variable chosen by the
   duplicate binding handlers or `#f'.  */
static inline SCM
resolve_duplicate_binding (SCM module, SCM sym,
			   SCM iface1, SCM var1,
			   SCM iface2, SCM var2)
{
  SCM result = SCM_BOOL_F;

  if (!scm_is_eq (var1, var2))
    {
      SCM val1, val2;
      SCM handlers, h, handler_args;

      val1 = SCM_VARIABLE_REF (var1);
      val2 = SCM_VARIABLE_REF (var2);

      val1 = (val1 == SCM_UNSPECIFIED) ? SCM_BOOL_F : val1;
      val2 = (val2 == SCM_UNSPECIFIED) ? SCM_BOOL_F : val2;

      handlers = SCM_MODULE_DUPLICATE_HANDLERS (module);
      if (scm_is_false (handlers))
	handlers = default_duplicate_binding_handlers ();

      handler_args = scm_list_n (module, sym,
				 iface1, val1, iface2, val2,
				 var1, val1,
				 SCM_UNDEFINED);

      for (h = handlers;
	   scm_is_pair (h) && scm_is_false (result);
	   h = SCM_CDR (h))
	{
	  result = scm_apply (SCM_CAR (h), handler_args, SCM_EOL);
	}
    }
  else
    result = var1;

  return result;
}

SCM scm_pre_modules_obarray;

/* Lookup SYM as an imported variable of MODULE.  */
static inline SCM
module_imported_variable (SCM module, SCM sym)
{
#define SCM_BOUND_THING_P scm_is_true
  register SCM var, imports;

  /* Search cached imported bindings.  */
  imports = SCM_MODULE_IMPORT_OBARRAY (module);
  var = scm_hashq_ref (imports, sym, SCM_UNDEFINED);
  if (SCM_BOUND_THING_P (var))
    return var;

  {
    /* Search the use list for yet uncached imported bindings, possibly
       resolving duplicates as needed and caching the result in the import
       obarray.  */
    SCM uses;
    SCM found_var = SCM_BOOL_F, found_iface = SCM_BOOL_F;

    for (uses = SCM_MODULE_USES (module);
	 scm_is_pair (uses);
	 uses = SCM_CDR (uses))
      {
	SCM iface;

	iface = SCM_CAR (uses);
	var = scm_module_variable (iface, sym);

	if (SCM_BOUND_THING_P (var))
	  {
	    if (SCM_BOUND_THING_P (found_var))
	      {
		/* SYM is a duplicate binding (imported more than once) so we
		   need to resolve it.  */
		found_var = resolve_duplicate_binding (module, sym,
						       found_iface, found_var,
						       iface, var);
		if (scm_is_eq (found_var, var))
		  found_iface = iface;
	      }
	    else
	      /* Keep track of the variable we found and check for other
		 occurences of SYM in the use list.  */
	      found_var = var, found_iface = iface;
	  }
      }

    if (SCM_BOUND_THING_P (found_var))
      {
	/* Save the lookup result for future reference.  */
	(void) scm_hashq_set_x (imports, sym, found_var);
	return found_var;
      }
  }

  return SCM_BOOL_F;
#undef SCM_BOUND_THING_P
}

SCM_DEFINE (scm_module_local_variable, "module-local-variable", 2, 0, 0,
	    (SCM module, SCM sym),
	    "Return the variable bound to @var{sym} in @var{module}.  Return "
	    "@code{#f} is @var{sym} is not bound locally in @var{module}.")
#define FUNC_NAME s_scm_module_local_variable
{
#define SCM_BOUND_THING_P(b) \
  (scm_is_true (b))

  register SCM b;

  if (scm_module_system_booted_p)
    SCM_VALIDATE_MODULE (1, module);

  SCM_VALIDATE_SYMBOL (2, sym);

  if (scm_is_false (module))
    return scm_hashq_ref (scm_pre_modules_obarray, sym, SCM_UNDEFINED);

  /* 1. Check module obarray */
  b = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym, SCM_UNDEFINED);
  if (SCM_BOUND_THING_P (b))
    return b;

  /* 2. Search imported bindings.  In order to be consistent with
     `module-variable', the binder gets called only when no imported binding
     matches SYM.  */
  b = module_imported_variable (module, sym);
  if (SCM_BOUND_THING_P (b))
    return SCM_BOOL_F;

  {
    /* 3. Query the custom binder.  */
    SCM binder = SCM_MODULE_BINDER (module);

    if (scm_is_true (binder))
      {
	b = scm_call_3 (binder, module, sym, SCM_BOOL_F);
	if (SCM_BOUND_THING_P (b))
	  return b;
      }
  }

  return SCM_BOOL_F;

#undef SCM_BOUND_THING_P
}
#undef FUNC_NAME

SCM_DEFINE (scm_module_variable, "module-variable", 2, 0, 0,
	    (SCM module, SCM sym),
	    "Return the variable bound to @var{sym} in @var{module}.  This "
	    "may be both a local variable or an imported variable.  Return "
	    "@code{#f} is @var{sym} is not bound in @var{module}.")
#define FUNC_NAME s_scm_module_variable
{
#define SCM_BOUND_THING_P(b) \
  (scm_is_true (b))

  register SCM var;

  if (scm_module_system_booted_p)
    SCM_VALIDATE_MODULE (1, module);

  SCM_VALIDATE_SYMBOL (2, sym);

  if (scm_is_false (module))
    return scm_hashq_ref (scm_pre_modules_obarray, sym, SCM_UNDEFINED);

  /* 1. Check module obarray */
  var = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym, SCM_UNDEFINED);
  if (SCM_BOUND_THING_P (var))
    return var;

  /* 2. Search among the imported variables.  */
  var = module_imported_variable (module, sym);
  if (SCM_BOUND_THING_P (var))
    return var;

  {
    /* 3. Query the custom binder.  */
    SCM binder;

    binder = SCM_MODULE_BINDER (module);
    if (scm_is_true (binder))
      {
	var = scm_call_3 (binder, module, sym, SCM_BOOL_F);
	if (SCM_BOUND_THING_P (var))
	  return var;
      }
  }

  return SCM_BOOL_F;

#undef SCM_BOUND_THING_P
}
#undef FUNC_NAME

scm_t_bits scm_tc16_eval_closure;

#define SCM_F_EVAL_CLOSURE_INTERFACE (1<<0)
#define SCM_EVAL_CLOSURE_INTERFACE_P(e) \
  (SCM_SMOB_FLAGS (e) & SCM_F_EVAL_CLOSURE_INTERFACE)

/* NOTE: This function may be called by a smob application
   or from another C function directly. */
SCM
scm_eval_closure_lookup (SCM eclo, SCM sym, SCM definep)
{
  SCM module = SCM_PACK (SCM_SMOB_DATA (eclo));
  if (scm_is_true (definep))
    {
      if (SCM_EVAL_CLOSURE_INTERFACE_P (eclo))
	return SCM_BOOL_F;
      return scm_call_2 (SCM_VARIABLE_REF (module_make_local_var_x_var),
			 module, sym);
    }
  else
    return scm_module_variable (module, sym);
}

SCM_DEFINE (scm_standard_eval_closure, "standard-eval-closure", 1, 0, 0,
	    (SCM module),
	    "Return an eval closure for the module @var{module}.")
#define FUNC_NAME s_scm_standard_eval_closure
{
  SCM_RETURN_NEWSMOB (scm_tc16_eval_closure, SCM_UNPACK (module));
}
#undef FUNC_NAME

SCM_DEFINE (scm_standard_interface_eval_closure,
	    "standard-interface-eval-closure", 1, 0, 0,
	    (SCM module),
	    "Return a interface eval closure for the module @var{module}. "
	    "Such a closure does not allow new bindings to be added.")
#define FUNC_NAME s_scm_standard_interface_eval_closure
{
  SCM_RETURN_NEWSMOB (scm_tc16_eval_closure | (SCM_F_EVAL_CLOSURE_INTERFACE<<16),
		      SCM_UNPACK (module));
}
#undef FUNC_NAME

SCM_DEFINE (scm_eval_closure_module,
	    "eval-closure-module", 1, 0, 0,
	    (SCM eval_closure),
	    "Return the module associated with this eval closure.")
/* the idea is that eval closures are really not the way to do things, they're
   superfluous given our module system. this function lets mmacros migrate away
   from eval closures. */
#define FUNC_NAME s_scm_eval_closure_module
{
  SCM_MAKE_VALIDATE_MSG (SCM_ARG1, eval_closure, EVAL_CLOSURE_P,
                         "eval-closure");
  return SCM_SMOB_OBJECT (eval_closure);
}
#undef FUNC_NAME

SCM
scm_module_lookup_closure (SCM module)
{
  if (scm_is_false (module))
    return SCM_BOOL_F;
  else
    return SCM_MODULE_EVAL_CLOSURE (module);
}

SCM
scm_current_module_lookup_closure ()
{
  if (scm_module_system_booted_p)
    return scm_module_lookup_closure (scm_current_module ());
  else
    return SCM_BOOL_F;
}

SCM_SYMBOL (sym_macroexpand, "macroexpand");

SCM_DEFINE (scm_module_transformer, "module-transformer", 1, 0, 0,
	    (SCM module),
	    "Returns the syntax expander for the given module.")
#define FUNC_NAME s_scm_module_transformer
{
  if (SCM_UNLIKELY (scm_is_false (module)))
    {
      SCM v = scm_hashq_ref (scm_pre_modules_obarray,
                             sym_macroexpand,
                             SCM_BOOL_F);
      if (scm_is_false (v))
        SCM_MISC_ERROR ("no module, and `macroexpand' unbound", SCM_EOL);
      return SCM_VARIABLE_REF (v);
    }
  else
    {
      SCM_VALIDATE_MODULE (SCM_ARG1, module);
      return SCM_MODULE_TRANSFORMER (module);
    }
}
#undef FUNC_NAME

SCM
scm_current_module_transformer ()
{
  return scm_module_transformer (scm_current_module ());
}

SCM_DEFINE (scm_module_import_interface, "module-import-interface", 2, 0, 0,
	    (SCM module, SCM sym),
	    "Return the module or interface from which @var{sym} is imported "
	    "in @var{module}.  If @var{sym} is not imported (i.e., it is not "
	    "defined in @var{module} or it is a module-local binding instead "
	    "of an imported one), then @code{#f} is returned.")
#define FUNC_NAME s_scm_module_import_interface
{
  SCM var, result = SCM_BOOL_F;

  SCM_VALIDATE_MODULE (1, module);
  SCM_VALIDATE_SYMBOL (2, sym);

  var = scm_module_variable (module, sym);
  if (scm_is_true (var))
    {
      /* Look for the module that provides VAR.  */
      SCM local_var;

      local_var = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym,
				 SCM_UNDEFINED);
      if (scm_is_eq (local_var, var))
	result = module;
      else
	{
	  /* Look for VAR among the used modules.  */
	  SCM uses, imported_var;

	  for (uses = SCM_MODULE_USES (module);
	       scm_is_pair (uses) && scm_is_false (result);
	       uses = SCM_CDR (uses))
	    {
	      imported_var = scm_module_variable (SCM_CAR (uses), sym);
	      if (scm_is_eq (imported_var, var))
		result = SCM_CAR (uses);
	    }
	}
    }

  return result;
}
#undef FUNC_NAME

SCM
scm_module_public_interface (SCM module)
{
  return scm_call_1 (SCM_VARIABLE_REF (module_public_interface_var), module);
}

/* scm_sym2var
 *
 * looks up the variable bound to SYM according to PROC.  PROC should be
 * a `eval closure' of some module.
 *
 * When no binding exists, and DEFINEP is true, create a new binding
 * with a initial value of SCM_UNDEFINED.  Return `#f' when DEFINEP as
 * false and no binding exists.
 *
 * When PROC is `#f', it is ignored and the binding is searched for in
 * the scm_pre_modules_obarray (a `eq' hash table).
 */

SCM
scm_sym2var (SCM sym, SCM proc, SCM definep)
#define FUNC_NAME "scm_sym2var"
{
  SCM var;

  if (SCM_NIMP (proc))
    {
      if (SCM_EVAL_CLOSURE_P (proc))
	{
	  /* Bypass evaluator in the standard case. */
	  var = scm_eval_closure_lookup (proc, sym, definep);
	}
      else
	var = scm_call_2 (proc, sym, definep);
    }
  else
    {
      SCM handle;

      if (scm_is_false (definep))
	var = scm_hashq_ref (scm_pre_modules_obarray, sym, SCM_BOOL_F);
      else
	{
	  handle = scm_hashq_create_handle_x (scm_pre_modules_obarray,
					      sym, SCM_BOOL_F);
	  var = SCM_CDR (handle);
	  if (scm_is_false (var))
	    {
	      var = scm_make_variable (SCM_UNDEFINED);
	      SCM_SETCDR (handle, var);
	    }
	}
    }

  if (scm_is_true (var) && !SCM_VARIABLEP (var))
    SCM_MISC_ERROR ("~S is not bound to a variable", scm_list_1 (sym));

  return var;
}
#undef FUNC_NAME

SCM
scm_c_module_lookup (SCM module, const char *name)
{
  return scm_module_lookup (module, scm_from_locale_symbol (name));
}

SCM
scm_module_lookup (SCM module, SCM sym)
#define FUNC_NAME "module-lookup"
{
  SCM var;
  SCM_VALIDATE_MODULE (1, module);

  var = scm_sym2var (sym, scm_module_lookup_closure (module), SCM_BOOL_F);
  if (scm_is_false (var))
    unbound_variable (FUNC_NAME, sym);
  return var;
}
#undef FUNC_NAME

SCM
scm_c_lookup (const char *name)
{
  return scm_lookup (scm_from_locale_symbol (name));
}

SCM
scm_lookup (SCM sym)
{
  SCM var =
    scm_sym2var (sym, scm_current_module_lookup_closure (), SCM_BOOL_F);
  if (scm_is_false (var))
    unbound_variable (NULL, sym);
  return var;
}

SCM
scm_c_module_define (SCM module, const char *name, SCM value)
{
  return scm_module_define (module, scm_from_locale_symbol (name), value);
}

SCM
scm_module_define (SCM module, SCM sym, SCM value)
#define FUNC_NAME "module-define"
{
  SCM var;
  SCM_VALIDATE_MODULE (1, module);

  var = scm_sym2var (sym, scm_module_lookup_closure (module), SCM_BOOL_T);
  SCM_VARIABLE_SET (var, value);
  return var;
}
#undef FUNC_NAME

SCM
scm_c_define (const char *name, SCM value)
{
  return scm_define (scm_from_locale_symbol (name), value);
}

SCM_DEFINE (scm_define, "define!", 2, 0, 0,
	    (SCM sym, SCM value),
	    "Define @var{sym} to be @var{value} in the current module."
            "Returns the variable itself. Note that this is a procedure, "
            "not a macro.")
#define FUNC_NAME s_scm_define
{
  SCM var;
  SCM_VALIDATE_SYMBOL (SCM_ARG1, sym);
  var = scm_sym2var (sym, scm_current_module_lookup_closure (), SCM_BOOL_T);
  SCM_VARIABLE_SET (var, value);
  return var;
}
#undef FUNC_NAME

SCM_DEFINE (scm_module_reverse_lookup, "module-reverse-lookup", 2, 0, 0,
	    (SCM module, SCM variable),
	    "Return the symbol under which @var{variable} is bound in "
	    "@var{module} or @var{#f} if @var{variable} is not visible "
	    "from @var{module}.  If @var{module} is @code{#f}, then the "
	    "pre-module obarray is used.")
#define FUNC_NAME s_scm_module_reverse_lookup
{
  SCM obarray;
  long i, n;

  if (scm_is_false (module))
    obarray = scm_pre_modules_obarray;
  else
    {
      SCM_VALIDATE_MODULE (1, module);
      obarray = SCM_MODULE_OBARRAY (module);
    }

  SCM_VALIDATE_VARIABLE (SCM_ARG2, variable);

  if (!SCM_HASHTABLE_P (obarray))
      return SCM_BOOL_F;

  /* XXX - We do not use scm_hash_fold here to avoid searching the
     whole obarray.  We should have a scm_hash_find procedure. */

  n = SCM_HASHTABLE_N_BUCKETS (obarray);
  for (i = 0; i < n; ++i)
    {
      SCM ls = SCM_HASHTABLE_BUCKET (obarray, i), handle;
      while (!scm_is_null (ls))
	{
	  handle = SCM_CAR (ls);

	  if (SCM_CAR (handle) == SCM_PACK (NULL))
	    {
	      /* FIXME: We hit a weak pair whose car has become unreachable.
		 We should remove the pair in question or something.  */
	    }
	  else
	    {
	      if (SCM_CDR (handle) == variable)
		return SCM_CAR (handle);
	    }

	  ls = SCM_CDR (ls);
	}
    }

  if (!scm_is_false (module))
    {
      /* Try the `uses' list.  */
      SCM uses = SCM_MODULE_USES (module);
      while (scm_is_pair (uses))
	{
	  SCM sym = scm_module_reverse_lookup (SCM_CAR (uses), variable);
	  if (scm_is_true (sym))
	    return sym;
	  uses = SCM_CDR (uses);
	}
    }

  return SCM_BOOL_F;
}
#undef FUNC_NAME

SCM_DEFINE (scm_get_pre_modules_obarray, "%get-pre-modules-obarray", 0, 0, 0,
	    (),
	    "Return the obarray that is used for all new bindings before "
	    "the module system is booted.  The first call to "
	    "@code{set-current-module} will boot the module system.")
#define FUNC_NAME s_scm_get_pre_modules_obarray
{
  return scm_pre_modules_obarray;
}
#undef FUNC_NAME

SCM_SYMBOL (scm_sym_system_module, "system-module");

void
scm_modules_prehistory ()
{
  scm_pre_modules_obarray = scm_c_make_hash_table (1533);
}

void
scm_init_modules ()
{
#include "libguile/modules.x"
  module_make_local_var_x_var = scm_c_define ("module-make-local-var!",
					    SCM_UNDEFINED);
  scm_tc16_eval_closure = scm_make_smob_type ("eval-closure", 0);
  scm_set_smob_apply (scm_tc16_eval_closure, scm_eval_closure_lookup, 2, 0, 0);

  the_module = scm_make_fluid ();
}

static void
scm_post_boot_init_modules ()
{
  SCM module_type = SCM_VARIABLE_REF (scm_c_lookup ("module-type"));
  scm_module_tag = (SCM_CELL_WORD_1 (module_type) + scm_tc3_struct);

  resolve_module_var = scm_c_lookup ("resolve-module");
  process_define_module_var = scm_c_lookup ("process-define-module");
  process_use_modules_var = scm_c_lookup ("process-use-modules");
  module_export_x_var = scm_c_lookup ("module-export!");
  the_root_module_var = scm_c_lookup ("the-root-module");
  default_duplicate_binding_procedures_var =
    scm_c_lookup ("default-duplicate-binding-procedures");
  module_public_interface_var = scm_c_lookup ("module-public-interface");

  scm_module_system_booted_p = 1;
}

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/


code:
/* classes: h_files */

#ifndef SCM_MODULES_H
#define SCM_MODULES_H

/* Copyright (C) 1998, 2000, 2001, 2002, 2003, 2006, 2007, 2008 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "libguile/__scm.h"

#include "libguile/validate.h"

SCM_API int scm_module_system_booted_p;
SCM_API scm_t_bits scm_module_tag;

#define SCM_MODULEP(OBJ) \
  (!SCM_IMP (OBJ) && SCM_CELL_TYPE (OBJ) == scm_module_tag)

#define SCM_VALIDATE_MODULE(pos, scm) SCM_MAKE_VALIDATE_MSG (pos, scm, MODULEP, "module")

/* NOTE: Indexes of module fields are dependent upon the definition of
 *       module-type in boot-9.scm.
 */

#define scm_module_index_obarray	0
#define scm_module_index_uses		1
#define scm_module_index_binder		2
#define scm_module_index_eval_closure	3
#define scm_module_index_transformer	4
#define scm_module_index_duplicate_handlers 7
#define scm_module_index_import_obarray 8

#define SCM_MODULE_OBARRAY(module) \
  SCM_PACK (SCM_STRUCT_DATA (module) [scm_module_index_obarray])
#define SCM_MODULE_USES(module) \
  SCM_PACK (SCM_STRUCT_DATA (module) [scm_module_index_uses])
#define SCM_MODULE_BINDER(module) \
  SCM_PACK (SCM_STRUCT_DATA (module) [scm_module_index_binder])
#define SCM_MODULE_EVAL_CLOSURE(module) \
  SCM_PACK (SCM_STRUCT_DATA (module)[scm_module_index_eval_closure])
#define SCM_MODULE_TRANSFORMER(module) \
  SCM_PACK (SCM_STRUCT_DATA (module)[scm_module_index_transformer])
#define SCM_MODULE_DUPLICATE_HANDLERS(module) \
  SCM_PACK (SCM_STRUCT_DATA (module)[scm_module_index_duplicate_handlers])
#define SCM_MODULE_IMPORT_OBARRAY(module) \
  SCM_PACK (SCM_STRUCT_DATA (module)[scm_module_index_import_obarray])

SCM_API scm_t_bits scm_tc16_eval_closure;

#define SCM_EVAL_CLOSURE_P(x)	SCM_TYP16_PREDICATE (scm_tc16_eval_closure, x)

SCM_API SCM scm_current_module (void);
SCM_API SCM scm_the_root_module (void);
SCM_API SCM scm_module_variable (SCM module, SCM sym);
SCM_API SCM scm_module_local_variable (SCM module, SCM sym);
SCM_API SCM scm_interaction_environment (void);
SCM_API SCM scm_set_current_module (SCM module);

SCM_API SCM scm_c_call_with_current_module (SCM module,
					    SCM (*func)(void *), void *data);
SCM_API void scm_dynwind_current_module (SCM module);

SCM_API SCM scm_c_lookup (const char *name);
SCM_API SCM scm_c_define (const char *name, SCM val);
SCM_API SCM scm_lookup (SCM symbol);
SCM_API SCM scm_define (SCM symbol, SCM val);

SCM_API SCM scm_c_module_lookup (SCM module, const char *name);
SCM_API SCM scm_c_module_define (SCM module, const char *name, SCM val);
SCM_API SCM scm_module_lookup (SCM module, SCM symbol);
SCM_API SCM scm_module_define (SCM module, SCM symbol, SCM val);
SCM_API SCM scm_module_export (SCM module, SCM symbol_list);
SCM_API SCM scm_module_reverse_lookup (SCM module, SCM variable);

SCM_API SCM scm_c_resolve_module (const char *name);
SCM_API SCM scm_resolve_module (SCM name);
SCM_API SCM scm_c_define_module (const char *name,
				 void (*init)(void *), void *data);
SCM_API void scm_c_use_module (const char *name);
SCM_API void scm_c_export (const char *name, ...);

SCM_API SCM scm_sym2var (SCM sym, SCM thunk, SCM definep);

SCM_API SCM scm_module_public_interface (SCM module);
SCM_API SCM scm_module_import_interface (SCM module, SCM sym);
SCM_API SCM scm_module_lookup_closure (SCM module);
SCM_API SCM scm_module_transformer (SCM module);
SCM_API SCM scm_current_module_lookup_closure (void);
SCM_API SCM scm_current_module_transformer (void);
SCM_API SCM scm_eval_closure_lookup (SCM eclo, SCM sym, SCM definep);
SCM_API SCM scm_standard_eval_closure (SCM module);
SCM_API SCM scm_standard_interface_eval_closure (SCM module);
SCM_API SCM scm_eval_closure_module (SCM eval_closure); /* deprecated already */
SCM_API SCM scm_get_pre_modules_obarray (void);
SCM_API SCM scm_lookup_closure_module (SCM proc);

SCM_INTERNAL void scm_modules_prehistory (void);
SCM_INTERNAL void scm_init_modules (void);

#endif  /* SCM_MODULES_H */

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/


code:
/* "net_db.c" network database support
 * Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2006, 2009, 2010 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

/* Written in 1994 by Aubrey Jaffer.
 * Thanks to Hallvard.Tretteberg@si.sintef.no for inspiration and discussion.
 * Rewritten by Gary Houston to be a closer interface to the C socket library.
 * Split into net_db.c and socket.c.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <verify.h>
#include <errno.h>

#include "libguile/_scm.h"
#include "libguile/feature.h"
#include "libguile/strings.h"
#include "libguile/vectors.h"
#include "libguile/dynwind.h"

#include "libguile/validate.h"
#include "libguile/net_db.h"
#include "libguile/socket.h"

#ifdef HAVE_STRING_H
#include <string.h>
#endif

#include <sys/types.h>

#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#ifdef __MINGW32__
#include "win32-socket.h"
#endif

#if !defined (HAVE_H_ERRNO) && !defined (__MINGW32__) && !defined (__CYGWIN__)
/* h_errno not found in netdb.h, maybe this will help.  */
extern int h_errno;
#endif

#if defined HAVE_HSTRERROR && !HAVE_DECL_HSTRERROR	\
  && !defined __MINGW32__ && !defined __CYGWIN__
/* Some OSes, such as Tru64 5.1b, lack a declaration for hstrerror(3).  */
extern const char *hstrerror (int);
#endif

SCM_SYMBOL (scm_host_not_found_key, "host-not-found");
SCM_SYMBOL (scm_try_again_key, "try-again");
SCM_SYMBOL (scm_no_recovery_key, "no-recovery");
SCM_SYMBOL (scm_no_data_key, "no-data");

static void scm_resolv_error (const char *subr, SCM bad_value)
{
#ifdef NETDB_INTERNAL
  if (h_errno == NETDB_INTERNAL)
    {
      /* errno supposedly contains a useful value.  */
      scm_syserror (subr);
    }
  else
#endif
    {
      SCM key;
      const char *errmsg;

      switch (h_errno)
	{
	case HOST_NOT_FOUND:
	  key = scm_host_not_found_key;
	  errmsg = "Unknown host";
	  break;
	case TRY_AGAIN:
	  key = scm_try_again_key;
	  errmsg = "Host name lookup failure";
	  break;
	case NO_RECOVERY:
	  key = scm_no_recovery_key;
	  errmsg = "Unknown server error";
	  break;
	case NO_DATA:
	  key = scm_no_data_key;
	  errmsg = "No address associated with name";
	  break;
	default:
	  scm_misc_error (subr, "Unknown resolver error", SCM_EOL);
	  errmsg = NULL;
	}

#ifdef HAVE_HSTRERROR
      errmsg = (const char *) hstrerror (h_errno);
#endif
      scm_error (key, subr, errmsg, SCM_BOOL_F, SCM_EOL);
    }
}

/* Should take an extra arg for address format (will be needed for IPv6).
   Should use reentrant facilities if available.
 */

SCM_DEFINE (scm_gethost, "gethost", 0, 1, 0,
            (SCM host),
	    "@deffnx {Scheme Procedure} gethostbyname hostname\n"
	    "@deffnx {Scheme Procedure} gethostbyaddr address\n"
	    "Look up a host by name or address, returning a host object.  The\n"
	    "@code{gethost} procedure will accept either a string name or an integer\n"
	    "address; if given no arguments, it behaves like @code{gethostent} (see\n"
	    "below).  If a name or address is supplied but the address can not be\n"
	    "found, an error will be thrown to one of the keys:\n"
	    "@code{host-not-found}, @code{try-again}, @code{no-recovery} or\n"
	    "@code{no-data}, corresponding to the equivalent @code{h_error} values.\n"
	    "Unusual conditions may result in errors thrown to the\n"
	    "@code{system-error} or @code{misc_error} keys.")
#define FUNC_NAME s_scm_gethost
{
  SCM result = scm_c_make_vector (5, SCM_UNSPECIFIED);
  SCM lst = SCM_EOL;
  struct hostent *entry;
  struct in_addr inad;
  char **argv;
  int i = 0;

  if (SCM_UNBNDP (host))
    {
#ifdef HAVE_GETHOSTENT
      entry = gethostent ();
#else
      entry = NULL;
#endif
      if (! entry)
	{
	  /* As far as I can tell, there's no good way to tell whether
             zero means an error or end-of-file.  The trick of
             clearing errno before calling gethostent and checking it
             afterwards doesn't cut it, because, on Linux, it seems to
             try to contact some other server (YP?) and fails, which
             is a benign failure.  */
	  return SCM_BOOL_F;
	}
    }
  else if (scm_is_string (host))
    {
      char *str = scm_to_locale_string (host);
      entry = gethostbyname (str);
      free (str);
    }
  else
    {
      inad.s_addr = htonl (scm_to_ulong (host));
      entry = gethostbyaddr ((char *) &inad, sizeof (inad), AF_INET);
    }

  if (!entry)
    scm_resolv_error (FUNC_NAME, host);

  SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (entry->h_name));
  SCM_SIMPLE_VECTOR_SET(result, 1, scm_makfromstrs (-1, entry->h_aliases));
  SCM_SIMPLE_VECTOR_SET(result, 2, scm_from_int (entry->h_addrtype));
  SCM_SIMPLE_VECTOR_SET(result, 3, scm_from_int (entry->h_length));
  if (sizeof (struct in_addr) != entry->h_length)
    {
      SCM_SIMPLE_VECTOR_SET(result, 4, SCM_BOOL_F);
      return result;
    }
  for (argv = entry->h_addr_list; argv[i]; i++);
  while (i--)
    {
      inad = *(struct in_addr *) argv[i];
      lst = scm_cons (scm_from_ulong (ntohl (inad.s_addr)), lst);
    }
  SCM_SIMPLE_VECTOR_SET(result, 4, lst);
  return result;
}
#undef FUNC_NAME

/* In all subsequent getMUMBLE functions, when we're called with no
   arguments, we're supposed to traverse the tables entry by entry.
   However, there doesn't seem to be any documented way to distinguish
   between end-of-table and an error; in both cases the functions
   return zero.  Gotta love Unix.  For the time being, we clear errno,
   and if we get a zero and errno is set, we signal an error.  This
   doesn't seem quite right (what if errno gets set as part of healthy
   operation?), but it seems to work okay.  We'll see.  */

#if defined(HAVE_GETNETENT) && defined(HAVE_GETNETBYNAME) && defined(HAVE_GETNETBYADDR)
SCM_DEFINE (scm_getnet, "getnet", 0, 1, 0,
            (SCM net),
	    "@deffnx {Scheme Procedure} getnetbyname net-name\n"
	    "@deffnx {Scheme Procedure} getnetbyaddr net-number\n"
	    "Look up a network by name or net number in the network database.  The\n"
	    "@var{net-name} argument must be a string, and the @var{net-number}\n"
	    "argument must be an integer.  @code{getnet} will accept either type of\n"
	    "argument, behaving like @code{getnetent} (see below) if no arguments are\n"
	    "given.")
#define FUNC_NAME s_scm_getnet
{
  SCM result = scm_c_make_vector (4, SCM_UNSPECIFIED);
  struct netent *entry;
  int eno;

  if (SCM_UNBNDP (net))
    {
      entry = getnetent ();
      if (! entry)
	{
	  /* There's no good way to tell whether zero means an error
             or end-of-file, so we always return #f.  See `gethost'
             for details. */
	  return SCM_BOOL_F;
	}
    }
  else if (scm_is_string (net))
    {
      char *str = scm_to_locale_string (net);
      entry = getnetbyname (str);
      eno = errno;
      free (str);
    }
  else
    {
      unsigned long netnum = scm_to_ulong (net);
      entry = getnetbyaddr (netnum, AF_INET);
      eno = errno;
    }

  if (!entry)
    SCM_SYSERROR_MSG ("no such network ~A", scm_list_1 (net), eno);

  SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (entry->n_name));
  SCM_SIMPLE_VECTOR_SET(result, 1, scm_makfromstrs (-1, entry->n_aliases));
  SCM_SIMPLE_VECTOR_SET(result, 2, scm_from_int (entry->n_addrtype));
  SCM_SIMPLE_VECTOR_SET(result, 3, scm_from_ulong (entry->n_net));
  return result;
}
#undef FUNC_NAME
#endif

#if defined (HAVE_GETPROTOENT) || defined (__MINGW32__)
SCM_DEFINE (scm_getproto, "getproto", 0, 1, 0,
            (SCM protocol),
	    "@deffnx {Scheme Procedure} getprotobyname name\n"
	    "@deffnx {Scheme Procedure} getprotobynumber number\n"
	    "Look up a network protocol by name or by number.  @code{getprotobyname}\n"
	    "takes a string argument, and @code{getprotobynumber} takes an integer\n"
	    "argument.  @code{getproto} will accept either type, behaving like\n"
	    "@code{getprotoent} (see below) if no arguments are supplied.")
#define FUNC_NAME s_scm_getproto
{
  SCM result = scm_c_make_vector (3, SCM_UNSPECIFIED);
  struct protoent *entry;
  int eno;

  if (SCM_UNBNDP (protocol))
    {
      entry = getprotoent ();
      if (! entry)
	{
	  /* There's no good way to tell whether zero means an error
             or end-of-file, so we always return #f.  See `gethost'
             for details. */
	  return SCM_BOOL_F;
	}
    }
  else if (scm_is_string (protocol))
    {
      char *str = scm_to_locale_string (protocol);
      entry = getprotobyname (str);
      eno = errno;
      free (str);
    }
  else
    {
      unsigned long protonum = scm_to_ulong (protocol);
      entry = getprotobynumber (protonum);
      eno = errno;
    }

  if (!entry)
    SCM_SYSERROR_MSG ("no such protocol ~A", scm_list_1 (protocol), eno);

  SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (entry->p_name));
  SCM_SIMPLE_VECTOR_SET(result, 1, scm_makfromstrs (-1, entry->p_aliases));
  SCM_SIMPLE_VECTOR_SET(result, 2, scm_from_int (entry->p_proto));
  return result;
}
#undef FUNC_NAME
#endif

#if defined (HAVE_GETSERVENT) || defined (__MINGW32__)
static SCM
scm_return_entry (struct servent *entry)
{
  SCM result = scm_c_make_vector (4, SCM_UNSPECIFIED);

  SCM_SIMPLE_VECTOR_SET(result, 0, scm_from_locale_string (entry->s_name));
  SCM_SIMPLE_VECTOR_SET(result, 1, scm_makfromstrs (-1, entry->s_aliases));
  SCM_SIMPLE_VECTOR_SET(result, 2, scm_from_uint16 (ntohs (entry->s_port)));
  SCM_SIMPLE_VECTOR_SET(result, 3, scm_from_locale_string (entry->s_proto));
  return result;
}

SCM_DEFINE (scm_getserv, "getserv", 0, 2, 0,
            (SCM name, SCM protocol),
	    "@deffnx {Scheme Procedure} getservbyname name protocol\n"
	    "@deffnx {Scheme Procedure} getservbyport port protocol\n"
	    "Look up a network service by name or by service number, and return a\n"
	    "network service object.  The @var{protocol} argument specifies the name\n"
	    "of the desired protocol; if the protocol found in the network service\n"
	    "database does not match this name, a system error is signalled.\n\n"
	    "The @code{getserv} procedure will take either a service name or number\n"
	    "as its first argument; if given no arguments, it behaves like\n"
	    "@code{getservent} (see below).")
#define FUNC_NAME s_scm_getserv
{
  struct servent *entry;
  char *protoname;
  int eno;

  if (SCM_UNBNDP (name))
    {
      entry = getservent ();
      if (!entry)
	{
	  /* There's no good way to tell whether zero means an error
             or end-of-file, so we always return #f.  See `gethost'
             for details. */
	  return SCM_BOOL_F;
	}
      return scm_return_entry (entry);
    }

  scm_dynwind_begin (0);

  protoname = scm_to_locale_string (protocol);
  scm_dynwind_free (protoname);

  if (scm_is_string (name))
    {
      char *str = scm_to_locale_string (name);
      entry = getservbyname (str, protoname);
      eno = errno;
      free (str);
    }
  else
    {
      entry = getservbyport (htons (scm_to_int (name)), protoname);
      eno = errno;
    }

  if (!entry)
    SCM_SYSERROR_MSG("no such service ~A", scm_list_1 (name), eno);

  scm_dynwind_end ();
  return scm_return_entry (entry);
}
#undef FUNC_NAME
#endif

#if defined(HAVE_SETHOSTENT) && defined(HAVE_ENDHOSTENT)
SCM_DEFINE (scm_sethost, "sethost", 0, 1, 0,
            (SCM stayopen),
	    "If @var{stayopen} is omitted, this is equivalent to @code{endhostent}.\n"
	    "Otherwise it is equivalent to @code{sethostent stayopen}.")
#define FUNC_NAME s_scm_sethost
{
  if (SCM_UNBNDP (stayopen))
    endhostent ();
  else
    sethostent (scm_is_true (stayopen));
  return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
#endif

#if defined(HAVE_SETNETENT) && defined(HAVE_ENDNETENT)
SCM_DEFINE (scm_setnet, "setnet", 0, 1, 0,
            (SCM stayopen),
	    "If @var{stayopen} is omitted, this is equivalent to @code{endnetent}.\n"
	    "Otherwise it is equivalent to @code{setnetent stayopen}.")
#define FUNC_NAME s_scm_setnet
{
  if (SCM_UNBNDP (stayopen))
    endnetent ();
  else
    setnetent (scm_is_true (stayopen));
  return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
#endif

#if defined (HAVE_SETPROTOENT) && defined (HAVE_ENDPROTOENT) || defined (__MINGW32__)
SCM_DEFINE (scm_setproto, "setproto", 0, 1, 0,
            (SCM stayopen),
	    "If @var{stayopen} is omitted, this is equivalent to @code{endprotoent}.\n"
	    "Otherwise it is equivalent to @code{setprotoent stayopen}.")
#define FUNC_NAME s_scm_setproto
{
  if (SCM_UNBNDP (stayopen))
    endprotoent ();
  else
    setprotoent (scm_is_true (stayopen));
  return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
#endif

#if defined (HAVE_SETSERVENT) && defined (HAVE_ENDSERVENT) || defined (__MINGW32__)
SCM_DEFINE (scm_setserv, "setserv", 0, 1, 0,
            (SCM stayopen),
	    "If @var{stayopen} is omitted, this is equivalent to @code{endservent}.\n"
	    "Otherwise it is equivalent to @code{setservent stayopen}.")
#define FUNC_NAME s_scm_setserv
{
  if (SCM_UNBNDP (stayopen))
    endservent ();
  else
    setservent (scm_is_true (stayopen));
  return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
#endif

/* Protocol-independent name resolution with getaddrinfo(3) & co.  */

SCM_SYMBOL (sym_getaddrinfo_error, "getaddrinfo-error");

/* Make sure the `AI_*' flags can be stored as INUMs.  */
verify (SCM_I_INUM (SCM_I_MAKINUM (AI_ALL)) == AI_ALL);

/* Valid values for the `ai_flags' to `struct addrinfo'.  */
SCM_VARIABLE_INIT (sym_ai_passive, "AI_PASSIVE",
		   SCM_I_MAKINUM (AI_PASSIVE));
SCM_VARIABLE_INIT (sym_ai_canonname, "AI_CANONNAME",
		   SCM_I_MAKINUM (AI_CANONNAME));
SCM_VARIABLE_INIT (sym_ai_numerichost, "AI_NUMERICHOST",
		   SCM_I_MAKINUM (AI_NUMERICHOST));
SCM_VARIABLE_INIT (sym_ai_numericserv, "AI_NUMERICSERV",
		   SCM_I_MAKINUM (AI_NUMERICSERV));
SCM_VARIABLE_INIT (sym_ai_v4mapped, "AI_V4MAPPED",
		   SCM_I_MAKINUM (AI_V4MAPPED));
SCM_VARIABLE_INIT (sym_ai_all, "AI_ALL",
		   SCM_I_MAKINUM (AI_ALL));
SCM_VARIABLE_INIT (sym_ai_addrconfig, "AI_ADDRCONFIG",
		   SCM_I_MAKINUM (AI_ADDRCONFIG));

/* Return a Scheme vector whose elements correspond to the fields of C_AI,
   ignoring the `ai_next' field.  This function is not exported because the
   definition of `struct addrinfo' is provided by Gnulib.  */
static SCM
scm_from_addrinfo (const struct addrinfo *c_ai)
{
  SCM ai;

  /* Note: The indices here must be kept synchronized with those used by the
     `addrinfo:' procedures in `networking.scm'.  */

  ai = scm_c_make_vector (6, SCM_UNDEFINED);
  SCM_SIMPLE_VECTOR_SET (ai, 0, scm_from_int (c_ai->ai_flags));
  SCM_SIMPLE_VECTOR_SET (ai, 1, scm_from_int (c_ai->ai_family));
  SCM_SIMPLE_VECTOR_SET (ai, 2, scm_from_int (c_ai->ai_socktype));
  SCM_SIMPLE_VECTOR_SET (ai, 3, scm_from_int (c_ai->ai_protocol));
  SCM_SIMPLE_VECTOR_SET (ai, 4,
			 scm_from_sockaddr (c_ai->ai_addr, c_ai->ai_addrlen));
  SCM_SIMPLE_VECTOR_SET (ai, 5,
			 c_ai->ai_canonname != NULL
			 ? scm_from_locale_string (c_ai->ai_canonname)
			 : SCM_BOOL_F);

  return ai;
}

SCM_DEFINE (scm_getaddrinfo, "getaddrinfo", 1, 5, 0,
	    (SCM name, SCM service, SCM hint_flags, SCM hint_family,
	     SCM hint_socktype, SCM hint_protocol),
	    "Return a list of @code{addrinfo} structures containing "
	    "a socket address and associated information for host @var{name} "
	    "and/or @var{service} to be used in creating a socket with "
	    "which to address the specified service.\n\n"
	    "@example\n"
	    "(let* ((ai (car (getaddrinfo \"www.gnu.org\" \"http\")))\n"
	    "       (s  (socket (addrinfo:fam ai) (addrinfo:socktype ai)\n"
	    "                   (addrinfo:protocol ai))))\n"
	    "  (connect s (addrinfo:addr ai))\n"
	    "  s)\n"
	    "@end example\n\n"
	    "When @var{service} is omitted or is @code{#f}, return "
	    "network-level addresses for @var{name}.  When @var{name} "
	    "is @code{#f} @var{service} must be provided and service "
	    "locations local to the caller are returned.\n"
	    "\n"
	    "Additional hints can be provided.  When specified, "
	    "@var{hint_flags} should be a bitwise-or of zero or more "
	    "constants among the following:\n\n"
	    "@table @code\n"
	    "@item AI_PASSIVE\n"
	    "Socket address is intended for @code{bind}.\n\n"
	    "@item AI_CANONNAME\n"
	    "Request for canonical host name, available via "
	    "@code{addrinfo:canonname}.  This makes sense mainly when "
	    "DNS lookups are involved.\n\n"
	    "@item AI_NUMERICHOST\n"
	    "Specifies that @var{name} is a numeric host address string "
	    "(e.g., @code{\"127.0.0.1\"}), meaning that name resolution "
	    "will not be used.\n\n"
	    "@item AI_NUMERICSERV\n"
	    "Likewise, specifies that @var{service} is a numeric port "
	    "string (e.g., @code{\"80\"}).\n\n"
	    "@item AI_ADDRCONFIG\n"
	    "Return only addresses configured on the local system.  It is "
	    "highly recommended to provide this flag when the returned "
	    "socket addresses are to be used to make connections; "
	    "otherwise, some of the returned addresses could be unreachable "
	    "or use a protocol that is not supported.\n\n"
	    "@item AI_V4MAPPED\n"
	    "When looking up IPv6 addresses, return mapped "
	    "IPv4 addresses if there is no IPv6 address available at all.\n\n"
	    "@item AI_ALL\n"
	    "If this flag is set along with @code{AI_V4MAPPED} when looking "
	    "up IPv6 addresses, return all IPv6 addresses "
	    "as well as all IPv4 addresses, the latter mapped to IPv6 "
	    "format.\n"
	    "@end table\n\n"
	    "When given, @var{hint_family} should specify the requested "
	    "address family, e.g., @code{AF_INET6}.  Similarly, "
	    "@var{hint_socktype} should specify the requested socket type "
	    "(e.g., @code{SOCK_DGRAM}), and @var{hint_protocol} should "
	    "specify the requested protocol (its value is interpretered "
	    "as in calls to @code{socket}).\n"
	    "\n"
	    "On error, an exception with key @code{getaddrinfo-error} is "
	    "thrown, with an error code (an integer) as its argument:\n\n"
	    "@example\n"
	    "(catch 'getaddrinfo-error\n"
            "  (lambda ()\n"
            "    (getaddrinfo \"www.gnu.org\" \"gopher\"))\n"
            "  (lambda (key errcode)\n"
            "    (cond ((= errcode EAI_SERVICE)\n"
            "           (display \"doesn't know about Gopher!\\n\"))\n"
            "          ((= errcode EAI_NONAME)\n"
            "           (display \"www.gnu.org not found\\n\"))\n"
            "          (else\n"
            "           (format #t \"something wrong: ~a\\n\"\n"
            "                   (gai-strerror errcode))))))\n"
	    "@end example\n"
	    "\n"
	    "Error codes are:\n\n"
	    "@table @code\n"
	    "@item EAI_AGAIN\n"
	    "The name or service could not be resolved at this time. Future "
	    "attempts may succeed.\n\n"
	    "@item EAI_BADFLAGS\n"
	    "@var{hint_flags} contains an invalid value.\n\n"
	    "@item EAI_FAIL\n"
	    "A non-recoverable error occurred when attempting to "
	    "resolve the name.\n\n"
	    "@item EAI_FAMILY\n"
	    "@var{hint_family} was not recognized.\n\n"
	    "@item EAI_NONAME\n"
	    "Either @var{name} does not resolve for the supplied parameters, "
	    "or neither @var{name} nor @var{service} were supplied.\n\n"
	    "@item EAI_SERVICE\n"
	    "@var{service} was not recognized for the specified socket type.\n\n"
	    "@item EAI_SOCKTYPE\n"
	    "@var{hint_socktype} was not recognized.\n\n"
	    "@item EAI_SYSTEM\n"
	    "A system error occurred; the error code can be found in "
	    "@code{errno}.\n"
	    "@end table\n"
	    "\n"
	    "Users are encouraged to read the "
	    "@url{http://www.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html,"
	    "POSIX specification} for more details.\n")
#define FUNC_NAME s_scm_getaddrinfo
{
  int err;
  char *c_name, *c_service;
  struct addrinfo c_hints, *c_result;
  SCM result = SCM_EOL;

  if (scm_is_true (name))
    SCM_VALIDATE_STRING (SCM_ARG1, name);

  if (!SCM_UNBNDP (service) && scm_is_true (service))
    SCM_VALIDATE_STRING (SCM_ARG2, service);

  scm_dynwind_begin (0);

  if (scm_is_string (name))
    {
      c_name = scm_to_locale_string (name);
      scm_dynwind_free (c_name);
    }
  else
    c_name = NULL;

  if (scm_is_string (service))
    {
      c_service = scm_to_locale_string (service);
      scm_dynwind_free (c_service);
    }
  else
    c_service = NULL;

  memset (&c_hints, 0, sizeof (c_hints));
  if (!SCM_UNBNDP (hint_flags))
    {
      c_hints.ai_flags = scm_to_int (hint_flags);
      if (!SCM_UNBNDP (hint_family))
	{
	  c_hints.ai_family = scm_to_int (hint_family);
	  if (!SCM_UNBNDP (hint_socktype))
	    {
	      c_hints.ai_socktype = scm_to_int (hint_socktype);
	      if (!SCM_UNBNDP (hint_family))
		c_hints.ai_family = scm_to_int (hint_family);
	    }
	}
    }

  err = getaddrinfo (c_name, c_service, &c_hints, &c_result);
  if (err == 0)
    {
      SCM *prev_addr;
      struct addrinfo *a;

      for (prev_addr = &result, a = c_result;
	   a != NULL;
	   a = a->ai_next, prev_addr = SCM_CDRLOC (*prev_addr))
	*prev_addr = scm_list_1 (scm_from_addrinfo (a));

      freeaddrinfo (c_result);
    }
  else
    scm_throw (sym_getaddrinfo_error, scm_list_1 (scm_from_int (err)));

  scm_dynwind_end ();

  return result;
}
#undef FUNC_NAME

/* Make sure the `EAI_*' flags can be stored as INUMs.  */
verify (SCM_I_INUM (SCM_I_MAKINUM (EAI_BADFLAGS)) == EAI_BADFLAGS);

/* Error codes returned by `getaddrinfo'.  */
SCM_VARIABLE_INIT (sym_eai_badflags, "EAI_BADFLAGS",
		   SCM_I_MAKINUM (EAI_BADFLAGS));
SCM_VARIABLE_INIT (sym_eai_noname, "EAI_NONAME",
		   SCM_I_MAKINUM (EAI_NONAME));
SCM_VARIABLE_INIT (sym_eai_again, "EAI_AGAIN",
		   SCM_I_MAKINUM (EAI_AGAIN));
SCM_VARIABLE_INIT (sym_eai_fail, "EAI_FAIL",
		   SCM_I_MAKINUM (EAI_FAIL));
SCM_VARIABLE_INIT (sym_eai_family, "EAI_FAMILY",
		   SCM_I_MAKINUM (EAI_FAMILY));
SCM_VARIABLE_INIT (sym_eai_socktype, "EAI_SOCKTYPE",
		   SCM_I_MAKINUM (EAI_SOCKTYPE));
SCM_VARIABLE_INIT (sym_eai_service, "EAI_SERVICE",
		   SCM_I_MAKINUM (EAI_SERVICE));
SCM_VARIABLE_INIT (sym_eai_memory, "EAI_MEMORY",
		   SCM_I_MAKINUM (EAI_MEMORY));
SCM_VARIABLE_INIT (sym_eai_system, "EAI_SYSTEM",
		   SCM_I_MAKINUM (EAI_SYSTEM));
SCM_VARIABLE_INIT (sym_eai_overflow, "EAI_OVERFLOW",
		   SCM_I_MAKINUM (EAI_OVERFLOW));

/* The following values are GNU extensions.  */
#ifdef EAI_NODATA
SCM_VARIABLE_INIT (sym_eai_nodata, "EAI_NODATA",
		   SCM_I_MAKINUM (EAI_NODATA));
#endif
#ifdef EAI_ADDRFAMILY
SCM_VARIABLE_INIT (sym_eai_addrfamily, "EAI_ADDRFAMILY",
		   SCM_I_MAKINUM (EAI_ADDRFAMILY));
#endif
#ifdef EAI_INPROGRESS
SCM_VARIABLE_INIT (sym_eai_inprogress, "EAI_INPROGRESS",
		   SCM_I_MAKINUM (EAI_INPROGRESS));
#endif
#ifdef EAI_CANCELED
SCM_VARIABLE_INIT (sym_eai_canceled, "EAI_CANCELED",
		   SCM_I_MAKINUM (EAI_CANCELED));
#endif
#ifdef EAI_NOTCANCELED
SCM_VARIABLE_INIT (sym_eai_notcanceled, "EAI_NOTCANCELED",
		   SCM_I_MAKINUM (EAI_NOTCANCELED));
#endif
#ifdef EAI_ALLDONE
SCM_VARIABLE_INIT (sym_eai_alldone, "EAI_ALLDONE",
		   SCM_I_MAKINUM (EAI_ALLDONE));
#endif
#ifdef EAI_INTR
SCM_VARIABLE_INIT (sym_eai_intr, "EAI_INTR",
		   SCM_I_MAKINUM (EAI_INTR));
#endif
#ifdef EAI_IDN_ENCODE
SCM_VARIABLE_INIT (sym_eai_idn_encode, "EAI_IDN_ENCODE",
		   SCM_I_MAKINUM (EAI_IDN_ENCODE));
#endif

SCM_DEFINE (scm_gai_strerror, "gai-strerror", 1, 0, 0,
	    (SCM error),
	    "Return a string describing @var{error}, an integer error code "
	    "returned by @code{getaddrinfo}.")
#define FUNC_NAME s_scm_gai_strerror
{
  return scm_from_locale_string (gai_strerror (scm_to_int (error)));
}
#undef FUNC_NAME

/* TODO: Add a getnameinfo(3) wrapper.  */

void
scm_init_net_db ()
{
  scm_add_feature ("net-db");
#include "libguile/net_db.x"
}

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/


code:
/* classes: h_files */

#ifndef SCM_NET_DB_H
#define SCM_NET_DB_H

/* Copyright (C) 1995,2000,2001, 2006, 2008, 2010 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "libguile/__scm.h"

SCM_API SCM scm_gethost (SCM host);
SCM_API SCM scm_getnet (SCM name);
SCM_API SCM scm_getproto (SCM name);
SCM_API SCM scm_getserv (SCM name, SCM proto);
SCM_API SCM scm_sethost (SCM arg);
SCM_API SCM scm_setnet (SCM arg);
SCM_API SCM scm_setproto (SCM arg);
SCM_API SCM scm_setserv (SCM arg);
SCM_API SCM scm_getaddrinfo (SCM, SCM, SCM, SCM, SCM, SCM);
SCM_API SCM scm_gai_strerror (SCM);
SCM_INTERNAL void scm_init_net_db (void);

#endif  /* SCM_NET_DB_H */

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/


code:
/* Copyright (C) 2002, 2006, 2008 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>
#include "libguile/_scm.h"

#if SCM_USE_NULL_THREADS

#include "libguile/null-threads.h"

static scm_i_pthread_key_t *all_keys = NULL;

static void
destroy_keys (void)
{
  scm_i_pthread_key_t *key;
  int again;

  do {
    again = 0;
    for (key = all_keys; key; key = key->next)
      if (key->value && key->destr_func)
	{
	  void *v = key->value;
	  key->value = NULL;
	  key->destr_func (v);
	  again = 1;
	}
  } while (again);
}

int
scm_i_pthread_key_create (scm_i_pthread_key_t *key,
			  void (*destr_func) (void *))
{
  if (all_keys == NULL)
    atexit (destroy_keys);

  key->next = all_keys;
  all_keys = key;
  key->value = NULL;
  key->destr_func = destr_func;

  return 0;
}

#endif /* SCM_USE_NULL_THREADS */

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/


code:
/* classes: h_files */

#ifndef SCM_NULL_THREADS_H
#define SCM_NULL_THREADS_H

/* Copyright (C) 2005, 2006 Free Software Foundation, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

/* The null-threads implementation.  We provide the subset of the
   standard pthread API that is used by Guile, but no new threads can
   be created.

   This file merely exits so that Guile can be compiled and run
   without using pthreads.  Improving performance via optimizations
   that are possible in a single-threaded program is not a primary
   goal.
*/

#include <errno.h>

/* Threads
*/
#define scm_i_pthread_t                     int
#define scm_i_pthread_self()                0
#define scm_i_pthread_create(t,a,f,d)       (*(t)=0, (void)(f), ENOSYS)
#define scm_i_pthread_detach(t)             do { } while (0)
#define scm_i_pthread_exit(v)               exit(0)
#define scm_i_pthread_cancel(t)             0
#define scm_i_pthread_cleanup_push(t,v)     0
#define scm_i_pthread_cleanup_pop(e)        0
#define scm_i_sched_yield()                 0

/* Signals
 */
#define scm_i_pthread_sigmask               sigprocmask

/* Mutexes
 */
#define SCM_I_PTHREAD_MUTEX_INITIALIZER     0
#define scm_i_pthread_mutex_t               int
#define scm_i_pthread_mutex_init(m,a)       (*(m) = 0)
#define scm_i_pthread_mutex_destroy(m)      do { (void)(m); } while(0)
#define scm_i_pthread_mutex_trylock(m)      ((*(m))++)
#define scm_i_pthread_mutex_lock(m)         ((*(m))++)
#define scm_i_pthread_mutex_unlock(m)       ((*(m))--)
#define scm_i_pthread_mutexattr_recursive   0

/* Condition variables
 */
#define SCM_I_PTHREAD_COND_INITIALIZER      0
#define scm_i_pthread_cond_t                int
#define scm_i_pthread_cond_init(c,a)        (*(c) = 0)
#define scm_i_pthread_cond_destroy(c)       do { (void)(c); } while(0)
#define scm_i_pthread_cond_signal(c)        (*(c) = 1)
#define scm_i_pthread_cond_broadcast(c)     (*(c) = 1)
#define scm_i_pthread_cond_wait(c,m)        (abort(), 0)
#define scm_i_pthread_cond_timedwait(c,m,t) (abort(), 0)

/* Onces
 */
#define scm_i_pthread_once_t                int
#define SCM_I_PTHREAD_ONCE_INIT             0
#define scm_i_pthread_once(o,f)             do { \
                                              if(!*(o)) { *(o)=1; f (); } \
                                            } while(0)

/* Thread specific storage
 */
typedef struct scm_i_pthread_key_t {
  struct scm_i_pthread_key_t *next;
  void *value;
  void (*destr_func) (void *);
} scm_i_pthread_key_t;

SCM_API int scm_i_pthread_key_create (scm_i_pthread_key_t *key,
				      void (*destr_func) (void *));
#define scm_i_pthread_setspecific(k,p)      ((k).value = (p))
#define scm_i_pthread_getspecific(k)        ((k).value)

/* Convenience functions
 */
#define scm_i_scm_pthread_mutex_lock        scm_i_pthread_mutex_lock
#define scm_i_dynwind_pthread_mutex_lock    scm_i_pthread_mutex_lock
#define scm_i_scm_pthread_cond_wait         scm_i_pthread_cond_wait
#define scm_i_scm_pthread_cond_timedwait    scm_i_pthread_cond_timedwait

#endif  /* SCM_NULL_THREADS_H */

/*
  Local Variables:
  c-file-style: "gnu"
  End:
*/