<commit_before>/****************************************************************************
 * stermcom.cc
 *
 *   Copyright (c) 2015 <NAME>
 *   This software is released under the MIT License.
 ****************************************************************************/
#include <fcntl.h>
#include <libgen.h>
#include <sys/select.h>
#include <unistd.h>

#include <cassert>
#include <cerrno>
#include <csignal>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <list>

#include "common_type.h"
#include "debug.h"
#include "file_descriptor.h"
#include "signal_settings.h"
#include "terminal_interface.h"

namespace {

using status_t = common::status_t;

volatile sig_atomic_t g_should_continue = 1;

struct Arguments {
  uint32_t baud_rate;
};

status_t setStdinToNonblock() {
  auto flags = fcntl(STDIN_FILENO, F_GETFL, 0);
  if (flags == -1) return status_t::kFailure;

  auto ret = fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
  if (ret == -1) return status_t::kFailure;

  return status_t::kSuccess;
}

status_t reopenStdin() {
  util::FileDescriptor fd("/dev/tty", O_RDONLY);
  if (fd.IsSuccess() == false) return status_t::kFailure;

  // Assign /dev/tty to STDIN_FILENO (/dev/tty is also being assigned to fd)
  dup2(fd, STDIN_FILENO);

  // fd is closed automatically
  return status_t::kSuccess;
}

status_t mainLoop(const int32_t &tty_fd, const Arguments &args) {
  fd_set fds_r, fds_w;
  uint8_t one_char;
  std::list<uint8_t> string_buffer{};
  ssize_t rw_size;

  // Support piping and redirection
  if (setStdinToNonblock() == status_t::kFailure) return status_t::kFailure;
  while (true) {
    rw_size = read(STDIN_FILENO, &one_char, 1);
    if (rw_size > 0) {
      string_buffer.push_back(one_char);
    } else {
      break;
    }
  }
  if (reopenStdin() == status_t::kFailure) return status_t::kFailure;

  util::TerminalInterface stdin_term(STDIN_FILENO), tty_term(tty_fd);

  if (stdin_term.SetRawMode() == status_t::kFailure)
    return status_t::kFailure;
  if (tty_term.SetRawMode() == status_t::kFailure)
    return status_t::kFailure;
  if (tty_term.SetBaudRate(args.baud_rate, util::direction_t::kOut) ==
      status_t::kFailure)
    return status_t::kFailure;
  if (tty_term.SetBaudRate(0, util::direction_t::kIn) == status_t::kFailure)
    return status_t::kFailure;

  assert(tty_fd > STDIN_FILENO && "Assertion for select()");

  while (g_should_continue) {
    FD_ZERO(&fds_r);
    FD_ZERO(&fds_r);

    FD_SET(STDIN_FILENO, &fds_r);
    FD_SET(tty_fd, &fds_r);
    FD_SET(tty_fd, &fds_w);

    errno = 0;
    auto ret = select(tty_fd + 1, &fds_r, &fds_w, nullptr, nullptr);
    if (ret == -1) {
      if (errno == EINTR) {
        DEBUG_PRINTF("Signal was caught when select() is waiting");
      } else {
        printf("Error\n");
        return status_t::kFailure;
      }
      break;
    }

    if (FD_ISSET(STDIN_FILENO, &fds_r)) {
      rw_size = read(STDIN_FILENO, &one_char, 1);
      if (one_char == 0x18) break;  // 0x18: Ctrl-x
      if (rw_size > 0) string_buffer.push_back(one_char);
    }
    if (FD_ISSET(tty_fd, &fds_w)) {
      if (!string_buffer.empty()) {
        rw_size = write(tty_fd, &string_buffer.front(), 1);
        if (rw_size > 0) string_buffer.pop_front();
      }
    }
    if (FD_ISSET(tty_fd, &fds_r)) {
      uint8_t tty_read_buffer;
      rw_size = read(tty_fd, &tty_read_buffer, 1);
      if (rw_size > 0) write(STDOUT_FILENO, &tty_read_buffer, 1);
    }<commit_msg>Exit when the terminal is closed<commit_after>/****************************************************************************
 * stermcom.cc
 *
 *   Copyright (c) 2015 <NAME>
 *   This software is released under the MIT License.
 ****************************************************************************/
#include <fcntl.h>
#include <libgen.h>
#include <sys/select.h>
#include <unistd.h>

#include <cassert>
#include <cerrno>
#include <csignal>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <list>

#include "common_type.h"
#include "debug.h"
#include "file_descriptor.h"
#include "signal_settings.h"
#include "terminal_interface.h"

namespace {

using status_t = common::status_t;

volatile sig_atomic_t g_should_continue = 1;

struct Arguments {
  uint32_t baud_rate;
};

status_t setStdinToNonblock() {
  auto flags = fcntl(STDIN_FILENO, F_GETFL, 0);
  if (flags == -1) return status_t::kFailure;

  auto ret = fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
  if (ret == -1) return status_t::kFailure;

  return status_t::kSuccess;
}

status_t reopenStdin() {
  util::FileDescriptor fd("/dev/tty", O_RDONLY);
  if (fd.IsSuccess() == false) return status_t::kFailure;

  // Assign /dev/tty to STDIN_FILENO (/dev/tty is also being assigned to fd)
  dup2(fd, STDIN_FILENO);

  // fd is closed automatically
  return status_t::kSuccess;
}

status_t mainLoop(const int32_t &tty_fd, const Arguments &args) {
  fd_set fds_r, fds_w;
  uint8_t one_char;
  std::list<uint8_t> string_buffer{};
  ssize_t rw_size;

  // Support piping and redirection
  if (setStdinToNonblock() == status_t::kFailure) return status_t::kFailure;
  while (true) {
    rw_size = read(STDIN_FILENO, &one_char, 1);
    if (rw_size > 0) {
      string_buffer.push_back(one_char);
    } else {
      break;
    }
  }
  if (reopenStdin() == status_t::kFailure) return status_t::kFailure;

  util::TerminalInterface stdin_term(STDIN_FILENO), tty_term(tty_fd);

  if (stdin_term.SetRawMode() == status_t::kFailure)
    return status_t::kFailure;
  if (tty_term.SetRawMode() == status_t::kFailure)
    return status_t::kFailure;
  if (tty_term.SetBaudRate(args.baud_rate, util::direction_t::kOut) ==
      status_t::kFailure)
    return status_t::kFailure;
  if (tty_term.SetBaudRate(0, util::direction_t::kIn) == status_t::kFailure)
    return status_t::kFailure;

  assert(tty_fd > STDIN_FILENO && "Assertion for select()");

  while (g_should_continue) {
    FD_ZERO(&fds_r);
    FD_ZERO(&fds_r);

    FD_SET(STDIN_FILENO, &fds_r);
    FD_SET(tty_fd, &fds_r);
    FD_SET(tty_fd, &fds_w);

    errno = 0;
    auto ret = select(tty_fd + 1, &fds_r, &fds_w, nullptr, nullptr);
    if (ret == -1) {
      if (errno == EINTR) {
        DEBUG_PRINTF("Signal was caught when select() is waiting");
      } else {
        printf("Error\n");
        return status_t::kFailure;
      }
      break;
    }

    if (FD_ISSET(STDIN_FILENO, &fds_r)) {
      rw_size = read(STDIN_FILENO, &one_char, 1);
      if (one_char == 0x18) break;  // 0x18: Ctrl-x
      if (rw_size > 0) string_buffer.push_back(one_char);
    }
    if (FD_ISSET(tty_fd, &fds_w)) {
      if (!string_buffer.empty()) {
        rw_size = write(tty_fd, &string_buffer.front(), 1);
        if (rw_size > 0) string_buffer.pop_front();
      }
    }
    if (FD_ISSET(tty_fd, &fds_r)) {
      uint8_t tty_read_buffer;
      rw_size = read(tty_fd, &tty_read_buffer, 1);
      if (rw_size == 0) {
        printf("The terminal is closed\n");
        break;
      }
      if (rw_size > 0) write(STDOUT_FILENO, &tty_read_buffer, 1);
    }
{'fraction_non_alphanumeric': 0.17647895335608646, 'fraction_numerical': 0.0100967007963595, 'mean_word_length': 3.952816901408451, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4062273', 'n_tokens_mistral': 2766, 'n_tokens_neox': 2627, 'n_words': 547}