about summary refs log tree commit diff
path: root/pkgs/shells/bash
diff options
context:
space:
mode:
authorMarko Durkovic <marko@miding.de>2014-10-23 14:38:54 +0200
committerRok Garbas <rok@garbas.si>2015-05-28 10:53:35 +0200
commit67ac12795a453b20288b8e28e83cdf5dc8cbf8da (patch)
tree52d4a203e549f6a20b023eb77c0d138ce232d88b /pkgs/shells/bash
parentb658196c6c00f02878e8e5c4c55c8cc871314e83 (diff)
cygwin: add cygwin compatible bash version.
Diffstat (limited to 'pkgs/shells/bash')
-rw-r--r--pkgs/shells/bash/bash-4.1-patches.nix21
-rw-r--r--pkgs/shells/bash/bash-4.1.17-9.src.patch987
-rw-r--r--pkgs/shells/bash/default.nix21
3 files changed, 1025 insertions, 4 deletions
diff --git a/pkgs/shells/bash/bash-4.1-patches.nix b/pkgs/shells/bash/bash-4.1-patches.nix
new file mode 100644
index 0000000000000..2affe1efa606e
--- /dev/null
+++ b/pkgs/shells/bash/bash-4.1-patches.nix
@@ -0,0 +1,21 @@
+# Automatically generated by `update-patch-set.sh'; do not edit.
+
+patch: [
+(patch "001" "0y02cbfnc5s3dnwr4fw2nz43f3b826f5084mk7qd0lzq12hpzr56")
+(patch "002" "1y3qzw6lx16vnb8hrw3zx01z25k773cbmgysvs3vvcw6w6fj4bij")
+(patch "003" "0v95ng8qa78dbh26rr6jpzkn3s6n78xymymkvvvkz35rpgfksxli")
+(patch "004" "17pzykkywh5jmdy1ikj9xyxm7qm29ii2fmrfpyjr1wy16jx67h3q")
+(patch "005" "06lwfgxx9kacz018nr4dmxqqrzipcg8pjn61gr6yfjv6s7c3k5ji")
+(patch "006" "0j9c1zhhwvc2p4cdxi77nxmcxa00cimaxwbhasyqgc606g7sp1jr")
+(patch "007" "19q5qba77hfda8g4xylh77awiakhr1d1asgbqcrbakxs50n2l0bl")
+(patch "008" "058j911q9wcbr93w59jnpgmdpx4qsq3gvd6m9nwgdk9j2hjjqb2f")
+(patch "009" "1lany70f0rx1i2xikzkahr1zskh8620j05ic0gc5x2p89ab0ch5x")
+(patch "010" "05fqv7w12g9izy332wypynilgxzdh87vy5q2pqq3bjdncyl5hxvr")
+(patch "011" "088n54yh5zp8aa917y1ng3802amchgal1acn0v0mdjqj0yi82aqv")
+(patch "012" "0vbvc1vxljyd882wk6rcd64xrf1lda6wirys41mqjbl0lalj8bi7")
+(patch "013" "1y7x62i0q3wkr9jdjzvm2rl9dp11vzp8fwkz2zb2x21l0pi25mya")
+(patch "014" "0gpizgbx1w712awpd11x5nvahpranl0aiq16nhp3xmm8vvz1wpar")
+(patch "015" "17nf6kw1vhmzn9nzb1vyn8r4wp2nl109f9yawzavjkf06670ln7c")
+(patch "016" "129hknigxhxrh1rbjhc4fm6argpjb6lp9fl616narbnzsv3qhc3l")
+(patch "017" "0vy02x6fmpd6i66n97r4fwrq9pncbgzya07iyca2bb6yyzmymgg5")
+]
diff --git a/pkgs/shells/bash/bash-4.1.17-9.src.patch b/pkgs/shells/bash/bash-4.1.17-9.src.patch
new file mode 100644
index 0000000000000..f236946483c24
--- /dev/null
+++ b/pkgs/shells/bash/bash-4.1.17-9.src.patch
@@ -0,0 +1,987 @@
+--- Makefile.in	2009-12-30 10:05:40.000000000 -0800
++++ Makefile.in	2014-10-08 13:50:27.419837900 -0700
+@@ -565,7 +565,7 @@ lint:
+ 	${MAKE} ${MFLAGS} CFLAGS='${GCC_LINT_FLAGS}' .made
+ 
+ version.h:  $(SOURCES) config.h Makefile 
+-	$(SHELL) $(SUPPORT_SRC)mkversion.sh -b -S ${topdir} -s $(RELSTATUS) -d $(Version) -o newversion.h \
++	$(SHELL) $(SUPPORT_SRC)mkversion.sh -S ${topdir} -s $(RELSTATUS) -d $(Version) -o newversion.h \
+ 		&& mv newversion.h version.h
+ 
+ bashversion$(EXEEXT):	patchlevel.h conftypes.h version.h buildversion.o $(SUPPORT_SRC)bashversion.c
+--- bashline.c	2014-10-08 13:45:09.240173500 -0700
++++ bashline.c	2014-10-08 13:50:27.419837900 -0700
+@@ -68,6 +68,12 @@
+ #  include "pcomplete.h"
+ #endif
+ 
++#ifdef __x86_64__
++#define IMP(x) __imp_##x
++#else
++#define IMP(x) _imp__##x
++#endif
++
+ /* These should agree with the defines for emacs_mode and vi_mode in
+    rldefs.h, even though that's not a public readline header file. */
+ #ifndef EMACS_EDITING_MODE
+@@ -239,6 +245,11 @@ int no_empty_command_completion;
+    are the only possible matches, even if FIGNORE says to. */
+ int force_fignore = 1;
+ 
++#if __CYGWIN__
++/* If set, shorten "foo.exe" to "foo" when they are the same file.  */
++int completion_strip_exe;
++#endif /* __CYGWIN__ */
++
+ /* Perform spelling correction on directory names during word completion */
+ int dircomplete_spelling = 0;
+ 
+@@ -446,11 +457,12 @@ initialize_readline ()
+   kseq[0] = CTRL('J');
+   kseq[1] = '\0';
+   func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+-  if (func == rl_vi_editing_mode)
++  extern rl_command_func_t *IMP(rl_vi_editing_mode);
++  if (func == rl_vi_editing_mode || func == IMP(rl_vi_editing_mode))
+     rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
+   kseq[0] = CTRL('M');
+   func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+-  if (func == rl_vi_editing_mode)
++  if (func == rl_vi_editing_mode || func == IMP(rl_vi_editing_mode))
+     rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
+ #if defined (VI_MODE)
+   rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
+@@ -469,7 +481,8 @@ initialize_readline ()
+   kseq[0] = '~';
+   kseq[1] = '\0';
+   func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+-  if (func == 0 || func == rl_tilde_expand)
++  extern rl_command_func_t *IMP(rl_tilde_expand);
++  if (func == 0 || func == rl_tilde_expand || func == IMP(rl_tilde_expand))
+     rl_bind_keyseq_in_map (kseq, bash_complete_username, emacs_meta_keymap);
+ 
+   rl_bind_key_if_unbound_in_map ('~', bash_possible_username_completions, emacs_ctlx_keymap);
+@@ -492,7 +505,8 @@ initialize_readline ()
+   kseq[0] = TAB;
+   kseq[1] = '\0';
+   func = rl_function_of_keyseq (kseq, emacs_meta_keymap, (int *)NULL);
+-  if (func == 0 || func == rl_tab_insert)
++  extern rl_command_func_t *IMP(rl_tab_insert);
++  if (func == 0 || func == rl_tab_insert || func == IMP(rl_tab_insert))
+     rl_bind_key_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);
+ 
+   /* Tell the completer that we want a crack first. */
+@@ -1826,6 +1840,14 @@ globword:
+       /* If we have found a match, and it is an executable file or a
+ 	 directory name, return it. */
+       if (match && executable_or_directory (val))
++#elif __CYGWIN__
++      /* executable_or_directory will do the right thing on //server,
++	 but calling stat("//server") is an order of magnitude slower
++	 than noting that readdir("//") only returns directories.  */
++      if (match && (searching_path ? executable_file (val)
++		    : ((val[0] == '/' && val[1] == '/'
++			&& ! strchr (&val[2], '/'))
++		       || executable_or_directory (val))))
+ #else
+       /* If we have found a match, and it is an executable file, return it.
+ 	 We don't return directory names when searching $PATH, since the
+@@ -1835,6 +1857,21 @@ globword:
+       if (match && (searching_path ? executable_file (val) : executable_or_directory (val)))
+ #endif
+ 	{
++#if __CYGWIN__
++          if (completion_strip_exe)
++            {
++              int val_len = strlen (val);
++              char *candidate;
++              if (val_len > 4 && !strcasecmp (&val[val_len - 4], ".exe")
++                  && (candidate = strdup (val)))
++                {
++                  candidate[val_len - 4] = '\0';
++                  if (same_file (val, candidate, NULL, NULL))
++                    temp[strlen (temp) - 4] = '\0';
++                  free (candidate);
++                }
++            }
++#endif
+ 	  free (val);
+ 	  val = "";		/* So it won't be NULL. */
+ 	  return (temp);
+@@ -2566,6 +2603,17 @@ test_for_directory (name)
+   int r;
+ 
+   fn = bash_tilde_expand (name, 0);
++#if __CYGWIN__
++  /* stat("//server") can only be successful as a directory, but takes
++     a several-second timeout to fail.  It is much faster to assume
++     that //server is a valid name than it is to wait for the stat,
++     even though it gives false positives on bad names.  */
++  if (fn[0] == '/' && fn[1] == '/' && ! strchr (&fn[2], '/'))
++    {
++      free (fn);
++      return 1;
++    }
++#endif /* __CYGWIN__ */
+   r = file_isdir (fn);
+   free (fn);
+ 
+--- builtins/evalfile.c	2009-10-19 14:38:21.000000000 -0700
++++ builtins/evalfile.c	2014-10-08 13:50:27.419837900 -0700
+@@ -148,10 +148,6 @@ file_error_and_exit:
+       return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
+     }      
+ 
+-#if defined (__CYGWIN__) && defined (O_TEXT)
+-  setmode (fd, O_TEXT);
+-#endif
+-
+   if (S_ISREG (finfo.st_mode) && file_size <= SSIZE_MAX)
+     {
+       string = (char *)xmalloc (1 + file_size);
+--- builtins/set.def	2009-01-04 11:32:23.000000000 -0800
++++ builtins/set.def	2014-10-08 13:50:27.419837900 -0700
+@@ -56,6 +56,13 @@ extern int dont_save_function_defs;
+ #if defined (READLINE)
+ extern int no_line_editing;
+ #endif /* READLINE */
++#if __CYGWIN__
++extern int igncr;
++static int set_minus_o_option_maybe (int, const char *, int);
++# define INTERACTIVE_ONLY ,1
++#else /* ! __CYGWIN__ */
++# define INTERACTIVE_ONLY
++#endif
+ 
+ $BUILTIN set
+ $FUNCTION set_builtin
+@@ -92,6 +99,9 @@ Options:
+ #if defined (HISTORY)
+           history      enable command history
+ #endif
++#if __CYGWIN__
++          igncr        on cygwin, ignore \r in line endings
++#endif
+           ignoreeof    the shell will not exit upon reading EOF
+           interactive-comments
+                        allow comments to appear in interactive commands
+@@ -181,28 +191,40 @@ const struct {
+   int *variable;
+   setopt_set_func_t *set_func;
+   setopt_get_func_t *get_func;
++#if __CYGWIN__
++  /* Cygwin users have taken to exporting SHELLOPTS for the
++     cygwin-specific igncr.  As a result, we need to make sure
++     SHELLOPTS parsing does not turn on interactive options when
++     exported from an interactive shell, but parsed in a
++     non-interactive setting, since some interactive options violate
++     POSIX /bin/sh rules.  */
++  int interactive_only;
++#endif /* __CYGWIN__ */
+ } o_options[] = {
+   { "allexport",  'a', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+ #if defined (BRACE_EXPANSION)
+   { "braceexpand",'B', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+ #endif
+ #if defined (READLINE)
+-  { "emacs",     '\0', (int *)NULL, set_edit_mode, get_edit_mode },
++  { "emacs",     '\0', (int *)NULL, set_edit_mode, get_edit_mode INTERACTIVE_ONLY},
+ #endif
+   { "errexit",	  'e', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+   { "errtrace",	  'E', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+   { "functrace",  'T', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+   { "hashall",    'h', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+ #if defined (BANG_HISTORY)
+-  { "histexpand", 'H', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
++  { "histexpand", 'H', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  INTERACTIVE_ONLY},
+ #endif /* BANG_HISTORY */
+ #if defined (HISTORY)
+-  { "history",   '\0', &enable_history_list, bash_set_history, (setopt_get_func_t *)NULL },
++  { "history",   '\0', &enable_history_list, bash_set_history, (setopt_get_func_t *)NULL INTERACTIVE_ONLY},
++#endif
++#if __CYGWIN__
++  { "igncr", '\0', &igncr, NULL, (setopt_get_func_t *)NULL },
+ #endif
+   { "ignoreeof", '\0', &ignoreeof, set_ignoreeof, (setopt_get_func_t *)NULL },
+   { "interactive-comments", '\0', &interactive_comments, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+   { "keyword",    'k', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+-  { "monitor",	  'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
++  { "monitor",	  'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  INTERACTIVE_ONLY},
+   { "noclobber",  'C', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+   { "noexec",	  'n', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+   { "noglob",	  'f', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+@@ -220,7 +242,7 @@ const struct {
+   { "privileged", 'p', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+   { "verbose",	  'v', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+ #if defined (READLINE)
+-  { "vi",        '\0', (int *)NULL, set_edit_mode, get_edit_mode },
++  { "vi",        '\0', (int *)NULL, set_edit_mode, get_edit_mode INTERACTIVE_ONLY},
+ #endif
+   { "xtrace",	  'x', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
+   {(char *)NULL, 0 , (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
+@@ -407,6 +429,15 @@ int
+ set_minus_o_option (on_or_off, option_name)
+      int on_or_off;
+      char *option_name;
++#if __CYGWIN__
++{
++  /* See cygwin comments above.  */
++  return set_minus_o_option_maybe (on_or_off, option_name, 0);
++}
++static int
++set_minus_o_option_maybe (int on_or_off, const char *option_name,
++			  int avoid_interactive)
++#endif /* __CYGWIN__ */
+ {
+   register int i;
+ 
+@@ -414,6 +445,10 @@ set_minus_o_option (on_or_off, option_na
+     {
+       if (STREQ (option_name, o_options[i].name))
+ 	{
++#if __CYGWIN__
++	  if (o_options[i].interactive_only && avoid_interactive)
++	    return EXECUTION_SUCCESS;
++#endif /* __CYGWIN__ */
+ 	  if (o_options[i].letter == 0)
+ 	    {
+ 	      SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name);
+@@ -539,7 +574,11 @@ parse_shellopts (value)
+   vptr = 0;
+   while (vname = extract_colon_unit (value, &vptr))
+     {
++#if __CYGWIN__
++    set_minus_o_option_maybe (FLAG_ON, vname, !interactive_shell);
++#else /* !__CYGWIN__ */
+       set_minus_o_option (FLAG_ON, vname);
++#endif
+       free (vname);
+     }
+ }
+--- builtins/shopt.def	2009-12-22 13:25:32.000000000 -0800
++++ builtins/shopt.def	2014-10-08 13:50:27.435414600 -0700
+@@ -85,6 +85,10 @@ extern int check_jobs_at_exit;
+ extern int autocd;
+ extern int glob_star;
+ 
++#if defined(__CYGWIN__) && defined(READLINE)
++extern int completion_strip_exe;
++#endif
++
+ #if defined (EXTENDED_GLOB)
+ extern int extended_glob;
+ #endif
+@@ -146,6 +150,9 @@ static struct {
+   { "compat31", &shopt_compat31, set_compatibility_level },
+   { "compat32", &shopt_compat32, set_compatibility_level },
+   { "compat40", &shopt_compat40, set_compatibility_level },
++#if defined(__CYGWIN__) && defined(READLINE)
++ { "completion_strip_exe", &completion_strip_exe, NULL },
++#endif
+ #if defined (READLINE)
+   { "dirspell", &dircomplete_spelling, (shopt_set_func_t *)NULL },
+ #endif
+--- config-top.h	2009-12-22 12:29:39.000000000 -0800
++++ config-top.h	2014-10-08 13:50:27.435414600 -0700
+@@ -75,10 +75,10 @@
+ #define KSH_COMPATIBLE_SELECT
+ 
+ /* System-wide .bashrc file for interactive shells. */
+-/* #define SYS_BASHRC "/etc/bash.bashrc" */
++//set by nix #define SYS_BASHRC "/etc/bash.bashrc"
+ 
+ /* System-wide .bash_logout for login shells. */
+-/* #define SYS_BASH_LOGOUT "/etc/bash.bash_logout" */
++//set by nix #define SYS_BASH_LOGOUT "/etc/bash.bash_logout"
+ 
+ /* Define this to make non-interactive shells begun with argv[0][0] == '-'
+    run the startup files when not in posix mode. */
+@@ -88,7 +88,7 @@
+    sshd and source the .bashrc if so (like the rshd behavior).  This checks
+    for the presence of SSH_CLIENT or SSH2_CLIENT in the initial environment,
+    which can be fooled under certain not-uncommon circumstances. */
+-/* #define SSH_SOURCE_BASHRC */
++//set by nix #define SSH_SOURCE_BASHRC
+ 
+ /* Define if you want the case-capitalizing operators (~[~]) and the
+    `capcase' variable attribute (declare -c). */
+--- doc/Makefile.in	2009-03-10 08:44:30.000000000 -0700
++++ doc/Makefile.in	2014-10-08 13:50:27.435414600 -0700
+@@ -170,7 +170,7 @@ bashref.html: $(BASHREF_FILES) $(HSUSER)
+ 	$(TEXI2HTML) -menu -monolithic -I $(TEXINPUTDIR) $(srcdir)/bashref.texi
+ 
+ bash.info: bashref.info
+-	${SHELL} ${INFOPOST} < $(srcdir)/bashref.info > $@ ; \
++	${SHELL} ${INFOPOST} < bashref.info > $@ ; \
+ 
+ bash.txt: bash.1
+ bash.ps: bash.1
+@@ -226,9 +226,9 @@ install: info installdirs bash.info
+ 	-$(INSTALL_DATA) $(srcdir)/bash.1 $(DESTDIR)$(man1dir)/bash${man1ext}
+ 	-$(INSTALL_DATA) $(srcdir)/bashbug.1 $(DESTDIR)$(man1dir)/bashbug${man1ext}
+ # uncomment the next lines to install the builtins man page
+-#	sed 's:bash\.1:man1/&:' $(srcdir)/builtins.1 > $${TMPDIR:-/var/tmp}/builtins.1
+-#	-$(INSTALL_DATA) $${TMPDIR:-/var/tmp}/builtins.1 $(DESTDIR)$(man1dir)/bash_builtins${man1ext}
+-#	-$(RM) $${TMPDIR:-/var/tmp}/builtins.1
++	sed 's:bash\.1:man1/&:' $(srcdir)/builtins.1 > $${TMPDIR:-/var/tmp}/builtins.1
++	-$(INSTALL_DATA) $${TMPDIR:-/var/tmp}/builtins.1 $(DESTDIR)$(man1dir)/bash_builtins${man1ext}
++	-$(RM) $${TMPDIR:-/var/tmp}/builtins.1
+ 	-if test -f bash.info; then d=.; else d=$(srcdir); fi; \
+ 	  $(INSTALL_DATA) $$d/bash.info $(DESTDIR)$(infodir)/bash.info
+ # run install-info if it is present to update the info directory
+--- doc/bash.1	2009-12-30 10:01:31.000000000 -0800
++++ doc/bash.1	2014-10-08 13:50:27.435414600 -0700
+@@ -1568,6 +1568,14 @@ subsequently reset.
+ Expands to the effective user ID of the current user, initialized at
+ shell startup.  This variable is readonly.
+ .TP
++.B EXECIGNORE
++A colon-separated list of extended glob (see \fBPattern Matching\fP)
++patterns. Files with full paths matching one of these patterns are
++not considered executable for the purposes of completion and PATH
++searching, but the \fB[\fP, \fB[[\fP, and \fBtest\fP builtins are not
++affected. Use this variable to deal with systems that set the
++executable bit on files that are not actually executable.
++.TP
+ .B FUNCNAME
+ An array variable containing the names of all shell functions
+ currently in the execution call stack.
+@@ -8772,6 +8780,10 @@ If set,
+ attempts spelling correction on directory names during word completion
+ if the directory name initially supplied does not exist.
+ .TP 8
++.B completion_strip_exe
++If set, whenever bash sees `foo.exe' during completion, it checks if
++`foo' is the same file and strips the suffix.
++.TP 8
+ .B dotglob
+ If set, 
+ .B bash
+--- doc/bashref.texi	2009-12-29 12:59:18.000000000 -0800
++++ doc/bashref.texi	2014-10-08 13:50:27.435414600 -0700
+@@ -4363,6 +4363,10 @@ If set, Bash
+ changes its behavior to that of version 3.1 with respect to quoted
+ arguments to the conditional command's =~ operator.
+ 
++@item completion_strip_exe
++If set, whenever bash sees `foo.exe' during completion, it checks if
++`foo' is the same file and strips the suffix.
++
+ @item dirspell
+ If set, Bash
+ attempts spelling correction on directory names during word completion 
+@@ -4892,6 +4896,14 @@ emacs shell buffer and disables line edi
+ The numeric effective user id of the current user.  This variable
+ is readonly.
+ 
++@item EXECIGNORE
++A colon-separated list of extended glob ((@pxref{Pattern Matching})
++patterns. Files with full paths matching one of these patterns are
++not considered executable for the purposes of completion and PATH
++searching, but the @code{[}, @code{[[}, and @code{test} builtins are
++not affected. Use this variable to deal with systems that set the
++executable bit on files that are not actually executable.
++
+ @item FCEDIT
+ The editor used as a default by the @option{-e} option to the @code{fc}
+ builtin command.
+--- doc/builtins.1	2008-08-12 06:24:40.000000000 -0700
++++ doc/builtins.1	2014-10-08 13:50:27.435414600 -0700
+@@ -12,6 +12,6 @@ shift, shopt, source, suspend, test, tim
+ ulimit, umask, unalias, unset, wait \- bash built-in commands, see \fBbash\fR(1)
+ .SH BASH BUILTIN COMMANDS
+ .nr zZ 1
+-.so bash.1
++.so man1/bash.1
+ .SH SEE ALSO
+ bash(1), sh(1)
+--- execute_cmd.c	2009-12-30 09:55:37.000000000 -0800
++++ execute_cmd.c	2014-10-08 13:50:27.435414600 -0700
+@@ -4004,7 +4004,7 @@ execute_function (var, words, flags, fds
+   char *debug_trap, *error_trap, *return_trap;
+ #if defined (ARRAY_VARS)
+   SHELL_VAR *funcname_v, *nfv, *bash_source_v, *bash_lineno_v;
+-  ARRAY *funcname_a, *bash_source_a, *bash_lineno_a;
++  ARRAY *funcname_a, *volatile bash_source_a, *volatile bash_lineno_a;
+ #endif
+   FUNCTION_DEF *shell_fn;
+   char *sfile, *t;
+@@ -4571,6 +4571,12 @@ execute_disk_command (words, redirects,
+ 	  hookf = find_function (NOTFOUND_HOOK);
+ 	  if (hookf == 0)
+ 	    {
++#if __CYGWIN__
++              /* Point out \r use to clueless users. The memory leak
++                 is harmless - we're about to exit. */
++              if (ansic_shouldquote (pathname))
++                pathname = ansic_quote (pathname, 0, NULL);
++#endif /* __CYGWIN__ */
+ 	      internal_error (_("%s: command not found"), pathname);
+ 	      exit (EX_NOTFOUND);	/* Posix.2 says the exit status is 127 */
+ 	    }
+@@ -4990,6 +4996,10 @@ do_piping (pipe_in, pipe_out)
+ 	dup_error (pipe_in, 0);
+       if (pipe_in > 0)
+ 	close (pipe_in);
++#if __CYGWIN__
++      /* Let stdio know that fd may have changed from text to binary.  */
++      freopen (NULL, "r", stdin);
++#endif /* __CYGWIN__ */
+     }
+   if (pipe_out != NO_PIPE)
+     {
+@@ -5005,5 +5015,12 @@ do_piping (pipe_in, pipe_out)
+ 	  if (dup2 (1, 2) < 0)
+ 	    dup_error (1, 2);
+ 	}
++#if __CYGWIN__
++      extern int sh_setlinebuf (FILE *);
++      /* Let stdio know that fd may have changed from text to binary.  */
++      freopen (NULL, "w", stdout);
++      /* Bash builtins (foolishly) rely on line-buffering. */
++      sh_setlinebuf (stdout);
++#endif /* __CYGWIN__ */
+     }
+ }
+--- findcmd.c	2009-06-05 13:25:38.000000000 -0700
++++ findcmd.c	2014-10-08 13:50:27.451015900 -0700
+@@ -43,6 +43,8 @@
+ #include "hashcmd.h"
+ #include "findcmd.h"	/* matching prototypes and declarations */
+ 
++#include <glob/strmatch.h>
++
+ extern int posixly_correct;
+ 
+ /* Static functions defined and used in this file. */
+@@ -71,6 +73,38 @@ int check_hashed_filenames;
+    containing the file of interest. */
+ int dot_found_in_search = 0;
+ 
++static struct ignorevar execignore =
++{
++  "EXECIGNORE",
++  (struct ign *)0,
++  0,
++  (char *)0,
++  (sh_iv_item_func_t *)0,
++};
++
++void
++setup_exec_ignore (char *varname)
++{
++  setup_ignore_patterns (&execignore);
++}
++
++/* Return whether we should never consider file executable
++ * even if the system tells us it is. */
++static int
++is_on_exec_blacklist (char *name)
++{
++  struct ign *p;
++  int flags = FNM_EXTMATCH | FNM_CASEFOLD;
++
++  for (p = execignore.ignores; p && p->val; p++)
++    {
++      if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
++      return (1);
++    }
++
++  return (0);
++}
++
+ /* Return some flags based on information about this file.
+    The EXISTS bit is non-zero if the file is found.
+    The EXECABLE bit is non-zero the file is executble.
+@@ -98,7 +132,7 @@ file_status (name)
+      file access mechanisms into account.  eaccess uses the effective
+      user and group IDs, not the real ones.  We could use sh_eaccess,
+      but we don't want any special treatment for /dev/fd. */
+-  if (eaccess (name, X_OK) == 0)
++  if (!is_on_exec_blacklist (name) && eaccess (name, X_OK) == 0)
+     r |= FS_EXECABLE;
+   if (eaccess (name, R_OK) == 0)
+     r |= FS_READABLE;
+--- findcmd.h	2009-01-04 11:32:29.000000000 -0800
++++ findcmd.h	2014-10-08 13:50:27.451015900 -0700
+@@ -31,5 +31,6 @@ extern char *find_user_command __P((cons
+ extern char *find_path_file __P((const char *));
+ extern char *search_for_command __P((const char *));
+ extern char *user_command_matches __P((const char *, int, int));
++extern void setup_exec_ignore __P((char *));
+ 
+ #endif /* _FINDCMD_H_ */
+--- general.c	2009-11-28 18:44:46.000000000 -0800
++++ general.c	2014-10-08 13:50:27.451015900 -0700
+@@ -43,6 +43,10 @@
+ 
+ #include <tilde/tilde.h>
+ 
++#ifdef __CYGWIN__
++#include <sys/cygwin.h>
++#endif
++
+ #if !defined (errno)
+ extern int errno;
+ #endif /* !errno */
+@@ -601,7 +605,8 @@ make_absolute (string, dot_path)
+     {
+       char pathbuf[PATH_MAX + 1];
+ 
+-      cygwin_conv_to_full_posix_path (string, pathbuf);
++      cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, string, pathbuf,
++			PATH_MAX + 1);
+       result = savestring (pathbuf);
+     }
+ #else
+--- input.c	2009-01-04 11:32:29.000000000 -0800
++++ input.c	2014-10-08 13:50:27.451015900 -0700
+@@ -43,6 +43,10 @@
+ #include "externs.h"
+ #include "quit.h"
+ 
++#if __CYGWIN__
++int igncr;
++#endif /* __CYGWIN__ */
++
+ #if !defined (errno)
+ extern int errno;
+ #endif /* !errno */
+@@ -193,6 +197,10 @@ make_buffered_stream (fd, buffer, bufsiz
+   bp->b_used = bp->b_inputp = bp->b_flag = 0;
+   if (bufsize == 1)
+     bp->b_flag |= B_UNBUFF;
++#if __CYGWIN__
++  if ((fcntl (fd, F_GETFL) & O_TEXT) != 0)
++    bp->b_flag |= B_TEXT;
++#endif /* __CYGWIN__ */
+   return (bp);
+ }
+ 
+@@ -361,11 +369,7 @@ duplicate_buffered_stream (fd1, fd2)
+ }
+ 
+ /* Return 1 if a seek on FD will succeed. */
+-#ifndef __CYGWIN__
+ #  define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
+-#else
+-#  define fd_is_seekable(fd) 0
+-#endif /* __CYGWIN__ */
+ 
+ /* Take FD, a file descriptor, and create and return a buffered stream
+    corresponding to it.  If something is wrong and the file descriptor
+@@ -474,6 +478,25 @@ b_fill_buffer (bp)
+   ssize_t nr;
+ 
+   CHECK_TERMSIG;
++#ifdef __CYGWIN__
++  /* lseek'ing on text files is problematic; lseek reports the true
++     file offset, but read collapses \r\n and returns a character
++     count.  We cannot reliably seek backwards if nr is smaller than
++     the seek offset encountered during the read, and must instead
++     treat the stream as unbuffered.  */
++  if ((bp->b_flag & (B_TEXT | B_UNBUFF)) == B_TEXT)
++    {
++      off_t offset = lseek (bp->b_fd, 0, SEEK_CUR);
++      nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
++      if (nr > 0 && nr < lseek (bp->b_fd, 0, SEEK_CUR) - offset)
++	{
++	  lseek (bp->b_fd, offset, SEEK_SET);
++	  bp->b_flag |= B_UNBUFF;
++	  nr = zread (bp->b_fd, bp->b_buffer, bp->b_size = 1);
++	}
++    }
++  else
++#endif /* __CYGWIN__ */
+   nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
+   if (nr <= 0)
+     {
+@@ -486,15 +509,6 @@ b_fill_buffer (bp)
+       return (EOF);
+     }
+ 
+-#if defined (__CYGWIN__)
+-  /* If on cygwin, translate \r\n to \n. */
+-  if (nr >= 2 && bp->b_buffer[nr - 2] == '\r' && bp->b_buffer[nr - 1] == '\n')
+-    {
+-      bp->b_buffer[nr - 2] = '\n';
+-      nr--;
+-    }
+-#endif
+-
+   bp->b_used = nr;
+   bp->b_inputp = 0;
+   return (bp->b_buffer[bp->b_inputp++] & 0xFF);
+@@ -543,6 +557,19 @@ buffered_getchar ()
+ {
+   CHECK_TERMSIG;
+ 
++#if __CYGWIN__
++  /* shopt igncr means to discard carriage returns from input stream.
++     If cr is the only character in the buffer, then recurse to pick
++     up the next character; otherwise flatten the buffer.  */
++  if (igncr)
++    {
++      int ch;
++      while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd]))
++	     == '\r')
++	;
++      return ch;
++    }
++#endif /* __CYGWIN__ */
+ #if !defined (DJGPP)
+   return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
+ #else
+--- input.h	2009-01-04 11:32:29.000000000 -0800
++++ input.h	2014-10-08 13:50:27.451015900 -0700
+@@ -48,6 +48,7 @@ enum stream_type {st_none, st_stdin, st_
+ #define B_ERROR		0x02
+ #define B_UNBUFF	0x04
+ #define B_WASBASHINPUT	0x08
++#define B_TEXT          0x10 /* Text stream, when O_BINARY is nonzero */
+ 
+ /* A buffered stream.  Like a FILE *, but with our own buffering and
+    synchronization.  Look in input.c for the implementation. */
+--- lib/sh/pathcanon.c	2008-08-12 11:01:37.000000000 -0700
++++ lib/sh/pathcanon.c	2014-10-08 13:50:27.451015900 -0700
+@@ -194,6 +194,8 @@ sh_canonpath (path, flags)
+ 	    *q++ = DIRSEP;
+ 	  while (*p && (ISDIRSEP(*p) == 0))
+ 	    *q++ = *p++;
++	}
++    }
+ 	  /* Check here for a valid directory with _path_isdir. */
+ 	  if (flags & PATH_CHECKEXISTS)
+ 	    {
+@@ -211,8 +213,7 @@ sh_canonpath (path, flags)
+ 		}
+ 	      *q = c;
+ 	    }
+-	}
+-    }
++
+ 
+   /* Empty string is really ``.'' or `/', depending on what we started with. */
+   if (q == result)
+--- lib/sh/pathphys.c	2008-08-12 11:01:23.000000000 -0700
++++ lib/sh/pathphys.c	2014-10-08 13:50:27.451015900 -0700
+@@ -35,6 +35,7 @@
+ #include <stdio.h>
+ #include <chartypes.h>
+ #include <errno.h>
++#include <stdlib.h>
+ 
+ #include "shell.h"
+ 
+@@ -76,6 +77,10 @@ sh_physpath (path, flags)
+      char *path;
+      int flags;
+ {
++#if __CYGWIN__
++  /* realpath does this right without all the hassle */
++  return realpath (path, NULL);
++#else
+   char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1];
+   char *result, *p, *q, *qsave, *qbase, *workpath;
+   int double_slash_path, linklen, nlink;
+@@ -249,6 +254,7 @@ error:
+     }
+ 
+   return (result);
++#endif /* !__CYGWIN__ */
+ }
+ 
+ char *
+--- lib/sh/tmpfile.c	2008-08-12 10:50:12.000000000 -0700
++++ lib/sh/tmpfile.c	2014-10-08 13:50:27.451015900 -0700
+@@ -40,7 +40,7 @@
+ extern int errno;
+ #endif
+ 
+-#define BASEOPENFLAGS	(O_CREAT | O_TRUNC | O_EXCL)
++#define BASEOPENFLAGS	(O_CREAT | O_TRUNC | O_EXCL | O_BINARY)
+ 
+ #define DEFAULT_TMPDIR		"."	/* bogus default, should be changed */
+ #define DEFAULT_NAMEROOT	"shtmp"
+@@ -94,7 +94,7 @@ get_tmpdir (flags)
+   if (tdir && (file_iswdir (tdir) == 0 || strlen (tdir) > PATH_MAX))
+     tdir = 0;
+ 
+-  if (tdir == 0)
++  if (tdir == 0 || !file_iswdir (tdir))
+     tdir = get_sys_tmpdir ();
+ 
+ #if defined (HAVE_PATHCONF) && defined (_PC_NAME_MAX)
+@@ -116,14 +116,15 @@ sh_mktmpname (nameroot, flags)
+   struct stat sb;
+   int r, tdlen;
+ 
+-  filename = (char *)xmalloc (PATH_MAX + 1);
++  filename = NULL;
+   tdir = get_tmpdir (flags);
+   tdlen = strlen (tdir);
+ 
+   lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
+ 
+ #ifdef USE_MKTEMP
+-  sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
++  if (asprintf (&filename, "%s/%s.XXXXXX", tdir, lroot) < 0)
++    return NULL;
+   if (mktemp (filename) == 0)
+     {
+       free (filename);
+@@ -136,7 +137,9 @@ sh_mktmpname (nameroot, flags)
+ 		(unsigned long) time ((time_t *)0) ^
+ 		(unsigned long) dollar_dollar_pid ^
+ 		(unsigned long) ((flags & MT_USERANDOM) ? get_random_number () : ntmpfiles++);
+-      sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
++      free (filename);
++      if (asprintf (&filename, "%s/%s-%lu", tdir, lroot, filenum) < 0)
++        return NULL;
+       if (tmpnamelen > 0 && tmpnamelen < 32)
+ 	filename[tdlen + 1 + tmpnamelen] = '\0';
+ #  ifdef HAVE_LSTAT
+@@ -161,14 +164,19 @@ sh_mktmpfd (nameroot, flags, namep)
+   char *filename, *tdir, *lroot;
+   int fd, tdlen;
+ 
+-  filename = (char *)xmalloc (PATH_MAX + 1);
++  filename = NULL;
+   tdir = get_tmpdir (flags);
+   tdlen = strlen (tdir);
+ 
+   lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
+ 
+ #ifdef USE_MKSTEMP
+-  sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
++  if (asprintf (&filename, "%s/%s.XXXXXX", tdir, lroot) < 0)
++    {
++      if (namep)
++        *namep = NULL;
++      return -1;
++    }
+   fd = mkstemp (filename);
+   if (fd < 0 || namep == 0)
+     {
+@@ -185,7 +193,13 @@ sh_mktmpfd (nameroot, flags, namep)
+ 		(unsigned long) time ((time_t *)0) ^
+ 		(unsigned long) dollar_dollar_pid ^
+ 		(unsigned long) ((flags & MT_USERANDOM) ? get_random_number () : ntmpfiles++);
+-      sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
++      free (filename);
++      if (asprintf (&filename, "%s/%s-%lu", tdir, lroot, filenum) < 0)
++        {
++          if (namep)
++            *namep = NULL;
++          return -1;
++        }
+       if (tmpnamelen > 0 && tmpnamelen < 32)
+ 	filename[tdlen + 1 + tmpnamelen] = '\0';
+       fd = open (filename, BASEOPENFLAGS | ((flags & MT_READWRITE) ? O_RDWR : O_WRONLY), 0600);
+--- parse.y	2014-10-08 13:45:10.394563700 -0700
++++ parse.y	2014-10-08 13:50:27.451015900 -0700
+@@ -1520,14 +1520,20 @@ yy_string_get ()
+   string = bash_input.location.string;
+ 
+   /* If the string doesn't exist, or is empty, EOF found. */
+-  if (string && *string)
++  while (string && *string)
+     {
+       c = *string++;
+       bash_input.location.string = string;
++#if __CYGWIN__
++      {
++        extern int igncr;
++        if (igncr && c == '\r')
++          continue;
++      }
++#endif /* __CYGWIN__ */
+       return (c);
+     }
+-  else
+-    return (EOF);
++  return (EOF);
+ }
+ 
+ static int
+@@ -5487,6 +5493,15 @@ report_syntax_error (message)
+      parser's complaining about by looking at current_token. */
+   if (current_token != 0 && EOF_Reached == 0 && (msg = error_token_from_token (current_token)))
+     {
++#if __CYGWIN__
++      /* Try to help clueless users. */
++      char *p = msg;
++      if (ansic_shouldquote (msg))
++        {
++          msg = ansic_quote (msg, 0, NULL);
++          free (p);
++        }
++#endif /* __CYGWIN__ */
+       parser_error (line_number, _("syntax error near unexpected token `%s'"), msg);
+       free (msg);
+ 
+--- pathexp.h	2009-01-04 11:32:40.000000000 -0800
++++ pathexp.h	2014-10-08 13:50:27.451015900 -0700
+@@ -86,7 +86,7 @@ struct ign {
+ typedef int sh_iv_item_func_t __P((struct ign *));
+ 
+ struct ignorevar {
+-  char *varname;	/* FIGNORE or GLOBIGNORE */
++  char *varname;	/* FIGNORE or GLOBIGNORE or EXECIGNORE */
+   struct ign *ignores;	/* Store the ignore strings here */
+   int num_ignores;	/* How many are there? */
+   char *last_ignoreval;	/* Last value of variable - cached for speed */
+--- redir.c	2009-09-17 07:04:18.000000000 -0700
++++ redir.c	2014-10-08 13:50:27.451015900 -0700
+@@ -437,7 +437,7 @@ here_document_to_fd (redirectee, ri)
+   /* In an attempt to avoid races, we close the first fd only after opening
+      the second. */
+   /* Make the document really temporary.  Also make it the input. */
+-  fd2 = open (filename, O_RDONLY, 0600);
++  fd2 = open (filename, O_RDONLY | O_BINARY, 0600);
+ 
+   if (fd2 < 0)
+     {
+@@ -453,14 +453,6 @@ here_document_to_fd (redirectee, ri)
+   if (unlink (filename) < 0)
+     {
+       r = errno;
+-#if defined (__CYGWIN__)
+-      /* Under CygWin 1.1.0, the unlink will fail if the file is
+-	 open. This hack will allow the previous action of silently
+-	 ignoring the error, but will still leave the file there. This
+-	 needs some kind of magic. */
+-      if (r == EACCES)
+-	return (fd2);
+-#endif /* __CYGWIN__ */
+       close (fd2);
+       free (filename);
+       errno = r;
+--- shell.c	2009-11-19 07:05:54.000000000 -0800
++++ shell.c	2014-10-08 13:50:27.466607600 -0700
+@@ -329,7 +329,10 @@ _cygwin32_check_tmp ()
+   struct stat sb;
+ 
+   if (stat ("/tmp", &sb) < 0)
+-    internal_warning (_("could not find /tmp, please create!"));
++    {
++      if (mkdir ("/tmp", S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX) != 0)
++        internal_warning (_("could not find /tmp, please create!"));
++    }
+   else
+     {
+       if (S_ISDIR (sb.st_mode) == 0)
+@@ -1471,10 +1474,6 @@ open_shell_script (script_name)
+      not match with ours. */
+   fd = move_to_high_fd (fd, 1, -1);
+ 
+-#if defined (__CYGWIN__) && defined (O_TEXT)
+-  setmode (fd, O_TEXT);
+-#endif
+-
+ #if defined (BUFFERED_INPUT)
+   default_buffered_input = fd;
+   SET_CLOSE_ON_EXEC (default_buffered_input);
+--- subst.c	2009-12-30 05:24:28.000000000 -0800
++++ subst.c	2014-10-08 13:50:27.466607600 -0700
+@@ -4921,10 +4921,6 @@ read_comsub (fd, quoted, rflag)
+   for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++)
+     skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL;
+ 
+-#ifdef __CYGWIN__
+-  setmode (fd, O_TEXT);		/* we don't want CR/LF, we want Unix-style */
+-#endif
+-
+   /* Read the output of the command through the pipe.  This may need to be
+      changed to understand multibyte characters in the future. */
+   while (1)
+@@ -4947,6 +4943,13 @@ read_comsub (fd, quoted, rflag)
+ #endif
+ 	  continue;
+ 	}
++#if __CYGWIN__
++      {
++	extern int igncr;
++	if (igncr && c == '\r')
++	  continue;
++      }
++#endif /* __CYGWIN__ */
+ 
+       /* Add the character to ISTRING, possibly after resizing it. */
+       RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE);
+@@ -5063,6 +5066,27 @@ command_substitute (string, quoted)
+       sys_error (_("cannot make pipe for command substitution"));
+       goto error_exit;
+     }
++#if __CYGWIN__
++  /* Passing a pipe through std fds can cause hangs when talking to a
++     non-cygwin child.  Move it.  */
++  if (fildes[0] < 3)
++    {
++      int fd = fcntl (fildes[0], F_DUPFD, 3);
++      close (fildes[0]);
++      fildes[0] = fd;
++    }
++  if (fildes[1] < 3)
++    {
++      int fd = fcntl (fildes[1], F_DUPFD, 3);
++      close (fildes[1]);
++      fildes[1] = fd;
++    }
++  if (fildes[0] < 0 || fildes[1] < 0)
++    {
++      sys_error (_("cannot make pipe for command substitution"));
++      goto error_exit;
++    }
++#endif /* __CYGWIN__ */
+ 
+   old_pid = last_made_pid;
+ #if defined (JOB_CONTROL)
+@@ -5130,6 +5154,12 @@ command_substitute (string, quoted)
+ 	  (fildes[0] != fileno (stdout)) &&
+ 	  (fildes[0] != fileno (stderr)))
+ 	close (fildes[0]);
++#if __CYGWIN__
++      /* Inform stdio if any text/binary changes happened. */
++      freopen (NULL, "w", stdout);
++      /* Bash builtins (foolishly) rely on line-buffering. */
++      sh_setlinebuf (stdout);
++#endif /* __CYGWIN__ */
+ 
+       /* The currently executing shell is not interactive. */
+       interactive = 0;
+--- variables.c	2014-10-08 13:45:10.285364600 -0700
++++ variables.c	2014-10-08 13:50:27.466607600 -0700
+@@ -4143,6 +4143,8 @@ static struct name_and_function special_
+   { "COMP_WORDBREAKS", sv_comp_wordbreaks },
+ #endif
+ 
++  { "EXECIGNORE", sv_execignore },
++
+   { "GLOBIGNORE", sv_globignore },
+ 
+ #if defined (HISTORY)
+@@ -4323,6 +4325,13 @@ sv_globignore (name)
+     setup_glob_ignore (name);
+ }
+ 
++/* What to do when EXECIGNORE changes. */
++void
++sv_execignore (char *name)
++{
++  setup_exec_ignore (name);
++}
++
+ #if defined (READLINE)
+ void
+ sv_comp_wordbreaks (name)
+--- variables.h	2009-08-16 13:10:15.000000000 -0700
++++ variables.h	2014-10-08 13:50:27.466607600 -0700
+@@ -351,6 +351,7 @@ extern void sv_ifs __P((char *));
+ extern void sv_path __P((char *));
+ extern void sv_mail __P((char *));
+ extern void sv_globignore __P((char *));
++extern void sv_execignore __P((char *));
+ extern void sv_ignoreeof __P((char *));
+ extern void sv_strict_posix __P((char *));
+ extern void sv_optind __P((char *));
diff --git a/pkgs/shells/bash/default.nix b/pkgs/shells/bash/default.nix
index af51ab4e76657..8774db33b5ab9 100644
--- a/pkgs/shells/bash/default.nix
+++ b/pkgs/shells/bash/default.nix
@@ -3,8 +3,21 @@
 assert interactive -> readline != null;
 
 let
-  realName = "bash-4.3";
+  version = if stdenv.isCygwin then "4.1" else "4.3";
+  realName = "bash-${version}";
+  shortName = if stdenv.isCygwin then "bash41" else "bash43";
   baseConfigureFlags = if interactive then "--with-installed-readline" else "--disable-readline";
+  sha256 = if version == "4.1" then
+      "1np1ggp1lv8idwfx3mcxl9rhadqdf4h3x4isa3dk8v9wm0j72qiz"
+    else
+      "1m14s1f61mf6bijfibcjm9y6pkyvz6gibyl8p4hxq90fisi8gimg";
+
+  basePatchFun = if version == "4.1" then
+      ./bash-4.1-patches.nix
+    else
+      ./bash-4.3-patches.nix;
+
+  extraPatches = stdenv.lib.optional stdenv.isCygwin ./bash-4.1.17-9.src.patch;
 in
 
 stdenv.mkDerivation rec {
@@ -12,7 +25,7 @@ stdenv.mkDerivation rec {
 
   src = fetchurl {
     url = "mirror://gnu/bash/${realName}.tar.gz";
-    sha256 = "1m14s1f61mf6bijfibcjm9y6pkyvz6gibyl8p4hxq90fisi8gimg";
+    inherit sha256;
   };
 
   NIX_CFLAGS_COMPILE = ''
@@ -30,11 +43,11 @@ stdenv.mkDerivation rec {
     (let
       patch = nr: sha256:
         fetchurl {
-          url = "mirror://gnu/bash/bash-4.3-patches/bash43-${nr}";
+          url = "mirror://gnu/bash/${realName}-patches/${shortName}-${nr}";
           inherit sha256;
         };
     in
-      import ./bash-4.3-patches.nix patch);
+      import basePatchFun patch) ++ extraPatches;
 
   crossAttrs = {
     configureFlags = baseConfigureFlags +