
/* Copyright 1994 by Michael Borland and Argonne National Laboratory,
 * all rights reserved.
 */
/* program: replace
 * purpose: replace strings in a file
 *
 * Michael Borland, 1990
 */
#include "mdb.h"
#include "scan.h"

#define SET_ORIGINAL 0
#define SET_REPLACEMENT 1
#define SET_FILENAME_REPLACEMENT 2
#define SET_CONFIRM 3
#define SET_NONDEC_CRT 4
#define SET_VERBOSE 5
#define SET_STRINGS 6
#define SET_STDIO 7
#define SET_FILTER 8
#define N_OPTIONS 9

char *option[N_OPTIONS] = {
  "original",
  "replacement",
  "filename_replacement",
  "confirm",
  "nondec_crt",
  "verbose",
  "strings",
  "stdio",
  "filter",
};

#define USAGE "replace {input [output] | -stdio | -strings=string[,string...]}\n\
 [-confirm] [-nondec_crt] [-verbose] [-filter=string[,string...]]\n\
{   -original=string[,string...] -replacement=string[,string...]\n\
      | -filename_replacement\n\
      }\n\n\
  Program by Michael Borland.  ("__DATE__")."

int replace_string1(char *t, char *s, char *orig, char *repl, int *confirm, int dec_crt);
long passesFilters(char *s, char **filter, long filters);
int flashing_wait = 0;

#include <signal.h>
#ifndef _WIN32
// void tt_detach_and_exit(int);
#  include <curses.h>
static WINDOW *main_window;
// int tt_attach(void);
// char rawread(void);
// void tt_detach(void);
#endif /*UNIX*/

#ifndef _WIN32
/* Below are the contents of what used to be mdblib/rawread.c */

void tt_detach_and_exit(int dummy) {
  echo();
  nocbreak();
  endwin();
  exit(1);
}

int tt_attach(void) {
  signal(SIGINT, tt_detach_and_exit);

  main_window = initscr();
  cbreak();
  noecho();
  return (1);
}

char rawread() {
  return (getch());
}

void tt_detach() {
  echo();
  nocbreak();
  endwin();
}

#endif /* UNIX */

int main(int argc, char **argv) {
  char **orig, **repl, *input, *output, *ptr, **filter;
  char **string_list;
  int n_pairs, i_pair, filters;
  char s[1024], t[1024];
  FILE *fpi = NULL, *fpo = NULL;
  SCANNED_ARG *s_arg;
  int i_arg, *count, dec_crt, n_strings = 0, use_stdio;
  char *input_fn, *output_fn;
  int replace_filename, confirm, tmp_file = 0, detach_tt, verbose;

  argc = scanargs(&s_arg, argc, argv);
  if (argc < 2 || argc > (3 + N_OPTIONS))
    bomb("too few or too many arguments", USAGE);

  input = output = NULL;
  orig = repl = filter = NULL;
  n_pairs = replace_filename = confirm = verbose = filters = 0;
  dec_crt = 1;
  string_list = NULL;
  use_stdio = 0;

  for (i_arg = 1; i_arg < argc; i_arg++) {
    if (s_arg[i_arg].arg_type == OPTION) {
      switch (match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
      case SET_ORIGINAL:
        if ((i_pair = s_arg[i_arg].n_items - 1) < 1 ||
            (n_pairs && i_pair != n_pairs) ||
            !(orig = s_arg[i_arg].list + 1))
          bomb("invalid -original syntax", USAGE);
        n_pairs = i_pair;
        break;
      case SET_REPLACEMENT:
        if ((i_pair = s_arg[i_arg].n_items - 1) < 1 ||
            (n_pairs && i_pair != n_pairs) ||
            !(repl = s_arg[i_arg].list + 1))
          bomb("invalid -replacement syntax", USAGE);
        n_pairs = i_pair;
        break;
      case SET_FILENAME_REPLACEMENT:
        replace_filename = 1;
        break;
      case SET_CONFIRM:
#ifdef _WIN32
        fprintf(stderr, "confirm option not available on Windows\n");
        return (1);
#endif
        confirm = 1;
        break;
      case SET_NONDEC_CRT:
        dec_crt = 0;
        break;
      case SET_VERBOSE:
        verbose = 1;
        break;
      case SET_STRINGS:
        if (s_arg[i_arg].n_items < 2)
          bomb("invalid -strings syntax", USAGE);
        string_list = s_arg[i_arg].list + 1;
        n_strings = s_arg[i_arg].n_items - 1;
        break;
      case SET_STDIO:
        use_stdio = 1;
        break;
      case SET_FILTER:
        if (s_arg[i_arg].n_items < 2)
          bomb("invalid -filter syntax", USAGE);
        filter = s_arg[i_arg].list + 1;
        filters = s_arg[i_arg].n_items - 1;
        break;
      default:
        bomb("unknown option given", USAGE);
        break;
      }
    } else {
      if (!input)
        fpi = fopen_e(input = s_arg[i_arg].list[0], "r", 0);
      else if (!output)
        fpo = fopen_e(output = s_arg[i_arg].list[0], "w", FOPEN_SAVE_IF_EXISTS);
      else
        bomb("too many filenames listed", USAGE);
    }
  }

  if (!((repl && orig) || replace_filename))
    bomb("insufficient information supplied", USAGE);
  if (!input && !string_list && !use_stdio)
    bomb("unsufficient information supplied", USAGE);

  count = tmalloc(sizeof(int) * (n_pairs + (replace_filename ? 1 : 0)));
  for (i_pair = 0; i_pair < n_pairs; i_pair++) {
    interpret_escapes(orig[i_pair]);
    interpret_escapes(repl[i_pair]);
    count[i_pair] = 0;
  }

  if (string_list) {
    int i_string;
    for (i_string = 0; i_string < n_strings; i_string++) {
      strcpy_ss(s, string_list[i_string]);
      for (i_pair = 0; i_pair < n_pairs; i_pair++) {
        count[i_pair] += replace_string1(t, s, orig[i_pair], repl[i_pair], &confirm, dec_crt);
        strcpy_ss(s, t);
      }
      puts(t);
    }
    exit(0);
  }

  if (input) {
    tmp_file = 0;

    if (!output) {
      output = tmpname(NULL);
      tmp_file = 1;
      fpo = fopen_e(output, "w", 0);
    }

    clean_filename(input);
    cp_str(&input_fn, input);
    if ((ptr = strchr(input, '.')))
      *ptr = 0;

    if (output) {
      cp_str(&output_fn, output);
      clean_filename(output);
      if ((ptr = strchr(output, '.')))
        *ptr = 0;
    } else
      output_fn = input_fn;
    if (replace_filename) {
      orig = trealloc(orig, sizeof(*orig) * (n_pairs + 1));
      repl = trealloc(repl, sizeof(*orig) * (n_pairs + 1));
      cp_str(orig + n_pairs, input);
      cp_str(repl + n_pairs, output);
      n_pairs++;
    }
  } else {
    fpi = stdin;
    fpo = stdout;
    confirm = verbose = 0;
  }

  detach_tt = 0;
#ifndef _WIN32
  if (confirm) {
    tt_attach();
    detach_tt = 1;
    puts("keys to press in confirm mode:\n    0, n, N   --   don't replace");
    puts("    1, y, Y   --   do replace\n    r, R      --   replace in rest of file without queries");
    puts("    s, S      --   no further replacements in file");
  }
#endif

  while (fgets(s, 1024, fpi)) {
    if (confirm >= 0) {
      if (!filters || passesFilters(s, filter, filters))
        for (i_pair = 0; i_pair < n_pairs; i_pair++) {
          count[i_pair] += replace_string1(t, s, orig[i_pair], repl[i_pair], &confirm, dec_crt);
          strcpy_ss(s, t);
        }
    }
    if (fputs(s, fpo) == EOF) {
      puts("error writing to file--program aborted");
#ifndef _WIN32
      tt_detach();
#endif
      exit(1);
    }
  }

  if (verbose)
    for (i_pair = 0; i_pair < n_pairs; i_pair++)
      printf("%d replacements of \"%s\" by \"%s\"\n",
             count[i_pair], orig[i_pair], repl[i_pair]);

#ifndef _WIN32
  if (detach_tt)
    tt_detach();
#endif

  if (tmp_file) {
    sprintf(s, "%s~", input_fn);
    rename(input_fn, s);
    rename(output_fn, input_fn);
  }
  return (0);
}

int replace_string1(char *t, char *s, char *orig, char *repl, int *confirm, int dec_crt) {
  char *ptr0, *ptr1;
  int count, lorig;
  char temp;

  ptr0 = s;
  t[0] = 0;
  count = 0;
  lorig = strlen(orig);

  while (((ptr1 = ptr0) && orig[0] == '^' && (orig[1] == 0 || strncmp(ptr0, orig + 1, strlen(orig + 1)) == 0)) ||
         (orig[0] != '^' && (ptr1 = str_in(ptr0, orig))) ||
         orig[lorig - 1] == '$') {
    if (orig[lorig - 1] == '$') {
      if (((long)strlen(ptr0)) <= (lorig - 1))
        break;
      if ((ptr1 = ptr0 + strlen(ptr0) - (lorig - 1))) {
        if (strncmp(ptr1, orig, lorig - 1) != 0)
          break;
      } else if (lorig != 1)
        break;
    }
#ifndef _WIN32
    if (*confirm > 0) {
      temp = *ptr1;
      *ptr1 = 0;
      if (dec_crt) {
        if (orig[0] == '^')
          printf("replace?: %s%s\033[0;1m%s\033[0m%s",
                 t, ptr0, orig, ptr1 + lorig - 1);
        else if (orig[lorig - 1] == '$')
          printf("replace?: %s%s\033[0;1m%s\033[0m%s",
                 t, ptr0, orig, ptr1 + lorig - 1);
        else
          printf("replace?: %s%s\033[0;1m%s\033[0m%s",
                 t, ptr0, orig, ptr1 + lorig);
      } else {
        printf("replace?: %s%s|>%s<|%s", t, ptr0, orig, ptr1 + lorig);
      }
      flashing_wait = 0;
      *ptr1 = temp;
      switch (rawread()) {
      case 's':
      case 'S':
        *confirm = -1;
        strcat(t, ptr0);
        return (count);
      case '0':
      case 'n':
      case 'N':
        if (orig[0] == '^') {
          strcpy_ss(t, s);
          return (0);
        }
        if (orig[lorig - 1] == '$') {
          strcpy_ss(t, s);
          return (0);
        }
        strncat(t, ptr0, ptr1 - ptr0 + lorig);
        ptr0 = ptr1 + lorig;
        continue;
      case 'r':
      case 'R':
        *confirm = 0;
        break;
      case '1':
      case 'y':
      case 'Y':
      default:
        break;
      }
    }
#endif
    if (orig[0] == '^') {
      strcat(t, repl);
      strcat(t, ptr0 + lorig - 1);
      return (1);
    }
    if (orig[lorig - 1] == '$') {
      *ptr1 = 0;
      strcat(t, ptr0);
      strcat(t, repl);
      return (1);
    }
    count++;
    *ptr1 = 0;
    strcat(t, ptr0);
    strcat(t, repl);
    ptr0 = ptr1 + lorig;
  }
  if (strlen(ptr0))
    ;
  strcat(t, ptr0);
  return (count);
}

long passesFilters(char *s, char **filter, long filters) {
  while (filters--) {
    if (!strstr(s, *filter++))
      return 0;
  }
  return 1;
}
