Logo Search packages:      
Sourcecode: ldapvi version File versions  Download package

misc.c

/* -*- show-trailing-whitespace: t; indent-tabs: t -*-
 * Copyright (c) 2003,2004,2005,2006 David Lichteblau
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <curses.h>
#include <term.h>
#include "common.h"
#include <readline/readline.h>
#include <readline/history.h>

int
carray_cmp(GArray *a, GArray *b)
{
      int d = memcmp(a->data, b->data, MIN(a->len, b->len));
      if (d) return d;
      if (a->len < b->len)
            return -1;
      else if (a->len == b->len)
            return 0;
      else
            return 1;
}

int
carray_ptr_cmp(const void *aa, const void *bb)
{
      GArray *a = *((GArray **) aa);
      GArray *b = *((GArray **) bb);
      return carray_cmp(a ,b);
}

void
fdcp(int fdsrc, int fddst)
{
      int n;
      char buf[4096];

      do {
            if ( (n = read(fdsrc, buf, sizeof(buf))) == -1) syserr();
            if (write(fddst, buf, n) != n) syserr();
      } while (n);
}

void
cp(char *src, char *dst, off_t skip, int append)
{
      int fdsrc, fddst;
      int flags = append ? O_WRONLY | O_APPEND : O_CREAT | O_EXCL | O_WRONLY;

      if ( (fdsrc = open(src, O_RDONLY)) == -1) syserr();
      if (lseek(fdsrc, skip, SEEK_SET) == -1) syserr();
      if ( (fddst = open(dst, flags, 0600)) == -1) syserr();
      fdcp(fdsrc, fddst);
      if (close(fdsrc) == -1) syserr();
      if (close(fddst) == -1) syserr();
}

void
fcopy(FILE *src, FILE *dst)
{
      int n;
      char buf[4096];

      for (;;) {
            if ( (n = fread(buf, 1, sizeof(buf), src)) == 0) {
                  if (feof(src)) break;
                  syserr();
            }
            if (fwrite(buf, 1, n, dst) != n) syserr();
      }
}

static void
print_charbag(char *charbag)
{
      int i;
      putchar('[');
      for (i = 0; charbag[i]; i++) {
            char c = charbag[i];
            if (c > 32)
                  putchar(c);
      }
      putchar(']');
}


char
choose(char *prompt, char *charbag, char *help)
{
      struct termios term;
      int c;

      if (tcgetattr(0, &term) == -1) syserr();
      term.c_lflag &= ~ICANON;
        term.c_cc[VMIN] = 1;
        term.c_cc[VTIME] = 0;
      for (;;) {
            if (tcsetattr(0, TCSANOW, &term) == -1) syserr();
            fputs(prompt, stdout);
            putchar(' ');
            print_charbag(charbag);
            putchar(' ');
            if (strchr(charbag, c = getchar()))
                  break;
            fputs("\nPlease enter one of ", stdout);
            print_charbag(charbag);
            putchar('\n');
            if (help) printf("  %s", help);
            putchar('\n');
      }
      term.c_lflag |= ICANON;
      if (tcsetattr(0, TCSANOW, &term) == -1) syserr();
      putchar('\n');
      return c;
}

static long
line_number(char *pathname, long pos)
{
      FILE *f;
      long line = 1;
      int c;

      if ( !(f = fopen(pathname, "r+"))) syserr();
      while (pos > 0) {
            switch ( c = getc_unlocked(f)) {
            case EOF:
                  goto done;
            case '\n':
                  if ( (c = getc_unlocked(f)) != EOF) {
                        ungetc(c, f);
                        line++;
                  }
                  /* fall through */
            default:
                  pos--;
            }
      }
done:
      if (fclose(f) == EOF) syserr();
      return line;
}

void
edit(char *pathname, long line)
{
      int childpid;
      int status;
      char *vi;

      vi = getenv("VISUAL");
      if (!vi) vi = getenv("EDITOR");
      if (!vi) vi = "vi";

      switch ( (childpid = fork())) {
      case -1:
            syserr();
      case 0:
            if (line > 0) {
                  char buf[20];
                  snprintf(buf, 20, "+%ld", line);
                  execlp(vi, vi, buf, pathname, 0);
            } else
                  execlp(vi, vi, pathname, 0);
            syserr();
      }

      if (waitpid(childpid, &status, 0) == -1) syserr();
      if (!WIFEXITED(status) || WEXITSTATUS(status))
            yourfault("editor died");
}

void
edit_pos(char *pathname, long pos)
{
      edit(pathname, pos > 0 ? line_number(pathname, pos) : -1);
}

static int
invalidp(char *ti)
{
      return ti == 0 || ti == (char *) -1;
}

void
view(char *pathname)
{
      int childpid;
      int status;
      char *pg;
      char *clear = tigetstr("clear");

      pg = getenv("PAGER");
      if (!pg) pg = "less";

      if (!invalidp(clear))
            putp(clear);

      switch ( (childpid = fork())) {
      case -1:
            syserr();
      case 0:
            execlp(pg, pg, pathname, 0);
            syserr();
      }

      if (waitpid(childpid, &status, 0) == -1) syserr();
      if (!WIFEXITED(status) || WEXITSTATUS(status))
            puts("pager died");
}

int
pipeview(int *fd)
{
      int childpid;
      char *pg;
      char *clear = tigetstr("clear");
      int fds[2];

      pg = getenv("PAGER");
      if (!pg) pg = "less";

      if (!invalidp(clear))
            putp(clear);

      if (pipe(fds) == -1) syserr();

      switch ( (childpid = fork())) {
      case -1:
            syserr();
      case 0:
            close(fds[1]);
            dup2(fds[0], 0);
            close(fds[0]);
            execlp(pg, pg, 0);
            syserr();
      }

      close(fds[0]);
      *fd = fds[1];
      return childpid;
}

void
pipeview_wait(int childpid)
{
      int status;

      if (waitpid(childpid, &status, 0) == -1) syserr();
      if (!WIFEXITED(status) || WEXITSTATUS(status))
            puts("pager died");
}

char *
home_filename(char *name)
{
      char *home = getenv("HOME");
      int n;
      char *result;

      if (!home) {
            fputs("Warning: You don't have a $HOME.\n", stderr);
            return 0;
      }

      n = strlen(home);
      result = xalloc(n + 1 + strlen(name) + 1);
      strcpy(result, home);
      result[n] = '/';
      strcpy(result + n + 1, name);
      return result;
}


static char *
history_filename()
{
      return home_filename(".ldapvi_history");
}

void
read_ldapvi_history()
{
      char *filename = history_filename();
      using_history();
      if (!filename)
            return;
      if (read_history(filename) && errno != ENOENT)
            perror("Oops, couldn't read history");
      free(filename);
}

void
write_ldapvi_history()
{
      char *filename = history_filename();
      if (!filename)
            return;
      if (write_history(filename))
            perror("Oops, couldn't write history");
      free(filename);
}

char *
getline(char *prompt, char *value)
{
      tdialog d;
      init_dialog(&d, DIALOG_DEFAULT, prompt, value);
      dialog(0, &d, 1, 0);
      return d.value ? d.value : xdup("");
}

char *
get_password()
{
      tdialog d;
      init_dialog(&d, DIALOG_PASSWORD, "Password: ", "");
      dialog(0, &d, 1, 0);
      return d.value ? d.value : xdup("");
}

static char *readline_default;

static int
cb_set_readline_default()
{
      rl_insert_text(readline_default);
      return 0;
}

void
display_password(void)
{
      int i;
      char *backup = xalloc(rl_end + 1);
      strncpy(backup, rl_line_buffer, rl_end);
      for (i = 0; i < rl_end; i++)
            rl_line_buffer[i] = '*';
      rl_redisplay();
      strncpy(rl_line_buffer, backup, rl_end);
}

static char *
getline2(char *prompt, char *value, int password, int history)
{
      char *str;

      if (password)
            rl_redisplay_function = display_password;

      readline_default = value;
      rl_startup_hook = cb_set_readline_default;
      str = readline(prompt);
      rl_startup_hook = 0;

      if (password)
            rl_redisplay_function = rl_redisplay;

      if (str && *str && history)
            add_history(str);
      return str;
}

void
init_dialog(tdialog *d, enum dialog_mode mode, char *prompt, char *value)
{
      d->mode = mode;
      d->prompt = prompt;
      d->value = value;
}

char *
append(char *a, char *b)
{
      int k = strlen(a);
      char *result = xalloc(k + strlen(b) + 1);
      strcpy(result, a);
      strcpy(result + k, b);
      return result;
}

void *
xalloc(size_t size)
{
      void *result = malloc(size);
      if (!result) {
            write(2, "\nmalloc error\n", sizeof("\nmalloc error\n") - 1);
            _exit(2);
      }
      return result;
}

char *
xdup(char *str)
{
      char *result;

      if (!str)
            return str;
      if (!(result = strdup(str))) {
            write(2, "\nstrdup error\n", sizeof("\nstrdup error\n") - 1);
            _exit(2);
      }
      return result;
}

int
adjoin_str(GPtrArray *strs, char *str)
{
      int i;
      for (i = 0; i < strs->len; i++)
            if (!strcmp(str, g_ptr_array_index(strs, i)))
                  return -1;
      g_ptr_array_add(strs, str);
      return i;
}

int
adjoin_ptr(GPtrArray *a, void *p)
{
      int i;
      for (i = 0; i < a->len; i++)
            if (g_ptr_array_index(a, i) == p)
                  return -1;
      g_ptr_array_add(a, p);
      return i;
}

void
dumb_dialog(tdialog *d, int n)
{
      GString *prompt = g_string_new("");
      int i;

      for (i = 0; i < n; i++) {
            g_string_assign(prompt, d[i].prompt);
            g_string_append(prompt, ": ");
            switch (d[i].mode) {
            case DIALOG_DEFAULT:
                  d[i].value = getline2(prompt->str, d[i].value, 0, 1);
                  break;
            case DIALOG_PASSWORD:
                  d[i].value = getline2(prompt->str, d[i].value, 1, 0);
                  break;
            case DIALOG_CHALLENGE:
                  printf("%s: %s\n", prompt->str, d[i].value);
                  break;
            }
      }
      g_string_free(prompt, 1);
}

enum dialog_rc {
      dialog_continue, dialog_done, dialog_goto, dialog_relative,
      dialog_help, dialog_clear
};

static Keymap dialog_keymap = 0;
static Keymap dialog_empty_keymap = 0;
static enum dialog_rc dialog_action;
static int dialog_next;

static int
cb_view_pre_input()
{
      rl_done = 1;
      return 0;
}

static int
cb_dialog_done(int a, int b)
{
      rl_done = 1;
      dialog_action = dialog_done;
      return 42;
}

static int
cb_dialog_goto(int a, int b)
{
      rl_done = 1;
      dialog_action = dialog_goto;
      dialog_next = a - 1;
      return 42;
}

static int
cb_dialog_prev(int a, int b)
{
      rl_done = 1;
      dialog_action = dialog_relative;
      dialog_next = - 1;
      return 42;
}

static int
cb_dialog_next(int a, int b)
{
      rl_done = 1;
      dialog_action = dialog_relative;
      dialog_next = 1;
      return 42;
}

static int
cb_dialog_help(int a, int b)
{
      rl_done = 1;
      dialog_action = dialog_help;
      return 42;
}

static int
cb_dialog_clear(int a, int b)
{
      rl_done = 1;
      dialog_action = dialog_clear;
      return 42;
}

#define DIALOG_HELP                                         \
"\nEdit the lines above using standard readline commands.\n"            \
"Use RET to edit each line in turn.\n"                            \
"\n"                                                  \
"Special keys:\n"                                     \
"  M-RET       Finish the dialog immediately.\n"                  \
"  C-p         Go back to the previous line.\n"                   \
"  C-n         Go to the next line (alias for RET).\n"                  \
"  M-g         With numeric prefix, go to the specified line.\n"  \
"\n"                                                  \
"Non-password lines are saved in the history.  Standard readline\n"     \
"bindings for history access include:\n"                    \
"  C-r         Incremental search through history.\n"             \
"  <up>/<down> Previous/next history entry.\n"

static void
dialog_rebuild(char *up, char *clreos,
             char *header, char **prompts, tdialog *d, int n,
             int target, int help)
{
      int i;

      putp(clreos);
      if (header) {
            putchar('\n');
            fputs(header, stdout);
            putchar('\n');
            fputs("Type M-h for help on key bindings.", stdout);
            putchar('\n');
            putchar('\n');
      }

      rl_pre_input_hook = cb_view_pre_input;
      for (i = 0; i < n; i++) {
            int passwordp = d[i].mode == DIALOG_PASSWORD;
            free(getline2(prompts[i], d[i].value, passwordp, 0));
            putchar('\n');
      }
      rl_pre_input_hook = 0;

      if (help) {
            fputs(DIALOG_HELP, stdout);
            for (i = 0; DIALOG_HELP[i]; i++)
                  if (DIALOG_HELP[i] == '\n')
                        putp(up);
      }

      for (i = 0; i < n - target; i++)
            putp(up);
}

static Keymap
set_meta_keymap(Keymap keymap, Keymap meta_keymap)
{
      if (!meta_keymap)
            meta_keymap = rl_copy_keymap((Keymap) keymap[27].function);
      keymap[27].type = ISKMAP;
      keymap[27].function = (rl_command_func_t *) meta_keymap;
}


static void
init_dialog_keymap(Keymap keymap)
{
      Keymap meta_keymap = (Keymap) keymap[27].function;
      rl_bind_key_in_map('L' - '@', cb_dialog_clear, keymap);
      rl_bind_key_in_map('P' - '@', cb_dialog_prev, keymap);
      rl_bind_key_in_map('N' - '@', cb_dialog_next, keymap);
      rl_bind_key_in_map('\r', cb_dialog_done, meta_keymap);
      rl_bind_key_in_map('g', cb_dialog_goto, meta_keymap);
      rl_bind_key_in_map('h', cb_dialog_help, meta_keymap);
}


void
dialog(char *header, tdialog *d, int n, int start)
{
      int i;
      char *up = tigetstr("cuu1");
      char *clreos = tigetstr("ed");
      char *clear = tigetstr("clear");
#if 0
      char *hsm = rl_variable_value("horizontal-scroll-mode");
#endif
      char *hsm = "off";
      Keymap original_keymap = rl_get_keymap();
      int max = 0;
      char **prompts;

      if (n == 0)
            return;

      if (invalidp(up) || invalidp(clreos) || invalidp(clear)) {
            puts("Dumb terminal.  Using fallback dialog.");
            dumb_dialog(d, n);
            return;
      }

      if (!dialog_keymap) {
            rl_initialize();
            dialog_keymap = rl_copy_keymap(original_keymap);
            dialog_empty_keymap = rl_make_bare_keymap();
            set_meta_keymap(dialog_keymap, 0);
            set_meta_keymap(dialog_empty_keymap, rl_make_bare_keymap());
            init_dialog_keymap(dialog_keymap);
            init_dialog_keymap(dialog_empty_keymap);
      }

      rl_variable_bind("horizontal-scroll-mode", "on");
      rl_inhibit_completion = 1; /* fixme */

      for (i = 0; i < n; i++)
            max = MAX(max, strlen(d[i].prompt));
      prompts = xalloc(sizeof(char *) * n);

      for (i = 0; i < n; i++) {
            char *prompt = d[i].prompt;
            int len = strlen(prompt);
            char *str = xalloc(max + 3);
            memset(str, ' ', max);
            strcpy(str + max - len, prompt);
            strcpy(str + max, ": ");
            prompts[i] = str;

            if (d[i].value)
                  d[i].value = xdup(d[i].value);
      }

      dialog_rebuild(up, clreos, header, prompts, d, n, start, 0);

      i = start;
      for (;;) {
            char *orig = d[i].value;
            int passwordp = d[i].mode == DIALOG_PASSWORD;

            dialog_action = dialog_continue;
            if (d[i].mode == DIALOG_CHALLENGE)
                  rl_set_keymap(dialog_empty_keymap);
            else
                  rl_set_keymap(dialog_keymap);
            d[i].value = getline2(prompts[i], orig, passwordp, !passwordp);
            if (orig)
                  free(orig);

            switch (dialog_action) {
            case dialog_continue:
                  dialog_next = i + 1;
                  break;
            case dialog_clear: /* fall through */
            case dialog_help:
                  dialog_next = i;
                  break;
            case dialog_relative:
                  dialog_next += i;
                  /* fall through */
            case dialog_goto:
                  if (dialog_next < 0 || dialog_next >= n)
                        dialog_next = i;
                  break;
            case dialog_done:
                  dialog_next = n;
                  break;
            }

            if (dialog_action == dialog_clear)
                  putp(clear);
            else {
                  if (header)
                        i += 4;
                  if (dialog_action != dialog_continue)
                        i--;
                  do putp(up); while (i--);
            }

            dialog_rebuild(up, clreos, header, prompts, d, n, dialog_next,
                         dialog_action == dialog_help);
            if (dialog_next >= n)
                  break;
            i = dialog_next;
      }

      for (i = 0; i < n; i++)
            free(prompts[i]);
      free(prompts);

      rl_set_keymap(original_keymap);
      rl_variable_bind("horizontal-scroll-mode", hsm);
      rl_inhibit_completion = 0;
}

Generated by  Doxygen 1.6.0   Back to index