todo : chdir-long.c exclude.c

2012-08-22  Juan Manuel Guerrero <juan.guerrero@gmx.de>

	* lib/fchdir.c (_gl_register_fd) [__DJGPP__, __DJGPP_MINOR__]: Call
	access instead of stat when compiled for DJGPP 2.03.

	* lib/fdopendir.c (fdopendir_with_dup) [__DJGPP__,  __DJGPP_MINOR__]:
	DJGPP 2.04 has no method to get the file desscriptor of an directory
	open with opendir.  So do not close the file descriptor associated to
	the directory.

	* lib/fts.c: Include dosname.h for ISSLASH nor IS_ABSOLUTE_FILE_NAME
	definition.
	(link_count_optimize_ok): Use ISSLASH instead of '/'.
	(fts_children): Use IS_ABSOLUTE_FILE_NAME instead of '/'.

	* lib/openat-proc.c (openat_proc_name) [__DJGPP__]: DJGPP does not
	support openning of "/proc/self/fd".  Return always -1 even if
	"/proc/self/fd" is passed as filename to open; this may be a valid
	DOS file name in some application.

	* lib/chdir-long.c: Include dosname.h.
	(find_non_slash): Use OS specific DIR_SEPARATOR_CHARS instead of
	hardcoded posix slash.
	(chdir_long): Acount for possible drive letter prefix.  Use OS specific
	DIR_SEPARATOR_CHARS instead of hardcoded posix slash.

	* lib/dosname.h: New macro DIR_SEPARATOR_CHARS defines charactes to be
	used by strspn calls.

	* lib/iswctype-impl.h (iswctype) [__DJGPP__]:  DJGPP has no wide char
	support thus return always 0.

	* lib/open.c: Include dosname.h for ISSLASH definition.
	(open): Check with ISSLASH.
	(rpl_open) [__DJGPP__]: Use access to determinate if given path is
	an existing directory and ISSLASH to allow for mixed backslash and
        slash as directory separator.

	* lib/progname.c [MSDOS]: Define new macro GET_LAST_SLASH to find
	the last directory separator character.  On DOS-like systems these
	are slash, backslash or colon.  For POSIX this is simple a slash.

	* src/dfa.c [MBS_SUPPORT]:  For systems without wide char/multi byte
	support remove realloc_trans_if_necessary, transit_state_singlebyte,
	match_anychar, match_mb_charset, check_matching_with_multibyte_ops,
	transit_state, transit_state_consume_1char, prepare_wc_buf.
	(dfaexec) [MBS_SUPPORT]:  Remove all calls to unsupported multi byte
	functions.

	* src/dosbuf.c:  No longer include config.h.
	[__DJGPP__]: Include pc.h for ScreenGetCursor prototype.
	(restore_blink_bit, screen_puts, msdos_screen_write, djgpp_grep_startup)
	[__DJGPP__]: New functions to support colorization without requiring
	ANSI.SYS driver (or its work-alike) to be loaded.  
	(msdos_screen_write): Added support to decode and execute erase part
	of the line to the right escape sequence (ESC-[K), that is used to
	initialize and finish SGR escape sequences.

	* src/main.c [__DJGPP__]: New macro IS_TERMINAL defined.  If compiled
	with DJGPP do not check for the term environment variable to decided
	which value should color_option have.
	(main): Use CANONICALIZE_PATH, STRIP_FULL_PATH_AND_EXTENSION and
	IS_TERMINAL.

	* src/mbsupport.h [__DJGPP__]:  DJGPP has no wide char support thus
	set MBS_SUPPORT always to 0 and MB_CUR_MAX always to 1.

	* src/system.h [HAVE_DOS_FILE_NAMES]:  New macro CANONICALIZE_PATH.
	New macro STRIP_FULL_PATH_AND_EXTENSION to strip extension and full
	path from argv[0].  Are no-op for all other systems.

	* src/dfasearch.c (EGexecute) [MBS_SUPPORT]:  Remove all calls to
	unsupported multi byte functions.  Use size_t instead of ptrdiff_t.










diff -aprNU5 grep-2.14.orig/lib/chdir-long.c grep-2.14/lib/chdir-long.c
--- grep-2.14.orig/lib/chdir-long.c	2012-01-22 10:00:56 +0000
+++ grep-2.14/lib/chdir-long.c	2012-08-22 22:00:46 +0000
@@ -26,10 +26,12 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
 #include <stdio.h>
 
+#include "dosname.h"
+
 #ifndef PATH_MAX
 # error "compile this file only if your system defines PATH_MAX"
 #endif
 
 /* The results of openat() in this file are not leaked to any
@@ -84,11 +86,11 @@ cdb_advance_fd (struct cd_buf *cdb, char
 
 /* Return a pointer to the first non-slash in S.  */
 static inline char * _GL_ATTRIBUTE_PURE
 find_non_slash (char const *s)
 {
-  size_t n_slash = strspn (s, "/");
+  size_t n_slash = strspn (s, DIR_SEPARATOR_CHARS);
   return (char *) s + n_slash;
 }
 
 /* This is a function much like chdir, but without the PATH_MAX limitation
    on the length of the directory name.  A significant difference is that
@@ -110,10 +112,11 @@ chdir_long (char *dir)
 {
   int e = chdir (dir);
   if (e == 0 || errno != ENAMETOOLONG)
     return e;
 
+  dir = dir + FILE_SYSTEM_PREFIX_LEN(dir);
   {
     size_t len = strlen (dir);
     char *dir_end = dir + len;
     struct cd_buf cdb;
     size_t n_leading_slash;
@@ -124,11 +127,11 @@ chdir_long (char *dir)
        must have failed and set errno to ENOENT.  */
     assert (0 < len);
     assert (PATH_MAX <= len);
 
     /* Count leading slashes.  */
-    n_leading_slash = strspn (dir, "/");
+    n_leading_slash = strspn (dir, DIR_SEPARATOR_CHARS);
 
     /* Handle any leading slashes as well as any name that matches
        the regular expression, m!^//hostname[/]*! .  Handling this
        prefix separately usually results in a single additional
        cdb_advance_fd call, but it's worthwhile, since it makes the
diff -aprNU5 grep-2.14.orig/lib/dosname.h grep-2.14/lib/dosname.h
--- grep-2.14.orig/lib/dosname.h	2012-01-07 15:36:02 +0000
+++ grep-2.14/lib/dosname.h	2012-08-22 22:00:46 +0000
@@ -31,13 +31,15 @@
           (_IS_DRIVE_LETTER ((Filename)[0]) && (Filename)[1] == ':' ? 2 : 0)
 # ifndef __CYGWIN__
 #  define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 1
 # endif
 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
+# define DIR_SEPARATOR_CHARS  "/\\"
 #else
 # define FILE_SYSTEM_PREFIX_LEN(Filename) 0
 # define ISSLASH(C) ((C) == '/')
+# define DIR_SEPARATOR_CHARS  "/"
 #endif
 
 #ifndef FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
 # define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 0
 #endif
diff -aprNU5 grep-2.14.orig/lib/fchdir.c grep-2.14/lib/fchdir.c
--- grep-2.14.orig/lib/fchdir.c	2012-01-07 15:36:02 +0000
+++ grep-2.14/lib/fchdir.c	2012-08-22 22:00:46 +0000
@@ -132,11 +132,21 @@ _gl_register_fd (int fd, const char *fil
 {
   struct stat statbuf;
 
   assert (0 <= fd);
   if (REPLACE_OPEN_DIRECTORY
+#if defined(__DJGPP__) && __DJGPP__ == 2 && __DJGPP_MINOR__ < 4
+      /*
+       *  Computation of stat is very expensive.
+       *  To determinate if PATH is an existing
+       *  directory, access on DOS platforms is
+       *  mostly a fast system call.
+       */
+      || (access (filename, D_OK) == 0))
+#else
       || (fstat (fd, &statbuf) == 0 && S_ISDIR (statbuf.st_mode)))
+#endif
     {
       if (!ensure_dirs_slot (fd)
           || (dirs[fd].name = get_name (filename)) == NULL)
         {
           int saved_errno = errno;
diff -aprNU5 grep-2.14.orig/lib/fdopendir.c grep-2.14/lib/fdopendir.c
--- grep-2.14.orig/lib/fdopendir.c	2012-01-07 15:36:02 +0000
+++ grep-2.14/lib/fdopendir.c	2012-08-22 22:00:46 +0000
@@ -113,11 +113,21 @@ fdopendir_with_dup (int fd, int older_du
           dir = fdopendir_with_dup (fd, dupfd, cwd);
           saved_errno = errno;
         }
       else
         {
-          close (fd);
+#ifdef __DJGPP__
+          /*
+           *  DJGPP has no method to get the
+           *  the file descriptor of an open
+           *  directory, so do not close it.
+           *  If CWD != NULL we are doing
+           *  fchdir/opendir(".")/restore_cwd(CWD).
+           */
+          if (!cwd)
+#endif
+            close (fd);
           dir = fd_clone_opendir (dupfd, cwd);
           saved_errno = errno;
           if (! dir)
             {
               int fd1 = dup (dupfd);
diff -aprNU5 grep-2.14.orig/lib/fts.c grep-2.14/lib/fts.c
--- grep-2.14.orig/lib/fts.c	2012-02-26 14:01:22 +0000
+++ grep-2.14/lib/fts.c	2012-08-22 22:00:48 +0000
@@ -50,10 +50,14 @@
 static char sccsid[] = "@(#)fts.c       8.6 (Berkeley) 8/14/94";
 #endif /* LIBC_SCCS and not lint */
 
 #include "fts_.h"
 
+#if !defined(ISSLASH) || !defined(IS_ABSOLUTE_FILE_NAME)
+# include "dosname.h"
+#endif
+
 #if HAVE_SYS_PARAM_H || defined _LIBC
 # include <sys/param.h>
 #endif
 #ifdef _LIBC
 # include <include/sys/stat.h>
@@ -812,11 +816,11 @@ link_count_optimize_ok (FTSENT const *p)
 /*
  * Special case of "/" at the end of the file name so that slashes aren't
  * appended which would cause file names to be written as "....//foo".
  */
 #define NAPPEND(p)                                                      \
-        (p->fts_path[p->fts_pathlen - 1] == '/'                         \
+        (ISSLASH(p->fts_path[p->fts_pathlen - 1])                       \
             ? p->fts_pathlen - 1 : p->fts_pathlen)
 
 FTSENT *
 fts_read (register FTS *sp)
 {
@@ -1156,11 +1160,11 @@ fts_children (register FTS *sp, int inst
          * does its chdir to the root of a traversal, we can lose -- we need to
          * chdir into the subdirectory, and we don't know where the current
          * directory is, so we can't get back so that the upcoming chdir by
          * fts_read will work.
          */
-        if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
+        if (p->fts_level != FTS_ROOTLEVEL || IS_ABSOLUTE_FILE_NAME(p->fts_accpath) ||
             ISSET(FTS_NOCHDIR))
                 return (sp->fts_child = fts_build(sp, instr));
 
         if ((fd = diropen (sp, ".")) < 0)
                 return (sp->fts_child = NULL);
diff -aprNU5 grep-2.14.orig/lib/iswctype-impl.h grep-2.14/lib/iswctype-impl.h
--- grep-2.14.orig/lib/iswctype-impl.h	2012-01-07 15:36:02 +0000
+++ grep-2.14/lib/iswctype-impl.h	2012-08-22 22:00:48 +0000
@@ -16,7 +16,11 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 int
 iswctype (wint_t wc, wctype_t desc)
 {
+#ifdef __DJGPP__
+  return 0;
+#else
   return ((int (*) (wint_t)) desc) (wc);
+#endif
 }
diff -aprNU5 grep-2.14.orig/lib/open.c grep-2.14/lib/open.c
--- grep-2.14.orig/lib/open.c	2012-01-07 15:36:02 +0000
+++ grep-2.14/lib/open.c	2012-08-22 22:23:42 +0000
@@ -25,10 +25,14 @@
 /* Get the original definition of open.  It might be defined as a macro.  */
 #include <fcntl.h>
 #include <sys/types.h>
 #undef __need_system_fcntl_h
 
+#ifndef ISSLASH
+# include "dosname.h"
+#endif
+
 static inline int
 orig_open (const char *filename, int flags, mode_t mode)
 {
   return open (filename, flags, mode);
 }
@@ -105,11 +109,11 @@ open (const char *filename, int flags, .
        - if O_WRONLY or O_RDWR is specified, open() must fail because the
          file does not contain a '.' directory.  */
   if (flags & (O_CREAT | O_WRONLY | O_RDWR))
     {
       size_t len = strlen (filename);
-      if (len > 0 && filename[len - 1] == '/')
+      if (len > 0 && ISSLASH(filename[len - 1]))
         {
           errno = EISDIR;
           return -1;
         }
     }
@@ -126,12 +130,22 @@ open (const char *filename, int flags, .
      dummy.  */
   if (REPLACE_OPEN_DIRECTORY && fd < 0 && errno == EACCES
       && ((flags & O_ACCMODE) == O_RDONLY
           || (O_SEARCH != O_RDONLY && (flags & O_ACCMODE) == O_SEARCH)))
     {
+#if defined(__DJGPP__) && __DJGPP__ == 2 && __DJGPP_MINOR__ < 4
+      /*
+       *  Computation of stat is very expensive.
+       *  To determinate if PATH is an existing
+       *  directory, access on DOS platforms is
+       *  mostly a fast system call.
+       */
+      if (access (filename, D_OK) == 0)
+#else
       struct stat statbuf;
       if (stat (filename, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+#endif
         {
           /* Maximum recursion depth of 1.  */
           fd = open ("/dev/null", flags, mode);
           if (0 <= fd)
             fd = _gl_register_fd (fd, filename);
@@ -156,15 +170,25 @@ open (const char *filename, int flags, .
      with ENOTDIR.  */
   if (fd >= 0)
     {
       /* We know len is positive, since open did not fail with ENOENT.  */
       size_t len = strlen (filename);
-      if (filename[len - 1] == '/')
+      if (ISSLASH(filename[len - 1]))
         {
+#ifdef __DJGPP__
+          /*
+           *  Computation of stat is very expensive.
+           *  To determinate if PATH is an existing
+           *  directory, access on DOS platforms is
+           *  mostly a fast system call.
+           */
+          if (access (filename, D_OK) == 0)
+#else
           struct stat statbuf;
 
           if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
+#endif
             {
               close (fd);
               errno = ENOTDIR;
               return -1;
             }
diff -aprNU5 grep-2.14.orig/lib/openat-proc.c grep-2.14/lib/openat-proc.c
--- grep-2.14.orig/lib/openat-proc.c	2012-01-07 15:36:02 +0000
+++ grep-2.14/lib/openat-proc.c	2012-08-22 22:00:48 +0000
@@ -64,20 +64,24 @@ openat_proc_name (char buf[OPENAT_BUFFER
       return buf;
     }
 
   if (! proc_status)
     {
+#ifdef __DJGPP__
+      int proc_self_fd = -1;
+#else
       /* Set PROC_STATUS to a positive value if /proc/self/fd is
          reliable, and a negative value otherwise.  Solaris 10
          /proc/self/fd mishandles "..", and any file name might expand
          to ".." after symbolic link expansion, so avoid /proc/self/fd
          if it mishandles "..".  Solaris 10 has openat, but this
          problem is exhibited on code that built on Solaris 8 and
          running on Solaris 10.  */
 
       int proc_self_fd = open ("/proc/self/fd",
                                O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
+#endif
       if (proc_self_fd < 0)
         proc_status = -1;
       else
         {
           /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
diff -aprNU5 grep-2.14.orig/lib/progname.c grep-2.14/lib/progname.c
--- grep-2.14.orig/lib/progname.c	2012-01-07 15:36:02 +0000
+++ grep-2.14/lib/progname.c	2012-08-22 22:00:48 +0000
@@ -25,10 +25,39 @@
 #include <errno.h> /* get program_invocation_name declaration */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+/* MS-DOS and similar non-Posix systems have some peculiarities:
+    - they use both `/' and `\\' as directory separator in file names;
+    - they can have a drive letter X: prepended to a file name;
+   These are all parameterized here.  */
+
+#ifdef MSDOS
+# include <libc/unconst.h>
+# undef  IS_SLASH
+# define IS_SLASH(c)  ((c) == '/' || (c) == '\\' || (c) == ':')
+# define GET_LAST_SLASH(filename)              \
+  (__extension__                               \
+    ({                                         \
+      char *_slash = NULL;                     \
+      if ((filename))                          \
+      {                                        \
+        _slash = unconst((filename), char *);  \
+        while (*_slash++)                      \
+          ;                                    \
+        while ((--_slash - (filename)))        \
+          if (IS_SLASH(*_slash))               \
+            break;                             \
+      }                                        \
+      (_slash);                                \
+    })                                         \
+  )
+#else
+# define GET_LAST_SLASH(filename)  (strrchr((filename), '/'))
+#endif
+
 
 /* String containing name the program is called with.
    To be initialized by main().  */
 const char *program_name = NULL;
 
@@ -54,13 +83,21 @@ set_program_name (const char *argv0)
       fputs ("A NULL argv[0] was passed through an exec system call.\n",
              stderr);
       abort ();
     }
 
-  slash = strrchr (argv0, '/');
+  slash = GET_LAST_SLASH (argv0);
   base = (slash != NULL ? slash + 1 : argv0);
-  if (base - argv0 >= 7 && strncmp (base - 7, "/.libs/", 7) == 0)
+  if (base - argv0 >= 7 && (strncmp (base - 7, "/.libs/", 7) == 0
+#ifdef MSDOS
+     || strncmp (base - 7, "\\.libs/", 7) == 0
+     || strncmp (base - 7, "\\.libs\\", 7) == 0
+     || strncmp (base - 7, "/_libs/", 7) == 0
+     || strncmp (base - 7, "\\_libs/", 7) == 0
+     || strncmp (base - 7, "\\_libs\\", 7) == 0
+#endif
+     ))
     {
       argv0 = base;
       if (strncmp (base, "lt-", 3) == 0)
         {
           argv0 = base + 3;
diff -aprNU5 grep-2.14.orig/src/dfa.c grep-2.14/src/dfa.c
--- grep-2.14.orig/src/dfa.c	2012-06-08 06:25:04 +0000
+++ grep-2.14/src/dfa.c	2012-08-22 22:00:48 +0000
@@ -1003,20 +1003,22 @@ parse_bracket_exp (void)
                                      || STREQ (str, "lower")) ? "alpha" : str);
                   const struct dfa_ctype *pred = find_pred (class);
                   if (!pred)
                     dfaerror (_("invalid character class"));
 
+#if MBS_SUPPORT
                   if (MB_CUR_MAX > 1 && !pred->single_byte_only)
                     {
                       /* Store the character class as wctype_t.  */
                       wctype_t wt = wctype (class);
 
                       REALLOC_IF_NECESSARY (work_mbc->ch_classes,
                                             ch_classes_al,
                                             work_mbc->nch_classes + 1);
                       work_mbc->ch_classes[work_mbc->nch_classes++] = wt;
                     }
+#endif /* MBS_SUPPORT */
 
                   for (c2 = 0; c2 < NOTCHAR; ++c2)
                     if (pred->func (c2))
                       setbit_case_fold_c (c2, ccl);
                 }
@@ -2811,10 +2813,11 @@ build_state_zero (struct dfa *d)
   MALLOC (d->success, d->tralloc);
   MALLOC (d->newlines, d->tralloc);
   build_state (0, d);
 }
 
+#if MBS_SUPPORT
 /* Multibyte character handling sub-routines for dfaexec.  */
 
 /* Initial state may encounter the byte which is not a single byte character
    nor 1st byte of a multibyte character.  But it is incorrect for initial
    state to accept such a byte.
@@ -3269,10 +3272,11 @@ prepare_wc_buf (const char *begin, const
   buf_end = (unsigned char *) (begin + i);
   mblen_buf[i] = 0;
   inputwcs[i] = 0;              /* sentinel */
 #endif /* MBS_SUPPORT */
 }
+#endif /* MBS_SUPPORT */
 
 /* Search through a buffer looking for a match to the given struct dfa.
    Find the first occurrence of a string matching the regexp in the
    buffer, and the shortest possible version thereof.  Return a pointer to
    the first character after the match, or NULL if none is found.  BEGIN
@@ -3302,20 +3306,23 @@ dfaexec (struct dfa *d, char const *begi
   p = (unsigned char const *) begin;
   trans = d->trans;
   saved_end = *(unsigned char *) end;
   *end = eol;
 
+#if MBS_SUPPORT
   if (d->mb_cur_max > 1)
     {
       MALLOC (mblen_buf, end - begin + 2);
       MALLOC (inputwcs, end - begin + 2);
       memset (&mbs, 0, sizeof (mbstate_t));
       prepare_wc_buf ((const char *) p, end);
     }
+#endif /* MBS_SUPPORT */
 
   for (;;)
     {
+#if MBS_SUPPORT
       if (d->mb_cur_max > 1)
         while ((t = trans[s]) != NULL)
           {
             if (p > buf_end)
               break;
@@ -3345,10 +3352,11 @@ dfaexec (struct dfa *d, char const *begi
                collating element).  Transition table might be updated.  */
             s = transit_state (d, s, &p);
             trans = d->trans;
           }
       else
+#endif /* MBS_SUPPORT */
         {
           while ((t = trans[s]) != NULL)
             {
               s1 = t[*p++];
               if ((t = trans[s1]) == NULL)
@@ -3376,30 +3384,34 @@ dfaexec (struct dfa *d, char const *begi
               *end = saved_end;
               return (char *) p;
             }
 
           s1 = s;
+#if MBS_SUPPORT
           if (d->mb_cur_max > 1)
             {
               /* Can match with a multibyte character (and multicharacter
                  collating element).  Transition table might be updated.  */
               s = transit_state (d, s, &p);
               trans = d->trans;
             }
           else
+#endif /* MBS_SUPPORT */
             s = d->fails[s][*p++];
           continue;
         }
 
       /* If the previous character was a newline, count it. */
       if ((char *) p <= end && p[-1] == eol)
         {
           if (count)
             ++*count;
 
+#if MBS_SUPPORT
           if (d->mb_cur_max > 1)
             prepare_wc_buf ((const char *) p, end);
+#endif /* MBS_SUPPORT */
         }
 
       /* Check if we've run off the end of the buffer. */
       if ((char *) p > end)
         {
diff -aprNU5 grep-2.14.orig/src/dfasearch.c grep-2.14/src/dfasearch.c
--- grep-2.14.orig/src/dfasearch.c	2012-08-07 15:01:52 +0000
+++ grep-2.14/src/dfasearch.c	2012-08-22 22:03:02 +0000
@@ -218,10 +218,11 @@ EGexecute (char const *buf, size_t size,
   size_t len, best_len;
   struct kwsmatch kwsm;
   size_t i, ret_val;
   mb_len_map_t *map = NULL;
 
+#if MBS_SUPPORT
   if (MB_CUR_MAX > 1)
     {
       if (match_icase)
         {
           /* mbtolower adds a NUL byte at the end.  That will provide
@@ -230,10 +231,11 @@ EGexecute (char const *buf, size_t size,
           if (start_ptr)
             start_ptr = case_buf + (start_ptr - buf);
           buf = case_buf;
         }
     }
+#endif /* MBS_SUPPORT */
 
   mb_start = buf;
   buflim = buf + size;
 
   for (beg = end = buf; end < buflim; beg = end)
diff -aprNU5 grep-2.14.orig/src/dosbuf.c grep-2.14/src/dosbuf.c
--- grep-2.14.orig/src/dosbuf.c	2012-05-08 08:04:40 +0000
+++ grep-2.14/src/dosbuf.c	2012-08-22 22:04:16 +0000
@@ -15,23 +15,24 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
    02110-1301, USA.  */
 
 /* Messy DOS-specific code for correctly treating binary, Unix text
-   and DOS text files.
+   and DOS text files, and for emulating a Posix terminal driver
+   wrt SGR (a.k.a. ANSI) color escape sequences.
 
    This has several aspects:
 
      * Guessing the file type (unless the user tells us);
      * Stripping CR characters from DOS text files (otherwise regex
        functions won't work correctly);
      * Reporting correct byte count with -b for any kind of file.
+     * Redirecting output with ANSI color commands to direct screen
+       writes.
 
 */
 
-#include <config.h>
-
 typedef enum {
   UNKNOWN, DOS_BINARY, DOS_TEXT, UNIX_TEXT
 } File_type;
 
 struct dos_map {
@@ -196,5 +197,347 @@ dossified_pos (off_t byteno)
         out_map_idx--;
     }
 
   return byteno + dos_pos_map[out_map_idx].add;
 }
+
+#ifdef __DJGPP__
+/*  Screen write redirection.  We need this to support colorization
+    without requiring ANSI.SYS driver (or its work-alike) to be loaded.
+
+    This function uses the DJGPP filesystem extensions mechanism.  It is
+    installed as a handler for handle-based functions (read/write/close)
+    for the standard output (but actually only handles writes, only if
+    the standard output is connected to the terminal, and only if user
+    asked for colorization).  When a buffer is written to the screen by
+    low-level functions of the DJGPP C library, our handler will be
+    called.  For any request that doesn't require colored screen writes
+    we return a zero to the caller, in which case the caller will handle
+    the output in the usual way (by eventually calling DOS).
+
+    When colorization *is* required, the buffer is written directly to
+    the screen while converting the ANSI escape sequences into calls to
+    DJGPP conio functions which change text attributes.  A non-zero value is
+    then returned to the caller to signal that the output has been handled.
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <conio.h>
+#include <sys/fsext.h>
+#include <go32.h> /* for `_dos_ds' */
+#include <sys/farptr.h>
+#include <pc.h> /* for `ScreenGetCursor' */
+
+static int norm_blink = -1, cur_blink = -1;
+static unsigned char norm_attr = 0, cur_attr = 0;
+static int isatty_stdout = -1;
+static size_t leftover = 0;
+
+/* Restore the BIOS blinking bit to its original value.  Called at exit.  */
+static void
+restore_blink_bit (void)
+{
+  if (cur_blink != norm_blink)
+    {
+      if (norm_blink > 0)
+        blinkvideo ();
+      else
+        intensevideo ();
+    }
+}
+
+/* Write a buffer to the screen video memory.  This expands the TAB
+   characters to the appropriate number of spaces, and also does TRT
+   with null characters and other non-printable characters, if
+   any.  */
+static void
+screen_puts (char *buf, char *buf_end)
+{
+  register char *p = buf, *q = p;
+  int row, col;
+  unsigned char c;
+
+  while (p < buf_end)
+    {
+      if (*p < ' ')
+        {
+          switch (*p)
+            {
+              case '\b':
+              case '\r':
+              case '\n':
+                /* nothing: cputs already does TRT with these */
+                break;
+              case '\t':
+                *p = '\0';
+                cputs (q);
+                ScreenGetCursor (&row, &col);
+                for (cputs (" "), col += 1; col % 8; col++)
+                  cputs (" ");
+                q = p + 1;
+                *p = '\t';
+                break;
+              default:
+                c = *p;
+                *p = '\0';
+                cputs (q);
+                cputs ("^");
+                putch (c | 0x40);
+                q = p + 1;
+                *p = c;
+                break;
+            }
+        }
+      p++;
+    }
+  /* Output whatever is left.  */
+  cputs (q);
+}
+
+#define ESC '\033'
+#define IS_SGR(s) (((s)[1] == '[') && ((s)[2] == 'm'))
+#define IS_EL(s)  (((s)[1] == '[') && ((s)[2] == 'K'))
+
+/* Screen writes redirector function.  */
+static int
+msdos_screen_write (__FSEXT_Fnumber func, int *retval, va_list rest_args)
+{
+  static char *cbuf = NULL;
+  static size_t cbuf_len = 0;
+  /* Only dark colors mentioned here, so that bold has visible effect.  */
+  static enum COLORS screen_color[] = {
+    BLACK,
+    RED,
+    GREEN,
+    BROWN,
+    BLUE,
+    MAGENTA,
+    CYAN,
+    LIGHTGRAY
+  };
+  char *anchor, *p_next;
+  unsigned char fg, bg;
+
+  int handle;
+  char *buf, *buf_end;
+  size_t buflen;
+
+  /* Avoid direct screen writes unless colorization was actually requested.
+     Otherwise, we will break programs that catch I/O from their children.  */
+  handle = va_arg (rest_args, int);
+  if (!color_option || func != __FSEXT_write ||
+       !(handle == STDOUT_FILENO ? isatty_stdout : isatty (handle)))
+    return 0;
+
+  buf = va_arg (rest_args, char *);
+  if (!buf)
+    {
+      errno = EINVAL;
+      *retval = -1;
+      return 1;
+    }
+
+  /* Allocate a sufficiently large buffer to hold the output.  */
+  buflen = va_arg (rest_args, size_t);
+  if (!cbuf)
+    {
+      struct text_info txtinfo;
+
+      cbuf_len = buflen + 1;
+      cbuf = (char *)xmalloc (cbuf_len);
+      gettextinfo (&txtinfo);
+      norm_attr = txtinfo.attribute; /* save the original text attribute */
+      cur_attr = norm_attr;
+      /* Does it normally blink when bg has its 3rd bit set?  */
+      norm_blink = (_farpeekb (_dos_ds, 0x465) & 0x20) ? 1 : 0;
+      cur_blink = norm_blink;
+    }
+  else if (buflen >= cbuf_len)
+    {
+      cbuf_len = buflen + 1 + leftover;
+      cbuf = (char *)xrealloc (cbuf, cbuf_len);
+    }
+  memcpy (cbuf + leftover, buf, buflen);
+  buf_end = cbuf + buflen + leftover;
+  *buf_end = '\0';
+
+  /* Current text attributes are used as baseline.  */
+  fg = cur_attr & 15;
+  bg = (cur_attr >> 4) & 15;
+
+  /* Walk the buffer, writing text directly to video RAM,
+     changing color attributes when an escape sequence is seen.  */
+  for (anchor = p_next = cbuf;
+       (p_next = memchr (p_next, ESC, buflen - (p_next - cbuf))) != 0; )
+    {
+      char *p = p_next;
+
+      /* If some chars seen since the last escape sequence,
+         write it out to the screen using current text attributes.  */
+      if (p > anchor)
+        {
+          *p = '\0'; /* `cputs' needs ASCIIZ string */
+          screen_puts (anchor, p);
+          *p = ESC; /* restore the ESC character */
+          anchor = p;
+        }
+
+      /* Handle the null escape sequence (ESC-[m), which is used to
+         restore the original color. */
+      if (IS_SGR(p))
+        {
+          textattr (norm_attr);
+          p += 3;
+          anchor = p_next = p;
+          continue;
+        }
+
+      /* Handle the erase in line to the right escape sequence (ESC-[K). */
+      if (IS_EL(p))
+        {
+          clreol();
+          p += 3;
+          anchor = p_next = p;
+          continue;
+        }
+
+      if (p[1] == '[') /* "Esc-[" sequence */
+        {
+          p += 2; /* get past "Esc-[" sequence */
+          p_next = p;
+          while (*p != 'm') /* `m' ends the escape sequence */
+            {
+              char *q;
+              long code = strtol (p, &q, 10);
+
+              if (!*q)
+                {
+                  /* Incomplete escape sequence.  Remember the part
+                     we've seen for the next time.  */
+                  leftover = q - anchor;
+                  if (leftover >= cbuf_len)
+                    {
+                      cbuf_len += 1 + leftover;
+                      cbuf = (char *)xrealloc (cbuf, cbuf_len);
+                    }
+                  strcpy (cbuf, anchor);
+                  *retval = buflen; /* that's a lie, but we have to! */
+                  return 1;
+                }
+
+              /* 
+                 Sanity checks:
+
+                 q > p unless p doesn't point to a number;
+                 SGR codes supported by ANSI.SYS are between 0 and 49;
+                 Each SGR code ends with a `;' or an `m'.
+
+                 If any of the above is violated, we just ignore the bogon.
+              */
+              if (q == p || code > 49 || code < 0 || (*q != 'm' && *q != ';'))
+                {
+                  p_next = q;
+                  break;
+                }
+              if (*q == ';') /* more codes to follow */
+                q++;
+
+              /* Convert ANSI codes to color fore- and background.  */
+              switch (code)
+                {
+                  case 0: /* all attributes off */
+                    fg = norm_attr & 15;
+                    bg = (norm_attr >> 4) & 15;
+                    break;
+                  case 1: /* intensity on */
+                    fg |= 8;
+                    break;
+                  case 4: /* underline on */
+                    fg |= 8; /* we can't, so make it bold instead */
+                    break;
+                  case 5: /* blink */
+                    if (cur_blink != 1)
+                      {
+                        blinkvideo (); /* ensure we are'nt in bright bg mode */
+                        cur_blink = 1;
+                      }
+                    bg |= 8;
+                    break;
+                  case 7: /* reverse video */
+                    {
+                      unsigned char t = fg;
+                      fg = bg;
+                      bg = t;
+
+                      /* If it was blinking before, let it blink after.  */
+                      if (fg & 8)
+                        bg |= 8;
+
+                      /* If the fg was bold, let the background be bold.  */
+                      if ((t & 8) && cur_blink != 0)
+                        {
+                          intensevideo ();
+                          cur_blink = 0;
+                        }
+                    }
+                    break;
+                  case 8: /* concealed on */
+                    fg = (bg & 7) | 8; /* make fg be like bg, only bright */
+                    break;
+                  case 30: case 31: case 32: case 33: /* foreground color */
+                  case 34: case 35: case 36: case 37:
+                    fg = (fg & 8) | (screen_color[code - 30] & 15);
+                    break;
+                  case 40: case 41: case 42: case 43: /* background color */
+                  case 44: case 45: case 46: case 47:
+                    bg = (bg & 8) | (screen_color[code - 40] & 15);
+                    break;
+                  case 39: /* default fg */
+                    fg = norm_attr & 15;
+                    break;
+                  case 49:
+                    bg = (norm_attr >> 4) & 15;
+                    break;
+                  default:
+                    p_next = q; /* ignore unknown codes */
+                    break;
+                }
+              p = q;
+            } /* while loop */
+
+          if (*p == 'm' && p > p_next)
+            {
+              /* They don't *really* want it invisible, do they?  */
+              if (fg == (bg & 7))
+                fg |= 8; /* make it concealed instead */
+
+              /* Construct the text attribute and set it.  */
+              cur_attr = (bg << 4) | fg;
+              textattr (cur_attr);
+              p_next = anchor = p + 1;
+            }
+          else
+            break;
+        }
+      else
+        p_next++;
+    }  /* for loop */
+
+  /* Output what's left in the buffer.  */
+  screen_puts (anchor, buf_end);
+  leftover = 0;
+  *retval = buflen;
+  return 1;
+}
+
+/* This is called before `main' to install our STDOUT redirector.  */
+static void __attribute__((constructor))
+djgpp_grep_startup (void)
+{
+  __FSEXT_set_function (STDOUT_FILENO, msdos_screen_write);
+  isatty_stdout = isatty (STDOUT_FILENO);
+  atexit (restore_blink_bit);
+}
+#endif  /* __DJGPP__ */
diff -aprNU5 grep-2.14.orig/src/main.c grep-2.14/src/main.c
--- grep-2.14.orig/src/main.c	2012-08-06 11:38:32 +0000
+++ grep-2.14/src/main.c	2012-08-22 22:05:08 +0000
@@ -52,10 +52,14 @@
 #define SEP_CHAR_REJECTED '-'
 #define SEP_STR_GROUP    "--"
 
 #define STREQ(a, b) (strcmp (a, b) == 0)
 
+#ifdef __DJGPP__
+# define IS_TERMINAL (isatty(STDOUT_FILENO))
+#endif
+
 #define AUTHORS \
   proper_name ("Mike Haertel"), \
   _("others, see <http://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>")
 
 /* When stdout is connected to a regular file, save its stat
@@ -1515,12 +1519,12 @@ grep_command_line_arg (char const *arg)
       filename = label ? label : _("(standard input)");
       return grepdesc (STDIN_FILENO, 1);
     }
   else
     {
-      filename = arg;
-      return grepfile (AT_FDCWD, arg, 1, 1);
+      filename = CANONICALIZE_PATH(arg);
+      return grepfile (AT_FDCWD, filename, 1, 1);
     }
 }
 
 _Noreturn void usage (int);
 void
@@ -1865,11 +1869,11 @@ main (int argc, char **argv)
   intmax_t default_context;
   FILE *fp;
   exit_failure = EXIT_TROUBLE;
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
-  program_name = argv[0];
+  program_name = STRIP_FULL_PATH_AND_EXTENSION(argv[0]);
 
   keys = NULL;
   keycc = 0;
   with_filenames = 0;
   eolbyte = '\n';
@@ -2002,11 +2006,11 @@ main (int argc, char **argv)
         keycc += cc;
         keys[keycc++] = '\n';
         break;
 
       case 'f':
-        fp = STREQ (optarg, "-") ? stdin : fopen (optarg, "r");
+        fp = STREQ (optarg, "-") ? stdin : fopen (CANONICALIZE_PATH(optarg), "r");
         if (!fp)
           error (EXIT_TROUBLE, errno, "%s", optarg);
         for (keyalloc = 1; keyalloc <= keycc + 1; keyalloc *= 2)
           ;
         keys = xrealloc (keys, keyalloc);
@@ -2181,11 +2185,15 @@ main (int argc, char **argv)
         break;
 
       }
 
   if (color_option == 2)
+#ifdef IS_TERMINAL
+    color_option = (IS_TERMINAL) ? 1 : 0;
+#else
     color_option = isatty (STDOUT_FILENO) && should_colorize ();
+#endif
   init_colorize ();
 
   /* POSIX.2 says that -q overrides -l, which in turn overrides the
      other output options.  */
   if (exit_on_match)
diff -aprNU5 grep-2.14.orig/src/mbsupport.h grep-2.14/src/mbsupport.h
--- grep-2.14.orig/src/mbsupport.h	2012-02-27 09:35:18 +0000
+++ grep-2.14/src/mbsupport.h	2012-08-22 22:06:14 +0000
@@ -17,10 +17,14 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
    02110-1301, USA.  */
 
 #include <stdlib.h>
 
+#ifdef __DJGPP__
+# define MBS_SUPPORT 0
+#endif
+
 #ifndef MBS_SUPPORT
 # define MBS_SUPPORT 1
 #endif
 
 #if ! MBS_SUPPORT
diff -aprNU5 grep-2.14.orig/src/system.h grep-2.14/src/system.h
--- grep-2.14.orig/src/system.h	2012-02-27 09:35:18 +0000
+++ grep-2.14/src/system.h	2012-08-22 22:12:32 +0000
@@ -29,10 +29,74 @@
 #include "minmax.h"
 #include "same-inode.h"
 
 #if O_BINARY
 # define HAVE_DOS_FILE_CONTENTS 1
+# if __DJGPP__ == 2
+#  if __DJGPP_MINOR__ < 4
+/* Has dir eacces bug.  */
+#   ifdef EISDIR
+#    define is_EISDIR(e, f) \
+       ((e) == EISDIR \
+        || ((e) == EACCES && isdir(f) && ((e) = EISDIR, 1)))
+#   else
+#    define is_EISDIR(e, f)  ((e) == EACCES && isdir (f))
+#   endif
+#  endif  /* __DJGPP_MINOR__ < 4  */
+
+#  include <libc/unconst.h>
+#  define IS_DIR_SEPARATOR(c)  ((c) == '/' || (c) == '\\' || (c) == ':')
+#  define STRIP_FULL_PATH_AND_EXTENSION(file_name)     \
+   (__extension__                                      \
+     ({                                                \
+        char *_dst, *_src;                             \
+        _dst = _src = unconst((file_name), char *);    \
+        while (*_src++)                                \
+          ;                                            \
+        while ((_src - _dst) && (*--_src != '.'))      \
+          ;                                            \
+        for (*_src = '\0'; (_src - _dst); _src--)      \
+          if (IS_DIR_SEPARATOR(*_src))                 \
+            break;                                     \
+        if (_src - _dst)                               \
+          while ((*_dst++ = *++_src))                  \
+            ;                                          \
+        (file_name);                                   \
+     })                                                \
+   )
+#  define STRIP_EXTENSION(file_name)                   \
+   (__extension__                                      \
+     ({                                                \
+        char *_begin, *_end;                           \
+        _begin = _end = unconst((file_name), char *);  \
+        while (*_end++)                                \
+          ;                                            \
+        while ((_end - _begin) && (*--_end != '.'))    \
+          ;                                            \
+        if (*_end == '.')                              \
+          *_end = '\0';                                \
+        (file_name);                                   \
+     })                                                \
+   )
+#  define CANONICALIZE_PATH(path)                      \
+   (__extension__                                      \
+     ({                                                \
+        if ((path))                                    \
+        {                                              \
+          char *_p = unconst((path), char *);          \
+          for (; *_p; _p++)                            \
+            if (*_p == '\\')                           \
+              *_p = '/';                               \
+        }                                              \
+        (path);                                        \
+     })                                                \
+   )
+# else  /* !__DJGPP__  */
+#  define STRIP_FULL_PATH_AND_EXTENSION(file_name)  (file_name)
+#  define STRIP_EXTENSION(file_name)                (file_name)
+#  define CANONICALIZE_PATH(path)                   (path)
+# endif  /* !__DJGPP__  */
 #endif
 
 #include <stdlib.h>
 #include <stddef.h>
 #include <limits.h>
