2005-11-22 Carl Worth <cworth@cworth.org>
authorCarl Worth <carl@theworths.org>
Tue, 22 Nov 2005 23:54:09 +0000 (23:54 +0000)
committerCarl Worth <carl@theworths.org>
Tue, 22 Nov 2005 23:54:09 +0000 (23:54 +0000)
        * TODO: Add TODO file listing all the stuff from the protocol that
        needs to be implemented.

        * src/Makefile.am:
        * configure.in:
        * ylwrap:
        * src/ttt-lex.h:
        * src/ttt-lex.l:
        * src/ttt-token.h: Add support for a flex-based tokenizer which
        tokenizes newlines and space-separated strings.

        * src/ttt-client.c: (_ttt_client_init), (_ttt_client_fini),
        (_append_to_request), (_free_request), (_read_request),
        (_execute_request): Use new tokenizer to tokenize input into an
        array of request strings.

ChangeLog
TODO [new file with mode: 0644]
configure.in
src/Makefile.am
src/ttt-client.c
src/ttt-lex.h [new file with mode: 0644]
src/ttt-lex.l [new file with mode: 0644]
src/ttt-token.h [new file with mode: 0644]
ylwrap [new file with mode: 0755]

index 6effd22d2c61d7053c09e339f2ed855a39969f76..090f1ec7c07886c4b2a86d3033805554e2fb1143 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2005-11-22  Carl Worth  <cworth@cworth.org>
+
+       * TODO: Add TODO file listing all the stuff from the protocol that
+       needs to be implemented.
+       
+       * src/Makefile.am:
+       * configure.in:
+       * ylwrap:
+       * src/ttt-lex.h:
+       * src/ttt-lex.l:
+       * src/ttt-token.h: Add support for a flex-based tokenizer which
+       tokenizes newlines and space-separated strings.
+
+       * src/ttt-client.c: (_ttt_client_init), (_ttt_client_fini),
+       (_append_to_request), (_free_request), (_read_request),
+       (_execute_request): Use new tokenizer to tokenize input into an
+       array of request strings.
+
 2005-11-15  Carl Worth  <cworth@cworth.org>
 
        * src/Makefile.am:
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..7972fce
--- /dev/null
+++ b/TODO
@@ -0,0 +1,52 @@
+  /----- Server, implemented in ttt-server
+ / /---- Client, implemented in ttt
+S C
+    1. Requests
+    1.1 HELO
+    1.2. Global commands
+    1.2.1. WHO
+    1.2.2. MESSAGE
+    1.2.3. HELP
+    1.2.4. QUIT
+    1.2.5. VERSION
+    1.3. Game management commands
+    1.3.1. INVITE
+    1.3.2. ACCEPT
+    1.4. In-game commands
+    1.4.1. SHOW
+    1.4.2. PART
+    1.4.3. MOVE
+    2. Asynchronous notification.  
+    2.1. Global notices
+    2.1.1. NOTICE USER <username>
+    2.1.2. NOTICE QUIT <username>
+    2.1.3. NOTICE INVITE <username>
+    2.1.4. NOTICE DISPOSE <game>
+    2.1.5. NOTICE MESSAGE <username> <text>
+    2.2. Game notices
+    2.2.1. Global game notices
+    2.2.1.1. NOTICE NEWGAME <username> <username>
+    2.2.1.2. NOTICE GAMEOVER <outcome> <username>
+    2.2.2. Move notices
+    2.2.2.1. NOTICE MOVE <username> <number>
+    3. Errors
+    3.1. Connection setup errors
+    3.1.1. ERROR NONAMESET
+    3.1.2. ERROR INVALIDNAME
+    3.2. Command format errors
+    3.2.1. ERROR COMMAND
+    3.2.2. ERROR SYNTAX
+    3.2.3. ERROR NOTNUMBER
+    3.2.4. ERROR NOTGRID
+    3.3. Global command errors.
+    3.4. Game management errors.
+    3.4.1. ERROR NOGAME
+    3.5. User information errors
+    3.5.1. ERROR NOUSER
+    3.6. In-game errors
+    3.6.1. Global game errors
+    3.6.1.1. ERROR NOTINGAME
+    3.6.1.2. ERROR NOTPLAYING
+    3.6.2. Moving errors 
+    3.6.2.1. ERROR NOTYOURTURN
+    
\ No newline at end of file
index 73cdd99bfe2887a66cb82029be9038e5dba3577c..96a500fbd43fdde4831471e3335266bae3174e9e 100644 (file)
@@ -14,6 +14,7 @@ AM_MAINTAINER_MODE
 
 AC_PROG_CC
 AC_STDC_HEADERS
+AM_PROG_LEX
 
 dnl ===========================================================================
 dnl Use lots of warning flags with GCC
index 4b336a05d2d9ff0c6bf8d55df1e89abfb4a83b14..c205ef940b4dfe060674cca5dcf5483c666227cb 100644 (file)
@@ -7,6 +7,7 @@ ttt_common_sources =            \
        ttt-board.h             \
        ttt-error.c             \
        ttt-error.h             \
+       ttt-lex.l               \
        ttt-socket.c            \
        ttt-socket.h            \
        x.c                     \
@@ -22,5 +23,6 @@ ttt_server_SOURCES =          \
        ttt-server.c
 
 AM_CFLAGS = $(WARN_CFLAGS) $(TTT_CFLAGS)
+AM_LFLAGS = --header-file=ttt-lex.h -Cr
 ttt_client_LDFLAGS = $(TTT_LIBS)
 ttt_server_LDFLAGS = $(TTT_LIBS) -lpthread
index 9d44945e506664678854fab51c41fa3fd04e870d..8139ada1de03cb04a2e7c7c986ebd0aa1d136983 100644 (file)
 
 #include "ttt-client.h"
 
+#include "ttt-error.h"
+#include "ttt-lex.h"
 #include "ttt-server.h"
 #include "ttt-socket.h"
-#include "ttt-error.h"
+#include "ttt-token.h"
 
 struct _ttt_client {
     pthread_mutex_t mutex;
@@ -31,34 +33,35 @@ struct _ttt_client {
 
     ttt_server_t *server;
     int socket;
+    yyscan_t scanner;
 
     int id;
 
-    char buf[TTT_CLIENT_BUF_SIZE];
-    char *buf_head;
-    char *buf_tail;
-
-    char *request;
-    int request_size;
-    int request_len;
+    char **request_strings;
+    int num_request_strings;
 };
 
+static void
+_free_request (ttt_client_t *client);
+
 static void
 _ttt_client_init (ttt_client_t *client,
                  ttt_server_t  *server,
                  int            socket)
 {
+    FILE *file;
+
     pthread_mutex_init (&client->mutex, NULL);
 
     client->server = server;
     client->socket = socket;
 
-    client->buf_head = client->buf;
-    client->buf_tail = client->buf;
+    file = xfdopen (socket, "r");
+    yylex_init(&client->scanner);
+    yyset_in (file, client->scanner);
 
-    client->request = NULL;
-    client->request_size = 0;
-    client->request_len = 0;
+    client->request_strings = NULL;
+    client->num_request_strings = 0;
 
     /* XXX: Probably want to register only as the result of the HELO
        command.  Not only will that match the protocol correctly, but
@@ -73,97 +76,76 @@ _ttt_client_fini (ttt_client_t *client)
 
     ttt_server_unregister_client (client->server, client);
 
+    yylex_destroy (client->scanner);
     shutdown (client->socket, SHUT_RDWR);
 
-    free (client->request);
+    _free_request (client);
 
     pthread_mutex_unlock (&client->mutex);
 
     pthread_mutex_destroy (&client->mutex);
 }
 
+/* XXX: The memory management for the request strings is pretty cheesy. */
 static void
 _append_to_request (ttt_client_t       *client,
-                   const char          *buf,
-                   int                  size)
+                   const char          *string)
 {
-    int size_needed = client->request_len + size;
-
-    if (size_needed > client->request_size) {
-       if (client->request_size == 0) {
-           client->request_size = size_needed;
-       } else {
-           while (size_needed > client->request_size)
-               client->request_size *= 2;
-       }
+    client->num_request_strings++;
+    client->request_strings =
+       xrealloc (client->request_strings,
+                 client->num_request_strings * sizeof (char *));
 
-       client->request = xrealloc (client->request, client->request_size);
-    }
-
-    memcpy (client->request + client->request_len,
-           buf, size);
-
-    client->request_len += size;
+    client->request_strings[client->num_request_strings - 1] = xstrdup (string);
 }
 
-static ttt_status_t
-_read_into_request_until (ttt_client_t *client, char delimeter)
+static void
+_free_request (ttt_client_t *client)
 {
-    ttt_bool_t found_delimeter = FALSE;
-    int bytes_read;
-    char *s;
+    int i;
 
-    client->request_len = 0;
+    for (i = 0; i < client->num_request_strings; i++)
+       free (client->request_strings[i]);
 
-    while (1) {
+    free (client->request_strings);
 
-       if (client->buf_tail >= client->buf_head) {
-           bytes_read = xread (client->socket,
-                               client->buf,
-                               TTT_CLIENT_BUF_SIZE);
-           if (bytes_read == 0)
-               return TTT_STATUS_EOF;
-           client->buf_head = client->buf;
-           client->buf_tail = client->buf_head + bytes_read;
-       }
-
-       for (s = client->buf_head; s < client->buf_tail; s++) {
-           if (*s == delimeter) {
-               found_delimeter = TRUE;
-               s++;
-               break;
-           }
-       }
-
-       _append_to_request (client,
-                           client->buf_head,
-                           s - client->buf_head);
-       client->buf_head = s;
-
-       if (found_delimeter)
-           return TTT_STATUS_SUCCESS;
-    }
+    client->request_strings = NULL;
+    client->num_request_strings = 0;
 }
 
 static ttt_status_t
 _read_request (ttt_client_t *client)
 {
-    ttt_status_t status;
-    static const char null_terminator = '\0';
+    ttt_token_t token;
+
+    _free_request (client);
 
-    status = _read_into_request_until (client, '\n');
-    if (status)
-       return status;
+    while (1) {
+       token = yylex (client->scanner);
+       /* Yes, EOF in two different enums is pretty ugly. */
+       if (token == TTT_TOKEN_EOF)
+           return TTT_STATUS_EOF;
+
+       if (token == TTT_TOKEN_NEWLINE) {
+           if (client->num_request_strings)
+               return TTT_STATUS_SUCCESS;
+           else
+               continue;
+       }
 
-    _append_to_request (client, &null_terminator, 1);
+       assert (token == TTT_TOKEN_STRING);
 
-    return TTT_STATUS_SUCCESS;
+       _append_to_request (client, yyget_text (client->scanner));
+    }
 }
 
 static ttt_error_t
 _execute_request (ttt_client_t *client)
 {
-    ttt_server_broadcast (client->server, client->request);
+    int i;
+
+    for (i=0; i < client->num_request_strings; i++)
+       ttt_server_broadcast (client->server, client->request_strings[i]);
 
     return TTT_ERROR_NONE;
 }
diff --git a/src/ttt-lex.h b/src/ttt-lex.h
new file mode 100644 (file)
index 0000000..84463a2
--- /dev/null
@@ -0,0 +1,316 @@
+#ifndef yyHEADER_H
+#define yyHEADER_H 1
+#define yyIN_HEADER 1
+
+#line 6 "ttt-lex.h"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 31
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+int yylex_init (yyscan_t* scanner);
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in  (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out  (FILE * out_str ,yyscan_t yyscanner );
+
+int yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (yyscan_t yyscanner);
+
+#define YY_DECL int yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef yytext_ptr
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+#line 15 "ttt-lex.l"
+
+#line 317 "ttt-lex.h"
+#undef yyIN_HEADER
+#endif /* yyHEADER_H */
diff --git a/src/ttt-lex.l b/src/ttt-lex.l
new file mode 100644 (file)
index 0000000..2a9d8e2
--- /dev/null
@@ -0,0 +1,16 @@
+  /* Definitions */
+
+%option reentrant
+%option noyywrap
+
+%{
+#include "ttt-token.h"
+%}
+
+%%
+
+  /* Rules */
+
+\r\n                   return TTT_TOKEN_NEWLINE;
+[^ \t\r\n][^ \t\r\n]*  return TTT_TOKEN_STRING;
+[ \t\r\n]              ;
diff --git a/src/ttt-token.h b/src/ttt-token.h
new file mode 100644 (file)
index 0000000..f063d60
--- /dev/null
@@ -0,0 +1,33 @@
+/* ttt-token.h - token types
+ *
+ * Copyright © 2005 Carl Worth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "ttt.h"
+
+#ifndef _TTT_TOKEN_H_
+#define _TTT_TOKEN_H_
+
+typedef enum {
+    TTT_TOKEN_EOF = 0,
+    TTT_TOKEN_STRING,
+    TTT_TOKEN_NEWLINE
+} ttt_token_t;
+
+#endif /* _TTT_TOKEN_H_ */
diff --git a/ylwrap b/ylwrap
new file mode 100755 (executable)
index 0000000..102bd89
--- /dev/null
+++ b/ylwrap
@@ -0,0 +1,223 @@
+#! /bin/sh
+# ylwrap - wrapper for lex/yacc invocations.
+
+scriptversion=2005-05-14.22
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005
+#   Free Software Foundation, Inc.
+#
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case "$1" in
+  '')
+    echo "$0: No files given.  Try \`$0 --help' for more information." 1>&2
+    exit 1
+    ;;
+  --basedir)
+    basedir=$2
+    shift 2
+    ;;
+  -h|--h*)
+    cat <<\EOF
+Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]...
+
+Wrapper for lex/yacc invocations, renaming files as desired.
+
+  INPUT is the input file
+  OUTPUT is one file PROG generates
+  DESIRED is the file we actually want instead of OUTPUT
+  PROGRAM is program to run
+  ARGS are passed to PROG
+
+Any number of OUTPUT,DESIRED pairs may be used.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v|--v*)
+    echo "ylwrap $scriptversion"
+    exit $?
+    ;;
+esac
+
+
+# The input.
+input="$1"
+shift
+case "$input" in
+  [\\/]* | ?:[\\/]*)
+    # Absolute path; do nothing.
+    ;;
+  *)
+    # Relative path.  Make it absolute.
+    input="`pwd`/$input"
+    ;;
+esac
+
+pairlist=
+while test "$#" -ne 0; do
+  if test "$1" = "--"; then
+    shift
+    break
+  fi
+  pairlist="$pairlist $1"
+  shift
+done
+
+# The program to run.
+prog="$1"
+shift
+# Make any relative path in $prog absolute.
+case "$prog" in
+  [\\/]* | ?:[\\/]*) ;;
+  *[\\/]*) prog="`pwd`/$prog" ;;
+esac
+
+# FIXME: add hostname here for parallel makes that run commands on
+# other machines.  But that might take us over the 14-char limit.
+dirname=ylwrap$$
+trap "cd `pwd`; rm -rf $dirname > /dev/null 2>&1" 1 2 3 15
+mkdir $dirname || exit 1
+
+cd $dirname
+
+case $# in
+  0) $prog "$input" ;;
+  *) $prog "$@" "$input" ;;
+esac
+ret=$?
+
+if test $ret -eq 0; then
+  set X $pairlist
+  shift
+  first=yes
+  # Since DOS filename conventions don't allow two dots,
+  # the DOS version of Bison writes out y_tab.c instead of y.tab.c
+  # and y_tab.h instead of y.tab.h. Test to see if this is the case.
+  y_tab_nodot="no"
+  if test -f y_tab.c || test -f y_tab.h; then
+    y_tab_nodot="yes"
+  fi
+
+  # The directory holding the input.
+  input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'`
+  # Quote $INPUT_DIR so we can use it in a regexp.
+  # FIXME: really we should care about more than `.' and `\'.
+  input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'`
+
+  while test "$#" -ne 0; do
+    from="$1"
+    # Handle y_tab.c and y_tab.h output by DOS
+    if test $y_tab_nodot = "yes"; then
+      if test $from = "y.tab.c"; then
+       from="y_tab.c"
+      else
+       if test $from = "y.tab.h"; then
+         from="y_tab.h"
+       fi
+      fi
+    fi
+    if test -f "$from"; then
+      # If $2 is an absolute path name, then just use that,
+      # otherwise prepend `../'.
+      case "$2" in
+       [\\/]* | ?:[\\/]*) target="$2";;
+       *) target="../$2";;
+      esac
+
+      # We do not want to overwrite a header file if it hasn't
+      # changed.  This avoid useless recompilations.  However the
+      # parser itself (the first file) should always be updated,
+      # because it is the destination of the .y.c rule in the
+      # Makefile.  Divert the output of all other files to a temporary
+      # file so we can compare them to existing versions.
+      if test $first = no; then
+       realtarget="$target"
+       target="tmp-`echo $target | sed s/.*[\\/]//g`"
+      fi
+      # Edit out `#line' or `#' directives.
+      #
+      # We don't want the resulting debug information to point at
+      # an absolute srcdir; it is better for it to just mention the
+      # .y file with no path.
+      #
+      # We want to use the real output file name, not yy.lex.c for
+      # instance.
+      #
+      # We want the include guards to be adjusted too.
+      FROM=`echo "$from" | sed \
+            -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
+            -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
+      TARGET=`echo "$2" | sed \
+            -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
+            -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
+
+      sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \
+          -e "s,$FROM,$TARGET," "$from" >"$target" || ret=$?
+
+      # Check whether header files must be updated.
+      if test $first = no; then
+       if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then
+         echo "$2" is unchanged
+         rm -f "$target"
+       else
+          echo updating "$2"
+          mv -f "$target" "$realtarget"
+        fi
+      fi
+    else
+      # A missing file is only an error for the first file.  This
+      # is a blatant hack to let us support using "yacc -d".  If -d
+      # is not specified, we don't want an error when the header
+      # file is "missing".
+      if test $first = yes; then
+        ret=1
+      fi
+    fi
+    shift
+    shift
+    first=no
+  done
+else
+  ret=$?
+fi
+
+# Remove the directory.
+cd ..
+rm -rf $dirname
+
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End: