Logo Search packages:      
Sourcecode: ne version File versions

request.c

/* Requester handling.

      Copyright (C) 1993-1998 Sebastiano Vigna 
      Copyright (C) 1999-2004 Todd M. Lewis and Sebastiano Vigna

      This file is part of ne, the nice editor.

      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, 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; see the file COPYING.  If not, write to the Free
      Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
      02111-1307, USA.  */


#include "ne.h"
#include "termchar.h"
#include <dirent.h>


/* This is the expected max length of the current directory name. */

#define CUR_DIR_MAX_SIZE            4096

/* These are the default allocation sizes for the entry array and for the
name array when reading a directory. The allocation sizes start with these
values, and they are doubled each time more space is needed. This ensures a
reasonable number of retries. */

#define DEF_ENTRIES_ALLOC_SIZE      256
#define DEF_NAMES_ALLOC_SIZE        (4*1024)



/* Prompts the user to choose one between several (num_entries) strings,
   contained in the entries array. The maximum string width is given as
   max_name_len. The strings are displayed as an array. More than one page will
   be available if there are many strings. If string n was selected with
   RETURN, n is returned; if string n was selected with TAB, -n-2 is returned.
   On escaping, ERROR is returned.

We rely on a series of auxiliary functions. */


static int x, y, page, names_per_line, names_per_page, num_entries, max_name_len;

static const char * const *entries;


/* This is the printing function used by the requester. It prints the
strings from the entries array existing in a certain page (a page contains
(lines-1)*names_per_line items) with max_name_len maximum width. */

static void print_strings(void) {

      int i,j;

      for(i = 0; i < ne_lines - 1; i++) {
            move_cursor(i, 0);
            clear_to_eol();

            for(j = 0; j < names_per_line; j++) {
                  if ((i + page * (ne_lines - 1)) * names_per_line + j < num_entries) {
                        move_cursor(i, j * max_name_len);
                        output_string(entries[(i + page * (ne_lines - 1)) * names_per_line + j], io_utf8);
                  }
            }
      }
}





static void request_move_to_sol(void) {
      x = 0;
}

static void request_move_to_eol(void) {
      if ((page * (ne_lines - 1) + y + 1) * names_per_line < num_entries) x = names_per_line - 1;
      else x = (num_entries - 1) % names_per_line;
}

static void request_move_to_sof(void) {

      int i = page;

      x = y = page = 0;
      if (i != page) print_strings();
}


static void request_move_to_eof(void) {

      int i = page;

      page = (num_entries - 1) / names_per_page;
      y = ((num_entries - 1) % names_per_page) / names_per_line;
      x = (num_entries - 1) % names_per_line;

      if (i != page) print_strings();
}


static void request_toggle_seof(void) {
      if (x + y+page == 0) request_move_to_eof();
      else request_move_to_sof();
}


static void request_prev_page(int force) {
      if (!force && y > 0) y = 0;
      else if (page) {
            page--;
            print_strings();
      }
}


static void request_next_page(int force) {
      if (!force && y < ne_lines - 2) y = ne_lines - 2;
      else if ((page + 1) * names_per_page < num_entries) {
            page++;
            print_strings();
      }
      if (page * names_per_page + y*names_per_line + x >= num_entries) request_move_to_eof();
}


static void request_move_up(void) {
      if (y > 0) y--;
      else if (page) {
            y = ne_lines - 2;
            request_prev_page(TRUE);
      }
}



static void request_move_inc_up(void) {
      if (x == 0) {
            if (y == 0) request_move_to_sof();
            else request_prev_page(FALSE);
      }
      else request_move_to_sol();
}


static void request_move_down(void) {
      if (page * names_per_page + (y + 1) * names_per_line + x >= num_entries) request_move_to_eof();
      else if (y < ne_lines - 2) y++;
      else if (page < num_entries / names_per_page) {
            y = 0;
            request_next_page(TRUE);
      }
}



void request_move_inc_down(void) {

      if (x == names_per_line - 1) {
            if (y == ne_lines - 2) request_move_to_eof();
            else request_next_page(FALSE);
      }
      else request_move_to_eol();
}



int request_strings(const char * const * const local_entries, const int local_num_entries, const int local_max_name_len) {

      action a;
      input_class ic;
      int c, i, n;

      assert(local_num_entries > 0);

      x = y = page = 0;
      entries = local_entries;
      num_entries = local_num_entries;
      max_name_len = local_max_name_len;

      if (!(names_per_line = ne_columns / (++max_name_len))) names_per_line = 1;
      names_per_page = names_per_line * (ne_lines - 1);

      print_strings();

      while(TRUE) {

            move_cursor(y, x * max_name_len);

            do c = get_key_code(); while((ic = CHAR_CLASS(c)) == IGNORE || ic == INVALID);

            n = page * names_per_page + y*names_per_line + x;

            switch(ic) {
                  case ALPHA:
                        if (n >= num_entries) n = num_entries - 1;

                        c = localised_up_case[(unsigned char)c];

                        for(i = 1; i < num_entries; i++)
                              if (localised_up_case[(unsigned char)entries[(n + i) % num_entries][0]] == c) {

                                    n = (n + i) % num_entries;

                                    if (n / names_per_page != page) {
                                          page = n / names_per_page;
                                          print_strings();
                                    }

                                    y = (n % names_per_page) / names_per_line;
                                    x = n % names_per_line;
                                    break;
                              }
                        break;

                  case TAB:
                        if (n >= num_entries) return ERROR;
                        else return -n - 2;

                  case RETURN:
                        if (n >= num_entries) return ERROR;
                        else return n;

                  case COMMAND:
                        if (c < 0) c = -c - 1;
                        if ((a = parse_command_line(key_binding[c], NULL, NULL, FALSE))>=0) {
                              switch(a) {

                              case MOVERIGHT_A:
                                    if (page * names_per_page + y*names_per_line + x != num_entries - 1) {
                                          if (++x == names_per_line) {
                                                x = 0;
                                                request_move_down();
                                          }
                                    }
                                    break;

                              case MOVELEFT_A:
                                    if (x + y+page != 0) {
                                          if (--x < 0) {
                                                x = names_per_line - 1;
                                                request_move_up();
                                          }
                                    }
                                    break;

                              case MOVESOL_A:
                                    request_move_to_sol();
                                    break;

                              case MOVEEOL_A:
                                    request_move_to_eol();
                                    break;

                              case TOGGLESEOL_A:
                                    if (x != 0) x = 0;
                                    else x = names_per_line - 1;
                                    break;

                              case LINEUP_A:
                                    if (page + y != 0) request_move_up();
                                    break;

                              case LINEDOWN_A:
                                    if (page * (ne_lines - 1) + y < (num_entries + names_per_line - 1) / names_per_line - 1) request_move_down();
                                    break;

                              case MOVEINCUP_A:
                                    request_move_inc_up();
                                    break;

                              case MOVEINCDOWN_A:
                                    request_move_inc_down();
                                    break;

                              case PAGEUP_A:
                              case PREVPAGE_A:
                                    request_prev_page(FALSE);
                                    break;

                              case PAGEDOWN_A:
                              case NEXTPAGE_A:
                                    request_next_page(FALSE);
                                    break;

                              case MOVESOF_A:
                                    request_move_to_sof();
                                    break;

                              case MOVEEOF_A:
                                    request_move_to_eof();
                                    break;

                              case TOGGLESEOF_A:
                                    request_toggle_seof();
                                    break;

                              case ESCAPE_A:
                                    return -1;
                              }
                        }
                        break;

                  default:
                        break;
            }
      }
}



/* The completion function. Returns NULL if no file matches start_prefix, or
      the longest prefix common to all files extending start_prefix. */

char *complete(const char *start_prefix) {

      int is_dir, unique = TRUE;
      char *p, *dir_name, *cur_dir_name, *cur_prefix = NULL, *result = NULL;
      DIR *d;
      struct dirent *de;
      
      if (!(cur_dir_name = ne_getcwd(CUR_DIR_MAX_SIZE))) return NULL;

      if (dir_name = str_dup(start_prefix)) {
            *(p = (char *)file_part(dir_name)) = 0;
            if (p != dir_name && chdir(tilde_expand(dir_name)) == -1) {
                  free(dir_name);
                  return NULL;
            }
      }

      start_prefix = file_part(start_prefix);

      if (d = opendir(CURDIR)) {
            
            while(!stop && (de = readdir(d))) {

                  if (is_prefix(start_prefix, de->d_name)) 
                        if (cur_prefix) {
                              cur_prefix[max_prefix(cur_prefix, de->d_name)] = 0;
                              unique = FALSE;
                        }
                        else {
                              cur_prefix = str_dup(de->d_name);
                              is_dir = is_directory(de->d_name);
                        }
            }
            
            closedir(d);
      }
      
   if (cur_prefix) {
         result = malloc(strlen(dir_name) + strlen(cur_prefix) + 2);
         strcat(strcat(strcpy(result, dir_name), cur_prefix), unique && is_dir ? "/" : "");
      }

      chdir(cur_dir_name);
      free(cur_dir_name);
      free(dir_name);
      free(cur_prefix);

      return result;
}


/* This is the file requester. It reads the directory in which the filename
lives, builds an array of strings and calls request_strings(). If a directory
name is returned, it enters the directory. Returns NULL on error or escaping, a
pointer to the selected filename if RETURN is pressed, or a pointer to the
selected filename (or directory) preceeded by a NUL if TAB is pressed (so by
checking whether the first character of the returned string is NUL you can
check which key the user pressed). */


char *request_files(const char * const filename, int use_prefix) {

      int i, num_entries, name_len, max_name_len, total_len, next_dir, is_dir,
            entries_alloc_size = DEF_ENTRIES_ALLOC_SIZE,
            names_alloc_size = DEF_NAMES_ALLOC_SIZE;

      char *dir_name, **entries = NULL, *names = NULL, *cur_dir_name, *result = NULL, *p;

      DIR *d;
      struct dirent *de;

      if (!(cur_dir_name = ne_getcwd(CUR_DIR_MAX_SIZE))) return NULL;

      if (dir_name = str_dup(filename)) {
            i = 0;
            if ((p = (char *)file_part(dir_name)) != dir_name) {
                  *p = 0;
                  i = chdir(tilde_expand(dir_name));
            }
            free(dir_name);
            if (i == -1) return NULL;
      }

      if (entries = malloc(sizeof(char *) * entries_alloc_size)) {
            if (names = malloc(sizeof(char) * names_alloc_size)) {
                  do {
                        next_dir = FALSE;

                        if (d = opendir(CURDIR)) {

                              num_entries = max_name_len = total_len = 0;

#ifdef _AMIGA
                              total_len = 2;
                              num_entries++;
                              strcpy(names, "/");
                              entries[0] = names;
#endif

                              stop = FALSE;

                              while(!stop && (de = readdir(d))) {
                                    is_dir = is_directory(de->d_name);
                                    if (use_prefix && !is_prefix(file_part(filename), de->d_name)) continue;
                                    name_len = strlen(de->d_name) + is_dir + 1;

                                    if (name_len > max_name_len) max_name_len = name_len;

                                    if (total_len + name_len > names_alloc_size) {
                                          char *t;
                                          t = realloc(names, sizeof(char) * (names_alloc_size = names_alloc_size * 2 + name_len));
                                          if (!t) break;
                                          names = t;
                                          /* Now adjust the entries to point to the newly reallocated strings */
                                          entries[0] = names;
                                          for (i = 1; i < num_entries; i++)
                                            entries[i] = entries[i - 1] + strlen(entries[i - 1]) + 1;
                                    }

                                    if (num_entries >= entries_alloc_size) {
                                          char **t;
                                          t = realloc(entries, sizeof(char *) * (entries_alloc_size *= 2));
                                          if (!t) break;
                                          entries = t;
                                    }

                                    strcpy(entries[num_entries] = names + total_len, de->d_name);
                                    if (is_dir) strcpy(names + total_len + name_len - 2, "/");
                                    total_len += name_len;
                                    num_entries++;
                              }

                              if (num_entries) {

#ifdef _AMIGA
                                    if (num_entries - 1) qsort(entries + 1, num_entries - 1, sizeof(char *), strcmpp);
#else
                                    if (num_entries - 2 >= 0) {
                                          if (!strcmp(entries[0], "./") && !strcmp(entries[1], "../")) {
                                                p = entries[0];
                                                entries[0] = entries[1];
                                                entries[1] = p;
                                          }
                                          if (num_entries - 2 > 0 )
                                                qsort(entries + 2, num_entries - 2, sizeof(char *), strcmpp);
                                    }
#endif

                                    if ((i = request_strings((const char * const *)entries, num_entries, max_name_len)) != ERROR) {
                                          p = entries[i >= 0 ? i : -i - 2];
                                          if (p[strlen(p) - 1] == '/' && i >= 0) {
#ifndef _AMIGA
                                                p[strlen(p) - 1] = 0;
#endif
                                                if (chdir(p)) ring_bell();
                                                else use_prefix = FALSE;
                                                next_dir = TRUE;
                                          }
                                          else {
                                                result = ne_getcwd(CUR_DIR_MAX_SIZE + strlen(p) + 2);
                                                if (strcmp(result, "/")) strcat(result, "/");
                                                strcat(result, p);
                                                if (i < 0) {
                                                      memmove(result + 1, result, strlen(result) + 1);
                                                      result[0] = 0;
                                                }
                                          }
                                    }
                              }

                              closedir(d);
                        }
                        else ring_bell();

                  } while(next_dir);

                  free(names);
            }
            free(entries);
      }

      chdir(cur_dir_name);
      free(cur_dir_name);

      return result;
}



/* Requests a file name. If no_file_req is FALSE, the file requester is firstly
   presented. If no_file_req is TRUE, or the file requester is escaped, a long
   input is performed with the given prompt and default_name. */

char *request_file(const buffer *b, const char *prompt, const char *default_name) {

      char *p = NULL;

      if (!b->opt.no_file_req) {
            print_message(info_msg[PRESSF1]);
            p = request_files(default_name, FALSE);
            reset_window();
            draw_status_bar();
            if (p && *p) return p;
      }

      if (p = request_string(prompt, p ? p + 1 : default_name, FALSE, TRUE, io_utf8)) return p;

      return NULL;
}


/* Presents to the user a list of the documents currently available.  It
   returns the number of the document selected, or -1 on escape or error. */

int request_document(void) {

      int i = -1, num_entries, max_name_len, total_len;
      char **entries, *names, *p, unnamed_name[] = UNNAMED_NAME;
      buffer *b = (buffer *)buffers.head;

      num_entries = max_name_len = total_len = 0;

      while(b->b_node.next) {
            p = b->filename ? b->filename : unnamed_name;

            if (strlen(p)>max_name_len) max_name_len = strlen(p);

            total_len += strlen(p) + 1;
            num_entries++;

            b = (buffer *)b->b_node.next;
      }

      max_name_len += 8;

      if (num_entries) {

            if (entries = malloc(sizeof(char *) * num_entries)) {
                  if (names = malloc(sizeof(char) * total_len)) {

                        p = names;

                        b = (buffer *)buffers.head;

                        for(i = 0; i < num_entries; i++) {
                              entries[i] = p;
                              strcpy(p, b->filename ? b->filename : unnamed_name);
                              p += strlen(p) + 1;

                              b = (buffer *)b->b_node.next;
                        }

                        i = request_strings((const char * const *)entries, num_entries, max_name_len);

                        reset_window();

                        free(names);
                  }
                  free(entries);
            }
      }

      return i;
}

Generated by  Doxygen 1.6.0   Back to index