ftp.nice.ch/pub/next/unix/editor/ne-1.0.NI.s.tar.gz#/ne-1.0.NI.s/src/request.c

This is request.c in view mode; [Download] [Up]

/* Requester handling.

   Copyright (C) 1993 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; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

In other words, you are welcome to use, share and improve this program.
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!  */


#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

/* This is the name taken by unnamed documents in the document selector. */

#define UNNAMED_NAME				"<unnamed>"

/* 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)



/* This function 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. The number of the
selected string is returned, or -1 on escaping.

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<lines-1; i++) {
		move_cursor(i, 0);
		clear_to_eol();

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


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<lines-2) y = 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 = lines-2;
		request_prev_page(TRUE);
	}
}


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<lines-2) y++;
	else if (page < num_entries/names_per_page) {
		y = 0;
		request_next_page(TRUE);
	}
}




int request_strings(const char * const * const local_entries, int local_num_entries, 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 = columns / (++max_name_len))) names_per_line = 1;
	names_per_page = names_per_line*(lines-1);

	print_strings();

	while(TRUE) {

		move_cursor(y, x*max_name_len);

		while((ic = char_class[c = get_key_code()]) == IGNORE);

		switch(ic) {
			case ALPHA:
				if ((n = page*names_per_page+y*names_per_line+x) >= num_entries) n = num_entries-1;

				c = up_case[(unsigned char)c];

				for(i=1; i<num_entries; i++)
					if (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 RETURN:
				n = page*names_per_page+y*names_per_line+x;
				if (n >= num_entries) return(-1);
				else return(n);

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

					case MOVE_RIGHT:
						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 MOVE_LEFT:
						if (x+y+page != 0) {
							if (--x<0) {
								x = names_per_line-1;
								request_move_up();
							}
						}
						break;

					case MOVE_TO_SOL:
						x = 0;
						break;

					case MOVE_TO_EOL:
						x = names_per_line-1;
						break;

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

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

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

					case PREV_PAGE:
						request_prev_page(FALSE);
						break;

					case NEXT_PAGE:
						request_next_page(FALSE);
						break;

					case MOVE_TO_SOF:
						request_move_to_sof();
						break;

					case MOVE_TO_EOF:
						request_move_to_eof();
						break;

					case TOGGLE_SOF_EOF:
						request_toggle_seof();
						break;

					case ESCAPE:
						return(-1);
					}
				}
				break;

			default:
				break;
		}
	}
}



/* This is the file requester. It reads the directory in which the filename
lives, builds an array of string and calls request_strings(). If a directory name
is returned, it enters the directory. Returns NULL on error or escaping. */


char *request_files(const char *filename) {

	int i, num_entries, name_len, max_name_len, total_len, next_dir, is_directory,
		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;
	struct stat s;

	if (!(cur_dir_name = getcwd(NULL, CUR_DIR_MAX_SIZE))) return(NULL);

	if (dir_name = str_dup(filename)) {
		if ((p = (char *)file_part(dir_name)) != dir_name) {
			*p = 0;
			chdir(tilde_expand(dir_name));
		}
		free(dir_name);
	}

	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_directory = !stat(de->d_name, &s) && S_ISDIR(s.st_mode);
						name_len = strlen(de->d_name) + is_directory + 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;
						}

						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_directory) 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)) >= 0) {
							p = entries[i];

							if (p[strlen(p)-1] == '/') {
#ifndef _AMIGA
								p[strlen(p)-1] = 0;
#endif
									chdir(p);
									next_dir = TRUE;
								}
							else {
								result = getcwd(NULL, CUR_DIR_MAX_SIZE+strlen(p));
								strcat(result, "/");
								strcat(result, p);
							}
						}
					}

					closedir(d);
				}

			} while(next_dir);

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

	chdir(cur_dir_name);
	free(cur_dir_name);

	return(result);
}



/* This function 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(buffer *b, const char *prompt, const char *default_name) {

	char *p;

	if (!b->no_file_req) {
		p = request_files(default_name);
		reset_window();
		if (p) return(p);
	}

	if (p = request_string(prompt, default_name, FALSE)) return(p);

	return(NULL);
}



/* This function 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);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.