#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include "dosdebug.h"
#include "dosmalloc.h"


#if (DOS_PIPE_DEBUG - 1) == 0
#define CHECK_ENV_VALUE(s)    ((s) && ((s)[0] == 'Y' || (s)[0] == 'y') && ((s)[1] == 'E' || (s)[1] == 'e') && ((s)[2] == 'S' || (s)[2] == 's'))
#define PIPE_LOG_BUFFER_SIZE  (256 * 1024)

#define PIPE_LOG_FILENAME     "bshpipe0.log"

static bool delete_pipe_log_file = true;
static int pipe_log_buffer_size = PIPE_LOG_BUFFER_SIZE;


static void
print_text(char *text_line, const int line_length)
{
  if (EOF != line_length)
  {
    const int consumed = pipe_log_buffer_ptr - pipe_log_buffer;
    if (pipe_log_buffer_size <= line_length + consumed)
    {
      FILE *pipe_log_file = fopen(pipe_log_filename, "a");
      if (!pipe_log_file)
      {
        xfree(text_line);
        return;
      }
      else
      {
        *pipe_log_buffer_ptr = '\0';
        fprintf(pipe_log_file, "%s", pipe_log_buffer);
        fclose(pipe_log_file);
        pipe_log_buffer_ptr = pipe_log_buffer;
      }
    }
    if (pipe_log_buffer_size <= line_length)
    {
      pipe_log_buffer_ptr = pipe_log_buffer;
      pipe_log_buffer = xrealloc(pipe_log_buffer, line_length + 1);
      if (pipe_log_buffer)
        pipe_log_buffer_ptr = pipe_log_buffer;
      else
      {
        pipe_log_buffer = pipe_log_buffer_ptr;
        xfree(text_line);
        return;
      }
    }
    memcpy(pipe_log_buffer_ptr, text_line, line_length);
    pipe_log_buffer_ptr += line_length;
    xfree(text_line);
  }
}

void
log_pipe_creation(const int index, const struct pipe_t *pipe_line)
{
  if (log_pipe_files)
  {
    char *log_line;
    int line_length;
    if (previous_script_name != bash_script_name)
    {
      const char format_string[] = "\n\n"
                                   "================================================================================\n"
                                   "                    script: %s\n"
                                   "================================================================================\n"
                                   "\n\n";
      line_length  = sizeof(format_string);
      line_length += strlen(bash_script_name);
      log_line = xmalloc(line_length + 1);
      if (log_line)
      {
        line_length = sprintf(log_line, format_string, bash_script_name);
        print_text(log_line, line_length);
      }
      if (previous_script_name)
        xfree(previous_script_name);
      previous_script_name = bash_script_name;
    }

    if (previous_script_command_line != bash_script_command_line)
    {
      const char format_string[] = "\n"
                                   "--------------------------------------------------------------------------------\n"
                                   "                    script line%s: %s\n"
                                   "--------------------------------------------------------------------------------\n"
                                   "\n";
#define SIZE 32
      char line_number[SIZE] = "";
      if (bash_script_line_number > -1)
        snprintf(line_number, SIZE, " %d", bash_script_line_number);
      bash_script_line_number = -1;
#undef SIZE
      line_length  = sizeof(format_string);
      line_length += strlen(line_number);
      line_length += strlen(bash_script_command_line);
      log_line = xmalloc(line_length + 1);
      if (log_line)
      {
        line_length = sprintf(log_line, format_string, line_number, bash_script_command_line);
        print_text(log_line, line_length);
      }
      if (previous_script_command_line)
        xfree(previous_script_command_line);
      previous_script_command_line = bash_script_command_line;
    }

    {
      const char format_string[] = "creating pipe #%d: %s\n"
                                   "  read  fd: %d\n"
                                   "  write fd: %d\n"
                                   "  bckup fd: %d\n";
      line_length  = sizeof(format_string);
      line_length += 3;
      line_length += strlen(pipe_line->name);
      line_length += 4 + 4 + 4;
      log_line = xmalloc(line_length + 1);
      if (log_line)
      {
        line_length = sprintf(log_line, format_string,
                                        index, pipe_line->name,
                                        pipe_line->fds[READ_END],
                                        pipe_line->fds[WRITE_END],
                                        pipe_line->fds[BACK_UP]);
        print_text(log_line, line_length);
      }
    }
  }

  delete_pipe_log_file = false;
}

void
log_pipe_operation(const int index, const char *operation, const char *status, const struct pipe_t *pipe_line)
{
  if (log_pipe_files)
  {
    char *log_line;
    int line_length;
    const char format_string[] = "%s of pipe #%d: %s\n"
                                 "  read  fd: %d\n"
                                 "  write fd: %d\n"
                                 "  bckup fd: %d\n"
                                 "  status: %s\n";
    line_length  = sizeof(format_string);
    line_length += strlen(operation);
    line_length += 3;
    line_length += strlen(pipe_line->name);
    line_length += 4 + 4 + 4;
    line_length += strlen(status);
    log_line = xmalloc(line_length + 1);
    if (log_line)
    {
      line_length = sprintf(log_line, format_string,
                                      operation, index,
                                      pipe_line->name,
                                      pipe_line->fds[READ_END],
                                      pipe_line->fds[WRITE_END],
                                      pipe_line->fds[BACK_UP],
                                      status);
      print_text(log_line, line_length);
    }
  }
}

void
log_pipe_dump_used_fd_array(const bool *const fd_in_use)
{
  if (log_pipe_files)
  {
    int i;
    for (i = 20; i < OPEN_MAX; i++)
    {
      if (fd_in_use[i] == true)
      {
        char *log_line;
        int line_length;
        const char format_string[] = "    fd_in_use[%03d] = TRUE\n";
        log_line = xmalloc(sizeof(format_string) + 1);
        if (log_line)
        {
          line_length = sprintf(log_line, format_string, i);
          print_text(log_line, line_length);
        }
      }
    }
  }
}

void
create_log_pipe_files(void)
{
  char *env_variable;

  /*  For debuging purposes only.  */
  env_variable = getenv("BASH_KEEP_PIPE_FILES");
  keep_pipe_files = CHECK_ENV_VALUE(env_variable) ? true : false;
  env_variable = getenv("BASH_LOG_PIPE_FILES");
  log_pipe_files = CHECK_ENV_VALUE(env_variable) ? true : false;

  if (log_pipe_files)
  {
    int length;
    char *filename, *tmpdir = "/dev/env/TMPDIR";
    if (access(tmpdir, D_OK))
    {
      tmpdir = "/dev/env/TMP";
      if (access(tmpdir, D_OK))
      {
        tmpdir = "/dev/env/TEMP";
        if (access(tmpdir, D_OK))
        {
          tmpdir = P_tmpdir;
          if (access(tmpdir, D_OK))
            tmpdir = ".";
        }
      }
    }
    filename = xmalloc(length = strlen(tmpdir) + sizeof("/"PIPE_LOG_FILENAME));
    if (!filename)
      log_pipe_files = false;
    else
    {
      FILE *pipe_log_file;
      char *numeric_tail = filename + length - sizeof("0.log");
      char *dot = numeric_tail + 1;
      int rc;

      sprintf(filename, "%s/%s", tmpdir, PIPE_LOG_FILENAME);
      if (0 == (rc = access(filename, F_OK)))
      {
        int i;
        for (i = 0; 0 == rc && i < 16777216; numeric_tail--)
        {
          do {
            sprintf(numeric_tail, "%x", ++i);
            *dot = '.';
          } while (0 == (rc = access(filename, F_OK)) && i % 15);
        }

        if (i == 16777216)
        {
          xfree(filename);
          log_pipe_files = false;
          return;
        }
      }
      pipe_log_file = fopen(filename, "w");
      if (!pipe_log_file)
      {
        xfree(filename);
        log_pipe_files = false;
        return;
      }
      fclose(pipe_log_file);

      pipe_log_buffer = xmalloc(PIPE_LOG_BUFFER_SIZE);
      if (!pipe_log_buffer)
        log_pipe_files = false;
      else
      {
        pipe_log_buffer_ptr = pipe_log_buffer;
        pipe_log_filename = filename;
        log_pipe_files = true;
      }
    }
  }
}

void
close_log_pipe_files(void)
{
  if (delete_pipe_log_file)
    remove(pipe_log_filename);
  else if (log_pipe_files && pipe_log_filename)
  {
    FILE *pipe_log_file = fopen(pipe_log_filename, "a");
    if (pipe_log_file)
    {
      *pipe_log_buffer_ptr = '\0';
      fprintf(pipe_log_file, "%s", pipe_log_buffer);
      fclose(pipe_log_file);
    }
    xfree(pipe_log_buffer);
  }
  xfree(pipe_log_filename);
}
#endif /* DOS_PIPE_DEBUG */
