From 42ad13ca342ae47b39d9a825ef4cd4341a438fd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Mon, 4 Mar 2002 21:39:57 +0100
Subject: [PATCH] New files. A lexer and a recursive-descent parser for config
 files.

Rev: src/parse_config.c:1.1
Rev: src/parse_config.h:1.1
---
 src/parse_config.c | 356 +++++++++++++++++++++++++++++++++++++++++++++
 src/parse_config.h |  55 +++++++
 2 files changed, 411 insertions(+)
 create mode 100644 src/parse_config.c
 create mode 100644 src/parse_config.h

diff --git a/src/parse_config.c b/src/parse_config.c
new file mode 100644
index 000000000..ee17f7640
--- /dev/null
+++ b/src/parse_config.c
@@ -0,0 +1,356 @@
+/* parse_config.c
+ *
+ * $id$
+ *
+ * Parsing of configuration files. */
+
+#include "parse_config.h"
+
+#include "format.h"
+#include "parse.h"
+#include "werror.h"
+#include "xalloc.h"
+
+#include <assert.h>
+#include <string.h>
+
+#define BUFFER (&(self->buffer))
+
+#include "parse_macros.h"
+
+#include "parse_config.c.x"
+
+/* GABA:
+   (class
+     (name config_setting)
+     (vars
+       (next object config_setting)
+       (type . "enum config_type")
+       (value string)))
+*/
+
+/* GABA:
+   (class
+     (name config_host)
+     (vars
+       (next object config_host)
+       (name string)
+       (settings object config_setting)))
+*/
+
+/* GABA:
+   (class
+     (name config_group)
+     (vars
+       (next object config_group)
+       (name string)
+       ; Group settings
+       (settings object config_setting)
+       (hosts object config_host)))
+*/
+
+enum token_type
+  { TOK_EOF, TOK_BEGIN_GROUP, TOK_END_GROUP, TOK_STRING, TOK_ERROR };
+
+/* FIXME: Keep track of linenumber */
+struct tokenizer
+{
+  struct simple_buffer buffer;
+  enum token_type type;
+  unsigned token_length;
+  const char *token;
+};
+
+static void
+tokenizer_init(struct tokenizer *self,
+	       unsigned length, const unsigned char *data)
+{
+  simple_buffer_init(&self->buffer, length, data);
+}
+
+static enum token_type
+next_token(struct tokenizer *self)
+{
+  /* FIXME: Share a char-class table with sexp_parser.c. */
+  static const char char_class[0x100] =
+    {
+      /* HT, LF, VT, FF, CR */
+      0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      /* SPACE */
+      1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      /* '{', '}' */
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,2,0,2,0,0,
+
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+      0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    };
+#define IS_SPACE(c) (char_class[c] & 1)
+#define IS_SEPARATOR(c) (char_class[c] & 3)
+  while (LEFT && IS_SPACE(*HERE))
+    ADVANCE(1);
+
+  if (!LEFT)
+    self->type = TOK_EOF;
+  else switch(*HERE)
+    {
+    case '{':
+      self->type = TOK_BEGIN_GROUP;
+      ADVANCE(1);
+      break;
+    case '}':
+      self->type = TOK_END_GROUP;
+      ADVANCE(1);
+      break;
+    default:
+      {
+	unsigned i;
+	self->token = HERE;
+	
+	for (i = 0; i<LEFT && !IS_SEPARATOR(HERE[i]); i++)
+	  ;
+	self->token_length = i;
+	ADVANCE(i);
+      }
+    }
+  return self->type;
+}
+
+/* Can only be called if self->type == TOK_STRING */
+static int
+looking_at(struct tokenizer *self, const char *word)
+{
+  unsigned length = strlen(word);
+
+  return (length == self->token_length
+	  && !memcmp(self->token, word, length));
+}
+
+static struct lsh_string *
+parse_word(struct tokenizer *self)
+{
+  struct lsh_string *s;
+  if (self->type != TOK_STRING)
+    return NULL;
+
+  s = ssh_format("%ls", self->token_length, self->token);
+  next_token(self);
+  return s;
+}
+
+static struct config_setting *
+parse_setting(struct tokenizer *self, struct config_setting *settings)
+{
+  struct lsh_string *s;
+  enum config_type type;
+  if (looking_at(self, "address"))
+    type = CONFIG_ADDRESS;
+  else if (looking_at(self, "user"))
+    type = CONFIG_USER;
+  else
+    return NULL;
+
+  next_token(self);
+  s = parse_word(self);
+  if (!s)
+    return NULL;
+  
+  {
+    /* Push new object on the list */
+    NEW(config_setting, n);
+    n->next = settings;
+    settings = n;
+  }
+  
+  settings->type = type;
+  settings->value = s;
+
+  return settings;
+}
+
+static struct config_setting *
+parse_host_settings(struct tokenizer *self)
+{
+  struct config_setting *settings = NULL;
+
+  while (self->type == TOK_STRING)
+    {
+      settings = parse_setting(self, settings);
+      if (!settings)
+	return NULL;
+    }
+  return settings;
+}
+
+static int
+parse_token(struct tokenizer *self, enum token_type type)
+{
+  if (self->type == type)
+    {
+      next_token(self);
+      return 1;
+    }
+  else
+    return 0;
+}
+
+static struct config_host *
+parse_hosts(struct tokenizer *self, struct config_host *hosts)
+{
+  while (self->type == TOK_STRING)
+    {
+      {
+	/* Push new object on the list */
+	NEW(config_host, n);
+	n->next = hosts;
+	hosts = n;
+      }
+      hosts->name = parse_word(self);
+      assert(hosts->name);
+      if (self->type == TOK_BEGIN_GROUP)
+	{
+	  hosts->settings = parse_host_settings(self);
+	  if (!parse_token(self, TOK_END_GROUP))
+	    return NULL;
+	}
+    }
+  return hosts;
+}
+
+#if 0
+static struct config_setting *
+parse_group(struct tokenizer *self)
+{
+  struct config_setting *settings = NULL;
+
+  while (self->type == TOK_STRING)
+    {
+      struct lsh_string *s;
+      {
+	/* Push new object on the list */
+	NEW(config_setting, n);
+	n->next = settings;
+	settings = n;
+      }
+      if (looking_at(self, "address"))
+	settings->type = CONFIG_ADDRESS;
+      else if (looking_at(self, "user"))
+	settings->type = CONFIG_USER;
+      else if (looking_at(self, "hosts"))
+	
+      else
+	return NULL;
+      
+      next_token(self);
+      if (self->type != TOK_STRING)
+	return NULL;
+
+      s = parse_word(self);
+      if (!s)
+	return NULL;
+      settings->value = s;
+    }
+  return settings;
+}
+#endif
+
+static struct config_group *
+parse_groups(struct tokenizer *self)
+{
+  struct config_group *groups = NULL;
+  while (self->type != TOK_EOF)
+    {
+      {
+	/* Push new object on the list */
+	NEW(config_group, n);
+	n->next = groups;
+	groups = n;
+      }
+      /* Name is optional */
+      groups->name = parse_word(self);
+      groups->settings = NULL;
+      groups->hosts = NULL;
+      
+      if (!parse_token(self, TOK_BEGIN_GROUP))
+	return NULL;
+
+      while (self->type != TOK_END_GROUP)
+	{
+	  if (looking_at(self, "hosts"))
+	    {
+	      groups->hosts = parse_hosts(self, groups->hosts);
+	      if (!groups->hosts)
+		return NULL;
+	    }
+	  else
+	    {
+	      groups->settings = parse_setting(self, groups->settings);
+	      if (!groups->settings)
+		return NULL;
+	    }
+	}
+      if (!parse_token(self, TOK_END_GROUP))
+	return NULL;
+    }
+  return groups;
+}
+
+struct config_group *
+config_parse_string(const struct lsh_string *s)
+{
+  struct tokenizer t;
+  tokenizer_init(&t, s->length, s->data);
+  next_token(&t);
+
+  return parse_groups(&t);
+}
+
+
+int
+config_lookup_host(const struct config_group *groups,
+		   const char *host,
+		   struct config_match *match)
+{
+  unsigned length = strlen(host);
+  int found = 0;
+  
+  for (; groups; groups = groups->next)
+    {
+      const struct config_host *hosts;
+      for (hosts = groups->hosts; hosts; hosts = hosts->next)
+	if (lsh_string_eq_l(hosts->name, length, host))
+	  {
+	    if (found)
+	      {
+		werror("Ambigous host name `%z' in configuration.\n");
+		return 1;
+	      }
+	    else
+	      {
+		found = 1;
+		match->group = groups->settings;
+		match->host = hosts->settings;
+	      }
+	  }
+    }
+  return found;
+}
+
+const struct lsh_string *
+config_get_setting(enum config_type type,
+		   const struct config_match *match)
+{
+  const struct config_setting *p;
+
+  for (p = match->host; p; p = p->next)
+    if (p->type == type)
+      return p->value;
+
+  for (p = match->group; p; p = p->next)
+    if (p->type == type)
+      return p->value;
+
+  return NULL;
+}
diff --git a/src/parse_config.h b/src/parse_config.h
new file mode 100644
index 000000000..07aef6e39
--- /dev/null
+++ b/src/parse_config.h
@@ -0,0 +1,55 @@
+/* parse_config.h
+ *
+ * $id$
+ *
+ * Parsing of configuration files. */
+
+/* lsh, an implementation of the ssh protocol
+ *
+ * Copyright (C) 2002 Niels M�ller
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef LSH_PARSE_CONFIG_H_INCLUDED
+#define LSH_PARSE_CONFIG_H_INCLUDED
+
+#include "lsh.h"
+
+enum config_type
+  { CONFIG_ADDRESS, CONFIG_USER };
+
+struct config_setting;
+struct config_group;
+
+struct config_group *
+config_parse_string(const struct lsh_string *s);
+
+struct config_match
+{
+  const struct config_setting *group;
+  const struct config_setting *host;
+};
+
+int
+config_lookup_host(const struct config_group *config,
+		   const char *host,
+		   struct config_match *match);
+
+const struct lsh_string *
+config_get_setting(enum config_type type,
+		   const struct config_match *match);
+
+#endif /* LSH_PARSE_CONFIG_H_INCLUDED */
-- 
GitLab