Add simplistic telnet control interface

This might turn into a complete wire protocol with special
client software. For now it will be a simple client interface
that you can use with telnet to do certain things.

This is using flex to implement the parsing. Implementation
and more commands will follow.
diff --git a/src/telnet_parser.l b/src/telnet_parser.l
new file mode 100644
index 0000000..3af0d58
--- /dev/null
+++ b/src/telnet_parser.l
@@ -0,0 +1,180 @@
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+/*
+ * I'm lazy and will not introduce lemon to this game. Our telnet
+ * interface is matching line based so we can have a pattern that
+ * is matching a line and everyone will be happy.
+ */
+
+%option never-interactive
+%option noyywrap
+%option	reentrant
+
+%{
+#include <string.h>
+#include <openbsc/telnet_interface.h>
+
+extern char *strndup(const char *s, size_t n);
+extern void telnet_write_help(int);
+extern void telnet_close_client(struct bsc_fd*);
+extern void telnet_error_client(int fd);
+extern void telnet_page(struct telnet_connection *con, const char *imsi, int page);
+extern void telnet_call(struct telnet_connection *con, const char *imsi,
+			const char* origin);
+extern void telnet_put_channel(struct telnet_connection*, const char *imsi);
+extern void telnet_get_channel(struct telnet_connection*, const char *imsi);
+extern void telnet_send_gsm_48(struct telnet_connection*);
+extern void telnet_send_gsm_11(struct telnet_connection*);
+
+static const int PAGE_LEN = 5; /* "page " */
+static const int CALL_LEN = 5; /* "call " */
+static const int PUT_LEN = 12; /* "put_channel " */
+static const int GET_LEN = 12; /* "get_channel " */
+static const int NET_LEN = 3;  /* "48 " "11 " */
+
+#define YY_EXTRA_TYPE struct telnet_connection*
+
+/* the string is null terminated */
+static int parse_hex(char *hex)
+{
+	int byte;
+	sscanf(hex, "%x", &byte);
+	return byte;
+}
+
+#define PREPARE_STRING(len) \
+		yytext[yyleng-1] = '\0'; \
+                char *str = yytext + len; \
+		char *pag = strstr(str, "\r"); \
+		if (pag) pag[0] = '\0'; \
+		pag = strstr(str, "\n"); \
+		if (pag) pag[0] = '\0';
+
+%}
+
+CMD_HELP	"help"
+CMD_EXIT	"exit"
+CMD_CLOSE	"close"
+CMD_PAGE	"page"
+CMD_GET_CHANNEL	"get_channel"
+CMD_PUT_CHANNEL "put_channel"
+CMD_CALL	"call"
+CMD_48		"48"
+CMD_11		"11"
+
+LINE_END	\n|\r\n
+HEX		[0][x][0-9a-zA-Z][0-9a-zA-Z]
+
+%s  READ_HEX_BYTES
+
+%%
+{CMD_HELP}{LINE_END}		{telnet_write_help(yyextra->fd.fd); yyterminate();}
+{CMD_EXIT}{LINE_END}		{telnet_close_client(&yyextra->fd); yyterminate();}
+{CMD_CLOSE}{LINE_END}		{telnet_close_client(&yyextra->fd); yyterminate();}
+{CMD_PAGE}[ ][0-9]+{LINE_END} {
+				    PREPARE_STRING(PAGE_LEN)
+                                    telnet_page(yyextra, str, 0);
+				    yyterminate();
+				}
+{CMD_PAGE}[ ][0-9]+[ ][0-2]{LINE_END} {
+				    PREPARE_STRING(PAGE_LEN)
+				    char *sp = strstr(str, " ");
+				    sp[0] = '\0';
+                                    telnet_page(yyextra, str, atoi(sp+1));
+				    yyterminate();
+				}
+{CMD_PUT_CHANNEL}[ ][0-9]+{LINE_END} {
+				    PREPARE_STRING(PUT_LEN)
+                                    telnet_put_channel(yyextra, str);
+				    yyterminate();
+				}
+{CMD_GET_CHANNEL}[ ][0-9]+{LINE_END} {
+				    PREPARE_STRING(GET_LEN)
+                                    telnet_get_channel(yyextra, str);
+				    yyterminate();
+				}
+{CMD_CALL}[ ][0-9]+[ ][0-9]+{LINE_END} {
+				    PREPARE_STRING(CALL_LEN)
+				    char *sp = strstr(str, " ");
+				    sp[0] = '\0';
+                                    telnet_call(yyextra, str, sp+1);
+				    yyterminate();
+				}
+{CMD_CALL}[ ][0-9]+{LINE_END} {
+				    PREPARE_STRING(CALL_LEN)
+                                    telnet_call(yyextra, str, NULL);
+				    yyterminate();
+				}
+<READ_HEX_BYTES>{HEX}		{
+				    if (yyextra->read >= sizeof(yyextra->commands)) {
+					yyterminate();
+				    }
+				    yytext[yyleng] = '\0';
+				    yyextra->commands[yyextra->read++] = parse_hex(yytext+2);
+				}
+<READ_HEX_BYTES>{LINE_END}	{
+				    if (yyextra->command == TELNET_COMMAND_11) {
+					telnet_send_gsm_11(yyextra);
+				    } else if (yyextra->command == TELNET_COMMAND_48) {
+					telnet_send_gsm_48(yyextra);
+				    }
+
+				    if (yyextra->imsi) {
+					free(yyextra->imsi);
+					yyextra->imsi = NULL;
+				    }
+				    yyterminate();
+				}
+<INITIAL>{CMD_48}[ ][0-9]+	{
+				    BEGIN READ_HEX_BYTES;
+				    yyextra->read = 0;
+				    yyextra->command = TELNET_COMMAND_48;
+				    yytext[yyleng-1] = '\0';
+				    yyextra->imsi = strdup(yytext);
+				}
+
+<INITIAL>{CMD_11}[ ][0-9]+	{
+				    BEGIN READ_HEX_BYTES;
+				    yyextra->read = 0;
+				    yyextra->command = TELNET_COMMAND_11;
+				    yytext[yyleng-1] = '\0';
+				    yyextra->imsi = strdup(yytext);
+				}
+
+
+
+[ \t\r\n]			/* Ommit */
+.				{ telnet_error_client(yyextra->fd.fd); yyterminate(); }
+
+%%
+
+void telnet_parse(struct telnet_connection *conn, char *buf)
+{
+	yyscan_t scanner;
+	yylex_init(&scanner);
+	yyset_extra(conn, scanner);
+	yy_scan_string(buf, scanner);
+	yylex(scanner);
+	yylex_destroy(scanner);
+
+	if (conn->imsi) {
+		free(conn->imsi);
+		conn->imsi = NULL;
+	}
+}