Thread View: comp.sources.reviewed
1 messages
1 total messages
Started by Mike Lijewski
Wed, 28 Oct 1992 02:41
v02i037: dired - (Ver. 1.8) directory editor in C++, Part05/05
Author: Mike Lijewski
Date: Wed, 28 Oct 1992 02:41
Date: Wed, 28 Oct 1992 02:41
1606 lines
50041 bytes
50041 bytes
Submitted-by: Mike Lijewski <lijewski@theory.tc.cornell.edu>
Posting-number: Volume 2, Issue 37
Archive-name: dired/part05
Supersedes: dired: Volume 2, Issue 7-12
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 5 (of 5)."
# Contents: version.h utilities.C
# Wrapped by lijewski@xtesoc2 on Mon Oct 5 14:53:45 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'version.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'version.h'\"
else
echo shar: Extracting \"'version.h'\" \(1191 characters\)
sed "s/^X//" >'version.h' <<'END_OF_FILE'
X/*
X** version.h - where our version number is defined
X**
X** version.h %M% %I% Delta'd: %U% %G% Mike Lijewski, CNSF
X**
X** Copyright (c) 1991, 1992 Cornell University
X** All rights reserved.
X**
X** Redistribution and use in source and binary forms are permitted
X** provided that: (1) source distributions retain this entire copyright
X** notice and comment, and (2) distributions including binaries display
X** the following acknowledgement: ``This product includes software
X** developed by Cornell University'' in the documentation or other
X** materials provided with the distribution and in all advertising
X** materials mentioning features or use of this software. Neither the
X** name of the University nor the names of its contributors may be used
X** to endorse or promote products derived from this software without
X** specific prior written permission.
X**
X** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X*/
X
X#ifndef _VERSION_H
X#define _VERSION_H
X
X#include "dired.h"
X
Xextern const char *const version = "Dired Version 1.8";
X
X#endif
END_OF_FILE
if test 1191 -ne `wc -c <'version.h'`; then
echo shar: \"'version.h'\" unpacked with wrong size!
fi
# end of 'version.h'
fi
if test -f 'utilities.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'utilities.C'\"
else
echo shar: Extracting \"'utilities.C'\" \(43137 characters\)
sed "s/^X//" >'utilities.C' <<'END_OF_FILE'
X/*
X** utilities.C - utility functions
X**
X** utilities.C 1.98 Delta\'d: 14:27:42 10/5/92 Mike Lijewski, CNSF
X**
X** Copyright \(c\) 1991, 1992 Cornell University
X** All rights reserved.
X**
X** Redistribution and use in source and binary forms are permitted
X** provided that: \(1\) source distributions retain this entire copyright
X** notice and comment, and \(2\) distributions including binaries display
X** the following acknowledgement: ``This product includes software
X** developed by Cornell University\'\' in the documentation or other
X** materials provided with the distribution and in all advertising
X** materials mentioning features or use of this software. Neither the
X** name of the University nor the names of its contributors may be used
X** to endorse or promote products derived from this software without
X** specific prior written permission.
X**
X** THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR
X** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X*/
X
X
X#include <ctype.h>
X
X#ifndef _IBMR2
X#include <libc.h>
X#endif /*_IBMR2*/
X
X#include <osfcn.h>
X#include <pwd.h>
X#include <signal.h>
X#include <stdlib.h>
X
X#ifdef _IBMR2
X#include <sys/access.h>
X#endif /*_IBMR2*/
X
X#include <sys/ioctl.h>
X#include <sys/stat.h>
X#include <sys/types.h>
X#include <sys/wait.h>
X
X#ifdef COMPLETION
X#include <dirent.h>
X#endif
X
X#include <string.h>
X#include <sys/errno.h>
X#include <unistd.h>
X
X#include "dired.h"
X#include "display.h"
X#include "keys.h"
X
X/*
X** expand_tilde - expects a string of the form "~ ...".
X** Returns a new string in volatile storage
X** with the user\'s home directory in place of the `~\'.
X** The user\'s home directory is always appended
X** in the form: "/usr/staff/mjlx"; a slash is not added to
X** the end of the home directory string. Returns the original
X** string if we cannot get the user\'s home directory. The
X** user should not attempt to delete the return value.
X*/
X
Xconst char *expand_tilde(const char *str)
X{
X static char *home = getenv("HOME");
X if (home == NULL)
X {
X struct passwd *user = getpwuid(getuid());
X if (user == NULL) return str;
X home = user->pw_dir;
X }
X if (*str != '~') return str;
X static String expansion;
X expansion = String(home) + (str + 1);
X return expansion;
X}
X
X/*
X** fgetline\(FILE *f\) - returns a pointer to the start of a line read
X** from fp, or the null pointer if we hit eof or get
X** an error from fgets\(\). Exits if new\(\) fails.
X** Strips the newline from the line.
X** Caller should free memory if desired.
X*/
X
Xstatic const int FGETLINE_BUFSIZE = 80; // chunksize for calls to new\(\)
X
Xchar *fgetline(FILE *fp)
X{
X char *buffer = new char[FGETLINE_BUFSIZE];
X
X char *result = fgets(buffer, FGETLINE_BUFSIZE, fp);
X if (result == 0)
X {
X //
X // either error or at eof
X //
X DELETE buffer;
X return 0;
X }
X
X if (buffer[strlen(buffer)-1] != '\n' && !feof(fp))
X {
X //
X // longer line than buffer can hold
X //
X char *restofline = fgetline(fp);
X
X if (restofline == 0) return 0; // eof or error
X
X char *longline = new char[strlen(buffer) + strlen(restofline) + 1];
X (void)strcat(strcpy(longline, buffer), restofline);
X
X DELETE restofline;
X DELETE buffer;
X
X if (longline[strlen(longline) - 1] == '\n')
X longline[strlen(longline) - 1] = '\0';
X
X return longline;
X }
X else
X {
X if (buffer[strlen(buffer) - 1] == '\n')
X buffer[strlen(buffer) - 1] = '\0';
X
X return buffer;
X }
X}
X
X/*
X** current_directory - This routine tries to determine the full pathname
X** of the current directory. The pointer returned is
X** new\(\)\'d storage.
X*/
X
Xchar *current_directory()
X{
X const int chunksize = 50;
X int size = chunksize;
X char *dir = new char[size];
X while (getcwd(dir, size) == 0)
X if (errno == ERANGE)
X {
X DELETE dir;
X dir = new char[size += chunksize];
X continue;
X }
X else
X //
X // Must have got an EACCES.
X //
X return 0;
X return dir;
X}
X
X/*
X** display_string - prints a string to the given the display, guaranteeing not
X** to print more than columns\(\) characters. If the string
X** exceeds the width of the window, a `!\' is placed in
X** the final column. Can be called with or without the
X** length of the string to be printed. In most places in
X** the code we know the exact length of the strings we
X** wish to print. Note that `len\' has a default value
X** of zero defined by the declaration in "dired.h".
X** We never call this when trying to write to the last
X** row on the screen. That is the dominion of message\(\).
X*/
X
Xvoid display_string(const char *str, size_t len)
X{
X size_t string_length = len == 0 ? strlen(str) : len;
X
X if (string_length < columns())
X {
X (void)fputs(str, stdout);
X cursor_wrap();
X }
X else if (string_length > columns())
X {
X (void)printf("%*.*s%c", columns() - 1, columns() - 1, str, '!');
X if (!AM || XN) cursor_wrap();
X }
X else
X {
X (void)fputs(str, stdout);
X if (!AM || XN) cursor_wrap();
X }
X}
X
X/*
X** get_directory_listing - Get a long listing of the given directory.
X** Returns 0 if we got other than a
X** "memory exhausted" error.
X*/
X
XDirList *get_directory_listing(char *dirname)
X{
X message("Reading directory ... ");
X
X String cmd = String(ls_cmd[the_sort_order()]) + dirname + " 2>/dev/null";
X FILE *fp = popen(cmd, "r");
X if (fp == 0) return 0;
X
X DirList *directory = new DirList(dirname);
X
X //
X // discard lines of the form:
X //
X // total 1116
X //
X char *line = fgetline(fp);
X DELETE line;
X if (fp == 0) return 0;
X
X while((line = fgetline(fp)) != 0) directory->add(new DirLine(&line));
X
X message("Reading directory ... done");
X
X if (feof(fp) && !ferror(fp))
X {
X (void)pclose(fp);
X return directory;
X }
X else
X return 0;
X}
X
X/*
X** error - Prints error message so it can be read. This is the error
X** function we call once we\'ve initialized the display.
X*/
X
Xvoid error(const char *str)
X{
X clear_display();
X move_cursor(rows()-1, 0);
X (void) printf(str);
X cursor_wrap();
X synch_display();
X term_display();
X exit(EXIT_FAILURE);
X}
X
Xvoid error(const char *fmt, int index)
X{
X clear_display();
X move_cursor(rows()-1, 0);
X (void) printf(fmt, index);
X cursor_wrap();
X synch_display();
X term_display();
X exit(EXIT_FAILURE);
X}
X
Xvoid error(const char *fmt, const char *file, int line)
X{
X clear_display();
X move_cursor(rows()-1, 0);
X (void) printf(fmt, file, line);
X cursor_wrap();
X synch_display();
X term_display();
X exit(EXIT_FAILURE);
X}
X
Xvoid error(const char *fmt, const char *file, int line, const char *str)
X{
X clear_display();
X move_cursor(rows()-1, 0);
X (void) printf(fmt, file, line, str);
X cursor_wrap();
X synch_display();
X term_display();
X exit(EXIT_FAILURE);
X}
X
X/*
X** update_modeline - this routine concatenates the two strings
X** into the modeline. The modeline
X** is displayed in standout mode if possible.
X** We never put more than columns\(\) characters into
X** the modeline. The modeline is the penultimate
X** line on the terminal screen. It does not
X** synch the display. If head == tail == 0, we
X** just display the old modeline. This happens
X** if for some reason we had to clear the screen.
X*/
X
Xvoid update_modeline(const char *head, const char *tail)
X{
X static char *oldline;
X move_to_modeline();
X enter_standout_mode();
X
X if (head == 0 && tail == 0)
X {
X //
X // Redisplay old modeline.
X //
X (void)fputs(oldline, stdout);
X end_standout_mode();
X return;
X }
X
X int len = (int)strlen(head);
X char *modeline = new char[columns() + 1];
X (void)strncpy(modeline, head, columns());
X modeline[columns()] = 0; // ensure it\'s null-terminated
X
X if (len < columns())
X {
X //
X // Write exactly columns\(\) characters to modeline.
X //
X for (int i = len; i < columns() - 1 && tail && *tail; i++, tail++)
X modeline[i] = *tail;
X if (i < columns() - 1)
X {
X modeline[i++] = ' ';
X for (; i < columns(); i++) modeline[i] = '-';
X }
X else if (tail && *tail)
X //
X // The string was overly long. Put a \'!\' in the last space
X // on the modeline to signify truncation.
X //
X modeline[columns() - 1] = '!';
X else
X //
X // Here len == columns\(\)-1 && there is nothing else in tail.
X //
X modeline[columns() - 1] = ' ';
X }
X else if (len > columns())
X modeline[columns() - 1] = '!';
X
X if (oldline)
X {
X update_screen_line(oldline, modeline, rows() - 2);
X DELETE oldline;
X }
X else
X (void)fputs(modeline, stdout);
X
X oldline = modeline;
X end_standout_mode();
X}
X
X/*
X** is_directory - returns non-zero if a directory, otherwise 0.
X** Also returns zero on error.
X*/
X
Xint is_directory(const char *dir)
X{
X struct stat stbuf;
X if (stat(dir, &stbuf) < 0) return 0;
X return S_ISDIR(stbuf.st_mode);
X}
X
X/*
X** is_regular_file - returns non-zero if a regular file, otherwise 0.
X** Also returns zero on error.
X*/
X
Xint is_regular_file(const char *file)
X{
X struct stat stbuf;
X if (stat(file, &stbuf) < 0) return 0;
X return S_ISREG(stbuf.st_mode);
X}
X
X/*
X** read_and_exec_perm - returns non-zero if we have read and execute
X** permission on the directory, otherwise 0.
X** Returns 0 on error.
X*/
X
Xint read_and_exec_perm(const char *dir)
X{
X return access(dir, R_OK | X_OK) == -1 ? 0 : 1;
X}
X
X/*
X** Find the column position of the first character of the filename
X** in the current line of the given DirList.
X**
X** The straight-forward way to do this is to walk the string from it\'s
X** tail to it\'s head until we hit some whitespace. This presumes
X** that filenames don\'t contain whitespace. The one special case
X** to worry about is if the file is a symbolic link. In that case we\'ll
X** have a filename field entry of the form
X**
X** Xm -> /usr/lpp/include/Xm
X*/
X
Xint goal_column(DirList *l)
X{
X DirLine *line = l->currLine();
X const char *tmp;
X
X#ifndef NO_SYMLINKS
X if ((tmp = strstr(line->line(), " -> ")) != 0)
X //
X // We have a symbolic link.
X //
X --tmp;
X else
X#endif
X tmp = line->line() + line->length() - 1;
X
X while(!isspace(*tmp)) --tmp;
X
X return tmp - line->line() + 1;
X}
X
X/*
X** get_file_name - returns the filename of the current line of the DirList
X** in volatile storage.
X**
X** If we have a symbolic link, we return the link not
X** the file pointed to.
X*/
X
Xconst char *get_file_name(DirList *dl)
X{
X static String file;
X file = &(dl->currLine()->line())[goal_column(dl)];
X
X //
X // Do we have a symbolic link?
X //
X char *result = strstr(file, " -> ");
X if (result) *result = '\0';
X
X return file;
X}
X
X/*
X** redisplay - this routine redisplays the DirList at the top
X** of our stack. It assumes that the physical screen
X** has become corrupted, clearing each line before writing
X** to it.
X*/
X
Xvoid redisplay()
X{
X DirList *dl = dir_stack->top();
X
X DirLine *ln = dl->firstLine();
X cursor_home();
X for (int i = 0; i < rows() - 2 && ln; i++, ln = ln->next())
X {
X clear_to_end_of_line();
X display_string(ln->line(), ln->length());
X }
X move_cursor(i, 0);
X for (; i < rows() - 2; i++)
X {
X clear_to_end_of_line();
X cursor_down();
X }
X
X move_to_modeline();
X clear_to_end_of_line();
X update_modeline();
X clear_message_line();
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(dl->savedYPos(), dl->savedXPos());
X
X synch_display();
X}
X
X//
X// forward declaration
X//
Xstatic void adjust_window();
X
X/*
X** eat_a_character - displays the message in standout mode and waits
X** until a character is typed. Deals with SIGWINCH
X** and SIGTSTP
X*/
X
Xvoid eat_a_character(const char *msg)
X{
X enter_standout_mode();
X message(msg);
X end_standout_mode();
X char c;
X while (1)
X if (read(0, &c, 1) < 0 // assume fails only when errno == EINTR
X#ifdef SIGWINCH
X || win_size_changed
X#endif
X )
X {
X#ifdef SIGWINCH
X if (win_size_changed)
X {
X win_size_changed = 0;
X adjust_window();
X redisplay();
X }
X#endif
X //
X // Must redisplay the message.
X //
X enter_standout_mode();
X message(msg);
X end_standout_mode();
X }
X else
X return;
X}
X
X/*
X** read_from_keybd - reads a character from the keyboard. It only returns
X** when it\'s successfully read a character. So if we
X** get suspended while waiting to read a character, the
X** signal handler will redisplay the screen and we\'ll
X** still be here waiting to read a character. This is
X** only used by the main command loop so that we know
X** that redisplay\(\) will do all the necessary redisplay
X** operations.
X*/
X
Xint read_from_keybd()
X{
X char c;
X while(1)
X if (read(0, &c, 1) < 0 // assume fails only when errno == EINTR
X#ifdef SIGWINCH
X || win_size_changed
X#endif
X )
X {
X#ifdef SIGWINCH
X if (win_size_changed)
X {
X win_size_changed = 0;
X adjust_window();
X redisplay();
X }
X#endif
X }
X else
X return c;
X}
X
X/*
X** execute - executes command using exec\(2\). Returns 1 if the exec
X** went OK, otherwise it returns 0. If `closem\' is true, which
X** is the default, we close file descriptors 0, 1 and 2.
X*/
X
Xint execute(const char *file, const char *argv[], int closem)
X{
X int status;
X pid_t pid = fork();
X switch(pid)
X {
X case -1: // error
X return 0;
X case 0: // in the child
X if (closem)
X {
X (void)close(0);
X (void)close(1);
X (void)close(2);
X }
X execvp(file, (char *const *)argv);
X //
X // Exec failed.
X //
X exit(1);
X default: // in the parent
X waitpid(pid, &status, 0);
X return status == 0 ? 1 : 0;
X }
X}
X
X/*
X** exec_with_system - execute the passed command using system\(3\).
X** If prompt == 1, which is the default, we prompt for
X** a key before returning. We use system\(3\) so that
X** shell and environment variables will be expanded.
X*/
X
Xvoid exec_with_system(const char *cmd, int prompt)
X{
X unsetraw();
X unset_signals();
X system(cmd);
X set_signals();
X setraw();
X if (prompt) eat_a_character("Press Any Key to Continue");
X}
X
X#ifdef SIGWINCH
X
X#include <sys/ioctl.h>
X
X/*
X** adjust_window - called to adjust our window after getting a SIGWINCH
X*/
X
Xstatic void adjust_window()
X{
X#ifdef TIOCGWINSZ
X struct winsize w;
X if (ioctl(2, TIOCGWINSZ, (char *)&w) == 0 && w.ws_row > 0) LI = w.ws_row;
X if (ioctl(2, TIOCGWINSZ, (char *)&w) == 0 && w.ws_col > 0) CO = w.ws_col;
X
X //
X // Is current line still on the screen?
X //
X if (dir_stack->top()->savedYPos() >= rows()-2)
X {
X dir_stack->top()->setCurrLine(dir_stack->top()->firstLine());
X dir_stack->top()->saveYXPos(0, goal_column(dir_stack->top()));
X }
X
X // need to adjust lastLine\(\)
X DirLine *ln = dir_stack->top()->firstLine();
X for (int i = 0; i < rows()-2 && ln; i++, ln = ln->next()) ;
X ln ? dir_stack->top()->setLast(ln->prev()) :
X dir_stack->top()->setLast(dir_stack->top()->tail());
X#endif
X}
X#endif
X
X#ifdef COMPLETION
X
X/*
X** string_sort - this is the sort routine we pass to qsort\(\) for
X** sorting the table of possible completions.
X*/
X
Xstatic int string_sort(const void *a, const void *b)
X{
X return strcmp(*(char **)a, *(char **)b);
X}
X
X/*
X** complete - read directory `dir\' for any completions of `prefix\'.
X** Returns zero on error; if prefix is null; if there are no
X** completions; or if the completion is the prefix itself.
X** Else it returns a string in volatile space containing the
X** longest possible completion of `prefix\', minus the
X** characters in the prefix. Used only by prompt\(\).
X*/
X
Xchar *complete(const char *directory, const char *prefix)
X{
X static char *completion = 0; // our volatile storage
X size_t prefix_length = strlen(prefix);
X const int chunksize = 10;
X int tablesize = chunksize;
X int matches = 0; // number of matches of `prefix\' in `directory\'
X
X DIR *dirp = opendir(directory);
X if (dirp == NULL) return 0;
X
X char **completions = new char*[tablesize * sizeof(char *)];
X
X struct dirent *entry;
X for (entry = readdir(dirp); entry != NULL; entry = readdir(dirp))
X {
X if (strncmp(entry->d_name, prefix, prefix_length) == 0)
X {
X //
X // We\'ve got a match.
X //
X completions[matches] = new char[entry->d_namlen + 1];
X (void)strcpy(completions[matches++], entry->d_name);
X if (matches == tablesize)
X {
X //
X // Grow table.
X //
X tablesize += chunksize;
X char **newtable = new char*[tablesize * sizeof(char *)];
X for (int i = 0; i < tablesize - chunksize; i++)
X newtable[i] = completions[i];
X DELETE completions;
X completions = newtable;
X }
X }
X }
X
X //
X // I should really be testing the rc here, but so many of the
X // machines I\'ve tested have this improperly prototyped to void,
X // that it\'s best not to for the time being.
X //
X (void)closedir(dirp);
X
X if (matches == 0)
X {
X DELETE completions;
X return 0;
X }
X
X if (matches == 1)
X {
X if (strcmp(prefix, completions[0]) == 0)
X {
X //
X // If the completion matches our input prefix, we return 0.
X //
X DELETE completions[0];
X DELETE completions;
X return 0;
X }
X DELETE completion;
X
X completion = new char[strlen(completions[0]) - prefix_length +1];
X (void)strcpy(completion, completions[0] + prefix_length);
X
X DELETE completions[0];
X DELETE completions;
X
X return completion;
X }
X
X qsort(completions, matches, sizeof(char **), string_sort);
X
X //
X // the completion cannot be longer than the first sorted item
X //
X size_t maxlen = strlen(completions[0]);
X char *tmp = new char[maxlen + 1];
X (void)strcpy(tmp, prefix);
X size_t i = prefix_length;
X char c = completions[0][i];
X for (; i < maxlen; i++, c = completions[0][i])
X {
X int stop = 0;
X for (int j = 1; j < matches; j++)
X if (c != completions[j][i])
X {
X stop = 1;
X break;
X }
X if (stop) break;
X tmp[i] = c;
X }
X tmp[i] = 0; // nullify the string
X if (strcmp(prefix, tmp) == 0)
X {
X //
X // If the completion matches our input prefix, we return 0.
X //
X DELETE tmp;
X for (i = 0; i < matches; i++) DELETE completions[i];
X DELETE completions;
X
X return 0;
X }
X DELETE completion;
X completion = new char[maxlen - prefix_length + 1];
X (void)strcpy(completion, tmp + prefix_length);
X
X DELETE tmp;
X
X for (i = 0; i < matches; i++) DELETE completions[i];
X DELETE completions;
X
X return completion;
X}
X
X#endif /*COMPLETION*/
X
X/*
X** prompt - displays `msg\' prompt and then collects the response.
X** The keys of the response are echoed as they\'re collected.
X** A response of 0 indicates that the command was aborted.
X** A response can contain any graphical character.
X** C-G will abort out of a prompt; Backspace works as expected.
X** Carriage return indicates the end of response.
X** Non-graphical characters are ignored. If do_completion != 0,
X** with do_completon == 0 by default, we do filename completion
X** on the TAB character. The response is kept in volatile storage,
X** so if the client needs to save it, they must make a copy of it.
X*/
X
X#ifdef COMPLETION
Xconst char *prompt(const char *msg, int do_completion)
X#else
Xconst char *prompt(const char *msg)
X#endif
X{
X static char *response; // our volatile storage
X
X size_t written = 0; // number of characters written to message line
X const char *abort_msg = "(C-g to Abort) ";
X String nmsg = String(abort_msg) + msg;
X size_t len = nmsg.length();
X
X move_to_message_line(); // it will have been already cleared by get_key\(\)
X
X if (len < columns())
X {
X (void)fputs(nmsg, stdout);
X written = len;
X }
X else
X {
X // Leave space for columns\(\)/2 + 1 characters.
X (void)fputs((const char *)nmsg + (len-columns()/2+1), stdout);
X written = columns()/2 - 1;
X }
X synch_display();
X
X //
X // We never echo into the last position in the message window.
X //
X size_t space_available = columns() - written; // available spaces in line
X
X DELETE response; // release any volatile storage
X response = new char[space_available + 1];
X size_t pos = 0; // index of next character in `response\'
X
X char key;
X for (;;)
X {
X if (read(0, &key, 1) < 0 // assume fails only when errno == EINTR
X#ifdef SIGWINCH
X || win_size_changed
X#endif
X )
X {
X#ifdef SIGWINCH
X if (win_size_changed)
X {
X win_size_changed = 0;
X adjust_window();
X redisplay();
X }
X#endif
X //
X // On a SIGTSTP the signal handler does the redisplay\(\),
X // so all we have to worry about is getting the prompt
X // redisplayed correctly.
X // Must make sure total output is less than screen width.
X //
X clear_message_line();
X response[pos] = 0;
X if (pos + len < columns())
X {
X //
X // Output message and response-to-date.
X //
X (void)fputs(nmsg, stdout);
X (void)fputs(response, stdout);
X space_available = columns() - pos - len;
X }
X else if (pos < columns())
X {
X //
X // Display the response.
X //
X (void)fputs(response, stdout);
X space_available = columns() - strlen(response);
X }
X else
X {
X //
X // Display the backend of the response
X //
X (void)fputs(&response[pos - columns()/2 + 1], stdout);
X space_available = columns()/2 + 1;
X }
X synch_display();
X }
X else if (isprint(key))
X {
X //
X // Echo character to message window and wait for another.
X //
X response[pos++] = key;
X space_available--;
X if (!space_available)
X {
X //
X // Need to allocate more room for the response.
X // Note that strlen\(response\) == pos
X //
X space_available = columns()/2 + 1;
X char *nresponse = new char[pos + space_available + 1];
X response[pos] = 0; // stringify response
X (void)strcpy(nresponse, response);
X DELETE response;
X response = nresponse;
X //
X // Shift prompt in message window so we
X // always have the end in view to which we\'re
X // adding characters as they\'re typed.
X //
X clear_message_line();
X (void)fputs(&response[pos - columns()/2 + 1], stdout);
X }
X else
X putchar(key);
X synch_display();
X }
X else
X switch (key)
X {
X#ifdef COMPLETION
X case KEY_TAB: // perform filename completion
X if (do_completion == 0 || pos == 0)
X {
X ding();
X break;
X }
X
X response[pos] = 0; // stringify response
X char *last = strrchr(response, '/');
X const char *current_directory = dir_stack->top()->name();
X char *completion = 0;
X
X if (response[0] == '~')
X {
X //
X // Expand the tilde before trying the completion,
X // but there is no use trying the completion if we don\'t
X // have a string of the form: "~/.*".
X //
X if (response[1] != '/')
X {
X ding();
X break;
X }
X String head(response);
X char *expansion = (char *) expand_tilde(head);
X char *last = strrchr(expansion, '/');
X expansion[last - expansion] = 0;
X completion = complete(expansion, last + 1);
X }
X else if (last == NULL)
X //
X // Filename is relative to our current directory.
X //
X completion = complete(current_directory, response);
X else
X {
X //
X // The response contains a slash.
X //
X if (response[0] == '/')
X {
X //
X // It\'s an absolute path.
X //
X if (pos == 1)
X {
X ding();
X break;
X }
X if (response == last)
X //
X // The first slash is also the last.
X //
X completion = complete("/", last + 1);
X else
X {
X String head(response);
X head[last - response] = 0;
X completion = complete(head, last + 1);
X }
X }
X else
X {
X //
X // It\'s relative to our current directory.
X //
X String head = String(current_directory)+ "/" +response;
X head[strlen(current_directory)+last-response+1] = 0;
X completion = complete(head, last + 1);
X }
X }
X if (completion == 0)
X {
X ding();
X break;
X }
X
X size_t clen = strlen(completion);
X pos += clen;
X space_available -= clen;
X if (space_available > 0)
X {
X //
X // We\'ve got the space to hold the completed filename.
X //
X (void)strcat(response, completion);
X (void)fputs(completion, stdout);
X }
X else
X {
X //
X // Allocate more space for response and adjust
X // message line.
X //
X char *nresponse = new char[pos + columns()/2 + 1];
X (void)strcpy(nresponse, response);
X (void)strcat(nresponse, completion);
X DELETE response;
X response = nresponse;
X clear_message_line();
X (void)fputs(&response[pos - columns()/2 + 1], stdout);
X space_available = columns()/2 + 1;
X }
X synch_display();
X break;
X
X#endif /*COMPLETION*/
X
X case KEY_CR: // we have the complete response
X response[pos] = 0;
X clear_message_line();
X synch_display();
X return response;
X
X case KEY_ABORT: // abort -- reset cursor to previous position
X move_cursor(dir_stack->top()->savedYPos(),
X dir_stack->top()->savedXPos());
X message("Aborted");
X return 0;
X
X case KEY_BKSP: // back up one character
X if (pos == 0)
X {
X ding();
X break;
X }
X backspace();
X DC ? delete_char_at_cursor() : clear_to_end_of_line();
X --pos;
X ++space_available;
X if (space_available == columns())
X {
X //
X // The only way this can happen is if we
X // had previously shifted the response to the left.
X // Now we must shift the response to the right.
X //
X clear_message_line();
X response[pos] = 0;
X if (pos + len < columns())
X {
X //
X // Output message and response-to-date.
X //
X (void)fputs(nmsg, stdout);
X (void)fputs(response, stdout);
X space_available = columns() - pos - len;
X }
X else if (pos < columns())
X {
X //
X // Display the response.
X //
X (void)fputs(response, stdout);
X space_available = columns() - strlen(response);
X } else
X {
X //
X // Display the backend of the response
X //
X (void)fputs(&response[pos - columns()/2 + 1], stdout);
X space_available = columns()/2 + 1;
X }
X }
X synch_display();
X break;
X
X default: ding(); break; // ignore other characters
X }
X }
X}
X
X/*
X** lines_displayed - returns the number of lines in the DirList
X** currently displayed on the screen.
X*/
X
Xint lines_displayed(DirList *dl)
X{
X DirLine *ln = dl->firstLine();
X for (int i = 1; ln != dl->lastLine(); i++, ln = ln->next()) ;
X return i;
X}
X
X/*
X** message - prints a message on the last line of the screen.
X** It is up to the calling process to put the cursor
X** back where it belongs. Synchs the display. It can
X** be called as either:
X**
X** message\(msg\);
X** or
X** message\(fmt, str\);
X**
X** In the later case it must be the case that the format `fmt\'
X** has exactly one `%\' into which the `str\' will be substituted
X** as in the ?printf\(\) functions.
X*/
X
Xstatic char message_window_dirty = 0;
X
Xvoid message(const char *fmt, const char *str)
X{
X char *msg; // the complete message to be output
X int allocated = 0; // was `msg\' allocated in new\(\) space?
X
X clear_message_line();
X
X if (str)
X {
X msg = new char[strlen(fmt) + strlen(str) + 1];
X const char *token = strchr(fmt, '%');
X if (token == 0)
X //
X // This shouldn\'t happen. But if it does, let\'s
X // just print the format `fmt\'.
X //
X msg = (char *)fmt;
X else
X {
X (void)strncpy(msg, fmt, token - fmt);
X msg[token - fmt] = 0; // strncpy doesn\'t nullify the string
X (void)strcat(msg, str);
X (void)strcat(msg, token + 1);
X allocated = 1;
X }
X }
X else
X msg = (char *)fmt;
X
X if (strlen(msg) < columns())
X (void)fputs(msg, stdout);
X else
X (void)printf("%*.*s", columns() - 1, columns() - 1, msg);
X
X synch_display();
X message_window_dirty = 1;
X if (allocated) DELETE msg;
X}
X
X/*
X** get_key - reads a key using getch\(\) and then clears the message window,
X** if it needs to be cleared. Used only by read_commands in the
X** main switch statement so that message\(\) doesn\'t need to sleep\(\)
X** and clear\(\) on the messages that get written. This way, the
X** message window is cleared after each keypress within the main
X** loop, when necessary. We also check for and deal with window
X** size changes and the UP and DOWN arrow keys here.
X*/
X
Xstruct arrow_key {
X int len;
X int *seq;
X arrow_key(const char *);
X};
X
Xarrow_key::arrow_key(const char *str)
X{
X if (str == 0)
X {
X //
X // The capability isn\'t defined.
X //
X len = 0;
X seq = 0;
X return;
X }
X
X seq = new int[12]; // should be ample
X
X int i = 0;
X do
X {
X switch (*str)
X {
X case '\\':
X {
X int c = *++str;
X switch (c)
X {
X case 'E': seq[i++] = 0x1b; break;
X case 'b': seq[i++] = '\b'; break;
X case 'f': seq[i++] = '\f'; break;
X case 'n': seq[i++] = '\n'; break;
X case 'r': seq[i++] = '\r'; break;
X case 't': seq[i++] = '\t'; break;
X case 'v': seq[i++] = '\v'; break;
X case '\\': seq[i++] = '\\'; break;
X case '\'': seq[i++] = '\''; break;
X case '\"': seq[i++] = '\"'; break;
X case '^': seq[i++] = '^'; break;
X default:
X error("invalid escape in /etc/termcap for arrow key: \\%c",
X c);
X break;
X }
X break;
X }
X case '^':
X {
X int c = *++str;
X if (isalpha(c))
X {
X seq[i] = (c > 'a' ? c - 'a' : c - 'A') + 1;
X i++; // g++ 2.2.2 chokes if I write seq\[i++\] = ...
X }
X else
X switch(c)
X {
X case '[': seq[i++] = 0x1b; break;
X case '\\': seq[i++] = 0x1c; break;
X case ']': seq[i++] = 0x1d; break;
X case '^': seq[i++] = 0x1e; break;
X case '_': seq[i++] = 0x1f; break;
X default:
X error("invalid control sequence for arrow key: ^%c",
X c);
X break;
X }
X }
X break;
X default:
X seq[i++] = *str;
X break;
X }
X } while (*++str);
X
X len = i;
X}
X
Xint get_key(DirList *dl)
X{
X int key, index = 0;
X static arrow_key up(KU), down(KD);
X static int *keys = 0;
X static int remaining = 0;
X
X if (keys == 0) keys = new int[max(up.len, down.len)];
X
X#ifdef SIGWINCH
X if (win_size_changed)
X {
X win_size_changed = 0;
X adjust_window();
X redisplay();
X }
X#endif
X
X if (remaining)
X {
X //
X // We have some characters left over from a partial match
X // of an arrow key; use them up.
X //
X key = keys[0];
X remaining--;
X for (int i = 0; i < remaining; i++) keys[i] = keys[i+1];
X return key;
X }
X else
X key = read_from_keybd();
X
X if (message_window_dirty)
X {
X clear_message_line();
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X message_window_dirty = 0;
X }
X
X //
X // Now deal with potential arrow keys.
X //
X if (KU || KD)
X {
X for (index = 0; (index < up.len && up.seq[index] == key) ||
X (index < down.len && down.seq[index] == key); index++)
X {
X if ((up.len - 1) == index && up.seq[index] == key)
X return KEY_ARROW_UP;
X if ((down.len - 1) == index && down.seq[index] == key)
X return KEY_ARROW_DOWN;
X if (index == (max(up.len, down.len) - 1)) break;
X keys[index] = key;
X key = read_from_keybd();
X }
X if (index == 0)
X return key; // no initial match -- the most usual case
X else
X {
X //
X // We had a partial match, but not a complete one.
X // We must return the characters which we\'ve read in
X // the proper order so that the main command loop can
X // check for matches. The problem here is the potential
X // ambiguity between what the terminal claims to be arrow
X // keys and what has been hardcoded as the commands for
X // dired.
X //
X keys[index] = key;
X key = keys[0]; // what we\'ll return to the command loop
X for (int i = 0; i < index; i++) keys[i] = keys[i+1];
X remaining = index;
X return key;
X }
X }
X else
X return key;
X}
X
X/*
X** cleanup - cleanup and exit after a SIGHUP, SIGTERM, SIGQUIT or SIGINT
X*/
X
Xvoid cleanup(int) { term_display(); exit(0); }
X
X/*
X** initial_listing - prints the initial listing screen. Called by dired\(\)
X** and read_commands\(\) when rereading the current directory.
X** Adjusts firstLine\(\), lastLine\(\) and currLine\(\).
X*/
X
Xvoid initial_listing(DirList *dl)
X{
X static int first = 1;
X
X DirLine *ln = dl->head();
X dl->setFirst(ln);
X dl->setCurrLine(ln);
X cursor_home();
X for (int i = 0; i < rows()-2 && ln; ln = ln->next(), i++)
X {
X if (!first) clear_to_end_of_line();
X display_string(ln->line(), ln->length());
X }
X
X ln ? dl->setLast(ln->prev()) : dl->setLast(dl->tail());
X
X //
X // Don\'t forget to clear any remaining lines in those
X // cases when the screen hasn\'t already been cleared and
X // there are potentially dirty lines from a previous dired\(\).
X //
X if (!first)
X {
X move_cursor(i, 0);
X for (; i < rows() - 2; i++)
X {
X clear_to_end_of_line();
X cursor_down();
X }
X }
X if (first) first = 0;
X}
X
X#ifdef SIGWINCH
X
X/*
X** winch - set flag indicating window size changed.
X*/
X
Xvoid winch(int)
X{
X (void)signal(SIGWINCH, SIG_IGN);
X win_size_changed = 1;
X (void)signal(SIGWINCH, winch);
X}
X
X#endif
X
X/*
X** set_signals - set up our signal handlers
X*/
X
Xvoid set_signals()
X{
X (void)signal(SIGHUP, cleanup);
X (void)signal(SIGINT, cleanup);
X (void)signal(SIGQUIT, cleanup);
X (void)signal(SIGTERM, cleanup);
X#ifdef SIGTSTP
X (void)signal(SIGTSTP, termstop);
X#endif
X#ifdef SIGWINCH
X (void)signal(SIGWINCH, winch);
X#endif
X}
X
X/*
X** unset_signals - set signals back to defaults
X*/
X
Xvoid unset_signals()
X{
X (void)signal(SIGHUP, SIG_DFL);
X (void)signal(SIGINT, SIG_DFL);
X (void)signal(SIGQUIT, SIG_DFL);
X (void)signal(SIGTERM, SIG_DFL);
X#ifdef SIGTSTP
X (void)signal(SIGTSTP, SIG_DFL);
X#endif
X#ifdef SIGWINCH
X (void)signal(SIGWINCH, SIG_DFL);
X#endif
X}
X
X/*
X** leftshift_current_line - shifts the current line in DirList left until
X** its tail is visible.
X*/
Xvoid leftshift_current_line(DirList *dl)
X{
X int inc = dl->currLine()->length()-columns()+1;
X move_cursor(dl->savedYPos(), 0);
X clear_to_end_of_line();
X display_string(&(dl->currLine()->line())[inc],columns()-1);
X dl->saveYXPos(dl->savedYPos(), max(goal_column(dl)-inc, 0));
X move_cursor(dl->savedYPos(), dl->savedXPos());
X}
X
X/*
X** rightshift_current_line - rightshifts current line to "natural" position.
X*/
Xvoid rightshift_current_line(DirList *dl)
X{
X move_cursor(dl->savedYPos(), 0);
X clear_to_end_of_line();
X display_string(dl->currLine()->line(), dl->currLine()->length());
X dl->saveYXPos(dl->savedYPos(), goal_column(dl));
X move_cursor(dl->savedYPos(), dl->savedXPos());
X}
X
X#ifdef NO_STRSTR
X/*
X** strstr - from Henry Spencers ANSI C library suite
X*/
X
Xchar *strstr(const char *s, const char *wanted)
X{
X register const char *scan;
X register size_t len;
X register char firstc;
X
X //
X // The odd placement of the two tests is so "" is findable.
X // Also, we inline the first char for speed.
X // The ++ on scan has been moved down for optimization.
X //
X firstc = *wanted;
X len = strlen(wanted);
X for (scan = s; *scan != firstc || strncmp(scan, wanted, len) != 0; )
X if (*scan++ == '\0') return NULL;
X return (char *)scan;
X}
X
X#endif /* NO_STRSTR */
X
X#ifdef NO_STRCHR
X/*
X** strchr - find first occurrence of a character in a string. From Henry
X** Spencer\'s string\(3\) implementation.
X*/
X
Xchar *strchr(const char *s, char charwanted)
X{
X register const char *scan;
X
X //
X // The odd placement of the two tests is so NULL is findable.
X //
X for (scan = s; *scan != charwanted;) /* ++ moved down for opt. */
X if (*scan++ == '\0') return 0;
X return (char *)scan;
X}
X
X/*
X** strrchr - find last occurrence of a character in a string. From Henry
X** Spencer\'s string\(3\) implementation.
X*/
X
Xchar *strrchr(const char *s, char charwanted)
X{
X register const char *scan;
X register const char *place;
X
X place = NULL;
X for (scan = s; *scan != '\0'; scan++)
X if (*scan == charwanted) place = scan;
X if (charwanted == '\0') return (char *)scan;
X return (char *)place;
X}
X
X#endif /* NO_STRCHR */
END_OF_FILE
if test 43137 -ne `wc -c <'utilities.C'`; then
echo shar: \"'utilities.C'\" unpacked with wrong size!
fi
# end of 'utilities.C'
fi
echo shar: End of archive 5 \(of 5\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 5 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
exit 0 # Just in case...
--
Andrew Patrick acting as Comp.Sources.Reviewed Moderator
Department of Communications, Ottawa, CANADA
csr@calvin.dgbt.doc.CA
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads