My interactive (using `read`) Bash-script application still suspends
itself after
executing second instance of a player.  I've only seen this happen in
the newest development version of Bash.

I tried to debug calls to functions in shtty.c and changes in
attributes made through them but I haven't seen anything
unusual as compared to the behavior in 4.3.  I also tried examining
stuffs in sig.c, nosig.c et al but I'm still unfamiliar with signal
handling and some conditionals are just too confusing for me.  Not
sure where to start or which significant part I should try to tackle.

The application executes player processes in an input pipe using `exec
N>` and process substitution.  No extra (sub)shell is also left
running as I use `exec` inside the process substitution command.

Code: http://sourceforge.net/p/playshell/code/ci/master/tree/
Bash version: 4.4-alpha to 20151106

# ./bash $(which playshell)
PlayShell 0.2-AfterRC1
Checking players.
Loading playlist.
Video output is disabled.
Repeating is enabled.
Shuffling is enabled.
Pause between plays is 5-10 seconds.
Restoring commands history.
: x
Playing |8| John Mayer - My Stupid Mouth.flac.
Waiting for 8 seconds.
Playing |3| John Mayer - Clarity.flac.
:
[1]+  Stopped                 ./bash $(which playshell)
/*
 * shtty.c -- abstract interface to the terminal, focusing on capabilities.
 */

/* Copyright (C) 1999 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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 3 of the License, or
   (at your option) any later version.

   Bash 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 Bash.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#include <shtty.h>
#include <stdio.h>
#include <string.h>

static TTYSTRUCT ttin, ttout;
static int ttsaved = 0;

void
shttyvariables_strvec_to_string (vec, buffer, sep)
      char** vec;
      char* buffer;
      char sep;
{
  char* p;
  char** it;
  int i = 0;
  size_t l = 0;

  p = vec[0];

  if (p)
  {
    i = strlen (p);
    memcpy (buffer, p, i);

    for (it = vec + 1; (p = *it); ++it)
    {
      buffer[i++] = sep;
      l = strlen (p);
      memcpy (buffer + i, p, l);
      i += l;
    }
  }

  buffer[i] = '\0';
}

char*
shtty_attr_to_string(ttp)
      TTYSTRUCT *ttp;
{
  char** vec;
  int i;
  size_t alloc_size;
  char* ret;

  vec = strvec_create (60);

  #define A(M, F) \
  if (ttp->M & F) { \
    vec[i++] = #F; \
    alloc_size += sizeof (#F); \
  }

  i = 0;
  alloc_size = 0;
  A(c_iflag, IGNBRK)
  A(c_iflag, BRKINT)
  A(c_iflag, IGNPAR)
  A(c_iflag, PARMRK)
  A(c_iflag, INPCK)
  A(c_iflag, ISTRIP)
  A(c_iflag, INLCR)
  A(c_iflag, IGNCR)
  A(c_iflag, ICRNL)
  A(c_iflag, IUCLC)
  A(c_iflag, IXON)
  A(c_iflag, IXANY)
  A(c_iflag, IXOFF)
  A(c_iflag, IMAXBEL)
  A(c_iflag, IUTF8)
  A(c_oflag, OPOST)
  A(c_oflag, OLCUC)
  A(c_oflag, ONLCR)
  A(c_oflag, OCRNL)
  A(c_oflag, ONOCR)
  A(c_oflag, ONLRET)
  A(c_oflag, OFILL)
  A(c_oflag, OFDEL)
  A(c_oflag, NLDLY)
  A(c_oflag, CRDLY)
  A(c_oflag, TABDLY)
  A(c_oflag, BSDLY)
  A(c_oflag, VTDLY)
  A(c_oflag, FFDLY)
  A(c_cflag, CBAUD)
  A(c_cflag, CBAUDEX)
  A(c_cflag, CSIZE)
  A(c_cflag, CSTOPB)
  A(c_cflag, CREAD)
  A(c_cflag, PARENB)
  A(c_cflag, PARODD)
  A(c_cflag, HUPCL)
  A(c_cflag, CLOCAL)
  // A(c_cflag, LOBLK)
  A(c_cflag, CIBAUD)
  A(c_cflag, CMSPAR)
  A(c_cflag, CRTSCTS)
  A(c_lflag, ICANON)
  A(c_lflag, XCASE)
  A(c_lflag, ECHO)
  A(c_lflag, ECHOE)
  A(c_lflag, ECHOK)
  A(c_lflag, ECHONL)
  A(c_lflag, ECHOCTL)
  A(c_lflag, ECHOPRT)
  A(c_lflag, ECHOKE)
  // A(c_lflag, DEFECHO)
  A(c_lflag, FLUSHO)
  A(c_lflag, NOFLSH)
  A(c_lflag, TOSTOP)
  A(c_lflag, PENDIN)
  A(c_lflag, IEXTEN)

  if (i == 0)
    ret = savestring("");
  else if (i == 1)
    ret = savestring(vec[0]);
  else {
    vec[i] = 0;
    strvec_sort (vec);
    ret = xmalloc (alloc_size);
    shttyvariables_strvec_to_string (vec, ret, ' ');
  }

  free(vec);
  #undef A
  
  return ret;
}

void
shtty_print_attr(prefix, ttp)
      char* prefix;
      TTYSTRUCT *ttp;
{
  char* str;
  str = shtty_attr_to_string (ttp);
  fprintf(stderr, "%s: %s\n", prefix, str);
  free(str);
}

int
ttgetattr(fd, ttp)
      int	fd;
      TTYSTRUCT *ttp;
{
  fprintf(stderr, "ttgetattr(%d, %p)\n", fd, ttp);
  int r;
#ifdef TERMIOS_TTY_DRIVER
  r = tcgetattr(fd, ttp);
#else
#  ifdef TERMIO_TTY_DRIVER
  r = ioctl(fd, TCGETA, ttp);
#  else
  r = ioctl(fd, TIOCGETP, ttp);
#  endif
#endif
  shtty_print_attr("ttgetattr", ttp);
  fprintf(stderr, "ttgetattr: ret: %d\n", r);
  return r;
}

int
ttsetattr(fd, ttp)
      int	fd;
      TTYSTRUCT *ttp;
{
  fprintf(stderr, "ttsetattr(%d, %p)\n", fd, ttp);
  shtty_print_attr("ttsetattr", ttp);
  int r;
#ifdef TERMIOS_TTY_DRIVER
  r = tcsetattr(fd, TCSADRAIN, ttp);
#else
#  ifdef TERMIO_TTY_DRIVER
  r = ioctl(fd, TCSETAW, ttp);
#  else
  r = ioctl(fd, TIOCSETN, ttp);
#  endif
#endif
  fprintf(stderr, "ttsetattr: ret: %d\n", r);
  return r;
}

void
ttsave()
{
  fprintf(stderr, "ttysave()\n");
  if (ttsaved) {
    fprintf(stderr, "ttysave: ttsaved\n");
  } else {
    ttgetattr (0, &ttin);
    ttgetattr (1, &ttout);
    ttsaved = 1;
  }
  fprintf(stderr, "ttysave: returns\n");
}

void
ttrestore()
{
  fprintf(stderr, "ttrestore()\n");
  if (ttsaved) {
    fprintf(stderr, "ttrestore: ttsaved\n");
    ttsetattr (0, &ttin);
    ttsetattr (1, &ttout);
    ttsaved = 0;
  }
  fprintf(stderr, "ttrestore: returns\n");
}

/* Retrieve the internally-saved attributes associated with tty fd FD. */
TTYSTRUCT *
ttattr (fd)
     int fd;
{
  fprintf(stderr, "ttattr(%d)\n", fd);
  TTYSTRUCT* r;
  if (ttsaved == 0)
    r = ((TTYSTRUCT *)0);
  else if (fd == 0)
    r = &ttin;
  else if (fd == 1)
    r = &ttout;
  else
    r = ((TTYSTRUCT *)0);
  fprintf(stderr, "ttattr: ret: %p\n", r);
  return r;
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in one-char-at-a-time mode.
 */
int
tt_setonechar(ttp)
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "tt_setonechar(%p)\n", ttp);
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)

  /* XXX - might not want this -- it disables erase and kill processing. */
  ttp->c_lflag &= ~ICANON;

  ttp->c_lflag |= ISIG;
#  ifdef IEXTEN
  ttp->c_lflag |= IEXTEN;
#  endif

  ttp->c_iflag |= ICRNL;	/* make sure we get CR->NL on input */
  ttp->c_iflag &= ~INLCR;	/* but no NL->CR */

#  ifdef OPOST
  ttp->c_oflag |= OPOST;
#  endif
#  ifdef ONLCR
  ttp->c_oflag |= ONLCR;
#  endif
#  ifdef OCRNL
  ttp->c_oflag &= ~OCRNL;
#  endif
#  ifdef ONOCR
  ttp->c_oflag &= ~ONOCR;
#  endif
#  ifdef ONLRET
  ttp->c_oflag &= ~ONLRET;
#  endif

  ttp->c_cc[VMIN] = 1;
  ttp->c_cc[VTIME] = 0;

#else

  ttp->sg_flags |= CBREAK;

#endif

  fprintf(stderr, "ttattr: tt_setonechar: ret: 0 (default)\n");
  return 0;
}

/* Set the tty associated with FD and TTP into one-character-at-a-time mode */
int
ttfd_onechar (fd, ttp)
     int fd;
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "ttfd_onechar()\n");
  int r;
  r = tt_setonechar(ttp) < 0 ? -1 : ttsetattr (fd, ttp);
  fprintf(stderr, "ttfd_onechar: ret: %d\n", r);
  return r;
}

/* Set the terminal into one-character-at-a-time mode */
int
ttonechar ()
{
  fprintf(stderr, "ttonechar()\n");
  TTYSTRUCT tt;
  int r;

  if (ttsaved == 0)
    r = -1;
  else {
    tt = ttin;
    r = ttfd_onechar(0, &tt);
  }
  fprintf(stderr, "ttonechar: ret: %d\n", r);
  return r;
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in no-echo mode.
 */
int
tt_setnoecho(ttp)
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "tt_setnoecho(%p)\n", ttp);
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
  ttp->c_lflag &= ~(ECHO|ECHOK|ECHONL);
#else
  ttp->sg_flags &= ~ECHO;
#endif

  fprintf(stderr, "tt_setnoecho: ret: 0 (default)\n");
  return 0;
}

/* Set the tty associated with FD and TTP into no-echo mode */
int
ttfd_noecho (fd, ttp)
     int fd;
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "ttfd_noecho(%d, %p)\n", fd, ttp);
  int r;
  r = tt_setnoecho(ttp) < 0 ? -1 : ttsetattr(fd, ttp);
  fprintf(stderr, "ttfd_noecho: ret: %d\n", r);
  return r;
}

/* Set the terminal into no-echo mode */
int
ttnoecho ()
{
  fprintf(stderr, "ttnoecho()\n");
  TTYSTRUCT tt;
  int r;
  if (ttsaved == 0)
    r = -1;
  else {
    tt = ttin;
    r = (ttfd_noecho (0, &tt));
  }
  fprintf(stderr, "ttnoecho: ret: %d\n", r);
  return r;
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in eight-bit mode (pass8).
 */
int
tt_seteightbit (ttp)
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "tt_seteightbit(%p)\n", ttp);
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
  ttp->c_iflag &= ~ISTRIP;
  ttp->c_cflag |= CS8;
  ttp->c_cflag &= ~PARENB;
#else
  ttp->sg_flags |= ANYP;
#endif
  fprintf(stderr, "tt_seteightbit: ret: 0 (default)\n");
  return 0;
}

/* Set the tty associated with FD and TTP into eight-bit mode */
int
ttfd_eightbit (fd, ttp)
     int fd;
     TTYSTRUCT *ttp;
{
  int r;
  fprintf(stderr, "ttfd_eightbit(%d, %p)\n", fd, ttp);
  r = tt_seteightbit (ttp) < 0 ? -1 : ttsetattr (fd, ttp);
  fprintf(stderr, "ttfd_eightbit: ret: %d\n", r);
  return r;
}

/* Set the terminal into eight-bit mode */
int
tteightbit ()
{
  TTYSTRUCT tt;
  fprintf(stderr, "tteightbit()\n");
  int r;
  if (ttsaved == 0)
    r = -1;
  else {
    tt = ttin;
    r = (ttfd_eightbit (0, &tt));
  }
  fprintf(stderr, "tteightbit: ret: %d\n", r);
  return r;
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in non-canonical input mode.
 */
int
tt_setnocanon (ttp)
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "tt_setnocanon(%p)\n", ttp);
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
  ttp->c_lflag &= ~ICANON;
#endif
  fprintf(stderr, "tt_setnocanon: ret: 0 (default)\n");
  return 0;
}

/* Set the tty associated with FD and TTP into non-canonical mode */
int
ttfd_nocanon (fd, ttp)
     int fd;
     TTYSTRUCT *ttp;
{
  int r;
  fprintf(stderr, "ttfd_nocanon(%d, %p)\n", fd, ttp);
  r = (tt_setnocanon (ttp) < 0) ? -1 : (ttsetattr (fd, ttp));
  fprintf(stderr, "ttfd_nocanon: ret: %d\n", r);
  return r;
}

/* Set the terminal into non-canonical mode */
int
ttnocanon ()
{
  fprintf(stderr, "ttnocanon()\n");
  TTYSTRUCT tt;
  int r;
  if (ttsaved == 0)
    r = -1;
  else {
    tt = ttin;
    r = (ttfd_nocanon (0, &tt));
  }
  fprintf(stderr, "ttnocanon: ret: %d\n", r);
  return r;
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in cbreak, no-echo mode.
 */
int
tt_setcbreak(ttp)
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "tt_setcbreak(%p)\n", ttp);
  int r;
  r = (tt_setonechar (ttp) < 0) ? -1 : (tt_setnoecho (ttp));
  fprintf(stderr, "tt_setcbreak: ret: %d\n", r);
  return r;
}

/* Set the tty associated with FD and TTP into cbreak (no-echo,
   one-character-at-a-time) mode */
int
ttfd_cbreak (fd, ttp)
     int fd;
     TTYSTRUCT *ttp;
{
  fprintf(stderr, "ttfd_cbreak(%d, %p)\n", fd, ttp);
  int r;
  r = (tt_setcbreak (ttp) < 0) ? -1 : (ttsetattr (fd, ttp));
  fprintf(stderr, "ttfd_cbreak: ret: %d\n", r);
  return r;
}

/* Set the terminal into cbreak (no-echo, one-character-at-a-time) mode */
int
ttcbreak ()
{
  TTYSTRUCT tt;
  fprintf(stderr, "ttcbreak()\n");
  int r;

  if (ttsaved == 0)
    r = -1;
  else {
    tt = ttin;
    r = (ttfd_cbreak (0, &tt));
  }
  fprintf(stderr, "ttcbreak: ret: %d\n", r);
}

Reply via email to