Various patches

  • From: Ingo van Lil <inguin@xxxxxx>
  • To: yadex@xxxxxxxxxxxxx
  • Date: Fri, 03 Sep 2010 00:39:00 +0200

 Hi there,

I'm hereby reporting back after many, many years of lurking. I've whipped up a couple of small patches against 1.7.901, maybe you'll find them useful:

* yadex-gcc4.patch:
Just a few minor changes (mainly in scripts/m7) to make Yadex compile on my Fedora 13 box: - getc and putc seem to be defined as preprocessor macros, so they can't be used as identifiers. As a quick workaround I just undefined the macros.
- The original getc and putc are in the root namespace, not in std::.
- Missing include (for UCHAR_MAX) in m7/input_buf.h
- Empty #elif in src/gfx.cc

* yadex-makedepend.patch:
I couldn't find the "makedepend" tool in F13, so the yadex.dep rule always silently created an empty file (the error messages were hidden by the redirection of stderr). So I've added an alternative way to create the dependencies using gcc.

* yadex-readline.patch:
Fancy new feature: If available, use GNU readline for CLI input. Advantages: full line editing, history and context-sensitive completion.

* yadex-close-window.patch:
This fixes a very annoying problem: Yadex crashes if any graphical window is closed using the X button. The patch simulates an ESC keystroke if the window is closed, so the window can be closed gracefully (after asking the user if he wants to discard his unsaved changes).

Cheers,
Ingo
commit 0a199d735e8ba1271576b82d77ee5da0993f9fbe
Author: Ingo van Lil <inguin@xxxxxx>
Date:   Thu Sep 2 23:58:53 2010 +0200

    Gracefully close window when the 'X' button is pressed.

diff --git a/src/gfx.cc b/src/gfx.cc
index 638e635..90182de 100644
--- a/src/gfx.cc
+++ b/src/gfx.cc
@@ -118,6 +118,7 @@ static pcolour_t *app_colour = 0;   // Pixel values for the 
app. colours
 static int DrawingMode    = 0;         // 0 = copy, 1 = xor
 static int LineThickness  = 0;         // 0 = thin, 1 = thick
 int      text_dot         = 0;         // DrawScreenText() debug flag
+Atom     wm_delete;
 #endif
 static acolour_t colour_stack[4];
 static int       colour_stack_pointer = 0;
@@ -471,6 +472,10 @@ int InitGfx (void)
     | ExposureMask
     | StructureNotifyMask);
 
+  // register for message from window manager if the window is closed
+  wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
+  XSetWMProtocols(dpy, win, &wm_delete, 1);
+
   /*
    *   Possibly load and query the font
    */
diff --git a/src/gfx.h b/src/gfx.h
index 0308670..d8709ef 100644
--- a/src/gfx.h
+++ b/src/gfx.h
@@ -96,6 +96,7 @@ extern int      win_bpp;      // The depth of win in bytes
 extern int     x_server_big_endian;    // Is the X server big-endian ?
 extern int      ximage_bpp;    // Number of bytes per pixels in XImages
 extern int      ximage_quantum;        // Pad XImage lines to a mult of that 
many B.
+extern Atom     wm_delete;     // window delete message from window manager
 #endif  // ifdef X_PROTOCOL
 #endif  // ifdef Y_X11
 extern int     text_dot;     // DrawScreenText()/DrawScreenString() debug flag
diff --git a/src/input.cc b/src/input.cc
index 8a3b582..9425fb6 100644
--- a/src/input.cc
+++ b/src/input.cc
@@ -414,6 +414,12 @@ switch (ev.type)
 #endif
       break;
       }
+
+   case ClientMessage:
+      if ((Atom)ev.xclient.data.l[0] == wm_delete)
+        // window has been closed, simulate ESC keypress
+        is.key = YK_ESC;
+      break;
    }  /* switch (ev.type) */
 }
 
commit 06800386415e4b701212a10b14c49ee796f50ca8
Author: Ingo van Lil <inguin@xxxxxx>
Date:   Wed Sep 1 21:31:43 2010 +0200

    gcc4 build fixes.

diff --git a/scripts/m7/input_buf.h b/scripts/m7/input_buf.h
index e2d5e4c..d285fa8 100644
--- a/scripts/m7/input_buf.h
+++ b/scripts/m7/input_buf.h
@@ -25,6 +25,7 @@ this program; if not, write to the Free Software Foundation, 
Inc.,
 
 #include <stddef.h>
 #include <stdio.h>
+#include <limits.h>
 
 #include <string>
 
diff --git a/scripts/m7/input_file.h b/scripts/m7/input_file.h
index e8f6247..a1da434 100644
--- a/scripts/m7/input_file.h
+++ b/scripts/m7/input_file.h
@@ -116,7 +116,7 @@ inline int Input_file::peekc ()
 {
   if (cnext == NOCHAR)
   {
-    cnext = std::getc (fp);
+    cnext = ::getc (fp);
     if (c == '\n')
     {
       nextline_++;
diff --git a/scripts/m7/output.h b/scripts/m7/output.h
index ca8911c..3b0d9b2 100644
--- a/scripts/m7/output.h
+++ b/scripts/m7/output.h
@@ -28,6 +28,13 @@ this program; if not, write to the Free Software Foundation, 
Inc.,
 
 #include <string>
 
+#ifdef putc
+# undef putc
+#endif
+
+#ifdef getc
+# undef getc
+#endif
 
 class Output
 {
diff --git a/scripts/m7/output_file.h b/scripts/m7/output_file.h
index 539f0ee..5cbdc20 100644
--- a/scripts/m7/output_file.h
+++ b/scripts/m7/output_file.h
@@ -87,7 +87,7 @@ inline int Output_file::putc (char c)
     bol = false;
   }
 
-  int r = std::putc (c, fp);
+  int r = ::putc (c, fp);
   if (r != EOF)
     col_++;
   if (c == '\n')
diff --git a/src/gfx.cc b/src/gfx.cc
index 370ed1b..638e635 100644
--- a/src/gfx.cc
+++ b/src/gfx.cc
@@ -310,7 +310,7 @@ int InitGfx (void)
     win_vis_id    = vis_info->visualid;
 #if defined _cplusplus || defined __cplusplus
     win_vis_class = vis_info->c_class;
-#elif
+#else
     win_vis_class = vis_info->class;
 #endif
     win_ncolours  = vis_info->colormap_size;
commit 58917624ac919ee5be7b1d3c1fa5bbbc93d99cab
Author: Ingo van Lil <inguin@xxxxxx>
Date:   Tue Aug 31 21:08:37 2010 +0200

    Use gcc (if available) to generate yadex.dep file.

diff --git a/GNUmakefile b/GNUmakefile
index 61c4652..c8bb3bc 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -767,6 +767,12 @@ $(OBJDIR)/files_share.man: $(OBJDIR)/config.share 
$(MAKEFILE)
 cache/yadex.dep: $(SRC_NON_GEN) src/config.h
        @echo "Generating $@"
        @test -d cache || mkdir cache
+ifeq ($(CC), gcc)
+       @gcc -MM $(CFLAGS) $(SRC_NON_GEN) >$(@D)/.$(@F)
+       @sed 's,^[^ ],$(OBJDIR)/,' $(@D)/.$(@F) >$@
+       @sed 's,^[^ ],$(DOBJDIR)/,' $(@D)/.$(@F) >>$@
+       @rm $(@D)/.$(@F)
+else
        @makedepend -f- -Y -Iatclib $(SRC_NON_GEN) 2>/dev/null          \
                | $(AWK) 'sub (/^src/, "") == 1 {                       \
                                print "'$(OBJDIR)'" $$0;                \
@@ -774,6 +780,7 @@ cache/yadex.dep: $(SRC_NON_GEN) src/config.h
                                next;                                   \
                        }' >$(@D)/.$(@F)
        @mv -f $(@D)/.$(@F) $@
+endif
 
 cache/copyright.man: $(MAKEFILE) scripts/copyright docsrc/copyright
        @echo "Generating $@"
@@ -854,7 +861,7 @@ cache/uptodate: scripts/youngest $(SRC_NON_GEN)
 
 # To compile the modules of Yadex
 # (normal and debugging versions)
-include cache/yadex.dep
+-include cache/yadex.dep
 
 # It's simpler to copy config.cc into src/ than to have a
 # compilation rule for just one file.
commit 78a996bae396724eeb0dd284829348926482ce49
Author: Ingo van Lil <inguin@xxxxxx>
Date:   Wed Sep 1 21:10:40 2010 +0200

    Use readline for CLI input.

diff --git a/GNUmakefile b/GNUmakefile
index c8bb3bc..f109a1f 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -134,20 +134,20 @@ MODULES_YADEX =                                           
                \
        macro           memory          menubar         menu            \
        mkpalette       mouse           names           nop             \
        objects         objinfo         oldmenus        palview         \
-       patchdir        pic2img         prefer          s_centre        \
-       s_door          s_lift          s_linedefs      s_merge         \
-       s_misc          s_prop          s_slice         s_split         \
-       s_swapf         s_vertices      sanity          scrnshot        \
-       selbox          selectn         selpath         selrect         \
-       serialnum       spritdir        sticker         swapmem         \
-       t_centre        t_flags         t_prop          t_spin          \
-       textures        things          trace           v_centre        \
-       v_merge         v_polyg         vectext         verbmsg         \
-       version         wadfile         wadlist         wadnamec        \
-       wadres          wads            wads2           warn            \
-       windim          x_centre        x_exchng        x_hover         \
-       x_mirror        x_rotate        x11             xref            \
-       yadex           ytime
+       patchdir        pic2img         prefer          readline        \
+       s_centre        s_door          s_lift          s_linedefs      \
+       s_merge         s_misc          s_prop          s_slice         \
+       s_split         s_swapf         s_vertices      sanity          \
+       scrnshot        selbox          selectn         selpath         \
+       selrect         serialnum       spritdir        sticker         \
+       swapmem         t_centre        t_flags         t_prop          \
+       t_spin          textures        things          trace           \
+       v_centre        v_merge         v_polyg         vectext         \
+       verbmsg         version         wadfile         wadlist         \
+       wadnamec        wadres          wads            wads2           \
+       warn            windim          x_centre        x_exchng        \
+       x_hover         x_mirror        x_rotate        x11             \
+       xref            yadex           ytime
 
 # All the modules of Atclib without path or extension.
 MODULES_ATCLIB =                                                       \
@@ -288,6 +288,11 @@ SRC = $(filter-out src/config.cc, $(SRC_YADEX))            
                \
 # The files on which youngest is run.
 SRC_NON_GEN = $(filter-out src/credits.cc src/prefix.cc src/version.cc, $(SRC))
 
+LIBS = -lX11 -lm -lc
+ifeq ($(HAVE_READLINE), 1)
+  LIBS += -lreadline
+endif
+
 # The object files
 OBJ_YADEX   = $(addprefix $(OBJDIR)/,  $(addsuffix .o, $(MODULES_YADEX)))
 DOBJ_YADEX  = $(addprefix $(DOBJDIR)/, $(addsuffix .o, $(MODULES_YADEX)))
@@ -497,7 +502,7 @@ yadex: $(OBJDIR)/yadex
 $(OBJDIR)/yadex: $(OBJ_YADEX) $(OBJ_ATCLIB) $(MAKEFILE)
        @echo "** Linking Yadex"
        $(CXX) $(LDFLAGS) -o $@ $(OBJ_YADEX) $(OBJ_ATCLIB) \
-         $(X11LD) -lX11 -lm -lc
+         $(X11LD) $(LIBS)
 
 .PHONY: test
 test:
diff --git a/configure b/configure
index 60c2afa..4ef2a65 100755
--- a/configure
+++ b/configure
@@ -36,6 +36,7 @@ HAVE_INTTYPES=
 HAVE_NANOSLEEP=
 HAVE_SNPRINTF=
 HAVE_USLEEP=
+HAVE_READLINE=
 INTERFACE=x11                  # "bgi" or "x11" (only "x11" is maintained)
 INTTYPES=
 LDFLAGS=
@@ -176,6 +177,7 @@ genh () {
     genbool NANOSLEEP
     genbool SNPRINTF
     genbool USLEEP
+    genbool READLINE
     [ -z "$INTTYPES" ] || printf '\n%s\n' "$INTTYPES"
     echo
     echo 'extern const char *yadex_etc_path[];'
@@ -447,6 +449,23 @@ int main (int argc, char *argv[])
 ' || true
 
 #
+#      Do we have readline() ?
+#
+LDFLAGS_OLD="$LDFLAGS"
+LDFLAGS="$LDFLAGS -lreadline"
+check "for readline" HAVE_READLINE '
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+int main (int argc, char *argv[])
+{
+  add_history("test");
+  return 0;
+}
+' || true
+LDFLAGS="$LDFLAGS_OLD"
+
+#
 #      Look for a C++ compiler
 #
 #      We try "g++" first, then "c++", then "cxx".
@@ -872,6 +891,7 @@ echo generating $BUILDDIR/Makefile.config
   echo "HAVE_NANOSLEEP    = $HAVE_NANOSLEEP"
   echo "HAVE_SNPRINTF     = $HAVE_SNPRINTF"
   echo "HAVE_USLEEP       = $HAVE_USLEEP"
+  echo "HAVE_READLINE     = $HAVE_READLINE"
   echo "INTERFACE         = $INTERFACE"
   echo "LDFLAGS           = $LDFLAGS"
   echo "MANDIR            = $MANDIR"
diff --git a/src/readline.cc b/src/readline.cc
new file mode 100644
index 0000000..0df9c4d
--- /dev/null
+++ b/src/readline.cc
@@ -0,0 +1,360 @@
+/*
+ *     readline.cc
+ *     Command line interface input function.
+ */
+
+
+/*
+This file is part of Yadex.
+
+Yadex incorporates code from DEU 5.21 that was put in the public domain in
+1994 by Raphaël Quinet and Brendon Wyber.
+
+The rest of Yadex is Copyright © 1997-2003 André Majorel and others.
+
+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.
+*/
+
+
+#include "yadex.h"
+
+#if defined(Y_READLINE)
+
+#include <string.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "wadfile.h"
+#include "wadlist.h"
+#include "wadres.h"
+#include "levels.h"
+#include "lumpdir.h"
+#include "patchdir.h"
+
+// completion generators for various command arguments
+static rl_compentry_func_t *c_filename = rl_filename_completion_function;
+static rl_compentry_func_t c_wadentry;
+static rl_compentry_func_t c_wadfile;
+static rl_compentry_func_t c_levelname;
+static rl_compentry_func_t c_spritename;
+static rl_compentry_func_t c_flatname;
+static rl_compentry_func_t c_patchname;
+static rl_compentry_func_t c_texname;
+
+#define MAX_ARGS 2
+
+struct command {
+   const char *name;
+   const char *shortcut;
+   rl_compentry_func_t *completers[MAX_ARGS];
+};
+
+// all commands and their argument completers
+static struct command commands[] = {
+   { "build",              "b", { c_filename } },
+   { "create",             "c", {} },
+   { "dump",               "d", { c_wadentry, c_filename } },
+   { "dump_linedef_types", " ", {} },
+   { "edit",               "e", { c_levelname } },
+   { "group",              "g", {} },
+   { "insert",             "i", { c_filename, c_wadentry } },
+   { "help",               "h", {} },
+   { "list",               "l", { c_wadfile, c_filename } },
+   { "master",             "m", { c_filename } },
+   { "make_gimp_palette",  " ", { c_filename } },
+   { "make_palette_ppm",   " ", { c_filename } },
+   { "quit",               "q", {} },
+   { "read",               "r", { c_filename } },
+   { "save",               "s", { c_wadentry, c_filename } },
+   { "set",                " ", {} },
+   { "view",               "v", { c_spritename } },
+   { "viewflat",           " ", { c_flatname } },
+   { "viewpal",            " ", {} },
+   { "viewpat",            " ", { c_patchname } },
+   { "viewtex",            " ", { c_texname } },
+   { "wads",               "w", {} },
+   { "xtract",             "x", { c_wadentry, c_filename } },
+};
+
+// completion generator: command names
+static char *command_generator (const char *text, int state)
+{
+   static size_t list_index;
+   static size_t len;
+
+   if (state == 0)
+      {
+      list_index = 0;
+      len = strlen (text);
+      }
+
+   while (list_index < sizeof (commands) / sizeof (command))
+      {
+      const char *cmd = commands[list_index].name;
+      list_index++;
+      if (strncmp (cmd, text, len) == 0)
+         return strdup (cmd);
+      if (strcmp (text, "?") == 0 && strcmp (cmd, "help") == 0)
+         return strdup (cmd);
+      }
+   return NULL;
+}
+
+// perform completion for the n-th argument of a given command
+static char **argument_completion (const char *text, const char *cmd, int arg)
+{
+   if (arg >= MAX_ARGS)
+      return NULL;
+
+   for (size_t i = 0; i < sizeof (commands) / sizeof (command); i++)
+      {
+      const struct command *command = &commands[i];
+      if (strcasecmp (cmd, command->name) == 0 ||
+          strcasecmp (cmd, command->shortcut) == 0)
+         {
+         if (command->completers[arg] != NULL)
+            return rl_completion_matches (text, command->completers[arg]);
+         break;
+         }
+      }
+   return NULL;
+}
+
+// global command line completion function
+static char **yadex_completion (const char *text, int start, int end)
+{
+   char **matches = NULL;
+   char *input = strdup (rl_line_buffer);
+
+   // find the command and the token position of the text to complete
+   char *token = strtok (input, " ");
+   char *cmd = token;
+   int arg = 0;
+   while (token != NULL && (token - input) < start)
+      {
+      token = strtok (NULL, " ");
+      arg++;
+      }
+
+   if (arg == 0)
+      // first token: command
+      matches = rl_completion_matches (text, command_generator);
+   else
+      // later token: argument
+      matches = argument_completion (text, cmd, arg - 1);
+
+   free (input);
+   rl_attempted_completion_over = 1;
+   return matches;
+}
+
+char *ReadLine (const char *prompt)
+{
+   rl_readline_name = "yadex";
+   rl_attempted_completion_function = yadex_completion;
+
+   char *input = readline(prompt);
+   if (input && input[0])
+      add_history(input);
+   return input;
+}
+
+// completion generator: WAD directory entries
+static char *c_wadentry (const char *text, int state)
+{
+   static MDirPtr entry;
+   static size_t len;
+
+   if (state == 0)
+      {
+      entry = MasterDir;
+      len = strlen (text);
+      }
+
+   if (len > WAD_NAME)
+      return NULL;
+
+   while (entry != 0)
+      {
+      MDirPtr e = entry;
+      entry = entry->next;
+      if (strncasecmp (e->dir.name, text, len) == 0)
+         return strdup (e->dir.name);
+      }
+   return NULL;
+}
+
+// completion generator: WAD file names
+static char *c_wadfile (const char *text, int state)
+{
+   static size_t len;
+
+   if (state == 0)
+      {
+      wad_list.rewind();
+      len = strlen (text);
+      }
+
+   const Wad_file *wf;
+   while (wad_list.get (wf))
+      {
+      if (strncmp (wf->pathname (), text, len) == 0)
+         return strdup (wf->pathname ());
+      }
+   return NULL;
+}
+
+// completion generator: level names
+static char *c_levelname (const char *text, int state)
+{
+   static MDirPtr entry;
+   static size_t len;
+
+   if (state == 0)
+      {
+      entry = MasterDir;
+      len = strlen (text);
+      }
+
+   if (len > WAD_NAME)
+      return NULL;
+
+   while (entry != 0)
+      {
+      MDirPtr e = entry;
+      entry = entry->next;
+      if (levelname2levelno (e->dir.name) &&
+          strncasecmp (e->dir.name, text, len) == 0)
+         return strdup (e->dir.name);
+      }
+   return NULL;
+}
+
+// completion generator: sprite names
+static char *c_spritename (const char *text, int state)
+{
+   static Lump_list list;
+   static size_t n;
+   static size_t len;
+
+   if (state == 0)
+      {
+      Wad_res wad_res (&MasterDir);
+      wad_res.sprites.list (list);
+      n = 0;
+      len = strlen (text);
+      }
+
+   while (n < list.size())
+      {
+      const char *name = list.data()[n++];
+      if (strncasecmp (name, text, len) == 0)
+         return strdup (name);
+      }
+   return NULL;
+}
+
+// completion generator: flat names
+static char *c_flatname (const char *text, int state)
+{
+   static size_t n;
+   static size_t len;
+
+   if (state == 0)
+      {
+      n = 0;
+      len = strlen (text);
+      }
+
+   ReadFTextureNames ();
+   char *res = NULL;
+   while (n < NumFTexture)
+      {
+      const char *name = flat_list[n++].name;
+      if (strncasecmp (name, text, len) == 0)
+         {
+         res = strdup (name);
+         break;
+         }
+      }
+   ForgetFTextureNames ();
+   return res;
+}
+
+// completion generator: patch names
+static char *c_patchname (const char *text, int state)
+{
+   static Patch_list list;
+   static size_t n;
+   static size_t len;
+
+   if (state == 0)
+      {
+      patch_dir.refresh (MasterDir);
+      patch_dir.list (list);
+      n = 0;
+      len = strlen (text);
+      }
+
+   while (n < list.size())
+      {
+      const char *name = list.data()[n++];
+      if (strncasecmp (name, text, len) == 0)
+         return strdup (name);
+      }
+   return NULL;
+}
+
+// completion generators: texture names
+static char *c_texname (const char *text, int state)
+{
+   static int n;
+   static size_t len;
+
+   if (state == 0)
+      {
+      patch_dir.refresh (MasterDir);
+      n = 0;
+      len = strlen (text);
+      }
+
+   ReadWTextureNames();
+   char *res = NULL;
+   while (n < NumWTexture)
+      {
+      const char *name = WTexture[n++];
+      if (strncasecmp (name, text, len) == 0)
+         {
+         res = strdup (name);
+         break;
+         }
+      }
+   ForgetWTextureNames();
+   return res;
+}
+
+#else
+
+// GNU readline not available; fall back to fgets()
+char *ReadLine (const char *prompt)
+{
+   char input[120];
+   printf ("%s", prompt);
+   if (fgets (input, sizeof input, stdin))
+      return strdup(input);
+   else
+      return NULL;
+}
+
+#endif
diff --git a/src/yadex.cc b/src/yadex.cc
index c5ce2bb..dd593d0 100644
--- a/src/yadex.cc
+++ b/src/yadex.cc
@@ -55,7 +55,6 @@ Street, Fifth Floor, Boston, MA 02110-1301, USA.
 #include "wadres.h"
 #include "wads2.h"
 
-
 /*
  *     Constants (declared in yadex.h)
  */
@@ -672,15 +671,17 @@ if (Debug && logfile != NULL)
 
 static void MainLoop ()
 {
-char input[120];
+char *input = NULL;
 char *com, *out;
 FILE *file, *raw;
 
 for (;;)
    {
    /* get the input */
-   printf ("yadex: ");
-   if (! fgets (input, sizeof input, stdin))
+   if (input != NULL)
+      free (input);
+   input = ReadLine("yadex: ");
+   if (input == NULL)
       {
       puts ("q");
       break;
diff --git a/src/yadex.h b/src/yadex.h
index 55ee107..1867ceb 100644
--- a/src/yadex.h
+++ b/src/yadex.h
@@ -502,6 +502,9 @@ const char *GetSectorTypeLongName (int);
 // nop.cc
 void nop (...);
 
+// readline.cc
+char *ReadLine (const char *prompt);
+
 // s_door.cc (previously in objects.cc)
 void MakeDoorFromSector (int); /* SWAP! */
 

Other related posts: