Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

v03i064: dired-2.0 - a directory editor (like emacs dired), Part00/07

15 views
Skip to first unread message

Kevin Braunsdorf

unread,
Oct 1, 1993, 6:41:59 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 64
Archive-name: dired-2.0/00


[No one really panned this product, but most of the reviews were long.
I thought maybe a second (shorter) review was in order: the author
revised the product and asked for it just to be posted.
-- Mod.
]

Platforms:
Linux using gcc 2.3.3 (possibly gcc 2.4.5)
SGI indigo (IRIX 4.0.5) , C++, 2.1.1
SunOS 4.1.3 using CC 3.0.1 (using gcc 2.4.1 as backend)
SunOS 4.1.3 using G++ 2.4.1
Linux 0.99.10 using G++ 2.3.3 and libc 4.3.3
Linux 0.99.10 using G++ 2.4.5 using libc 4.4
MIPs 4.52
SGI Indigo running IRIX 4.0.5F with (SGI supplied) Cfront 3.0
IRIX 4.0.1 and (SGI supplied) Cfront 2.1.
HP/Apollo running sr10.3.5 with HP/Apollo C++ (2.1.0).
SGI Indigo R4000 running IRIX 4.0.5F with (cfront based) SGI C++ 3.0
SGI Indigo R3000 running IRIX 4.0.1 with (cfront based) SGI C++ 2.1
Linux 0.99pl4 with gcc 2.3.3 and libc 4.2 (SLS 1.0 distribution)
Linux 0.99pl9 with gcc 2.3.3 and libc 4.3.3 (SLS 1.01 distribution)
<others reported no problems or no review at all>
Maybes:
NeXT 3.x

Gripes: [addressed in the new code, so I hear. --Mod.]

[SGI]
compiler errors:
$ make -k
[deleted].

[SunOS 4.1.3]
Following the instructions it was easy to set up the defines needed
to compile dired. I wasn't able to use TERMIOS due to incorrect
prototypes for tcgetattr() et al. The actual flags used were:

CFLAGS = -DTEMPLATES -DL_AND_G -DCOMPLETION -O
TERMFLAGS = -DTERMIO
LIBS = -ltermcap

The file commands.C wouldn't compile due to missing prototypes for
symlink() and rename(). These prototype are to be found in the
<sysent.h> header on this system.

The most worrying slew of warnings came display.C. These were
caused by symbols such as NL0 being redefined. This was caused
by the inclusion of <sys/ioctl.h> which defines the symbols in a
different manner from <termio.h>.

The prototype for ioctl() is also included in <sysent.h>, but although
this is a valid header for CC, it is not for g++.

There were also warnings from CC that it couldn't find a template file
Queue.c (note small C), and other messages, but dired linked and ran.

[Another warning was that given by the gcc backend, which complained
about memcopy() taking conflicting types -- all the fault of the
header files]

[SunOS 4.1.3 G++ 2.4.1]
The following defines were used:

CFLAGS = -DTEMPLATES -DL_AND_G -DCOMPLETION -O2
TERMFLAGS = -DTERMIO
LIBS = -ltermcap

I still had to use TERMIO rather than TERMIOS, as above, and also the
warnings about NL0 et al being redefined. This was cured by taking
the inclusion of <sys/ioctl.h> out of display.C, since a prototype
for ioctl() exists in <unistd.h> (as do symlink() and rename())

So with the removal of the inclusion of <sys/ioctl.h> I was able to
type "make" and get an executable with no errors or warnings.

[Linux G++ 2.3.3]
The flags used were:

CFLAGS = -DMEMMOVE -DCOMPLETION -O2
TERMFLAGS = -DTERMIOS
LIBS = -ltermcap

I was unable to use -DTEMPLATES since this caused g++ to complain
of an internal error.

There was also a problem that there was no definition of major()
and minor(). I created defn's based on the way they are arranged,
but wouldn't a simple comparison of the 'st_dev' suffice?

[Linux G++]

The flags used were:

CFLAGS = -DMEMMOVE -DTEMPLATES -DCOMPLETION -DTERMIOS
TERMFLAGS = -DTERMIOS
LIBS = -ltermcap

Use of templates was succesful using this version of g++.

Also, there was no problem with missing defn's for major() and
minor(), since these are now defined when including <sys/stat.h>.

I would say that compilation under Linux, with the current version
of gcc and the C library, is easier than under SunOS.


Comments:

About my only use for emacs is to fire up its dired, so I am glad to
see a standalone tool to give me the functionality (especially under
Linux, where I don't have the space to spare for a full emacs).

I found that it closely matches the keystrokes used by the emacs
dired, so I was able to switch over quickly. It does differ from the
keystrokes and actions in some ways, but I felt that were this was
done, it was done for the better.

It also has the ability to change the ordering of the files (based on
modified, changed or accessed time) something lacking in the standard
emacs dired. However, when changing the ordering, a directory rescan
has to be performed manually.

I like the way you can define your own mapping from keys to
functions. This means that I can define actions that my fingers know
about, that my brain doesn't. Eg "^X^C quit", "ZZ quit" and "^Xd
edit-directory". I was a little surprised to find that you have to
define all of the keys rather than overriding already defined keys.
Perhaps augmenting the system diredrc could be done with a function of
"undef" to undefine
a key sequence.

The online help is very useful -- it gives a mapping of keys and their
functions. This means that the less often used commands can be found
without resorting to the manual. It also has the advantage of
containing the mappings actually defined. It would be nice of you
could use the same kind of keys for moving up and down the help window
that is used in the dired window.

I like the way that it deals with showing lines too long to fit the
screen -- especially useful with symbolic links.

One bug is that the "---- Dired: directory ----" line does not seem to
take notice of the current width of the line.

There is a problem when compiled for SunOS. The screen is not redrawn
when resized, nor when the screen has changed size while suspended
(the screen comes up blank). A manual redraw cures this.

When I try to view a file that has been removed since the scan of the
directory, I get a suggesting that it is not a regular file, rather
than the fact it is actually missing.

One niggle is that since I use less as a pager, it is not necessary
for dired to pause after viewing a file -- perhaps the rc syntax could
be extended to allow settings (such as "{,no}pagerpause").

Although it has been updated to recognize '*.gz' as gzip'ped files, it
only allows gunzip'ping of '*.z' files. It needs to recognise '*.gz',
and possibly also recognise other extensions that gunzip is happy with
(*.tgz).

When attempting to delete a symbolic link, dired uses rmdir(), which
(of course) fails, although attempting to view a symlink performs the
correct behaviour, dependent on the target.

Although there is code to try and protect meta-characters from the
shell (passing filename as "command 'file'"), the code fails to parse
filenames with spaces in, nor deal with filenames containing a "'"
character. Agreed this is a failing of the emacs dired, but perhaps
this demonstrates the need to read the directory using readdir(),
rather than using "ls", as well as removing calls to system().

All in all, a very useful tool. Simple and effective.


[another person]
Found and fixed two bugs: one coredump, did not recognize .gz files.
Continuing...


There were several system calls for which the Apollo include-files
had no prototypes.

The test for gunzip tests the wrong suffix (".z" rather than ".gz")

The display-sorting does not work properly (I see the display
sorted only if I re-read the directory with ^R).


[another]
I spent most time on IRIX 4.0.5F, which worked well except for
viewing files that were symbolic links. Under Linux there were some
problems with the screen handling. IRIX 4.0.1 had a problem with a
dropped line in the directory display.


More Comments:

Great program. Probably widely useful; I don't know how much has
changed since the last post but this one could be posted with only
minor revisions - see the following comments. I expect to use this
program daily.

I found dired quick and simple to get going. However, the
instructions in the Makefile were not completely clear when I wore my
newbie cap. I would suggest adding example settings of the CFLAGS
variable for common architectures.

I would be prepared to spend more time getting dired to work better
under IRIX. My Linux setups are too old to be really useful for
further testing.

The long list of comments is meant as a compliment, not as criticism
(I liked the program so much that I spent time helping to make it
better 8-). The patches included are small and even though the
guidelines for reviewers (v1.9) discourages reviewing a patched
version, the functionality of the program was not changed
significantly by the changes. On the other hand, the patches are
hopefully helpful to the author, as are the reports of problems on the
tested architectures.


Bug reports/fixes: [addressed in rework. --Mod]

In the documentation, no mention is made that 'v' views a directory
or that 'v' and 'q' are dual commands. This should be added.

I would prefer a more compact online help; it may be possible to fit
all the frequently used stuff on one 80x23 page (which is probably a
useful minimum COLUMNSxLINES), with a supplement page for the other
commands. 'dired' mode in emacs does it in the bottom line - this
seems like a good idea for a minimal command set, in addition to the
full multipage help.

The code assumes '.z' is the gzip default extension; gzip now uses
'.gz', though '.z' is still understood. I include a patch - since
most of my files are gzip'ed, this was really irritating.

This comment on the use of preprocessor symbols should not affect the
semantics (and hence functioning) of the program. However, it seems
inconsistent to use

#ifdef SYMBOL1
<some stuff>
#elif SYMBOL2
<some more>
#else
<and more>
#endif

which occurs in several places in the code, since the #ifdef tests if
SYMBOL1 is #defined whereas the #elif tests if SYMBOL2 evaluates to
non-zero. On all versions of cpp that I have tried this means that
#define SYMBOL2 0
will make
#if SYMBOL2
false but
#if defined(SYMBOL2)
true.

There seems to be a bug in utilities.C.current_directory():
const int AutodirLen = strlen(AutomountDir);
by changing this to
int AutodirLen...
the 'core dumped' on IRIX went away. I'm not sure if this is a bug in
the SGI compiler, or in the code, but it cost very little to make this
non-const. This is included in the patch.

The backspace key is frequently set up to generate a delete
character, for instance by the default console setup under Linux, and
by many of our users of NCSA Telnet who use Emacs. I include a patch
with a check for the delete character at the help screen prompt.

Under IRIX, viewing directories that are symbolic links works, but
viewing files does not (it worked on Linux)... [deleted -- Mod.]

Under Linux the cursor was not placed on the filenames but stayed
on the left hand side of the screen after moving it off the first
line. This may be due to an old implementation of curses - someone
with a newer Linux setup should be able to check this.

Patches: [deleted.]
....


Kevin Braunsdorf

unread,
Oct 1, 1993, 6:48:23 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 65
Archive-name: dired-2.0/01

#! /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 1 (of 7)."
# Contents: dired.1 dired.lpr diredrc Makefile
# Wrapped by lijewski@xtesoc2 on Wed Sep 29 08:11:57 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'dired.1' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'dired.1'\"
else
echo shar: Extracting \"'dired.1'\" \(13353 characters\)
sed "s/^X//" >'dired.1' <<'END_OF_FILE'
X.\"
X.\" $Id: dired.1,v 1.7 1993/04/16 16:58:21 lijewski Exp $
X.\"
X.TH DIRED 1
X.SH NAME
Xdired \- a directory editor
X.SH SYNOPSIS
X.B "dired [-t | -u | -c] [dirname1 dirname2 ...]"
X.br
X
X.SH DESCRIPTION
X.I Dired
Xis a directory editor modelled after Dired Mode in GNU Emacs.
XIt displays a window consisting of a long listing of a
X.I directory
X, or the current directory if invoked without a directory argument.
XBy default, the listing is in the format of an
X.I " ls -al "
Xcommand, which means that the listing lines are sorted alphabetically.
XThe file or directory on the line
Xcontaining the cursor is called the current file, with that line
Xknown as the current line. The listing can also be sorted by the
Xmodification time, access time, or inode-change time of the files in
Xthe directory by specifying one of the options `-t', `-u', or `-c',
Xrespectively. The sorting order can be changed from within
X.I dired
Xby using the `O' (order) command.
X.I Dired
Xprovides numerous commands for operating on the current file
Xincluding, but not limited to,
Xediting, paging, deleting, renaming, copying and compressing.
X.PP
XCommands, by default, are a superset of those in
X.I vi
Xand
X.I emacs.
XIn addition, on startup, dired will look for a user-specific
Xversion of a startup file
X.I .diredrc
Xwithin which the user can redefine the default bindings of keys to
Xcommands. This is described after the description of the default
Xbindings of keys to commands, which follows.
X.PP
X.SH COMMANDS
XIn the following descriptions, ^X means control-X.
XESC stands for the ESCAPE key; for example ESC-v means the
Xtwo character sequence "ESCAPE", then "v". TAB stands for the tab
Xkey. UP_ARROW stands for the up arrow key and DOWN_ARROW stands for
Xthe down arrow key, if your keyboard supports arrow keys.
X.PP
XA number of commands require arguments;
X.I dired
Xwill prompt for the necessary argument(s) in such cases.
XThe prompt always appears in the last line of the window.
XAll graphical characters are accepted as legal when responding to a prompt.
XTyping a carriage return signifies the end of response.
XTyping a ^G aborts out of the prompt, canceling the command.
XBackspace and DELETE work as expected.
X^W deletes the word preceding the cursor, including any whitespace
Xwhich may come between the cursor and the preceding word.
X^U deletes the complete response.
XAll other non-graphical characters are ignored, though signal generating
Xkeys (^C, ^\\ and ^Z) do the right thing in a prompt, as they do elsewhere
Xin
X.I dired.
XFilename completion is
Xavailable for the `E' and `c' commands. Typing a tab (the TAB key)
Xwhen in the prompt for these commands attempts to form the longest
Xunique file or directory name.
X.PP
X.IP "? or H "
XHelp - display a summary of these commands.
X.PP
X.IP "^V or ^F"
XScrolls listing up one window, leaving one line of overlap between the
Xwindows.
X.PP
X.IP "RETURN or n or j or ^N or SPC or DOWN_ARROW"
XMove cursor to next line in listing. Scrolls listing up one line when
Xcursor is on the last line in the window.
X.PP
X.IP "^D "
XScrolls listing up half a window and attempts to leave the cursor
Xon the same line in the listing.
X.PP
X.IP "^B or ESC-v"
XScrolls listing down one window, leaving one line of overlap between
Xthe previous window and the next window.
X.PP
X.IP "k or p or ^Y or ^P or UP_ARROW"
XMove cursor to previous line in the listing.
XScrolls listing down one line when
Xcursor is on the first line in the window.
X.PP
X.IP "^U"
XScrolls the listing down half a window and attempts to leave the
Xcursor on the same line in the listing.
X.PP
X.IP "^L"
XRepaint the screen.
X.PP
X.IP "< or ESC-<"
XGo to the first line in the listing.
X.PP
X.IP "> or ESC->"
XGo to the last line in the listing.
X.PP
X.IP /string
XSearch forward in the listing for the next line whose filename
Xcontains the string. The search starts beginning with the line
Xfollowing the current line. A search without a string, repeats the
Xprevious search.
X.PP
X.IP \estring
XSearch backward in the listing for a line whose filename
Xcontains the string. The search starts beginning with the line
Xpreceding the current line. A search without a string, repeats the
Xprevious search.
X.PP
X.IP V
XPrints the version number of
X.I dired
Xbeing run.
X.PP
X.IP q
XExits from the most recently edited directory back to the previously
Xedited one; otherwise it edits the next directory on the command line.
XDirectories, when edited, are always pushed onto a stack.
XIf their are no more directories to be edited, exits
X.I dired.
X.IP Q
XExits
X.I dired.
X.PP
X.IP c
XCopies the current file to another file, prompting for a name
Xfor the new file. If the first letter of the name of the new file is
Xa `~', the full pathname of the user's home directory will be
Xsubstituted for the `~'. Prompts to confirm the overwriting of an
Xexisting file. Filename completion via the TAB key
Xis enabled when prompting for the filename.
X.PP
X.IP d
XDeletes the current file, prompting for affirmation of the delete.
XTyping a `y' or `Y' to the prompt goes ahead with the delete. Any other key
Xcancels the delete request.
X.PP
X.IP "e or f "
XInvokes an editor to edit the current file.
XThe editor is taken from the environment variable EDITOR,
Xor defaults to "
X.I vi
X".
XIf the file is a directory, runs
X.I dired
Xon that directory, pushing the directory currently being edited onto a
Xstack. The `q'
Xcommand will exit from the new directory listing putting one back into
Xthe previous directory listing. Editing the file `..', edits the
Xparent directory of the current directory. It is thus possible to
Xwalk both up and down the directory tree.
X.PP
X.IP "m or v "
XInvokes a pager on the current file.
XThe pager is taken from the environment variable PAGER,
Xor defaults to "
X.I more
X".
X.PP
X.IP r
XRenames the current file, prompting for the new name.
X.PP
X.IP u
XUnzip the current file using GNU unzip (gunzip).
X.PP
X.IP z
XZip the current file using GNU zip (gzip).
X.PP
X.IP C
XCompress the current file.
X.PP
X.IP E
XPrompts for the name of a directory to edit and then edits it. If the
Xfirst character of the directory is `~', the full pathname of user's
Xhome directory is substituted for the `~', making it easier to edit
Xdirectories relative to the user's home directory. Alternatively, one
Xmay simply type `! dired dirname'; that is,
X.I dired
Xcan be called from within itself. Filename completion via the TAB key
Xis enabled when prompting for the directory name.
X.PP
X.IP G
XChange the group of the current file, prompting for the new group.
X.PP
X.IP L
XLink the current file to another file, prompting for the name of the
Xnew file.
X.PP
X.IP M
XChange the mode of the current file, prompting for the new mode. The
Xmode can be either octal, as in `755', or symbolic as in `go+r'.
XThese are the same modes the `chmod' command accepts.
X.PP
X.IP P
XPrints the current file. The printer together with all relevant
Xoptions are taken from the environment variable DIREDPRT.
XIf DIREDPRT is not defined,
X.I lpr
Xis used by default. It should be noted that in networked
Xenvironments,
X.I lpr
Xis almost never the correct choice.
X.PP
X.IP "O "
XPrompts for one of the characters `a', `c', `t', or `u' and changes
Xthe sort order to alphabetical, inode-change time,
Xmodification time, or access time, respectively. The sort order only
Xtakes affect when a directory is first edited;
Xthe sort order of
Xthe currently displayed directory is not changed. To change the sort
Xorder of the currently displayed directory, use this command to change
Xthe sort order and then the `g' or `R' command to re-read the current
Xdirectory.
X.PP
X.IP "R or g "
XRereads the current directory and updates the display. This command
Xis useful if you've executed a shell command which added, deleted or
Xmodified a file or files in the current directory. Otherwise,
X.I dired
Xdoesn't know that the contents of the directory have been modified.
XThis command will leave the cursor on the same file it started
Xon, provided that file is still in the current directory.
X.PP
X.IP S
XCreate a symbolic link to the current file, prompting for the
Xname of the symbolic link.
X.PP
X.IP U
XUncompress the current file.
X.PP
X.IP !cmd
XExecutes the shell command
X.I cmd
Xusing
X.I system(3)
Xfor execution. A `%' in
X.I cmd
Xis replaced with the current filename before being executed. This
Xprovides a convenient shorthand way to execute commands on the current
Xfile, for which the author hasn't provided a key to execute
Xthe command.
X.PP
X.IP !!
XRe-executes the previously issued shell command. Does not re-expand
Xany `%' characters to the name of the now current file.
X.PP
X.IP ![
XRe-executes the previously issued shell command, re-expanding any `%'
Xto the name of the now current file.
X.PP
X.IP !
XInvokes a shell. Uses the shell in the environment variable SHELL, or
Xdefaults to "
X.I sh
X".
X.PP
XOn startup
X.I dired
Xsearches for an initialization file named
X.I .diredrc,
Xfirst in the current directory and then in the user's home directory.
XIf a startup file is found, it is expected to contain mappings of
Xcommands to keypresses; the default keymap will not be enabled.
XThe following is a sample
X.I .diredrc
Xcorresponding to the default keymap.
XYour system administrator should have installed a copy of this as
X.I diredrc
Xsomewhere, when
X.I dired
Xwas installed.
X.nf
X
Xcursor_down j
Xcursor_down n
Xcursor_down 0x20 # the space character
Xcursor_down ^N
Xcursor_down \r
Xcursor_up k
Xcursor_up p
Xcursor_up ^P
Xcursor_up ^Y
Xpage_up ^F
Xpage_up ^V
Xpage_down ^B
Xpage_down ^[v # ESC-v for emacs emulation
Xhalfpage_up ^D
Xhalfpage_down ^U
Xtop_of_file <
Xtop_of_file ^[< # ESC-< for emacs emulation
Xbottom_of_file >
Xbottom_of_file ^[> # ESC-> for emacs emulation
Xedit_file e
Xedit_file f
Xpage_file m
Xpage_file v
Xcopy_file c
Xdelete_file d
Xrename_file r
Xcompress_file C
Xedit_directory E
Xchgrp_file G
Xhelp ?
Xhelp H
Xlink_file L
Xchmod_file M
Xchange_sort_order O
Xprint_file P
Xreread_directory g
Xreread_directory R
Xsymlink_file S
Xgunzip_file u
Xuncompress_file U
Xsearch_forward /
Xsearch_backward \\
Xshell_command !
Xversion V
Xgzip_file z
Xredisplay ^L
Xpop_this_directory q
Xquit Q
X
X.fi
X.PP
XThe up and down arrow keys are always installed if supported by your
Xterminal.
XThe '#' character begins a comment.
XThe command name may be preceded by spaces or tabs, and must be
Xseparated from the key sequence representing it by at least one space or tab.
XUnrecognized commands are ignored.
XKey sequences may contain any positive number of ASCII characters,
Xexcluding the NULL character.
XControl keys can be input literally; as a two character sequence
Xof the form '^X', with the case of the character not significant; as
Xone of the valid ANSI C backslash escape
Xsequences '\\a', '\\b','\\f', '\\n', '\\r', '\\t', '\\v';
Xor via one of the the numeric escapes sequences '\\nnn', '\\0nnn' or '\\0Xnn'.
XSince spaces and tabs are used as token separators, they cannot be a
Xpart of a key binding when typed in literally.
XYou must be careful when redefining keys that the key sequences
Xuniquely identify their respective commands.
X.PP
X.SH OPTIONS
XCommand line options are described below.
X.PP
X.IP -c
XInstructs
X.I dired
Xto sort the files in the directory listing by inode-change time,
Xinstead of alphabetically, which is the default.
X.IP -t
XInstructs
X.I dired
Xto sort the files in the directory listing by modification time.
X.IP -u
XInstructs
X.I dired
Xto sort the files in the directory listing by access time.
X.PP
XOnly one of these three options should be specified on the command
Xline. If more than one is present, the last one is the one which
Xtakes affect.
X
X.PP
X.IP dirname
X.I Dired
Xcan be invoked with or without a directory as argument. If it is not
Xpassed a directory to edit, it uses the current directory. Otherwise,
Xthe directory argument passed to
X.I dired
Xcan be an absolute pathname or a pathname relative to the current
Xdirectory, in the usual UNIX tradition.
X
X.SH "ENVIRONMENT VARIABLES"
X.IP COLUMNS
XThis value, if nonzero, overrides the number of columns as read
Xfrom termcap(3). Hence by setting
X.B COLUMNS
Xit is easy to use a
Xdifferent number of columns than termcap(3) expects for a given
Xterminal type.
X.IP DIREDPRT
XThe full command name, including any necessary arguments, of the command to
Xprint a file.
X.IP "EDITOR "
XThe name of the editor (used by the `e' and `f' commands).
X.IP "LINES "
XThis value, if nonzero, overrides the number of lines as read
Xfrom termcap(3). Hence by setting
X.B LINES
Xit is easy to use a
Xdifferent number of lines than termcap(3) expects for a given
Xterminal type.
X.IP "PAGER "
XThe name of the pager (used by the `m' and `v' commands).
X.IP "SHELL "
XThe shell used to execute the `!' command, as well as to expand filenames.
X.IP "TERM "
XThe type of terminal on which
X.I dired
Xis being run.
X.IP "HOME "
XThe user's home directory.
X
X.br
X.SH AUTHORS
XWritten by Mike Lijewski.
X
END_OF_FILE
if test 13353 -ne `wc -c <'dired.1'`; then
echo shar: \"'dired.1'\" unpacked with wrong size!
fi
# end of 'dired.1'
fi
if test -f 'dired.lpr' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'dired.lpr'\"
else
echo shar: Extracting \"'dired.lpr'\" \(15344 characters\)
sed "s/^X//" >'dired.lpr' <<'END_OF_FILE'
X
XDIRED(1) USER COMMANDS DIRED(1)
X
XNAME
X dired - a directory editor
X
XSYNOPSIS
X dired [-t | -u | -c] [dirname1 dirname2 ...]
X
X
XDESCRIPTION
X Dired is a directory editor modelled after Dired Mode in GNU
X Emacs. It displays a window consisting of a long listing of
X a directory , or the current directory if invoked without a
X directory argument. By default, the listing is in the for-
X mat of an ls -al command, which means that the listing
X lines are sorted alphabetically. The file or directory on
X the line containing the cursor is called the current file,
X with that line known as the current line. The listing can
X also be sorted by the modification time, access time, or
X inode-change time of the files in the directory by specify-
X ing one of the options `-t', `-u', or `-c', respectively.
X The sorting order can be changed from within dired by using
X the `O' (order) command. Dired provides numerous commands
X for operating on the current file including, but not limited
X to, editing, paging, deleting, renaming, copying and
X compressing.
X
X Commands, by default, are a superset of those in vi and
X emacs. In addition, on startup, dired will look for a user-
X specific version of a startup file .diredrc within which the
X user can redefine the default bindings of keys to commands.
X This is described after the description of the default bind-
X ings of keys to commands, which follows.
X
XCOMMANDS
X In the following descriptions, ^X means control-X. ESC
X stands for the ESCAPE key; for example ESC-v means the two
X character sequence "ESCAPE", then "v". TAB stands for the
X tab key. UP_ARROW stands for the up arrow key and DOWN_ARROW
X stands for the down arrow key, if your keyboard supports
X arrow keys.
X
X A number of commands require arguments; dired will prompt
X for the necessary argument(s) in such cases. The prompt
X always appears in the last line of the window. All graphi-
X cal characters are accepted as legal when responding to a
X prompt. Typing a carriage return signifies the end of
X response. Typing a ^G aborts out of the prompt, canceling
X the command. Backspace and DELETE work as expected. ^W
X deletes the word preceding the cursor, including any whi-
X tespace which may come between the cursor and the preceding
X word. ^U deletes the complete response. All other non-
X graphical characters are ignored, though signal generating
X keys (^C, ^\ and ^Z) do the right thing in a prompt, as they
X do elsewhere in dired. Filename completion is available for
X the `E' and `c' commands. Typing a tab (the TAB key) when
X in the prompt for these commands attempts to form the long-
X est unique file or directory name.
X
X ? or H
X Help - display a summary of these commands.
X
X ^V or ^F
X Scrolls listing up one window, leaving one line of
X overlap between the windows.
X
X RETURN or n or j or ^N or SPC or DOWN_ARROW
X Move cursor to next line in listing. Scrolls listing
X up one line when cursor is on the last line in the win-
X dow.
X
X ^D Scrolls listing up half a window and attempts to leave
X the cursor on the same line in the listing.
X
X ^B or ESC-v
X Scrolls listing down one window, leaving one line of
X overlap between the previous window and the next win-
X dow.
X
X k or p or ^Y or ^P or UP_ARROW
X Move cursor to previous line in the listing. Scrolls
X listing down one line when cursor is on the first line
X in the window.
X
X ^U Scrolls the listing down half a window and attempts to
X leave the cursor on the same line in the listing.
X
X ^L Repaint the screen.
X
X < or ESC-<
X Go to the first line in the listing.
X
X > or ESC->
X Go to the last line in the listing.
X
X /string
X Search forward in the listing for the next line whose
X filename contains the string. The search starts begin-
X ning with the line following the current line. A
X search without a string, repeats the previous search.
X
X \string
X Search backward in the listing for a line whose
X filename contains the string. The search starts begin-
X ning with the line preceding the current line. A
X search without a string, repeats the previous search.
X
X V Prints the version number of dired being run.
X
X q Exits from the most recently edited directory back to
X the previously edited one; otherwise it edits the next
X directory on the command line. Directories, when
X edited, are always pushed onto a stack. If their are
X no more directories to be edited, exits dired.
X
X Q Exits dired.
X
X c Copies the current file to another file, prompting for
X a name for the new file. If the first letter of the
X name of the new file is a `~', the full pathname of the
X user's home directory will be substituted for the `~'.
X Prompts to confirm the overwriting of an existing file.
X Filename completion via the TAB key is enabled when
X prompting for the filename.
X
X d Deletes the current file, prompting for affirmation of
X the delete. Typing a `y' or `Y' to the prompt goes
X ahead with the delete. Any other key cancels the
X delete request.
X
X e or f
X Invokes an editor to edit the current file. The editor
X is taken from the environment variable EDITOR, or
X defaults to " vi ". If the file is a directory, runs
X dired on that directory, pushing the directory
X currently being edited onto a stack. The `q' command
X will exit from the new directory listing putting one
X back into the previous directory listing. Editing the
X file `..', edits the parent directory of the current
X directory. It is thus possible to walk both up and
X down the directory tree.
X
X m or v
X Invokes a pager on the current file. The pager is
X taken from the environment variable PAGER, or defaults
X to " more ".
X
X r Renames the current file, prompting for the new name.
X
X u Unzip the current file using GNU unzip (gunzip).
X
X z Zip the current file using GNU zip (gzip).
X
X C Compress the current file.
X
X E Prompts for the name of a directory to edit and then
X edits it. If the first character of the directory is
X `~', the full pathname of user's home directory is sub-
X stituted for the `~', making it easier to edit
X directories relative to the user's home directory.
X Alternatively, one may simply type `! dired dirname';
X that is, dired can be called from within itself.
X Filename completion via the TAB key is enabled when
X prompting for the directory name.
X
X G Change the group of the current file, prompting for the
X new group.
X
X L Link the current file to another file, prompting for
X the name of the new file.
X
X M Change the mode of the current file, prompting for the
X new mode. The mode can be either octal, as in `755',
X or symbolic as in `go+r'. These are the same modes the
X `chmod' command accepts.
X
X P Prints the current file. The printer together with all
X relevant options are taken from the environment vari-
X able DIREDPRT. If DIREDPRT is not defined, lpr is used
X by default. It should be noted that in networked
X environments, lpr is almost never the correct choice.
X
X O Prompts for one of the characters `a', `c', `t', or `u'
X and changes the sort order to alphabetical, inode-
X change time, modification time, or access time, respec-
X tively. The sort order only takes affect when a direc-
X tory is first edited; the sort order of the currently
X displayed directory is not changed. To change the sort
X order of the currently displayed directory, use this
X command to change the sort order and then the `g' or
X `R' command to re-read the current directory.
X
X R or g
X Rereads the current directory and updates the display.
X This command is useful if you've executed a shell com-
X mand which added, deleted or modified a file or files
X in the current directory. Otherwise, dired doesn't
X know that the contents of the directory have been modi-
X fied. This command will leave the cursor on the same
X file it started on, provided that file is still in the
X current directory.
X
X S Create a symbolic link to the current file, prompting
X for the name of the symbolic link.
X
X U Uncompress the current file.
X
X !cmd Executes the shell command cmd using system(3) for exe-
X cution. A `%' in cmd is replaced with the current
X filename before being executed. This provides a con-
X venient shorthand way to execute commands on the
X current file, for which the author hasn't provided a
X key to execute the command.
X
X !! Re-executes the previously issued shell command. Does
X not re-expand any `%' characters to the name of the now
X current file.
X
X ![ Re-executes the previously issued shell command, re-
X expanding any `%' to the name of the now current file.
X
X ! Invokes a shell. Uses the shell in the environment
X variable SHELL, or defaults to " sh ".
X
X On startup dired searches for an initialization file named
X .diredrc, first in the current directory and then in the
X user's home directory. If a startup file is found, it is
X expected to contain mappings of commands to keypresses; the
X default keymap will not be enabled. The following is a sam-
X ple .diredrc corresponding to the default keymap. Your sys-
X tem administrator should have installed a copy of this as
X diredrc somewhere, when dired was installed.
X
X cursor_down j
X cursor_down n
X cursor_down 0x20 # the space character
X cursor_down ^N
X cursor_down
X
X cursor_up k
X cursor_up p
X cursor_up ^P
X cursor_up ^Y
X page_up ^F
X page_up ^V
X page_down ^B
X page_down ^[v # ESC-v for emacs emulation
X halfpage_up ^D
X halfpage_down ^U
X top_of_file <
X top_of_file ^[< # ESC-< for emacs emulation
X bottom_of_file >
X bottom_of_file ^[> # ESC-> for emacs emulation
X edit_file e
X edit_file f
X page_file m
X page_file v
X copy_file c
X delete_file d
X rename_file r
X compress_file C
X edit_directory E
X chgrp_file G
X help ?
X help H
X link_file L
X chmod_file M
X change_sort_order O
X print_file P
X reread_directory g
X reread_directory R
X symlink_file S
X gunzip_file u
X uncompress_file U
X search_forward /
X search_backward \
X shell_command !
X version V
X gzip_file z
X redisplay ^L
X pop_this_directory q
X quit Q
X
X
X The up and down arrow keys are always installed if supported
X by your terminal. The '#' character begins a comment. The
X command name may be preceded by spaces or tabs, and must be
X separated from the key sequence representing it by at least
X one space or tab. Unrecognized commands are ignored. Key
X sequences may contain any positive number of ASCII charac-
X ters, excluding the NULL character. Control keys can be
X input literally; as a two character sequence of the form
X '^X', with the case of the character not significant; as one
X of the valid ANSI C backslash escape sequences '\a',
X '\b','\f', '\n', '\r', '\t', '\v'; or via one of the the
X numeric escapes sequences '\nnn', '\0nnn' or '\0Xnn'. Since
X spaces and tabs are used as token separators, they cannot be
X a part of a key binding when typed in literally. You must
X be careful when redefining keys that the key sequences
X uniquely identify their respective commands.
X
XOPTIONS
X Command line options are described below.
X
X -c Instructs dired to sort the files in the directory
X listing by inode-change time, instead of alphabeti-
X cally, which is the default.
X
X -t Instructs dired to sort the files in the directory
X listing by modification time.
X
X -u Instructs dired to sort the files in the directory
X listing by access time.
X
X Only one of these three options should be specified on the
X command line. If more than one is present, the last one is
X the one which takes affect.
X
X
X dirname
X Dired can be invoked with or without a directory as
X argument. If it is not passed a directory to edit, it
X uses the current directory. Otherwise, the directory
X argument passed to dired can be an absolute pathname or
X a pathname relative to the current directory, in the
X usual UNIX tradition.
X
X
XENVIRONMENT VARIABLES
X COLUMNS
X This value, if nonzero, overrides the number of columns
X as read from termcap(3). Hence by setting COLUMNS it
X is easy to use a different number of columns than
X termcap(3) expects for a given terminal type.
X
X DIREDPRT
X The full command name, including any necessary argu-
X ments, of the command to print a file.
X
X EDITOR
X The name of the editor (used by the `e' and `f' com-
X mands).
X
X LINES
X This value, if nonzero, overrides the number of lines
X as read from termcap(3). Hence by setting LINES it is
X easy to use a different number of lines than termcap(3)
X expects for a given terminal type.
X
X PAGER
X The name of the pager (used by the `m' and `v' com-
X mands).
X
X SHELL
X The shell used to execute the `!' command, as well as
X to expand filenames.
X
X TERM The type of terminal on which dired is being run.
X
X HOME The user's home directory.
X
X
XAUTHORS
X Written by Mike Lijewski.
END_OF_FILE
if test 15344 -ne `wc -c <'dired.lpr'`; then
echo shar: \"'dired.lpr'\" unpacked with wrong size!
fi
# end of 'dired.lpr'
fi
if test -f 'diredrc' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'diredrc'\"
else
echo shar: Extracting \"'diredrc'\" \(1699 characters\)
sed "s/^X//" >'diredrc' <<'END_OF_FILE'
X#
X# This is a sample keyboard configuration file for dired.
X# Comments start with a '#' character and go to the end of the line.
X#
X# Valid lines are of the form:
X#
X# [ \t]*command_name[ \t]+string
X#
X
Xcursor_down j
Xcursor_down n
Xcursor_down \0x20 # the space character
Xcursor_down ^N
Xcursor_down \r
X# down arrow key will be automatically installed, if supported by your terminal
Xcursor_up k
Xcursor_up p
Xcursor_up ^P
Xcursor_up ^Y
X# up arrow key will be automatically installed, if supported by your terminal
Xpage_up ^F
Xpage_up ^V
Xpage_down ^B
Xpage_down ^[v
Xhalfpage_up ^D
Xhalfpage_down ^U
Xtop_of_file <
Xtop_of_file ^[<
Xbottom_of_file >
Xbottom_of_file ^[>
Xedit_file e
Xedit_file f
Xpage_file m
Xpage_file v
Xcopy_file c
Xdelete_file d
Xrename_file r
Xcompress_file C
Xedit_directory E
Xchgrp_file G
Xhelp ?
Xhelp H
Xlink_file L
Xchmod_file M
Xchange_sort_order O
Xprint_file P
Xreread_directory g
Xreread_directory R
X# define this only if you have symbolic links
Xsymlink_file S
Xgunzip_file u
Xuncompress_file U
Xsearch_forward /
Xsearch_backward \
Xshell_command !
Xversion V
Xgzip_file z
Xredisplay ^L
Xpop_this_directory q
Xquit Q
END_OF_FILE
if test 1699 -ne `wc -c <'diredrc'`; then
echo shar: \"'diredrc'\" unpacked with wrong size!
fi
# end of 'diredrc'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(4966 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Makefile for `dired'
X#
X# $Id: Makefile,v 1.19 1993/09/29 12:02:12 lijewski Exp $
X#
X
X#
X# Your C++ compiler goes here.
X#
XCC = CC
X
X#
X# flags you need to compile:
X#
X# Add -O if you trust your optimizer.
X#
X# Add -DNO_SYMLINKS if your OS doesn't support symbolic links. If
X# you don't define this, it is assumed that your OS supports symbolic
X# links.
X#
X# Add -DMEMMOVE if your machine has memmove(3).
X#
X# Add -DTEMPLATES is your compiler supports templates.
X#
X# Add -DL_AND_G if your version of `ls' needs the both the `l' and `g'
X# flags in order to display both the owner and group of files in a long
X# listing. Typically, BSD-based OSs need this and SYSV ones don't.
X#
X# Add -DCOMPLETION if you want to enable tab-completion on the `E'
X# and `c' commands. As currently written this assumes that you have
X# the POSIX directory routines in <dirent.h>. In particular, I use
X# opendir(), readdir() and closedir().
X#
X# Add -DSIGINTERRUPT if you need to call siginterrupt(2) in order to
X# guarantee that signals will interrupt slow system calls. If you
X# don't have siginterrupt(2), you most certainly don't need it. Even
X# if you have it you may not need it, though defining -DSIGINTERRUPT
X# in this case should be a no-op. The best bet is to define this if
X# you have siginterrupt(2). Note: Suns need this.
X#
X# Add -DNOWAITPID if you don't have waitpid(2).
X#
XCFLAGS = -DCOMPLETION -DL_AND_G -DSIGINTERRUPT -O
X
X#
X# Those flags needed to compile in the type of terminal
X# control you have. Use -DTERMIOS if you have <termios.h>, the POSIX
X# terminal control. Use -DTERMIO if you have <termio.h>, the SYSV
X# terminal control. Otherwise, the default assumes you have <sgtty.h>,
X# the BSD terminal control.
X#
X# If you choose to use -DTERMIOS and have problems, try -DTERMIO. On
X# at least two systems I've tried, the vendor hasn't had all the
X# include files set up correctly to include <unistd.h> together with
X# <osfcn.h>, among others.
X#
XTERMFLAGS = -DTERMIO
X
X#
X# libraries needed:
X#
X# -ltermcap on BSD-like systems
X# -ltermlib on SYSV-like systems
X# -lcurses on systems w/o the above libraries
X#
XLIBS = -ltermcap
X
X#
X# Program to use to install dired -- use "cp" if you don't have install.
X#
XINSTALL = install -c
X
X#
X# If you are using "cp" as "INSTALL", make this empty.
X#
XINSTFLAGS = -s
X
X
X#
X# Directory in which to install dired.
X#
XBINDIR = /usr/local/bin
X
X#
X# Directory in which to install diredrc.
X#
XLIBDIR = /usr/local/lib
X
X#
X# Directory in which to install the manpage `dired.1'.
X#
XMANDIR = /usr/local/man/man1
X
X#
X# Where C++ include files are -- needed only for makedepend.
X# You shouldn't need to run make depend, so this needn't be mucked
X# with.
X#
XINCLUDEDIR = /home/voycrs/CenterLine/c++_1.1.0-r1.0/sun4-40/include
X
X##############################################################################
X# nothing should need to be changed below here.
X##############################################################################
X
XMAKESHELL = /bin/sh
XSHELL = /bin/sh
X
XHDR = Queue.h GStack.h String.h classes.h commands.h dired.h \
X display.h keys.h version.h
X
XMISC = dired.1 dired.lpr diredrc Makefile ChangeLog INSTALL MANIFEST README
X
XOBJ = Queue.o GStack.o String.o classes.o commands.o dired.o display.o \
X keys.o utilities.o
X
XSRC = Queue.C GStack.C String.C classes.C command1.C command2.C \
X dired.C display.C keys.C utilities.C
X
Xdired: $(OBJ) $(HDR)
X $(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS)
X
Xdisplay.o: display.C display.h
X $(CC) $(CFLAGS) $(TERMFLAGS) -c display.C
X
X#
X# Commands.C is built from command1.C and command2.C the first time
X# it is needed. This is so that the shar files don't get too big.
X#
Xcommands.C: command1.C command2.C
X cat command1.C command2.C > commands.C
X
X#
X# for ObjectCenter
X#
Xdired.src:
X #load $(CFLAGS) $(TERMFLAGS) GStack.C Queue.C String.C classes.C \
X commands.C dired.C display.C keys.C utilities.C
X #load -ltermcap
X
X
Xinstall: dired diredrc dired.1
X $(INSTALL) $(INSTFLAGS) dired $(BINDIR)
X $(INSTALL) diredrc $(LIBDIR)
X $(INSTALL) dired.1 $(MANDIR)
X
Xdepend: $(SRC)
X makedepend -I$(INCLUDEDIR) $(SRC)
X
Xclean:
X -rm -f core *.o *..c dired
X
Xrealclean:
X -rm -f commands.C core *.o *~ *..c dired
X
X#
X# This depends on Rich Salz' cshar program.
X#
Xshar: $(SRC) $(HDR) $(MISC)
X cshar -n1 -e7 -o dired-01 dired.1 dired.lpr diredrc Makefile
X cshar -n2 -e7 -o dired-02 command1.C classes.h dired.h
X cshar -n3 -e7 -o dired-03 command2.C display.h keys.h
X cshar -n4 -e7 -o dired-04 classes.C dired.C display.C commands.h
X cshar -n5 -e7 -o dired-05 version.h utilities.C
X cshar -n6 -e7 -o dired-06 String.C String.h GStack.C GStack.h Queue.C
X cshar -n7 -e7 -o dired-07 Queue.h keys.C ChangeLog INSTALL MANIFEST README
X
Xtar:
X tar cf dired.tar $(SRC) $(HDR) $(MISC)
X compress -f dired.tar
X
X.SUFFIXES: .C .o
X
X.C.o:
X $(CC) $(CFLAGS) -c $<
X
X# DO NOT DELETE THIS LINE -- make depend depends on it.
END_OF_FILE
if test 4966 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
echo shar: End of archive 1 \(of 7\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 7 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

Kevin Braunsdorf

unread,
Oct 1, 1993, 6:50:11 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 66
Archive-name: dired-2.0/02

#! /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 2 (of 7)."
# Contents: command1.C classes.h dired.h


# Wrapped by lijewski@xtesoc2 on Wed Sep 29 08:11:57 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH

if test -f 'command1.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'command1.C'\"
else
echo shar: Extracting \"'command1.C'\" \(35201 characters\)
sed "s/^X//" >'command1.C' <<'END_OF_FILE'
X//
X// command1.C - some of the commands called from the main command loop.
X// Command1.C and command2.C are concatenated during the
X// make into commands.C, which consists of the main command
X// loop and all commands called from within that loop.
X//
X// $Id: command1.C,v 1.15 1993/09/27 19:40:09 lijewski Exp $
X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X
X
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#ifdef sgi
X#include <sys/sysmacros.h> /* to get minor() and major() macros */
X#endif
X
X#ifdef apollo /* no prototypes defined for these... */
Xextern "C"
X{
X int read (int, const char *, int);
X int link (const char *, const char *);
X int symlink (const char *, const char *);
X int unlink (const char *);
X int rmdir (const char *);
X int chdir (const char *);
X}
X#else
X#include <unistd.h>
X#endif
X
X#include "String.h"
X#include "commands.h"
X#include "dired.h"
X#include "display.h"
X#include "keys.h"
X#include "version.h"
X
X
X//
X// These functions aren't prototyped under ObjectCenter 2.0 on my Sun.
X//
X#if defined(sun) && defined(__CLCC__)
Xextern "C"
X{
X int symlink (const char*, const char*);
X int rename (const char*, const char*);
X}
X#endif
X
X
X//
X// Scroll the listing up one line. We only call this routine when
X// we KNOW that there is at least one line below the window which
X// can be scrolled into it and the cursor is on the last line of the screen.
X//
X
Xstatic void scroll_up_one_line (DirList *dl)
X{
X dl->setFirst(dl->firstLine()->next());
X dl->setLast(dl->lastLine()->next());
X dl->setCurrLine(dl->lastLine());
X
X if (CS)
X {
X scroll_listing_up_one();
X display_string(dl->currLine()->line(), dl->currLine()->length());
X }
X else if (DL || SF)
X {
X clear_modeline();
X scroll_screen_up_one();
X update_modeline();
X move_cursor(rows()-3, 0);
X display_string(dl->currLine()->line(), dl->currLine()->length());
X }
X else
X redisplay();
X
X dl->saveYXPos(rows()-3, goal_column(dl));
X move_cursor(rows()-3, dl->savedXPos());
X}
X
X
X//
X// Move cursor down one line.
X//
X
Xvoid cursor_down_one_line (DirList* dl)
X{
X if (dl->atEndOfList()) return;
X
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);
X
X if (dl->savedYPos() < rows() - 3)
X {
X //
X // There are still more lines below us in the window
X // so we just move the cursor down one line.
X //
X dl->setCurrLine(dl->currLine()->next());
X int x = goal_column(dl);
X if (x == dl->savedXPos())
X cursor_down();
X else
X move_cursor(dl->savedYPos() + 1, x);
X dl->saveYXPos(dl->savedYPos() + 1, x);
X }
X else
X //
X // We are on the last line on the screen and there
X // are more lines to display. Scroll up one line
X // and leave the cursor on the next logical line.
X //
X scroll_up_one_line(dl);
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X
X synch_display();
X}
X
X
X//
X// Scroll the listing down one line. We only call this routine when we KNOW
X// that the head of the listing is not visible and the cursor is on the
X// first line in the window.
X//
X
Xstatic void scroll_down_one_line (DirList *dl)
X{
X if (lines_displayed(dl) == rows() - 2)
X //
X // Must update lastLine.
X // We previously had a screenfull of lines.
X //
X dl->setLast(dl->lastLine()->prev());
X
X dl->setFirst(dl->firstLine()->prev());
X dl->setCurrLine(dl->firstLine());
X
X if (CS)
X {
X scroll_listing_down_one();
X display_string(dl->currLine()->line(), dl->currLine()->length());
X }
X else if (AL || SR)
X {
X clear_modeline();
X scroll_screen_down_one();
X update_modeline();
X cursor_home();
X display_string(dl->currLine()->line(), dl->currLine()->length());
X }
X else
X redisplay();
X
X dl->saveYXPos(0, goal_column(dl));
X move_cursor(0, dl->savedXPos());
X}
X
X
X//
X// Move cursor up one line.
X//
X
Xvoid cursor_up_one_line (DirList* dl)
X{
X if (dl->atBegOfList()) return;
X
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);
X
X if (dl->savedYPos() != 0)
X {
X //
X // We are not at the top of the window so can move up.
X //
X dl->setCurrLine(dl->currLine()->prev());
X int x = goal_column(dl);
X if (x == dl->savedXPos() && UP)
X cursor_up();
X else
X move_cursor(dl->savedYPos() - 1, x);
X dl->saveYXPos(dl->savedYPos() - 1, x);
X }
X else
X //
X // We are on the first line of the window and there are
X // lines preceding us in the directory listing.
X //
X scroll_down_one_line(dl);
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X
X synch_display();
X}
X
X
X//
X// Scroll listing up one full window, leaving one line of overlap.
X//
X
Xvoid scroll_up_full_window (DirList *dl)
X{
X if (dl->lastLine() == dl->tail())
X return;
X
X DirLine *ln = dl->lastLine();
X dl->setFirst(ln);
X dl->setCurrLine(ln);
X
X clear_display_area();
X
X for (int i = 0; i < rows() - 2 && ln; i++, ln = ln->next())
X display_string(ln->line(), ln->length());
X
X if (ln)
X dl->setLast(ln->prev());
X else
X dl->setLast(dl->tail());
X
X dl->saveYXPos(0, goal_column(dl));
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(0, dl->savedXPos());
X
X synch_display();
X}
X
X
X//
X// Try to scroll listing down one full window, with one line of overlap.
X// This routine is only called when we KNOW that there is at least one
X// line "above" the current listing. Only change the current line if it
X// flows off the "bottom" of the screen.
X//
X
Xvoid scroll_down_full_window (DirList *dl)
X{
X if (dl->firstLine() == dl->head())
X return;
X
X DirLine *ln = dl->firstLine();
X for (int y = 0; y < rows() - 3 && ln != dl->head(); y++, ln = ln->prev())
X ;
X
X //
X // y == # of lines preceding firstLine to add to screen.
X //
X
X dl->setFirst(ln);
X
X clear_display_area();
X
X for (int j = 0; j < rows()-2 && ln; j++, ln = ln->next())
X display_string(ln->line(), ln->length());
X
X if (ln)
X dl->setLast(ln->prev());
X
X if (dl->savedYPos()+y >= rows()-2)
X {
X dl->setCurrLine(dl->lastLine());
X dl->saveYXPos(rows()-3, goal_column(dl));
X }
X else
X dl->saveYXPos(dl->savedYPos()+y, dl->savedXPos());
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//
X// Scroll listing up half a window. We try to leave the cursor
X// on the file it was on previously, otherwise it is left on the
X// first file in the screen.
X//
X
Xvoid scroll_up_half_window (DirList *dl)
X{
X if (dl->lastLine() == dl->tail())
X return;
X
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);
X
X DirLine *ln = dl->firstLine();
X for (int i = 0; i < (rows() - 2)/2; i++, ln = ln->next())
X ;
X dl->setFirst(ln);
X
X if (CS || DL || SF || DLN)
X {
X if (CS)
X scroll_listing_up_N((rows()-2)/2);
X else
X {
X clear_modeline();
X scroll_screen_up_N((rows()-2)/2);
X update_modeline();
X }
X move_cursor(rows() - 2 -((rows()-2)/2), 0);
X ln = dl->lastLine()->next();
X for (i = 0; i < (rows() - 2)/2 && ln; i++, ln = ln->next())
X display_string(ln->line(), ln->length());
X
X if (ln)
X dl->setLast(ln->prev());
X else
X dl->setLast(dl->tail());
X }
X else
X {
X clear_display_area();
X
X for (i = 0; i < rows() - 2 && ln->next(); i++, ln = ln->next())
X display_string(ln->line(), ln->length());
X
X if (i != rows()-2)
X {
X //
X // We hit last line before outputing all that we could.
X // Must output lastLine == tail.
X //
X display_string(ln->line(), ln->length());
X dl->setLast(ln);
X }
X else
X dl->setLast(ln->prev());
X }
X
X int pos = dl->savedYPos() - (rows()-2)/2;
X if (pos < 0)
X {
X pos = 0;
X dl->setCurrLine(dl->firstLine());
X }
X
X dl->saveYXPos(pos, goal_column(dl));
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(pos, dl->savedXPos());
X
X synch_display();
X}
X
X
X//
X// Try to scroll listing down half a window. If freshen is true, which
X// is the default, the screen is refreshed. It is important to note
X// that we may not be able to scroll down a complete half window, since we
X// always anchor the head of the listing to the first line in the screen.
X//
X
Xvoid scroll_down_half_window (DirList *dl, int freshen)
X{
X if (dl->firstLine() == dl->head())
X return;
X
X if (dl->firstLine() != dl->head())
X {
X //
X // We can scroll down. Try to leave the cursor on the file
X // it started out on. Otherwise, leave it on the
X // (rows-2)/2 line, which was the previous firstLine.
X //
X DirLine *ln = dl->firstLine();
X for (int i = 0; i < (rows()-2)/2 && ln->prev(); i++, ln = ln->prev())
X ;
X dl->setFirst(ln);
X
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);
X
X if (CS || AL || ALN || SR)
X {
X if (CS)
X scroll_listing_down_N(i);
X else
X {
X clear_modeline();
X scroll_screen_down_N(i);
X update_modeline();
X clear_message_line();
X }
X cursor_home();
X for (int j = 0; j < i; j++, ln = ln->next())
X display_string(ln->line(), ln->length());
X ln = dl->firstLine();
X for (int i = 0; i < rows()-2 && ln->next(); i++, ln = ln->next())
X ;
X dl->setLast(ln);
X }
X else
X {
X clear_display_area();
X for (int i = 0; i < rows()-2 && ln->next(); i++, ln = ln->next())
X display_string(ln->line(), ln->length());
X
X if (i != rows() - 2)
X {
X //
X // We hit last line before outputing all that we could.
X // Must output lastLine == tail.
X //
X display_string(ln->line(), ln->length());
X dl->setLast(ln);
X i++; // So we know how many lines have been written;
X }
X else
X dl->setLast(ln->prev());
X }
X
X int pos = i + dl->savedYPos();
X if (pos > rows() - 3)
X {
X pos = rows() - 3;
X dl->setCurrLine(dl->lastLine());
X }
X
X dl->saveYXPos(pos, goal_column(dl));
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(pos, dl->savedXPos());
X
X if (freshen)
X synch_display();
X }
X}
X
X
X//
X// Position cursor on first line in listing.
X//
X
Xvoid goto_first (DirList *dl)
X{
X if (dl->atBegOfList())
X return;
X
X if (dl->head() != dl->firstLine())
X initial_listing(dl);
X else
X {
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);
X dl->setCurrLine(dl->head());
X }
X
X dl->saveYXPos(0, goal_column(dl));
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(0, dl->savedXPos());
X
X synch_display();
X}
X
X
X//
X// Position cursor on last file in listing.
X//
X
Xvoid goto_last (DirList *dl)
X{
X if (dl->atEndOfList())
X return;
X
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);
X
X dl->setCurrLine(dl->tail());
X
X if (dl->tail() == dl->lastLine())
X {
X //
X // Only need to reposition the cursor.
X //
X dl->saveYXPos(lines_displayed(dl) - 1, goal_column(dl));
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X else
X {
X //
X // Redisplay end of listing & update our view.
X //
X DirLine *ln = dl->tail();
X dl->setLast(ln);
X
X clear_display_area();
X
X for (int i = 0; i < rows() - 2; i++, ln = ln->prev())
X {
X move_cursor(rows() - 3 - i, 0);
X display_string(ln->line(), ln->length());
X }
X dl->setFirst(ln->next());
X dl->saveYXPos(rows() - 3,goal_column(dl));
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(rows() -3 , dl->savedXPos());
X }
X
X synch_display();
X}
X
X
X//
X// Prompt for a directory to edit and edit it, provided it is a valid
X// directory. If the first character of the directory is tilde, the
X// tilde is expanded to the user's home directory.
X//
X
Xvoid edit_prompted_for_directory (DirList *dl, const KeyMap& keymap)
X{
X#ifdef COMPLETION
X const char *dir = prompt("Directory to edit: ", 1); // prompt w/ completion
X#else
X const char *dir = prompt("Directory to edit: ");
X#endif
X
X if (dir == 0)
X {
X //
X // Command was aborted.
X //
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X dir = expand_tilde(dir);
X
X if (!is_directory(dir))
X {
X message("`%' is not a valid directory name", dir);
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X if (!read_and_exec_perm(dir))
X {
X message("need read & exec permission to edit `%'", dir);
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X dired(dir, keymap);
X}
X
X
X//
X// Rereads the listing line for the current line in the DirList,
X// which refers to file, and then updates the listing line.
X//
X
Xstatic void reread_listing_line (DirList *dl, const char *file)
X{
X String cmd(ls_cmd[the_sort_order()]);
X cmd += "'";
X cmd += file;
X cmd += "'";
X
X FILE *fp = popen(cmd, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)cmd);
X
X char *new_line = fgetline(fp);
X if (fp == 0)
X error("File %s, line %d: fgetline() failed", __FILE__, __LINE__);
X (void)pclose(fp);
X
X dl->currLine()->update(&new_line);
X}
X
X
X//
X// Edit the current file in the DirList.
X//
X
Xvoid edit_current_file (DirList *dl, const KeyMap& keymap)
X{
X const char *file = get_file_name(dl);
X
X if (is_directory(file))
X {
X if (strcmp(file, ".") == 0 || (strcmp(file, "..") == 0 &&
X strcmp(dl->name(), "/") == 0))
X return;
X
X if (!read_and_exec_perm(file))
X {
X message("need read & exec permissions to edit `%'", file);
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X dired(file, keymap);
X }
X else if (is_regular_file(file))
X {
X char *editor = getenv("EDITOR");
X if (editor == 0)
X editor = "vi";
X
X String cmd(editor);
X cmd += " '";
X cmd += file;
X cmd += "'";
X
X exec_with_system(cmd, 0);
X
X //
X // Re-read the listing line for this file
X // and insert into the directory listing.
X // This way, changes in file characteristics are
X // reflected in the soon-to-be updated listing.
X //
X reread_listing_line(dl, file);
X redisplay();
X }
X else
X {
X message("can only edit regular files and directories");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X }
X}
X
X
X//
X// Ask user to type any key to continue.
X//
X
Xinline void type_any_key_to_continue (void)
X{
X eat_a_character("Press Any Key to Continue");
X}
X
X
X//
X// Attempt to page the current file in the DirList.
X// If we have a directory, we edit it.
X//
X
Xvoid page_current_file (DirList *dl, const KeyMap& keymap)
X{
X const char *file = get_file_name(dl);
X if (is_directory(file))
X {
X if (strcmp(file, ".") == 0 || (strcmp(file, "..") == 0 &&
X strcmp(dl->name(), "/") == 0))
X return;
X
X if (!read_and_exec_perm(file))
X {
X message("need read & exec permissions to edit `%'", file);
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X dired(file, keymap);
X }
X else if (is_regular_file(file))
X {
X char *pager = getenv("PAGER");
X if (pager == 0)
X pager = "more";
X
X String cmd(pager);
X cmd += " '";
X cmd += file;
X cmd += "'";
X
X exec_with_system(cmd);
X
X if (the_sort_order() == ACCESS_TIME ||
X the_sort_order() == INODE_CHANGE_TIME)
X //
X // Re-read the listing line for this file
X // and insert into the directory listing.
X // This way, changes in file characteristics are
X // reflected in the soon-to-be updated listing.
X // Since we have only paged the file, the listing should only
X // change, if we are interested in the access or inode-
X // modification times.
X //
X reread_listing_line(dl, file);
X redisplay();
X }
X else
X {
X message("can only page through regular files and directories");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X }
X}
X
X
X//
X// Inserts the current line into the DirList after line y. Only called
X// if y != rows-3. That is, this routine is for the case when we do
X// not need to scroll the screen to place the line into its logical
X// place on the screen.
X//
X
Xstatic void insert_line (DirList *dl, int y )
X{
X if (CS)
X {
X insert_listing_line(y + 1);
X display_string(dl->currLine()->line(), dl->currLine()->length());
X }
X else if(AL)
X {
X clear_modeline();
X insert_blank_line(y + 1);
X display_string(dl->currLine()->line(), dl->currLine()->length());
X update_modeline();
X }
X else
X redisplay();
X}
X
X
X//
X// Inserts the listing line for dest into the DirList after line y
X// in the current window.
X//
X
Xstatic void insert_into_dirlist (DirList *dl, const char *dest, int y)
X{
X char *pos = strrchr(dest, '/');
X //
X // pos is non-zero in those cases where dest contains
X // `..' trickery. Say, ../this-dir/new-file.
X //
X String command(ls_cmd[the_sort_order()]);
X command += "'";
X command += (pos ? pos + 1 : dest);
X command += "'";
X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);
X if (fp == 0)
X error("File %s, line %d: fgetline() failed", __FILE__, __LINE__);
X (void)pclose(fp);
X
X int nlines = lines_displayed(dl);
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);
X dl->setCurrLine(dl->insert(&new_line));
X
X if (nlines == rows() - 2)
X {
X if (y == rows() - 3)
X {
X //
X // We must scroll listing up.
X //
X dl->setFirst(dl->firstLine()->next());
X dl->setLast(dl->currLine());
X
X if (CS)
X {
X scroll_listing_up_one();
X display_string(dl->currLine()->line(), dl->currLine()->length());
X }
X else if (DL || SF)
X {
X clear_modeline();
X scroll_screen_up_one();
X update_modeline();
X move_cursor(rows()-3, 0);
X display_string(dl->currLine()->line(), dl->currLine()->length());
X }
X else
X redisplay();
X
X dl->saveYXPos(y, goal_column(dl));
X move_cursor(y, dl->savedXPos());
X }
X else
X {
X //
X // Just insert line.
X //
X dl->setLast(dl->lastLine()->prev());
X insert_line(dl, y);
X dl->saveYXPos(y+1, goal_column(dl));
X move_cursor(y+1, dl->savedXPos());
X }
X }
X else
X {
X insert_line(dl, y);
X dl->saveYXPos(y + 1, goal_column(dl));
X move_cursor(y + 1, dl->savedXPos());
X if (nlines == y + 1)
X //
X // The new line becomes the new lastLine.
X //
X dl->setLast(dl->currLine());
X }
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X}
X
X
X//
X// Returns true if a y or Y is typed in response to the msg.
X// Deals with SIGWINCH and SIGTSTP. Does not synch the display.
X//
X
Xstatic int yes_or_no (const char *msg)
X{
X message(msg);
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 message(msg);
X }
X else
X {
X clear_message_line();
X return c == 'y' || c == 'Y';
X }
X
X}
X
X
X//
X// Search through the line list, starting with the given DirLine,
X// for a line whose filename contains a match for str. The
X// matching is strictly a string match using strstr, not a
X// regex match. Returns the first matching line found, or 0
X// if none found. pos is a pointer to an int containing
X// the position in the window of ln. On exit, it contains the
X// position of the matching line, relative to the starting position.
X// If forward is nonzero (the default), we search forward,
X// otherwise we search backwards. If str is NULL, we redo the
X// previous search.
X//
X
Xstatic DirLine *search (DirLine *ln, const char *str, int *pos, char forward=1)
X{
X static String saved_str;
X const char *name;
X
X if (*str)
X //
X // Save copy of str so we can redo searches.
X //
X saved_str = str;
X else if (saved_str != "")
X //
X // We have a previous search string.
X //
X str = saved_str;
X else
X return 0;
X
X while (ln)
X {
X#ifndef NO_SYMLINKS
X if ((name = BMstrstr(ln->line(), " -> ")) != 0)
X //
X // We have a symbolic link.
X //
X --name;
X else
X#endif
X name = ln->line() + ln->length();
X
X while (!isspace(*name)) --name;
X if (BMstrstr(name, str))
X return ln;
X
X if (forward)
X {
X ln = ln->next();
X (*pos)++;
X }
X else
X {
X ln = ln->prev();
X (*pos)--;
X }
X }
X
X return 0;
X}
X
X
X//
X// Returns true if the file file, which is relative to the DirList dl,
X// is actually in the DirList's directory. Returns false on any error.
X//
X
Xstatic int in_same_directory (DirList *dl, const char *file)
X{
X const char *last = strrchr(file, '/');
X
X //
X // If there are no slashes in the filename, it has to be in the
X // same directory.
X //
X if (last == 0)
X return 1;
X
X if (last == file)
X last += 1; // Watch out for root.
X String dir(file);
X dir[last - file] = 0;
X struct stat buf1, buf2;
X
X#ifdef NO_SYMLINKS
X if (stat((const char*)dir, &buf1) < 0 || stat(dl->name(), &buf2) < 0)
X#else
X if (lstat((const char*)dir, &buf1) < 0 || lstat(dl->name(), &buf2) < 0)
X#endif
X return 0;
X
X //
X // Otherwise, if its parent directory and the starting directory have
X // the same inode number and device number, the file is in the same
X // directory.
X //
X return buf1.st_ino == buf2.st_ino &&
X major(buf1.st_dev) == major(buf2.st_dev) &&
X minor(buf1.st_dev) == minor(buf2.st_dev);
X}
X
X
X//
X// Copy current file to destination. Update window appropriately.
X//
X
Xstatic void copy_file (DirList *dl, const char *src, const char *dest)
X{
X const char *cp = "cp";
X const char *args[4];
X
X args[0] = cp;
X args[1] = src;
X args[2] = dest;
X args[3] = 0;
X
X if (is_regular_file(dest))
X {
X String msg("overwrite '");
X msg += dest;
X msg += "'? (y|n) ";
X
X if (yes_or_no(msg))
X if (execute(cp, args))
X {
X message("`copy' was successful");
X
X if (in_same_directory(dl, dest))
X {
X //
X // dest is in our current directory.
X // It may contain /s due to .. or ~ funny business.
X //
X int pos = 0;
X const char *slash = strrchr(dest, '/');
X if (slash)
X dest = slash + 1;
X DirLine *found = search(dl->firstLine(), dest, &pos);
X if (found && pos < rows() - 2)
X {
X //
X // And it is also in our current window.
X //
X String cmd(ls_cmd[the_sort_order()]);
X cmd += "'";
X cmd += dest;
X cmd += "'";
X
X FILE *fp = popen(cmd, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)cmd);
X
X char *new_line = fgetline(fp);
X if (fp == 0)
X error("File %s, line %d: fgetline() failed",
X __FILE__, __LINE__);
X (void)pclose(fp);
X
X update_screen_line(found->line(), new_line, pos);
X found->update(&new_line);
X }
X }
X }
X else
X message("`copy' failed");
X
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X else if (is_directory(dest))
X {
X String file(dest);
X if (dest[strlen(dest) - 1] != '/')
X file += "/";
X file += src;
X
X if (is_regular_file(file))
X {
X String msg("overwrite `");
X msg += file;
X msg += "'? (y|n) ";
X
X if (yes_or_no(msg))
X if (execute(cp, args))
X message("`copy' was successful");
X else
X message("`copy' failed");
X }
X else
X {
X //
X // Just do the cp.
X //
X if (execute(cp, args))
X message("`copy' was successful");
X else
X message("`copy' failed");
X }
X
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X else
X {
X //
X // Dest does not already exist.
X //
X if (execute(cp, args))
X {
X message("`copy' was successful");
X //
X // Is dest in our current directory?
X //
X if (in_same_directory(dl, dest))
X insert_into_dirlist(dl, dest, dl->savedYPos());
X else
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X else
X {
X message("`copy' failed");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X }
X}
X
X
X//
X// Attempt to copy current file to another.
X//
X
Xvoid copy_current_file (DirList *dl)
X{
X const char *src = get_file_name(dl);
X
X if (strcmp(src, ".") == 0 || strcmp(src, "..") == 0)
X {
X message("`copying' of `.' and `..' is not allowed");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X#ifdef COMPLETION
X const char *dest = prompt("Copy to: ", 1); // Prompt with completion.
X#else
X const char *dest = prompt("Copy to: ");
X#endif
X
X if (dest == 0)
X {
X //
X // Command was aborted.
X //
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X else if (*dest == 0)
X {
X message("not a valid file name");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X dest = expand_tilde(dest);
X
X copy_file(dl, src, dest);
X
X synch_display();
X}
X
X
X//
X// Attempt to link file to dest.
X//
X
Xstatic void link_file (DirList *dl, const char *file, const char *dest)
X{
X if (!link(file, dest))
X {
X message("`link' was successful");
X if (in_same_directory(dl, dest))
X insert_into_dirlist(dl, dest, dl->savedYPos());
X else
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X else
X {
X message("`link' failed");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X}
X
X
X//
X// Attempt to make a link to the current file.
X//
X
Xvoid link_current_file (DirList *dl)
X{
X const char *file = get_file_name(dl);
X const char *link = prompt("Link to: ");
X
X if (link == 0)
X {
X //
X // Command was aborted.
X //
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X else if (*link == 0)
X {
X message("not a valid file name");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X link = expand_tilde(link);
X
X link_file(dl, file, link);
X
X synch_display();
X}
X
X
X#ifndef NO_SYMLINKS
X//
X// Symbolically link file to dest.
X//
X
Xstatic void symlink_file (DirList *dl, const char *file, const char *dest)
X{
X if (in_same_directory(dl, dest))
X {
X //
X // Don't need to expand pathname of `file'.
X //
X if (!symlink(file, dest))
X {
X message("`symlink' was successful");
X insert_into_dirlist(dl, dest, dl->savedYPos());
X }
X else
X {
X message("`symlink' failed");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X }
X else
X {
X //
X // Use full pathname of `file'.
X //
X String pathname(dl->name());
X if (pathname != "/")
X pathname += "/";
X pathname += file;
X
X if (!symlink((const char*)pathname, dest))
X {
X message("`symlink' was successful");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X else
X {
X message("`symlink' failed");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X }
X}
X
X
X//
X// Attempt to create a symbolic link to the current file.
X//
X
Xvoid symlink_current_file (DirList *dl)
X{
X const char *file = get_file_name(dl);
X const char *link = prompt("Name of symbolic link: ");
X
X if (link == 0)
X {
X //
X // Command was aborted.
X //
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X else if (*link == 0)
X {
X message("not a valid file name");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X
X link = expand_tilde(link);
X
X symlink_file(dl, file, link);
X
X synch_display();
X}
X#endif
X
X
X//
X// Delete the current line in the DirList and update both the screen
X// and data structures appropriately. `y' is the position in the window
X// of the current line.
X//
X
Xstatic void remove_listing_line (DirList *dl, int y)
X{
X if (dl->lastLine() != dl->tail())
X {
X //
X // Last line in listing is not in window, so we have to set
X // a new one. We can just scroll up one line.
X //
X dl->setLast(dl->lastLine()->next());
X dl->deleteLine();
X
X if (CS || DL)
X {
X if (CS)
X delete_listing_line(y);
X else
X {
X clear_modeline();
X delete_screen_line(y);
X update_modeline();
X }
X move_cursor(rows()-3, 0);
X display_string(dl->lastLine()->line(), dl->lastLine()->length());
X }
X else
X {
X clear_to_end_of_screen(y);
X move_cursor(y, 0);
X DirLine *ln = dl->currLine();
X for (int i = y; i < rows()-2; i++, ln = ln->next())
X display_string(ln->line(), ln->length());
X update_modeline();
X }
X
X dl->saveYXPos(y, goal_column(dl));
X }
X else
X {
X //
X // Last line of listing is visible in window,
X // so firstLIne and lastLine will not change.
X //
X if (dl->atWindowTop() && dl->atWindowBot())
X {
X //
X // The last line in the window is also the first line.
X // Since we do not allow deletion of . or .., there
X // must be more viewable lines. Scroll down half
X // a window to put more into view.
X //
X scroll_down_half_window(dl, 0);
X dl->deleteLine();
X DirLine *ln = dl->firstLine();
X for (int pos = 0; ln != dl->tail(); pos++, ln = ln->next())
X ;
X dl->saveYXPos(pos, goal_column(dl));
X move_cursor(pos+1, 0);
X clear_to_end_of_line();
X move_cursor(pos, dl->savedXPos());
X }
X else if (dl->atWindowBot())
X {
X //
X // We want to delete the last line in the window.
X //
X dl->deleteLine();
X move_cursor(y, 0);
X clear_to_end_of_line();
X dl->saveYXPos(y-1, goal_column(dl));
X move_cursor(y-1, dl->savedXPos());
X }
X else
X {
X //
X // We are in the middle of the listing.
X //
X dl->deleteLine();
X if (CS || DL)
X {
X if (CS)
X delete_listing_line(y);
X else
X {
X clear_modeline();
X delete_screen_line(y);
X update_modeline();
X }
X }
X else
X {
X clear_to_end_of_screen(y);
X move_cursor(y, 0);
X for (DirLine *ln = dl->currLine(); ln; ln = ln->next())
X display_string(ln->line(), ln->length());
X update_modeline();
X }
X dl->saveYXPos(y, goal_column(dl));
X }
X }
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(dl->savedYPos(), dl->savedXPos());
X}
END_OF_FILE
if test 35201 -ne `wc -c <'command1.C'`; then
echo shar: \"'command1.C'\" unpacked with wrong size!
fi
# end of 'command1.C'
fi
if test -f 'classes.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'classes.h'\"
else
echo shar: Extracting \"'classes.h'\" \(5397 characters\)
sed "s/^X//" >'classes.h' <<'END_OF_FILE'
X//
X// classes.h - the declarations of the classes used in keeping track
X// of the lines in a directory and their placement on the screen.
X//
X// $Id: classes.h,v 1.5 1993/06/20 01:51:53 lijewski Exp $
X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X
X#ifndef DIRED_CLASSES_H
X#define DIRED_CLASSES_H
X
X#include <stddef.h>
X#include <stdlib.h>
X#include <string.h>
X
X#include "GStack.h"
X#include "String.h"
X
X
X//
X// A class which contains one line of the long listing of a directory.
X//
X
Xclass DirLine
X{
X public:
X
X friend class DirList;
X
X DirLine (char **line);
X ~DirLine ();
X
X static DirLine *freeList; // we manage our own storage for DirLines
X enum { chunksize = 50 }; // size blocks of DirLines we allocate
X void *operator new(size_t size);
X void operator delete(void *object);
X
X const char* line () const;
X int length () const;
X
X DirLine* next () const;
X DirLine* prev () const;
X
X void update (char **newline);
X
X private:
X
X //
X // Disallow these operations by not providing definitions.
X // Also keep compiler from generating default versions of these.
X //
X DirLine ();
X DirLine (const DirLine &);
X DirLine& operator= (const DirLine &);
X
X String _line;
X DirLine* _next;
X DirLine* _prev;
X};
X
X
X//
X// A class which manages a doubly-linked list of DirLines.
X// It also maintains our current notion of what is and isn't
X// visible in the window.
X//
X
Xclass DirList
X{
X public:
X
X DirList (char *);
X ~DirList ();
X
X DirLine* head () const;
X DirLine* tail () const;
X DirLine* firstLine () const;
X DirLine* lastLine () const;
X DirLine* currLine () const;
X
X DirList* next () const;
X DirList* prev () const;
X
X int savedXPos () const;
X int savedYPos () const;
X
X void setFirst (DirLine *e);
X void setLast (DirLine *e);
X void setCurrLine (DirLine *ln);
X
X void setNext (DirList *l);
X void setPrev (DirList *l);
X
X const char* name () const;
X int nelems () const;
X void saveYXPos (int y, int x);
X
X int atBegOfList () const;
X int atEndOfList () const;
X
X int atWindowTop () const;
X int atWindowBot () const;
X
X DirLine* insert (char **);
X void add(DirLine *);
X void deleteLine ();
X
X private:
X
X //
X // Disallow these operations by not providing definitions.
X // Also keep compiler from generating default versions of these.
X //
X DirList ();
X DirList (const DirList &);
X DirList& operator= (const DirList &);
X
X DirLine* _head;
X DirLine* _tail;
X char* _name; // full pathname of the directory
X int _nelems;
X short _saved_x; // saved x cursor position
X short _saved_y; // saved y cursor position
X DirLine* _firstLine; // first viewable dirLine in curses window
X DirLine* _lastLine; // last viewable dirLine in curses window
X DirLine* _currLine; // line cursor is on in curses window
X DirList* _next;
X DirList* _prev;
X};
X
X
X//
X// A class which manages a stack of DirLists.
X//
X
Xclass DirStack : public GStack
X{
X public:
X
X void push (DirList *list);
X DirList* pop ();
X
X unsigned long nelems () const;
X DirList* top () const;
X};
X
X
X//
X// DirLine inlines.
X//
X
Xinline DirLine::DirLine (char **line) : _line(line) { _next = _prev = 0; }
X
Xinline DirLine::~DirLine () { }
X
Xinline const char* DirLine::line () const { return _line; }
X
Xinline int DirLine::length () const { return _line.length(); }
X
Xinline DirLine* DirLine::next () const { return _next; }
X
Xinline DirLine* DirLine::prev () const { return _prev; }
X
Xinline void DirLine::update (char **newline) { _line = String(newline); }
X
X
X//
X// DirList inlines.
X//
X
Xinline DirLine* DirList::head () const { return _head; }
X
Xinline DirLine* DirList::tail () const { return _tail; }
X
Xinline DirLine* DirList::firstLine () const { return _firstLine; }
X
Xinline DirLine* DirList::lastLine () const { return _lastLine; }
X
Xinline DirLine* DirList::currLine () const { return _currLine; }
X
Xinline DirList* DirList::next () const { return _next; }
X
Xinline DirList* DirList::prev () const { return _prev; }
X
Xinline int DirList::savedXPos () const { return _saved_x; }
X
Xinline int DirList::savedYPos () const { return _saved_y; }
X
Xinline void DirList::setFirst (DirLine *e) { _firstLine = e; }
X
Xinline void DirList::setLast (DirLine *e) { _lastLine = e; }
X
Xinline void DirList::setCurrLine (DirLine *ln) { _currLine = ln; }
X
Xinline void DirList::setNext (DirList *l) { _next = l; }
X
Xinline void DirList::setPrev (DirList *l) { _prev = l; }
X
Xinline const char* DirList::name () const { return _name; }
X
Xinline int DirList::nelems () const { return _nelems; }
X
Xinline void DirList::saveYXPos (int y, int x)
X{
X _saved_x = (short)x;
X _saved_y = (short)y;
X}
X
Xinline int DirList::atBegOfList () const { return _currLine == _head; }
X
Xinline int DirList::atEndOfList () const { return _currLine == _tail; }
X
Xinline int DirList::atWindowTop () const { return _currLine == _firstLine; }
X
Xinline int DirList::atWindowBot () const { return _currLine == _lastLine; }
X
X
X//
X// DirStack inlines.
X//
X
Xinline DirList* DirStack::top () const { return (DirList*) GStack::top(); }
X
Xinline void DirStack::push (DirList *list) { GStack::push(list); }
X
Xinline DirList* DirStack::pop () { return (DirList*) GStack::pop(); }
X
Xinline unsigned long DirStack::nelems () const { return GStack::entries(); }
X
X
X#endif /* DIRED_CLASSES_H */
END_OF_FILE
if test 5397 -ne `wc -c <'classes.h'`; then
echo shar: \"'classes.h'\" unpacked with wrong size!
fi
# end of 'classes.h'
fi
if test -f 'dired.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'dired.h'\"
else
echo shar: Extracting \"'dired.h'\" \(4135 characters\)
sed "s/^X//" >'dired.h' <<'END_OF_FILE'
X//
X// dired.h - global declarations
X//
X// $Id: dired.h,v 1.9 1993/09/19 20:28:52 lijewski Exp $
X//
X
X#ifndef DIRED_H
X#define DIRED_H
X
X
X#include <stdio.h>
X#include <string.h>
X
X#include "String.h"
X#include "keys.h"
X#include "classes.h"
X#include "commands.h"
X
X
X#ifdef apollo
Xextern "C" int putenv(const char*);
X#endif
X#if defined(linux) || defined(__sgi)
X#define major(x) (int)(((unsigned)(x)>>8)&0x7F)
X#define minor(x) (int)((x)&0xFF)
X#endif
X#ifdef __sgi
Xextern "C" int ioctl( int fildes, int request, ...);
X#endif
X#if defined(apollo) || defined(__sgi)
X#define SIGFUNC(f) void f(int ...)
X#else
X#define SIGFUNC(f) void f(int)
X#endif
X
X
X//
X// The four ways we sort a directory listing.
X//
Xenum sort_order { ALPHABETICALLY,
X MODIFICATION_TIME,
X ACCESS_TIME,
X INODE_CHANGE_TIME };
X
X//
X// GLOBALS
X//
Xextern DirStack *dir_stack; // our directory stack
Xextern sort_order how_to_sort; // default sort order
Xextern const char *const ls_cmd[4]; // command to get long listing
Xextern const char *const modeline_prefix; // modeline prefix
Xextern const char *const version; // our version number
Xextern int win_size_changed; // window size has changed?
Xextern int message_window_dirty; // something in message window?
X
X
X//
X// For POSIX compatibility.
X//
X#ifndef EXIT_FAILURE
X#define EXIT_FAILURE 1
X#endif
X#ifndef EXIT_SUCCESS
X#define EXIT_SUCCESS 0
X#endif
X
X
X//
X// from `commands.C'
X//
Xextern void read_commands (DirList *, const KeyMap&);
X
X
X//
X// from `dired.C'
X//
Xextern void dired (const char *dirname, const KeyMap&);
X
X
X//
X// from `utilities.C'
X//
Xextern char *current_directory (void);
Xextern const char* decode (const char* str);
Xextern void display_string (const char *str, size_t len = 0);
Xextern void eat_a_character (const char *msg);
Xextern const char* encode (char* str);
Xextern void error (const char *str);
Xextern void error (const char *fmt, int index);
Xextern void error (const char *fmt, const char *file, int line);
Xextern void error (const char *fmt, const char *file, int line,
X const char *str);
Xextern void exec_with_system (const char *cmd, int prompt = 1);
Xextern int execute (const char *file, const char *argv[], int closem=1);
Xextern const char *expand_tilde (const char *str);
Xextern char *fgetline (FILE *fp);
Xextern DirList *get_directory_listing (char *dirname);
Xextern const char *get_file_name (DirList *list);
Xextern int goal_column (DirList *l);
Xextern void init_screen_and_kbdr (void);
Xextern void initialize (void);
Xextern void initial_listing (DirList *dl);
Xextern int is_directory (const char *dir);
Xextern int is_regular_file (const char *file);
Xextern void leftshift_current_line (DirList *dl);
Xextern int lines_displayed (DirList *dl);
Xextern void message (const char *fmt, const char *str = 0);
X#ifdef COMPLETION
Xextern const char *prompt (const char *msg, int do_completion = 0);
X#else
Xextern const char *prompt (const char *msg);
X#endif
Xextern SIGFUNC(exit_nicely);
Xextern int read_and_exec_perm (const char *dir);
Xextern char read_from_keybd (void);
Xextern void redisplay (void);
Xextern void rightshift_current_line (DirList *dl);
Xextern void set_signals (void);
Xextern char *BMstrstr (const char *, const char *);
Xextern int tokenize (const char *line,
X const char *separators,
X char**& tokens);
Xextern void update_modeline (const char * = 0, const char * = 0);
Xextern void unset_signals (void);
Xextern SIGFUNC(winch);
X
X
X// max - the maximum of two integer arguments.
Xinline int max(int x, int y) { return x >= y ? x : y; }
X
X
X// set_sort_order - sets the sorting order.
Xinline void set_sort_order(sort_order order) { how_to_sort = order; }
X
X
X// the_sort_order - returns the sorting order
Xinline sort_order the_sort_order(void) { return how_to_sort; }
X
X#endif /*DIRED_H*/
END_OF_FILE
if test 4135 -ne `wc -c <'dired.h'`; then
echo shar: \"'dired.h'\" unpacked with wrong size!
fi
# end of 'dired.h'
fi
echo shar: End of archive 2 \(of 7\).
cp /dev/null ark2isdone

Kevin Braunsdorf

unread,
Oct 1, 1993, 6:58:08 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 67
Archive-name: dired-2.0/03

#! /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 3 (of 7)."
# Contents: command2.C display.h keys.h
# Wrapped by lijewski@xtesoc2 on Wed Sep 29 08:11:58 1993


PATH=/bin:/usr/bin:/usr/ucb ; export PATH

if test -f 'command2.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'command2.C'\"
else
echo shar: Extracting \"'command2.C'\" \(35773 characters\)
sed "s/^X//" >'command2.C' <<'END_OF_FILE'
X//
X// command2.C - the main command loop and some of the commands themselves.
X// Command1.C and command2.C are concatenated during the make
X// into commands.C, which consists of the main command loop and
X// all commands called from within that loop.
X//
X// $Id: command2.C,v 1.12 1993/09/19 20:28:52 lijewski Exp $
X//


X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X
X

X//
X// Delete the current file which is a regular file.
X// Update the window appropriately.
X//
X
Xstatic void delete_file (DirList *dl, const char *file)
X{
X if (unlink(file) != -1)
X remove_listing_line(dl, dl->savedYPos());
X else
X {
X message("`deletion' failed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X}
X
X
X//

X// Delete the current file which is a directory.
X// Update the window appropriately.
X//
X
Xstatic void delete_directory (DirList *dl, const char *dirname)
X{
X if (rmdir(dirname) != -1)
X remove_listing_line(dl, dl->savedYPos());
X else
X {
X message("`deletion' failed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X}
X
X
X//

X// Attempt to delete current file.
X//
X
Xvoid delete_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);
X

X if (strcmp(file, ".") == 0 || strcmp(file, "..") == 0)
X {
X message("`deletion' of `.' and `..' is not allowed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X String msg("delete `");


X msg += file;
X msg += "'? (y|n) ";
X

X if (is_directory(file) && yes_or_no(msg))
X delete_directory(dl, file);
X else if (yes_or_no(msg))
X delete_file(dl, file);
X else
X //
X // Do NOT do the delete.


X //
X move_cursor(dl->savedYPos(), dl->savedXPos());
X

X synch_display();
X}
X
X
X//

X// Updates the current line in the listing and its
X// representation on the screen.
X//
X
Xstatic void update_current_line (DirList *dl, char *new_line)
X{
X if (strlen(new_line) <= columns() && dl->currLine()->length() <= columns())
X {
X //
X // The most common case.
X //
X update_screen_line(dl->currLine()->line(), new_line, dl->savedYPos());
X dl->currLine()->update(&new_line);
X dl->saveYXPos(dl->savedYPos(), goal_column(dl));


X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X else
X {
X //

X // Either the old or the new line must be shifted.
X // Probably not worth trying to use update_screen_line.
X //
X dl->currLine()->update(&new_line);
X dl->saveYXPos(dl->savedYPos(), goal_column(dl));


X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else

X {
X move_cursor(dl->savedYPos(), 0);
X clear_to_end_of_line();
X display_string(new_line);


X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X }
X}
X
X
X//

X// Rename src to dest and update window appropriately.
X//
X
Xstatic void rename_file (DirList *dl, const char *src, const char *dest)
X{
X if (rename(src, dest) != -1)
X {
X message("`rename' was successful");
X if (in_same_directory(dl, dest))
X {
X //
X // The important point to note about the following code
X // is that if dest is a directory, we need to add a "-d "
X // to the ls command as in
X //
X // ls -ld source
X //
X String command = ls_cmd[the_sort_order()];
X if (is_directory(dest))
X command += "-d ";
X command += "'";
X command += dest;


X command += "'";
X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);
X if (fp == 0)
X error("File %s, line %d: fgetline() failed",

X __FILE__, __LINE__);
X (void) pclose(fp);
X
X update_current_line(dl, new_line);
X }
X else
X remove_listing_line(dl, dl->savedYPos());
X }
X else
X {
X message("`rename' failed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X}
X
X
X//

X// Attempt to rename the current file.
X//
X
Xvoid rename_current_file (DirList *dl)
X{
X const char *from_file = get_file_name(dl);
X
X if (strcmp(from_file, ".") == 0 || strcmp(from_file, "..") == 0)
X {
X message("`renaming' of `.' and `..' is not allowed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X const char *to_file = prompt("Rename to: ");
X
X if (to_file == 0)


X {
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X return;
X }

X else if (*to_file == 0)


X {
X message("not a valid file name");
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X to_file = expand_tilde(to_file);
X
X rename_file(dl, from_file, to_file);


X
X synch_display();
X}
X
X
X//

X// Compress the current file and update the window.
X//
X
Xstatic void compress_file (DirList *dl, const char *file)
X{
X String cmd("compress");
X cmd += " -f '";
X cmd += file;
X cmd += "' 1>/dev/null 2>&1";
X
X message("Compressing ... ");
X
X if (!system(cmd))
X {


X String command(ls_cmd[the_sort_order()]);
X command += "'";

X command += file;
X command += ".Z' 2>/dev/null";


X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);

X if (new_line)
X //
X // Only update the line if we were able to read the line.
X // Some versions of compress do not properly return a failure
X // code if they do not compress the file. Hence we probably
X // still have the uncompressed file lying around.
X //
X update_current_line(dl, new_line);
X (void) pclose(fp);
X
X message("Compressing ... done");
X }
X else
X message("`compress' failed");
X}
X
X
X//
X// Attempt to compress current file.
X//
X
Xvoid compress_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);
X

X //
X // Disallow compressing of symbollically linked files.
X //
X if (BMstrstr(&(dl->currLine()->line())[dl->savedXPos()], " -> "))
X {
X message("compress'ing symbollically linked files not allowed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X if (file[strlen(file)-1] == 'Z' && file[strlen(file) - 2] == '.')
X {
X message("file appears to already be compressed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X if (is_regular_file(file))
X compress_file(dl, file);
X else
X message("can only `compress' regular files");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X

X synch_display();
X}
X
X
X//

X// Compress the current file using zip and update the window.
X//
X
Xstatic void zip_file (DirList *dl, const char *file)
X{
X String cmd = "gzip";
X cmd += " -9 '";
X cmd += file;
X cmd += "' 1>/dev/null 2>&1";
X
X message("gzip'ing ... ");
X
X if (!system(cmd))
X {
X String command = ls_cmd[the_sort_order()];
X command += "'";
X command += file;
X command += ".gz' 2>/dev/null";


X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);

X if (new_line)
X //
X // Only update the line if we were able to read the line.
X //
X update_current_line(dl, new_line);
X (void) pclose(fp);
X
X message("gzip'ing ... done");
X }
X else
X message("`gzip' failed");
X}
X
X
X//
X// Attempt to zip current file.
X//
X
Xvoid zip_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);
X

X //
X // Disallow zipping of symbollically linked files.
X //
X if (BMstrstr(&(dl->currLine()->line())[dl->savedXPos()], " -> "))
X {
X message("gzip'ing symbollically linked files not allowed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X if (file[strlen(file) - 1] == 'z' &&
X file[strlen(file) - 2] == 'g' &&
X file[strlen(file) - 3] == '.')
X {
X message("file appears to already be gzip'ed");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X if (is_regular_file(file))
X zip_file(dl, file);
X else
X message("can only `gzip' regular files");


X
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X}
X
X
X//

X// Change the group of the current file & update the window appropriately.
X//
X
Xvoid chgrp_file (DirList *dl, const char *file, const char *group)
X{
X const char *chgrp = "chgrp";


X const char *args[4];
X

X args[0] = chgrp;
X args[1] = group;
X args[2] = file;


X args[3] = 0;
X

X if (execute(chgrp, args))
X {
X String command = ls_cmd[the_sort_order()];
X if (is_directory(file)) command += "-d ";
X command += "'";
X command += file;


X command += "'";
X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);
X if (fp == 0)
X error("File %s, line %d: fgetline() failed", __FILE__, __LINE__);
X (void) pclose(fp);
X

X update_current_line(dl, new_line);
X }
X else
X message("`chgrp' failed");
X}
X
X
X//
X// Attempt to chgrp current file.
X//
X
Xstatic void chgrp_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);

X const char *group = prompt("Change to group: ");
X
X if (group == 0)


X {
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X return;
X }

X else if (*group == 0)
X {
X message("not a valid group");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X chgrp_file(dl, file, group);


X
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X}
X
X
X//

X// Change the mode of the current file & update the window appropriately.
X//
X
Xstatic void chmod_file (DirList *dl, const char *file, const char *mode)
X{
X const char *chmod = "chmod";


X const char *args[4];
X

X args[0] = chmod;
X args[1] = mode;
X args[2] = file;


X args[3] = 0;
X

X if (execute(chmod, args))
X {
X String command = ls_cmd[the_sort_order()];
X if (is_directory(file))
X command += "-d ";
X command += "'";
X command += file;


X command += "'";
X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);
X if (fp == 0)
X error("File %s, line %d: fgetline() failed", __FILE__, __LINE__);
X (void) pclose(fp);
X

X update_current_line(dl, new_line);
X }
X else
X message("`chmod' failed");
X}
X
X
X//
X// Attempt to chmod the current file.
X//
X
Xvoid chmod_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);

X const char *mode = prompt("Change to mode: ");
X
X if (mode == 0)


X {
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X return;
X }

X else if (*mode == 0)
X {
X message("not a valid file mode");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X chmod_file(dl, file, mode);


X
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X}
X
X
X//

X// Attempt to print current file.
X//
X
Xvoid print_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);

X char *printer = getenv("DIREDPRT");
X if (printer == 0)
X printer = "lpr";
X
X String cmd(printer);


X cmd += " '";
X cmd += file;
X cmd += "'";
X

X if (is_regular_file(file))
X if (!system(cmd))
X message("`%s' printed successfully", file);
X else
X message("print of `%s' failed", file);
X else
X message("`%s' isn't a regular file", file);


X
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X}
X
X
X//

X// Uncompress the file at line y in the DirList & update
X// the window appropriately.
X//
X
Xstatic void uncompress_file (DirList *dl, const char *file)
X{
X String cmd("uncompress");


X cmd += " '";
X cmd += file;

X cmd += "' 1>/dev/null 2>&1";
X
X message("Uncompressing ... ");
X
X if (!system(cmd))
X {
X char *dot = strrchr(file, '.');
X //
X // Remove .Z suffix.
X //
X *dot = 0;
X


X String command(ls_cmd[the_sort_order()]);
X command += "'";

X command += file;
X command += "' 2>/dev/null";


X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);

X if (new_line)
X //
X // Only update the line if we were able to read the line.
X // Some versions of compress do not properly return a failure
X // code if they do not uncompress the file. Hence we probably
X // still have the .Z file lying around.
X //
X update_current_line(dl, new_line);
X (void) pclose(fp);
X
X message("Uncompressing ... done");
X }
X else
X message("`uncompress' failed");
X}
X
X
X//
X// Attempt to uncompress current file.
X//
X
Xvoid uncompress_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);
X

X if (file[strlen(file)-1] != 'Z' && file[strlen(file)-1] != '.')
X {
X message("can only `uncompress' compressed files");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X uncompress_file(dl, file);


X
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X}
X
X
X//

X// Search forward for file or directory matching string. The search
X// always starts with the file following the current file. If a
X// match occurs, the cursor is placed on the new current file, with
X// that line placed at the top of the window.
X//
X
Xvoid search_forward (DirList *dl)
X{
X const char *str = prompt("Search forward for: ");
X if (str == 0)


X {
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X return;
X }
X

X DirLine *ln = dl->currLine();

X if (ln == dl->tail())
X {
X message("no match");


X move_cursor(dl->savedYPos(), dl->savedXPos());

X return;
X }
X
X int pos = dl->savedYPos()+1;
X DirLine *found = search(ln->next(), str, &pos);
X if (found)


X {
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);

X dl->setCurrLine(found);
X }
X
X if (found && pos < rows()-2)
X {
X //
X // We found a match in our current window.
X // Only need to update the cursor position.
X //


X dl->saveYXPos(pos, goal_column(dl));
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(pos, dl->savedXPos());
X }

X else if (found)
X {
X //
X // We found a match, but it is out of our current view.
X // Place the matched line at the top of the window.
X //
X ln = found;


X dl->setFirst(ln);
X
X clear_display_area();
X

X for (int i = 0; i < rows() - 2 && ln; i++, ln = ln->next())
X display_string(ln->line(), ln->length());
X
X if (ln)
X dl->setLast(ln->prev());
X else
X dl->setLast(dl->tail());
X
X dl->saveYXPos(0, goal_column(dl));
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(0, dl->savedXPos());
X }

X else
X {
X message("no match");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X }
X

X synch_display();
X}
X
X
X//

X// Search backward for file or directory matching string. The search
X// always starts with the file following the current file. If a
X// match occurs, the cursor is placed on the new current file, with
X// that line placed at the top of the window.
X//
X
Xvoid search_backward (DirList *dl)
X{
X const char *str = prompt("Search backward for: ");
X if (str == 0)


X {
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X return;
X }
X

X DirLine *ln = dl->currLine();

X if (ln == dl->head())
X return;
X
X int pos = dl->savedYPos()-1;
X DirLine *found = search(ln->prev(), str, &pos, 0);
X if (found)


X {
X if (dl->currLine()->length() > columns())
X rightshift_current_line(dl);

X dl->setCurrLine(found);
X }
X
X if (found && pos >= 0)
X {
X //
X // We found a match in our current window.
X // Only need to update the cursor position.
X //


X dl->saveYXPos(pos, goal_column(dl));
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(pos, dl->savedXPos());
X }

X else if (found)
X {
X //
X // We found a match, but it is out of our
X // current view. Place the matched line at the top
X // of the window. Since we are searching backwards
X // and the match is out of our present window, we
X // must have a whole new window to display.
X //
X dl->setFirst(ln = found);
X
X clear_display_area();
X
X for (int i = 0; i < rows() - 2 && ln->next(); i++, ln = ln->next())


X display_string(ln->line(), ln->length());
X

X dl->setLast(ln->prev());


X dl->saveYXPos(0, goal_column(dl));
X
X if (dl->currLine()->length() > columns())
X leftshift_current_line(dl);
X else
X move_cursor(0, dl->savedXPos());
X }

X else
X {
X message("no match");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X }

X synch_display();
X}
X
X
X//

X// The help file is maintained as an array of Strings. It
X// must be initialized after the KeyMap is calculated and sorted
X// and before help() is called.
X//
Xstatic String* HelpFile;
X
X
X//
X// Dimension of HelpFile
X//
Xstatic int HelpFileDim;
X
X
X//
X// Initializes the help file.
X//
X
Xvoid initialize_helpfile (const KeyMap& keymap)
X{
X const int DIM = keymap.entries();
X
X HelpFile = ::new String[DIM];
X HelpFileDim = DIM;
X
X int longest_prefix = 0;
X
X for (int i = 0; i < DIM; i++)
X {
X HelpFile[i] = decode(keymap[i]->sequence);
X if (HelpFile[i].length() > longest_prefix)
X longest_prefix = HelpFile[i].length();
X }
X
X //
X // Add two more spaces for easy reading.
X //
X longest_prefix += 2;
X
X for (i = 0; i < DIM; i++)
X {
X for (int pad = longest_prefix - HelpFile[i].length(); pad; pad--)
X HelpFile[i] += " ";
X HelpFile[i] += keymap[i]->help_msg;


X }
X}
X
X
X//

X// Help message for the message window when displaying help.
X//
Xconst char *const HelpMessage[] =
X{
X "Space scrolls forward. Other keys quit.",
X "Space forward, Backspace/Delete backward. Other keys quit.",
X "Backspace/Delete scrolls backward. Other keys quit."
X};
X
X
X//
X// Give some help. Deal with SIGWINCH and SIGTSTP.
X//
X
Xvoid help (DirList *dl)
X{
X update_modeline("----- HELP");
X
X int position = 0;
X char key;
X do
X {
X clear_display_area();
X
X for (int i = 0; i < rows() - 2 && i + position < HelpFileDim; i++)
X display_string(HelpFile[position+i],HelpFile[position+i].length());
X
X clear_message_line();
X
X if (position + rows() -2 >= HelpFileDim)
X //
X // The tail of the help message.
X //
X (void) fputs(HelpMessage[2], stdout);
X else if (position == 0)
X //
X // The head of the help message.
X //
X (void) fputs(HelpMessage[0], stdout);
X else
X //
X // Somewhere in between.
X //
X (void) fputs(HelpMessage[1], stdout);
X
X synch_display();
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 else if (key == ' ')
X {
X if (position >= HelpFileDim - 1)
X goto finished;
X position += rows() - 2;
X }
X else if (key == *BC || key == *DC)
X {
X if (position == 0)
X goto finished;
X position -= rows() - 2;
X }
X else
X goto finished; // Return to the listing.
X }
X while (position < HelpFileDim - 1);
X
X finished:
X
X update_modeline(modeline_prefix, dl->name());


X redisplay();
X}
X
X
X//

X// Reread the current directory and put it up fresh on the screen.
X// We attempt to put the cursor back onto the previous current file,
X// if that file still exists.
X//
X
Xvoid reread_current_directory (DirList * &dlr)
X{
X char *dirname = new char[strlen(dlr->name()) + 1];
X (void) strcpy(dirname, dlr->name());
X const char *old_current_file = get_file_name(dlr);
X
X //
X // Position in old DirList of old_current_file.
X //
X int old_pos;
X DirLine *ln = dlr->head();
X for (old_pos = 0; ln != dlr->currLine(); ln = ln->next(), old_pos++)
X ;
X
X delete dir_stack->pop();
X
X DirList *dl = get_directory_listing(dirname);
X if (dl == 0)
X error("File %s, line %d: couldn't read directory `%s'",
X __FILE__, __LINE__, dirname);
X //
X // Update dir_list in read_commands.
X //
X dlr = dl;
X
X dir_stack->push(dl);
X
X ln = dl->head();


X int pos = 0;

X const char *name;


X
X while (ln)
X {
X#ifndef NO_SYMLINKS
X if ((name = BMstrstr(ln->line(), " -> ")) != 0)
X //
X // We have a symbolic link.
X //
X --name;
X else
X#endif
X name = ln->line() + ln->length();
X

X for (int namelen = 0; isspace(*name) == 0; name--, namelen++)
X ;
X
X if (strncmp(name + 1, old_current_file, namelen) == 0)
X break;


X
X ln = ln->next();

X pos++;
X }
X
X if (ln == 0)
X {
X //
X // `old_current_file' doesn't exist anymore. We must
X // choose another file to make current. This can only
X // happen when the file was removed by some means unknown
X // to dired -- the infamous `!' command.
X //
X ln = dl->head();
X for (pos = 0; pos < old_pos && ln; ln = ln->next(), pos++)
X ;
X if (ln == 0)
X {
X //
X // This can happen if the user has does something dastardly such
X // as a `!' command which removes part of the tail of the listing
X // that contained the current file.
X //
X ln = dl->tail();
X --pos;
X }
X }
X
X dl->setCurrLine(ln);
X
X if (pos < rows() - 2)
X {
X //
X // Display starting at the head.
X //
X ln = dl->head();
X dl->setFirst(ln);
X dl->saveYXPos(pos, goal_column(dl));


X }
X else
X {
X //

X // Center dl->currLine.
X //
X ln = dl->currLine();
X for (int i = 0; i < (rows() - 1)/2; i++, ln = ln->prev())
X ;
X dl->setFirst(ln);
X dl->saveYXPos((rows() - 1)/2, goal_column(dl));
X }
X
X clear_display_area();
X
X for (int i = 0; i < rows() - 2 && ln; ln = ln->next(), i++)


X display_string(ln->line(), ln->length());
X
X if (ln)
X dl->setLast(ln->prev());
X else
X dl->setLast(dl->tail());
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//

X// Unzip the file at line y in the DirList & update the window appropriately.
X//
X
Xvoid unzip_file (DirList *dl, const char *file)
X{
X String cmd = "gunzip";


X cmd += " '";
X cmd += file;

X cmd += "' 1>/dev/null 2>&1";
X
X message("gunzip'ing ... ");
X
X if (!system(cmd))
X {
X char *dot = strrchr(file, '.');
X //
X // Remove .z suffix.
X //
X *dot = 0;
X
X String command = ls_cmd[the_sort_order()];
X command += "'";
X command += file;
X command += "' 2>/dev/null";


X
X FILE *fp = popen(command, "r");
X if (fp == 0)
X error("File %s, line %d: popen(%s) failed",
X __FILE__, __LINE__, (const char *)command);
X
X char *new_line = fgetline(fp);

X if (new_line)
X //
X // Only update the line if we were able to read the line.
X //
X update_current_line(dl, new_line);
X (void) pclose(fp);
X
X message("gnzip'ing ... done");
X }
X else
X message("`gunzip' failed");
X}
X
X
X//
X// Attempt to unzip current file.
X//
X
Xstatic void unzip_current_file (DirList *dl)


X{
X const char *file = get_file_name(dl);

X int len = strlen(file);
X
X if (len < 4 || strcmp(&file[len-3], ".gz"))
X {
X message("can only `unzip' gzip'ed files");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X

X unzip_file(dl, file);


X
X move_cursor(dl->savedYPos(), dl->savedXPos());

X synch_display();
X}
X
X
X//

X// Expand any percent signs in cmd to file. The result is stored
X// in volatile storage. We must be careful about %s embedded in file itself.
X//
X
Xstatic const char *expand_percent (const char *cmd, const char *file)
X{
X static String expanded_cmd;
X const char *pos = strchr(cmd, '%');
X
X if (!pos)
X return cmd;
X
X expanded_cmd = "";
X for (const char *oldpos = cmd; pos; oldpos=pos+1, pos=strchr(oldpos, '%'))
X {
X expanded_cmd += String(oldpos, pos - oldpos);
X expanded_cmd += file;
X }
X expanded_cmd += oldpos;
X
X return (const char *)expanded_cmd;
X}
X
X
X//
X// Execute a shell command. % is expanded to the current filename.
X// If *cmd == 0, start up a shell.
X// If *cmd == !, reexecute most recent shell command.
X//
X
Xvoid shell_command (DirList *dl)
X{
X static String original_cmd;
X static String saved_cmd;
X static String saved_shell;


X const char *file = get_file_name(dl);

X const char *cmd = prompt("!");
X
X if (cmd == 0)
X {
X //
X // Command aborted.


X //
X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }

X else if (*cmd == 0)
X {
X //
X // Start up a shell.
X //
X if (saved_shell == "")
X saved_shell = getenv("SHELL");
X if (saved_shell == "")
X saved_shell = "sh";
X saved_cmd = original_cmd = saved_shell;
X
X const char *slash = strrchr(saved_shell, '/');
X const char *args[2];
X args[0] = slash ? slash + 1 : (const char *)saved_shell;
X args[1] = 0;
X
X message("Starting interactive shell ...");
X
X cursor_wrap();
X synch_display();
X unsetraw();
X unset_signals();
X
X execute(saved_shell, args, 0);
X
X set_signals();
X setraw();
X }
X else if (*cmd == '[')
X {
X //
X // Re-expand previous original command, if it contains a %.
X // Otherwise, re-execute previous saved command since the original
X // and saved will be the same.
X //
X if (original_cmd != "")
X {
X //
X // Expand the %.
X //
X saved_cmd = expand_percent(original_cmd, file);
X message(saved_cmd);
X cursor_wrap();
X
X exec_with_system(saved_cmd);
X }
X else
X {
X message("No Previous Shell Command");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X }

X else if (*cmd == '!')
X {
X //
X // Re-execute previously saved command.
X //
X if (saved_cmd != "")
X {
X message(saved_cmd);
X cursor_wrap();
X
X exec_with_system(saved_cmd);
X }
X else
X {
X message("No Previous Shell Command");


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X return;
X }
X }

X else
X {
X //

X // Expand and then execute command.
X //
X original_cmd = cmd;
X saved_cmd = expand_percent(original_cmd, file);
X message(saved_cmd);
X cursor_wrap();
X
X exec_with_system(saved_cmd);
X }


X redisplay();
X}
X
X
X//

X// Query the user for a new sorting order - expects to read a single
X// character - and change the sort order to that value, if it is a
X// valid value. Deals with SIGWINCH and SIGTSTP.
X//
X
Xvoid change_sort_order (DirList *dl)
X{
X const char *msg = "Set sort order to? [a, c, t, u]: ";
X message(msg);
X
X char key;
X while (1)
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 // Redisplay the message - termstop takes care of the rest
X // in the case of SIGTSTP.
X //


X message(msg);
X }
X else

X switch(key)
X {
X case 'a':
X set_sort_order(ALPHABETICALLY);
X message("Sorting `alphabetically' now.");
X goto end;
X case 'c':
X set_sort_order(INODE_CHANGE_TIME);
X message("Sorting by `inode-change time' now.");
X goto end;
X case 't':
X set_sort_order(MODIFICATION_TIME);
X message("Sorting by `modification time' now.");
X goto end;
X case 'u':
X set_sort_order(ACCESS_TIME);
X message("Sorting by `access time' now.");
X goto end;
X default:
X message("Sort order not changed.");
X goto end;
X }
X end:


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X}
X
X

X//
X// Pop a directory. If this is the last directory in the directory
X// stack we return 1 which signifies that we must exit the command
X// execution loop.
X//
X
Xint pop_a_directory (DirList*& dl)
X{
X delete dir_stack->pop();
X
X if (dir_stack->isEmpty())
X return 1;
X
X dl = dir_stack->top();
X
X //
X // Update our current directory.
X //
X if (chdir(dl->name()) < 0)
X error("File %s, line %d: couldn't chdir() to `%s'",
X __FILE__, __LINE__, dl->name());
X //
X // We track the CWD and PWD variables if defined,
X // so that applications, such as emacs, which use them
X // will work properly.
X //
X if (getenv("CWD"))
X {
X static String str;
X static String ostr;
X str = "CWD=";
X str += dl->name();
X if (putenv(str) < 0)
X error("File %s, line %d: putenv(%s) failed.",
X __FILE__, __LINE__, dl->name());
X ostr = str;
X }
X if (getenv("PWD"))
X {
X static String str;
X static String ostr;
X str = "PWD=";
X str += dl->name();
X if (putenv(str) < 0)
X error("File %s, line %d: putenv(%s) failed.",
X __FILE__, __LINE__, dl->name());
X ostr = str;
X }
X
X update_modeline(modeline_prefix, dl->name());
X redisplay();


X
X return 0;
X}
X
X
X//

X// Bye!
X//
X
Xvoid quit (DirList*)
X{
X deinit_screen_and_kbdr();
X exit(0);
X}
X
X
X//
X// Print out the version string.
X//
X
Xvoid display_version_string (DirList* dl)
X{
X message(version);


X move_cursor(dl->savedYPos(), dl->savedXPos());
X synch_display();
X}
X
X

X//
X// The command loop.
X//
X
Xvoid read_commands (DirList *dir_list, const KeyMap& keymap)
X{
X CommandCode cmd;
X
X for (;;)
X {
X if (dir_stack->isEmpty())
X return;
X
X cmd = get_command(keymap);
X
X if (message_window_dirty)
X {
X clear_message_line();
X move_cursor(dir_list->savedYPos(), dir_list->savedXPos());
X synch_display();
X message_window_dirty = 0;
X }
X
X switch (cmd)
X {
X case CURSOR_DOWN: cursor_down_one_line(dir_list); break;
X
X case CURSOR_UP: cursor_up_one_line(dir_list); break;
X
X case PAGE_UP: scroll_up_full_window(dir_list); break;
X
X case PAGE_DOWN: scroll_down_full_window(dir_list); break;
X
X case HALFPAGE_UP: scroll_up_half_window(dir_list); break;
X
X case HALFPAGE_DOWN: scroll_down_half_window(dir_list); break;
X
X case TOP_OF_FILE: goto_first(dir_list); break;
X
X case BOTTOM_OF_FILE: goto_last(dir_list); break;
X
X case EDIT_FILE: edit_current_file(dir_list, keymap); break;
X
X case PAGE_FILE: page_current_file(dir_list, keymap); break;
X
X case COPY_FILE: copy_current_file(dir_list); break;
X
X case DELETE_FILE: delete_current_file(dir_list); break;
X
X case RENAME_FILE: rename_current_file(dir_list); break;
X
X case COMPRESS_FILE: compress_current_file(dir_list); break;
X
X case EDIT_DIRECTORY:
X
X edit_prompted_for_directory(dir_list, keymap); break;
X
X case CHGRP_FILE: chgrp_current_file(dir_list); break;
X
X case HELP: help(dir_list); break;
X
X case LINK_FILE: link_current_file(dir_list); break;
X
X case CHMOD_FILE: chmod_current_file(dir_list); break;
X
X case CHANGE_SORT_ORDER: change_sort_order(dir_list); break;
X
X case PRINT_FILE: print_current_file(dir_list); break;
X
X case REREAD_DIRECTORY: reread_current_directory(dir_list); break;
X
X#ifndef NO_SYMLINKS
X case SYMLINK_FILE: symlink_current_file(dir_list); break;
X#endif
X
X case GUNZIP_FILE: unzip_current_file(dir_list); break;
X
X case UNCOMPRESS_FILE: uncompress_current_file(dir_list); break;
X
X case SEARCH_FORWARD: search_forward(dir_list); break;
X
X case SEARCH_BACKWARD: search_backward(dir_list); break;
X
X case SHELL_COMMAND: shell_command(dir_list); break;
X
X case GZIP_FILE: zip_current_file(dir_list); break;
X
X case REDISPLAY: redisplay(); break;
X
X case VERSION: display_version_string(dir_list); break;
X
X case POP_THIS_DIRECTORY:
X
X if (pop_a_directory(dir_list))
X return;
X break;
X
X case QUIT: quit(dir_list); break;
X
X case UNKNOWN: ding(); break;
X
X default:
X error("file %s, line %d, how did this happen?",
X __FILE__, __LINE__);
X break;
X
X }
X }
X}
END_OF_FILE
if test 35773 -ne `wc -c <'command2.C'`; then
echo shar: \"'command2.C'\" unpacked with wrong size!
fi
# end of 'command2.C'
fi
if test -f 'display.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'display.h'\"
else
echo shar: Extracting \"'display.h'\" \(6934 characters\)
sed "s/^X//" >'display.h' <<'END_OF_FILE'
X//
X// display.h
X//
X// external definitions needed for interfacing with display.C
X//
X// $Id: display.h,v 1.7 1993/06/20 01:51:53 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X

X#ifndef DIRED_DISPLAY_H
X#define DIRED_DISPLAY_H
X
X
X//
X// termcap capabilities we'll try to use
X//
Xextern char *AL; // insert blank line before cursor
Xextern char *ALN; // insert N blank lines before cursor
Xextern int AM; // automatic margins?
Xextern char *BC; // backspace, if not BS
Xextern int BS; // ASCII backspace works
Xextern char *CD; // clear to end of display
Xextern char *CE; // clear to end of line
Xextern char *CL; // clear screen
Xextern int CO; // number of columns
Xextern char *CM; // cursor motion
Xextern char *CR; // cursor beginning of line
Xextern char *CS; // set scroll region
Xextern int DA; // backing store off top?
Xextern int DB; // backing store off bottom?
Xextern char *DC; // delete character at cursor
Xextern char *DL; // delete line cursor is on
Xextern char *DLN; // delete N lines at cursor
Xextern char *DM; // string to enter delete mode
Xextern char *DO; // cursor down
Xextern char *ED; // string to end delete mode
Xextern int HC; // hardcopy terminal?
Xextern char *HO; // cursor home
Xextern char *IS; // initialize terminal
Xextern char *KD; // down arrow key
Xextern char *KE; // de-initialize keypad
Xextern char *KH; // HOME key
Xextern char *KHH; // END key
Xextern char *KN; // PgDn key
Xextern char *KP; // PgUp key
Xextern char *KS; // initialize keypad (for arrow keys)
Xextern char *KU; // up arrrow key
Xextern char *LE; // cursor back one column
Xextern int LI; // number of rows
Xextern char *LL; // cursor to lower left
Xextern int OS; // terminal overstrikes?
Xextern char PC; // pad character
Xextern char *PCstr; // pad string
Xextern char *SE; // end standout mode
Xextern char *SF; // scroll screen up one line
Xextern char *SO; // enter standout mode
Xextern char *SR; // scroll screen down one line
Xextern char *TE; // end cursor addressing mode
Xextern char *TI; // enter cursor addressing mode
Xextern char *UP; // cursor up
Xextern char *VE; // end visual mode
Xextern char *VS; // enter visual mode
Xextern char *XN; // strange wrap behaviour
X
X
X//
X// termcap routines
X//
Xextern "C"
X{
X extern short ospeed; // terminal speed - needed by tputs()
X#if !defined(__GNUG__) || __GNUG__ == 2
X int tgetent (const char *buf, const char *name);
X int tgetflag (const char *);
X int tgetnum (const char *);
X char *tgetstr (const char *, char **);
X char *tgoto (const char *, int, int);
X int tputs (const char *, int, int (*)(int));
X#endif
X}
X
X
X//
X// functions defined in display.C
X//
X
Xextern void backspace (void);
Xextern void clear_display_area (void);
Xextern void clear_message_line (void);
Xextern void clear_to_end_of_screen (int);
Xextern void deinit_screen_and_kbdr (void);
Xextern void delete_listing_line (int);
Xextern void initialize (void);
Xextern void insert_listing_line (int);
Xextern int outputch (int);
Xextern void scroll_listing_up_one (void);
Xextern void scroll_listing_down_one (void);
Xextern void scroll_listing_up_N (int);
Xextern void scroll_listing_down_N (int);
Xextern void scroll_screen_up_one (void);
Xextern void scroll_screen_down_one (void);
Xextern void scroll_screen_up_N (int);
Xextern void scroll_screen_down_N (int);
Xextern void setraw (void);
Xextern void termcap (const char *);
Xextern SIGFUNC(termstop);
Xextern void unsetraw (void);
Xextern void update_screen_line (const char *, const char *, int);
X
X//
X// Output a string capability from termcap to the terminal. The second
X// argument, which defaults to 1, is the number of rows affected.
X//
X
Xinline void output_string_capability(const char *capability, int affected = 1)
X{
X if (capability)
X tputs(capability, affected, outputch);
X}
X
Xinline int rows() { return LI; }
X
Xinline int columns() { return CO; }
X
Xinline void initialize_terminal() { output_string_capability(IS); }
X
Xinline void synch_display() { (void)fflush(stdout); }
X
Xinline void enter_cursor_addressing_mode() { output_string_capability(TI); }
X
Xinline void enable_keypad() { output_string_capability(KS); }
X
Xinline void disable_keypad() { output_string_capability(KE); }
X
Xinline void enter_visual_mode() { output_string_capability(VS); }
X
Xinline void end_visual_mode() { output_string_capability(VE); }
X
Xinline void end_cursor_addressing_mode() { output_string_capability(TE); }
X
Xinline void enter_standout_mode() { output_string_capability(SO); }
X
Xinline void end_standout_mode() { output_string_capability(SE); }
X
Xinline void enter_delete_mode() { output_string_capability(DM); }
X
Xinline void end_delete_mode() { output_string_capability(ED); }
X
Xinline void move_cursor(int row, int column)
X{
X if (column >= columns())
X column = columns()-1;
X output_string_capability(tgoto(CM, column, row));
X}
X
Xinline void cursor_home()
X{
X HO ? output_string_capability(HO) : move_cursor(0, 0);
X}
X
Xinline void clear_to_end_of_line() { output_string_capability(CE); }
X
Xinline void move_to_modeline() { move_cursor(rows() - 2, 0); }
X
Xinline void move_to_message_line()
X{
X if (LL)
X output_string_capability(LL);
X else
X move_cursor(rows()-1, 0); }
X
Xinline void clear_modeline() { move_to_modeline(); clear_to_end_of_line(); }
X
Xinline void cursor_up() { output_string_capability(UP); }
X
Xinline void delete_char_at_cursor()
X{
X if (DM)
X output_string_capability(DM);
X output_string_capability(DC);
X if (ED)
X output_string_capability(ED);
X}
X
Xinline void delete_screen_line(int y)
X{
X move_cursor(y, 0);
X output_string_capability(DL, rows()-y);
X}
X
Xinline void insert_blank_line(int y)
X{
X move_cursor(y, 0);
X output_string_capability(AL, rows()-y);
X}
X
Xinline void cursor_down() { output_string_capability(DO); }
X
Xinline void cursor_beginning_of_line() { output_string_capability(CR); }
X
Xinline void cursor_wrap()
X{
X cursor_beginning_of_line();
X cursor_down();
X}
X
X//
X// immediately ring the bell
X//
X
Xinline void ding() {
X //
X // This should be `output('\a')', but some braindead C compilers when
X // used as the backend to Cfront, don't recognize '\a' as the BELL.
X //
X outputch(7);
X synch_display();
X}
X
X#endif
END_OF_FILE
if test 6934 -ne `wc -c <'display.h'`; then
echo shar: \"'display.h'\" unpacked with wrong size!
fi
# end of 'display.h'
fi
if test -f 'keys.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'keys.h'\"
else
echo shar: Extracting \"'keys.h'\" \(2489 characters\)
sed "s/^X//" >'keys.h' <<'END_OF_FILE'
X//
X// keys.h - contains declarations needed for virtual keyboard.
X//
X// $Id: keys.h,v 1.8 1993/06/20 01:51:53 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X

X#ifndef KEYS_H
X#define KEYS_H
X
X
X//
X// Valid keyboard generated commands.
X//
Xenum CommandCode
X{
X CURSOR_DOWN,
X CURSOR_UP,
X PAGE_UP,
X PAGE_DOWN,
X HALFPAGE_UP,
X HALFPAGE_DOWN,
X TOP_OF_FILE,
X BOTTOM_OF_FILE,
X EDIT_FILE,
X PAGE_FILE,
X COPY_FILE,
X DELETE_FILE,
X RENAME_FILE,
X COMPRESS_FILE,
X EDIT_DIRECTORY,
X CHGRP_FILE,
X HELP,
X LINK_FILE,
X CHMOD_FILE,
X CHANGE_SORT_ORDER,
X PRINT_FILE,
X REREAD_DIRECTORY,
X#ifndef NO_SYMLINKS
X SYMLINK_FILE,
X#endif
X GUNZIP_FILE,
X UNCOMPRESS_FILE,
X SEARCH_FORWARD,
X SEARCH_BACKWARD,
X SHELL_COMMAND,
X VERSION,
X GZIP_FILE,
X REDISPLAY,
X POP_THIS_DIRECTORY,
X QUIT,
X UNKNOWN
X};
X
X
X//
X// This is a forward definition an array of help strings for
X// each of the functions. This assumes that the CommandCode
X// enums are integers from 0 through UNKNOWN.
X//
X
Xextern const char* Help[];
X
X
X//
X// Ordered pair of a CommandCode with the name of the human-readable
X// name of the command. Used only when initializing a keymap.
X//
X
Xstruct Command
X{
X CommandCode keycode;
X char* name;
X};
X
X
X//
X// This maps a CommandCode to the sequence of characters which must
X// be typed at the keyboard to invoke the command. Also holds the help
X// message for the command.
X//
X
Xstruct EncodedCommand
X{
X CommandCode keycode;
X String sequence;
X const char* help_msg;
X
X EncodedCommand (CommandCode c, const char* s)
X :keycode(c), sequence(s), help_msg(Help[c]) { }
X EncodedCommand (CommandCode c, const String& s)
X :keycode(c), sequence(s), help_msg(Help[c]) { }
X};
X
X
X//
X// Class modelling a KeyMap.
X//
X
Xclass KeyMap
X{
X public:
X
X KeyMap ();
X ~KeyMap ();
X
X KeyMap (const KeyMap& rhs);
X
X KeyMap& operator= (const KeyMap& rhs);
X
X void add (CommandCode code, const char* seq);
X void add (CommandCode code, const String& seq);
X
X int entries () const { return nentries; }
X const EncodedCommand* operator[] (int i) const { return map[i]; }
X
X const EncodedCommand** Map() const { return map; }
X
X private:
X
X int nentries;
X int size;
X EncodedCommand** map;
X};
X
X
X//
X// Exported functions.
X//
X
Xextern void initialize_keymap_from_file (KeyMap& k, const char* file);
Xextern CommandCode get_command (const KeyMap& keymap);
X
X#endif /*KEYS_H*/
END_OF_FILE
if test 2489 -ne `wc -c <'keys.h'`; then
echo shar: \"'keys.h'\" unpacked with wrong size!
fi
# end of 'keys.h'
fi
echo shar: End of archive 3 \(of 7\).
cp /dev/null ark3isdone

Kevin Braunsdorf

unread,
Oct 1, 1993, 7:03:00 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 68
Archive-name: dired-2.0/04

#! /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 4 (of 7)."
# Contents: classes.C dired.C display.C commands.h


# Wrapped by lijewski@xtesoc2 on Wed Sep 29 08:11:58 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH

if test -f 'classes.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'classes.C'\"
else
echo shar: Extracting \"'classes.C'\" \(3669 characters\)
sed "s/^X//" >'classes.C' <<'END_OF_FILE'
X//
X// classes.C - contains definitions of the member functions which
X// are not defined in the relevant class declarations.
X//
X// $Id: classes.C,v 1.6 1993/06/20 01:51:53 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X

X#include <new.h>


X#include <stdio.h>
X#include <string.h>
X

X#include "classes.h"
X#include "dired.h"
X
X
XDirList::DirList (char *name)
X{
X _head = _tail = 0;
X _next = _prev = 0;
X _nelems = 0;
X _name = name;
X}
X
X
X//
X// Inserts a new DirLine, constructed from the given char**, after
X// the current line in the DirList.
X//
X
XDirLine *DirList::insert (char **line)
X{
X DirLine *new_line = new DirLine(line);
X DirLine *ln = currLine();
X
X if (atEndOfList())
X {
X ln->_next = new_line;
X new_line->_prev = ln;
X _tail = new_line;
X }
X else
X {
X new_line->_next = ln->next();
X new_line->_prev = ln;
X ln->_next->_prev = new_line;
X ln->_next = new_line;
X }
X _nelems++;
X return new_line;
X}
X
X
X//
X// Adds the DirLine to the listing maintained by DirList.
X//
X
Xvoid DirList::add (DirLine *link)
X{
X if (nelems())
X {
X _tail->_next = link;
X _tail->_next->_prev = tail();
X _tail = link;
X _nelems++;
X }
X else
X {
X _head = _tail = link;
X _nelems = 1;


X }
X}
X
X
X//

X// Delete the current listing line in the window and update our view.
X// The width of our view always decreases by one. If the calling procedure
X// adds more lines to the screen, they will have to reset
X// lastLine and/or firstLine, but currLine does not need to change.
X//
X
Xvoid DirList::deleteLine ()
X{
X DirLine *line = currLine();
X
X if (atBegOfList())
X {
X //
X // that is, firstLine() == head()
X //
X _head = _firstLine = _currLine = head()->next();
X _head->_prev = 0;
X }
X else if (atWindowTop())
X {
X //
X // but firstLine() != head()
X //
X _firstLine = _currLine = line->next();
X line->_next->_prev = line->prev();
X line->_prev->_next = line->next();
X }
X else if (atEndOfList())
X {
X //
X // lastLine() == tail()
X //
X _tail = _lastLine = _currLine = line->prev();
X _tail->_next = 0;
X }
X else
X {
X _currLine = line->next();
X line->_next->_prev = line->prev();
X line->_prev->_next = line->next();
X }
X
X _nelems--;
X delete line;
X}
X
X
XDirList::~DirList ()
X{
X if (nelems())
X {
X DirLine *tmp = tail(), *prev = tail()->prev();
X while(tmp)
X {
X delete tmp;
X if ((tmp = prev) != 0)
X prev = tmp->prev();
X }
X delete tmp;
X delete [] _name;


X }
X}
X
X
X//

X// The definition of the head of the freelist that DirLine::operator new()
X// uses to dole out dirLines efficiently.
X//
X
XDirLine *DirLine::freeList;
X
Xtypedef void (*PEHF)();
X
Xvoid *DirLine::operator new (size_t size)
X{
X if (size != sizeof(DirLine))
X return ::new char[size];
X
X DirLine *line = freeList;
X
X if (line)
X freeList = line->next();


X else
X {
X //

X // We rely on a freestore exception catcher to catch
X // heap allocation failures.
X //
X DirLine *block = (DirLine *) ::new char[chunksize * sizeof(DirLine)];
X
X for (int i = 0; i < chunksize - 1; i++)
X block[i]._next = (DirLine *)&block[i + 1];
X block[chunksize - 1]._next = 0;
X line = block;
X freeList = &block[1];
X }
X return line;
X}
X
X
Xvoid DirLine::operator delete (void *object)
X{
X DirLine *line = (DirLine *)object;
X line->_next = freeList;
X freeList = line;
X}
END_OF_FILE
if test 3669 -ne `wc -c <'classes.C'`; then
echo shar: \"'classes.C'\" unpacked with wrong size!
fi
# end of 'classes.C'
fi
if test -f 'dired.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'dired.C'\"
else
echo shar: Extracting \"'dired.C'\" \(8400 characters\)
sed "s/^X//" >'dired.C' <<'END_OF_FILE'
X//
X// dired - a directory editor modelled after GNU Emacs Dired mode.
X//
X// Written in C++ using the termcap library
X//
X// $Id: dired.C,v 1.13 1993/08/24 19:19:35 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X

X#include <ctype.h>
X#include <new.h>
X#include <signal.h>


X#include <stdio.h>
X#include <string.h>
X

X#ifdef apollo
Xextern "C" int chdir (const char *);


X#else
X#include <unistd.h>
X#endif
X
X#include "String.h"

X#include "dired.h"
X#include "display.h"
X
X//
X// This function isn't defined under ObjectCenter 2.0 on my Sun.
X// ... nor is it defined with Apollo C++.
X//
X#if (defined(sun) && defined(__CLCC__)) || defined(apollo)
Xextern "C" int siginterrupt (int, int);
X#endif
X
X
X//
X// the stack of directories being edited
X//
XDirStack *dir_stack;
X
X
X//
X// The default sort order is ALPHABETICALLY.
X//
Xsort_order how_to_sort = ALPHABETICALLY;
X
X
X//
X// commands to get long directory listings:
X//
X// ls_cmd[0] gives alphabetical listing
X// ls_cmd[1] gives listing sorted by modification time
X// ls_cmd[2] gives listing sorted by access time
X// ls_cmd[3] gives listing sorted by inode-change time
X//
Xconst char *const ls_cmd[4] =
X#ifdef L_AND_G
X { "ls -agl ", "ls -aglt ", "ls -agltu ", "ls -acglt " };
X#else
X { "ls -al ", "ls -alt ", "ls -altu ", "ls -aclt " };
X#endif
X
X
X//
X// the modeline prefix
X//
Xconst char *const modeline_prefix = "----- Dired: ";
X
X
X//
X// has window size changed -- should really be a sig_atomic_t
X//
Xint win_size_changed;
X
X
X//
X// exception handler for new - called once in main
X//
X
Xstatic void free_store_exception (void)
X{
X error("exiting, memory exhausted, sorry");
X}
X
X
X//
X// Edit the given directory. We must have read and execute permission
X// to edit a directory; calling routines must guarantee this.
X//
X
Xvoid dired (const char *dirname, const KeyMap& keymap)
X{
X if (chdir(dirname) < 0)


X error("File %s, line %d: couldn't chdir() to `%s'",

X __FILE__, __LINE__, dirname);
X
X char *fullname = current_directory();
X
X if (fullname == 0)
X error("File %s, line %d: current_directory() failed on `%s'.",


X __FILE__, __LINE__, dirname);
X

X DirList *dir_list = get_directory_listing(fullname);
X
X if (dir_list == 0)


X error("File %s, line %d: couldn't read directory `%s'",

X __FILE__, __LINE__, fullname);
X //
X // We track the CWD and PWD variables, if they are defined, so that
X // applications such as emacs which use them will work properly.


X //
X if (getenv("CWD"))
X {
X static String str;
X static String ostr;
X str = "CWD=";

X str += fullname;


X if (putenv(str) < 0)
X error("File %s, line %d: putenv(%s) failed.",

X __FILE__, __LINE__, fullname);


X ostr = str;
X }
X

X if (getenv("PWD"))
X {
X static String str;
X static String ostr;
X str = "PWD=";

X str += fullname;


X if (putenv(str) < 0)
X error("File %s, line %d: putenv(%s) failed.",

X __FILE__, __LINE__, fullname);


X ostr = str;
X }
X

X dir_stack->push(dir_list);
X initial_listing(dir_list);
X update_modeline(modeline_prefix, fullname);
X clear_message_line();
X dir_list->saveYXPos(0, goal_column(dir_list));
X
X if (dir_list->currLine()->length() > columns())
X leftshift_current_line(dir_list);
X else


X move_cursor(dir_list->savedYPos(), dir_list->savedXPos());
X

X synch_display();
X
X read_commands(dir_list, keymap); // main command loop
X}
X
X
X//
X// This is the sort routine we pass to qsort for sorting
X// an array of EncodedCommand*s -- the internal representation
X// of a KeyMap.
X//
X
Xstatic int ECcmp (const void *a, const void *b)
X{
X const char* str1 = (*(EncodedCommand**)a)->sequence;
X const char* str2 = (*(EncodedCommand**)b)->sequence;
X return strcmp(str1, str2);
X}
X
X
Xstatic void usage (void)
X{
X (void)printf("dired [-t | -u | -c] [dirname]\n");
X}
X
X
Xint main (int, char *argv[])
X{
X //
X // Process options - the only options we accept are -t -u or -c.
X //
X while (*++argv && **argv == '-')
X {
X if (strcmp(*argv, "-t") == 0)
X {
X set_sort_order(MODIFICATION_TIME);
X continue;
X }
X else if (strcmp(*argv, "-u") == 0)
X {
X set_sort_order(ACCESS_TIME);
X continue;
X }
X else if (strcmp(*argv, "-c") == 0)
X {
X set_sort_order(INODE_CHANGE_TIME);
X continue;
X }
X else
X {
X (void)printf("unknown option `%s'\n", *argv);
X usage();
X exit(1);
X }
X }
X
X if (!isatty(0) || !isatty(1))
X {
X (void)fputs("stdin & stdout must be terminals\n", stderr);
X exit(EXIT_FAILURE);
X }
X
X //
X // If you do not have SIGINTERRUPT then signals almost surely interrupt
X // read. If you do have it, you will need this to ensure that signals
X // interrupt slow system calls -- we are only interested in read.
X //
X#ifdef SIGINTERRUPT
X#ifdef SIGTSTP
X if (siginterrupt(SIGTSTP, 1) < 0)
X {
X perror("siginterrupt(SIGTSTP)");
X exit(1);
X }
X#endif
X#ifdef SIGWINCH
X if (siginterrupt(SIGWINCH, 1) < 0)
X {
X perror("siginterrupt(SIGWINCH)");
X exit(1);
X }
X#endif
X#endif /*SIGINTERRUPT*/
X
X set_new_handler(free_store_exception);
X initialize();
X set_signals();
X
X KeyMap ourKeyMap;
X
X initialize_keymap_from_file(ourKeyMap, ".diredrc");
X
X //
X // Always activate up and down arrow, PgUp and PgDn,
X // and Home and End keys, if defined.
X //
X if (KU) ourKeyMap.add(CURSOR_UP, encode(KU));
X if (KD) ourKeyMap.add(CURSOR_DOWN, encode(KD));
X if (KH) ourKeyMap.add(TOP_OF_FILE, encode(KH));
X if (KHH) ourKeyMap.add(BOTTOM_OF_FILE, encode(KHH));
X if (KN) ourKeyMap.add(PAGE_UP, encode(KN));
X if (KP) ourKeyMap.add(PAGE_DOWN, encode(KP));
X
X //
X // Check the sanity of the supplied KeyMap. We first sort the
X // KeyMap and then do a pairwise search for proper substrings.
X //
X qsort(ourKeyMap.Map(),ourKeyMap.entries(),sizeof(EncodedCommand**),ECcmp);
X
X int conflicts = 0;
X for (int i = 1; i < ourKeyMap.entries(); i++)
X {
X if (strstr(ourKeyMap[i]->sequence, ourKeyMap[i-1]->sequence))
X {
X conflicts++;
X String msg(decode(ourKeyMap[i-1]->sequence));
X msg += " conflicts with ";
X msg += decode(ourKeyMap[i]->sequence);
X message(msg);
X sleep(2);
X }
X }
X if (conflicts)
X error("sorry, no conflicts allowed -- please fix your .diredrc");
X
X initialize_helpfile(ourKeyMap);
X
X DirStack directories;
X
X dir_stack = &directories;
X
X char* dirname;
X if (*argv == 0)
X {
X dirname = ".";
X if (!is_directory(dirname))
X error("File %s, line %d: `%s' isn't a directory",
X __FILE__, __LINE__, dirname);
X if (!read_and_exec_perm(dirname))
X error("File %s, line %d: need read & exec permission to edit `%s'",


X __FILE__, __LINE__, dirname);
X

X dired(dirname, ourKeyMap);
X }
X else
X do
X {
X dirname = *argv++;
X if (!is_directory(dirname))
X {
X if (*argv)
X {
X String msg("`");
X msg += dirname;
X msg += "' isn't a directory, skipping ...";
X message(msg);
X sleep(2);
X }
X else
X error("File %s, line %d: `%s' isn't a directory",
X __FILE__, __LINE__, dirname);
X continue;
X }
X if (!read_and_exec_perm(dirname))
X {
X if (*argv)
X {
X String msg("need read & execute permission to edit `");
X msg += dirname;
X msg += "', skipping ...";
X message(msg);
X sleep(2);
X }
X else
X error("File %s, line %d: need r & x permission to edit `%s'",
X __FILE__, __LINE__, dirname);
X continue;
X }
X
X dired(dirname, ourKeyMap);
X }
X while (*argv);
X
X deinit_screen_and_kbdr();


X
X return 0;
X}

END_OF_FILE
if test 8400 -ne `wc -c <'dired.C'`; then
echo shar: \"'dired.C'\" unpacked with wrong size!
fi
# end of 'dired.C'
fi
if test -f 'display.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'display.C'\"
else
echo shar: Extracting \"'display.C'\" \(20128 characters\)
sed "s/^X//" >'display.C' <<'END_OF_FILE'
X//
X// Routines controlling the physical display
X//
X// $Id: display.C,v 1.13 1993/09/19 20:28:52 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X

X#include <signal.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <sys/ioctl.h>
X
X#ifdef sgi
Xextern "C" int ioctl (int, int, ...);
X#endif
X
X#ifdef apollo
X#undef TERMIOS
X#undef TERMIO
Xextern "C" int getpid (void);


X#else
X#include <unistd.h>
X#endif
X

X#ifdef TERMIOS
X#include <termios.h>
X#include <unistd.h>
X#elif TERMIO
X#include <termio.h>
X#else
X#include <sgtty.h>
X#endif
X
X#include "dired.h"
X#include "display.h"
X
X
X//
X// These functions aren't properly prototyped by ObjectCenter 2.0 on my Sun.
X// Nor do they appear to be properly prototyped with g++ on my Sun.


X//
X#if defined(sun) && defined(__CLCC__)
Xextern "C"
X{

X int tcgetattr (int, struct termios*);
X int tcsetattr (int, int, struct termios*);
X speed_t cfgetospeed (const struct termios*);
X int tcsetattr (int, int, struct termios*);
X}
X#endif
X
X
X//
X// The definition of ospeed -- needed by the termcap routines.
X//
Xshort ospeed;
X
X
X//
X// termcap capabilities we use
X//
Xchar *AL; // insert blank line before cursor
Xchar *ALN; // insert N blank lines at cursor
Xint AM; // automatic margins?
Xchar *BC; // backspace, if not BS
Xint BS; // ASCII backspace works
Xchar *CD; // clear to end of display
Xchar *CE; // clear to end of line
Xchar *CL; // clear screen
Xint CO; // number of columns
Xchar *CM; // cursor motion
Xchar *CR; // cursor beginning of line
Xchar *CS; // set scroll region
Xint DA; // backing store off top?
Xint DB; // backing store off bottom?
Xchar *DC; // delete character at cursor
Xchar *DL; // delete line cursor is on
Xchar *DLN; // delete N lines from cursor
Xchar *DM; // string to enter delete mode
Xchar *DO; // cursor down
Xchar *ED; // string to end delete mode
Xint HC; // hardcopy terminal?
Xchar *IS; // initialize terminal
Xchar *HO; // cursor home
Xchar *KD; // down arrow key
Xchar *KE; // de-initialize keypad
Xchar *KH; // HOME key
Xchar *KHH; // END key
Xchar *KN; // PgDn key
Xchar *KP; // PgUp key
Xchar *KS; // initialize keypad -- for arrow keys
Xchar *KU; // up arrrow key
Xchar *LE; // cursor back one column
Xint LI; // number of rows
Xchar *LL; // cursor to lower left
Xint OS; // terminal overstrikes?
Xchar PC; // pad character
Xchar *PCstr; // pad string
Xchar *SE; // end standout mode
Xchar *SF; // scroll screen up one line
Xchar *SO; // enter standout mode
Xchar *SR; // scroll screen down one line
Xchar *TE; // end cursor addressing mode
Xchar *TI; // enter cursor addressing mode
Xchar *UP; // cursor up
Xchar *VE; // end visual mode
Xchar *VS; // enter visual mode
Xchar *XN; // strange wrap behaviour
X
X
Xvoid backspace (void)
X{
X if (BS)
X putchar('\b');
X else if (LE)
X output_string_capability(LE);
X else
X output_string_capability(BC);
X}
X
Xvoid clear_message_line (void)
X{
X move_to_message_line();
X clear_to_end_of_line();
X}
X
X//
X// Reads termcap file setting all the terminal capabilities which we will use.
X//
X
Xvoid termcap (const char *term_type)
X{
X static char capability_buffer[512], *bp = capability_buffer;
X char termcap_buffer[2048];
X
X switch (tgetent(termcap_buffer, term_type))
X {
X case -1:
X (void)fputs("couldn't open termcap database\n", stderr);
X exit(1);
X case 0:
X (void)fprintf(stderr, "invalid terminal type: `%s'\n", term_type);
X exit(1);
X default: break;
X }
X
X AL = tgetstr("al", &bp);
X ALN = tgetstr("AL", &bp);
X AM = tgetflag("am");
X BC = tgetstr("bc", &bp);
X BS = tgetflag("bs");
X CD = tgetstr("cd", &bp);
X CE = tgetstr("ce", &bp);
X CL = tgetstr("cl", &bp);
X CM = tgetstr("cm", &bp);
X CR = tgetstr("cr", &bp);
X CS = tgetstr("cs", &bp);
X DA = tgetflag("da");
X DB = tgetflag("db");
X DC = tgetstr("dc", &bp);
X DL = tgetstr("dl", &bp);
X DLN = tgetstr("DL", &bp);
X DM = tgetstr("dm", &bp);
X DO = tgetstr("do", &bp);
X ED = tgetstr("ed", &bp);
X HC = tgetflag("hc");
X HO = tgetstr("ho", &bp);
X IS = tgetstr("is", &bp);
X KD = tgetstr("kd", &bp);
X KE = tgetstr("ke", &bp);
X KH = tgetstr("kh", &bp);
X KHH = tgetstr("kH", &bp);
X KN = tgetstr("kN", &bp);
X KP = tgetstr("kP", &bp);
X KS = tgetstr("ks", &bp);
X KU = tgetstr("ku", &bp);
X LE = tgetstr("le", &bp);
X LL = tgetstr("ll", &bp);
X OS = tgetflag("os");
X PCstr = tgetstr("pc", &bp);
X SE = tgetstr("se", &bp);
X SF = tgetstr("sf", &bp);
X SO = tgetstr("so", &bp);
X SR = tgetstr("sr", &bp);
X TE = tgetstr("te", &bp);
X TI = tgetstr("ti", &bp);
X UP = tgetstr("up", &bp);
X VE = tgetstr("ve", &bp);
X VS = tgetstr("vs", &bp);
X XN = tgetstr("xn", &bp);
X
X PC = PCstr ? PCstr[0] : 0;
X
X if (!BC && !LE && !BS)
X {
X (void)fputs("terminal can't backspace - unusable\n", stderr);
X exit(1);
X }
X
X if (!DC)
X DC = "\177";
X if (!BC)
X BC = LE ? LE : "\b";
X if (!CR)
X CR = "\r";
X if (!DO)
X DO = SF ? SF : "\n";
X
X const char *tmp = getenv("LINES");
X if (tmp)
X LI = atoi(tmp);
X tmp = getenv("COLUMNS");
X if (tmp)
X CO = atoi(tmp);
X
X#ifdef TIOCGWINSZ
X struct winsize win;
X if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&win) == 0)
X {
X if (LI == 0 && win.ws_row > 0)
X LI = win.ws_row;
X if (CO == 0 && win.ws_col > 0)
X CO = win.ws_col;
X }
X#endif
X
X if (CO == 0)
X CO = tgetnum("co");
X if (LI == 0)
X LI = tgetnum("li");
X
X if (LI == -1 || CO == -1 || HC || !CM || !CE)
X {
X (void)fputs("terminal too dumb to be useful\n", stderr);
X exit(1);
X }
X if (LI < 5)
X {
X (void)fputs("too few rows to be useful\n", stderr);
X exit(1);


X }
X}
X
X
X//

X// Puts terminal into raw mode. Cbreak mode actually, but why be pedantic.
X// Flow control is disabled as well as BREAK keys. Echoing is turned off
X// as well as signal generation. Hence keyboard generated signals must be
X// simulated. Also sets ospeed.
X//
X
X#ifdef TERMIOS
Xstatic struct termios tty_mode; /* save tty mode here */
X#elif TERMIO
Xstatic struct termio tty_mode; /* save tty mode here */
X#else
Xstatic struct sgttyb oarg; /* save tty stuff here */
Xstatic struct tchars otarg;
Xstatic struct ltchars oltarg;
X#endif
X
Xvoid setraw (void)
X{
X#ifdef TERMIOS
X struct termios temp_mode;
X
X if (tcgetattr(STDIN_FILENO, &temp_mode) < 0)
X {
X perror("tcgetattr");
X exit(EXIT_FAILURE);
X }
X
X tty_mode = temp_mode; /* save for latter restoration */
X
X temp_mode.c_iflag &= ~(IGNBRK|ICRNL|INLCR);
X temp_mode.c_lflag &= ~(ICANON|ECHO|IEXTEN);
X temp_mode.c_oflag &= ~OPOST;
X temp_mode.c_cc[VQUIT] = 28; // C-\ is QUIT
X temp_mode.c_cc[VMIN] = 1; // min #chars to satisfy read
X temp_mode.c_cc[VTIME] = 0; // read returns immediately
X
X if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &temp_mode) < 0)
X {
X perror("tcsetattr");
X exit(EXIT_FAILURE);
X }
X
X ospeed = cfgetospeed(&temp_mode);
X#elif TERMIO
X struct termio temp_mode;
X
X if (ioctl(fileno(stdin), TCGETA, (char *)&temp_mode) < 0)
X {
X perror("ioctl - TCGETA");
X exit(EXIT_FAILURE);
X }
X
X tty_mode = temp_mode; /* save for latter restoration */
X
X temp_mode.c_iflag &= ~(IGNBRK|ICRNL|INLCR);
X temp_mode.c_lflag &= ~(ICANON|ECHO);
X temp_mode.c_oflag &= ~OPOST;
X temp_mode.c_cc[VQUIT] = 28; // C-\ is QUIT
X temp_mode.c_cc[VMIN] = 1; // min #chars to satisfy read
X temp_mode.c_cc[VTIME] = 0; // read returns immediately
X
X if (ioctl(fileno(stdin), TCSETA, (char *)&temp_mode) < 0)
X {
X perror("ioctl - TCSETA");
X exit(EXIT_FAILURE);
X }
X
X ospeed = temp_mode.c_cflag & CBAUD;
X#else
X struct sgttyb arg;
X struct tchars targ;
X struct ltchars ltarg;
X
X if (ioctl(fileno(stdin), TIOCGETP, (char *)&arg) < 0)
X {
X perror("ioctl - TIOCGETP");
X exit(EXIT_FAILURE);
X }
X if (ioctl(fileno(stdin), TIOCGETC, (char *)&targ) < 0)
X {
X perror("ioctl - TIOCGETC");
X exit(EXIT_FAILURE);
X }
X if (ioctl(fileno(stdin), TIOCGLTC, (char *)&ltarg) < 0)
X {
X perror("ioctl - TIOCGLTC");
X exit(EXIT_FAILURE);
X }
X
X oarg = arg;
X otarg = targ;
X oltarg = ltarg;
X
X arg.sg_flags=((arg.sg_flags&~(ECHO|CRMOD))|CBREAK) ;
X targ.t_eofc = -1; // turn off end-of-file character
X targ.t_brkc = -1; // turn off break delimiter
X ltarg.t_dsuspc = -1; // turn off delayed suspend character
X ltarg.t_rprntc = -1; // turn off reprint line character
X ltarg.t_flushc = -1; // turn off flush character
X ltarg.t_werasc = -1; // turn off erase work character
X ltarg.t_lnextc = -1; // turn off literal next char
X
X if (ioctl(fileno(stdin), TIOCSETN, (char *)&arg) < 0)
X {
X perror("ioctl - TIOCSETN");
X exit(EXIT_FAILURE);
X }
X if (ioctl(fileno(stdin), TIOCSETC, (char *)&targ) < 0)
X {
X perror("ioctl - TIOCSETC");
X exit(EXIT_FAILURE);
X }
X if (ioctl(fileno(stdin), TIOCSLTC, (char *)&ltarg) < 0)
X {
X perror("ioctl - TIOCSLTC");
X exit(EXIT_FAILURE);
X }
X
X ospeed = arg.sg_ospeed;


X#endif
X}
X
X
X//

X// Restore the terminal mode to whatever it was on the most recent call
X// to the setraw function above. Exits with EXIT_FAILURE on failure.
X//
X
Xvoid unsetraw (void)
X{
X#ifdef TERMIOS
X if (tcsetattr(0, TCSAFLUSH, &tty_mode) < 0)
X {
X perror("tcsetattr");
X exit(EXIT_FAILURE);
X }
X#elif TERMIO
X if (ioctl(fileno(stdin), TCSETA, (char *)&tty_mode) < 0)
X {
X perror("ioctl - TCSETA");
X exit(EXIT_FAILURE);
X }
X#else
X if (ioctl(fileno(stdin), TIOCSETN, (char *)&oarg) < 0)
X {
X perror("ioctl - TIOSETN");
X exit(EXIT_FAILURE);
X }
X if (ioctl(fileno(stdin), TIOCSETC, (char *)&otarg) < 0)
X {
X perror("ioctl - TIOSETC");
X exit(EXIT_FAILURE);
X }
X if (ioctl(fileno(stdin), TIOCSLTC, (char *)&oltarg) < 0)
X {
X perror("ioctl - TIOSLTC");
X exit(EXIT_FAILURE);
X }


X#endif
X}
X
X
X//

X// A function to output a single character. Termcap routines NEED a function.
X//
X
Xint outputch (int ch) { return putchar(ch); }
X
X
X//
X// Initialize screen and keyboard.
X//
X
Xvoid init_screen_and_kbdr (void)
X{
X setraw();
X enter_cursor_addressing_mode();
X enter_visual_mode();
X enable_keypad();


X synch_display();
X}
X
X
X//

X// Get ready to do some work.
X//
X
Xvoid initialize (void)
X{
X setvbuf(stdout, 0, _IOFBF, 0); // fully buffer stdout
X setvbuf(stdin , 0, _IONBF, 0); // no buffering on stdin
X
X const char *term = getenv("TERM");
X if (term == 0 || *term == 0)
X {
X (void)fputs("please set your TERM variable appropriately\n", stderr);
X exit(1);
X }
X
X termcap(term);
X init_screen_and_kbdr();
X}
X
X
X//
X// Go back to original modes
X//
X
Xvoid deinit_screen_and_kbdr (void)
X{
X move_to_message_line();
X clear_to_end_of_line();
X disable_keypad();
X end_visual_mode();
X end_cursor_addressing_mode();


X synch_display();
X unsetraw();
X}

X
X
X//
X// Scrolls the listing window up n lines. The cursor is left in column 0
X// of the first line to scroll into the window. Must have CS capability.
X//
X
Xvoid scroll_listing_up_N (int n)
X{
X output_string_capability(tgoto(CS, rows()-3, 0));
X move_cursor(rows()-3, 0);
X for (int i = 0; i < n; i++)
X cursor_down();
X output_string_capability(tgoto(CS, rows()-1, 0));
X move_cursor(rows()-2-n, 0);
X}
X
X
X//
X// Scrolls the listing window down by n. The cursor is left in
X// HOME position. Must have CS capability.
X//
X
Xvoid scroll_listing_down_N (int n)
X{
X output_string_capability(tgoto(CS, rows()-3, 0));
X move_cursor(0, 0);
X for (int i = 0; i < n; i++)
X output_string_capability(SR, rows()-2);
X output_string_capability(tgoto(CS, rows()-1, 0));
X cursor_home();
X}
X
X
X//
X// Scrolls the listing window up one row. The cursor is left in column
X// 0 of rows-3 row. Assumes CS capability.
X//
X
Xvoid scroll_listing_up_one (void)
X{
X output_string_capability(tgoto(CS, rows()-3, 0));
X move_cursor(rows()-3, 0);
X cursor_down();
X output_string_capability(tgoto(CS, rows()-1, 0));
X move_cursor(rows()-3, 0);
X}
X
X
X//
X// Scrolls the listing window down one row. The cursor is left at HOME.
X// Assumes CS capability.
X//
X
Xvoid scroll_listing_down_one (void)
X{
X output_string_capability(tgoto(CS, rows()-3, 0));
X cursor_home();
X output_string_capability(SR, rows()-2);
X output_string_capability(tgoto(CS, rows()-1, 0));
X cursor_home();
X}
X
X
X//
X// Inserts a blank line at line y, scrolling everything from y on down
X// one line. We only call this routine when we KNOW that
X// y != rows-3 - the last listing line. Leaves the cursor in column 0
X// of the opened up line. Must have CS capability.
X//
X
Xvoid insert_listing_line (int y)
X{
X output_string_capability(tgoto(CS, rows()-3, y));
X move_cursor(y, 0);
X output_string_capability(SR, rows()-3-y);
X output_string_capability(tgoto(CS, rows()-1, 0));
X move_cursor(y, 0);
X}
X
X
X//
X// Deletes line at line y, scrolling the lines below y up. We only call
X// this routine when we KNOW that there is at least one line in need of
X// being scrolled up. Must have CS capability.
X//
X
Xvoid delete_listing_line (int y)
X{
X move_cursor(y, 0);
X clear_to_end_of_line();
X output_string_capability(tgoto(CS, rows()-3, y));
X move_cursor(rows()-3, 0);
X cursor_down();
X output_string_capability(tgoto(CS, rows()-1, 0));
X}
X
X
X//
X// Service a SIGTSTP.
X//
X
X#ifdef SIGTSTP
XSIGFUNC(termstop)
X{
X (void)signal(SIGTSTP, SIG_IGN);
X#ifdef SIGWINCH
X (void)signal(SIGWINCH, SIG_IGN);
X#endif
X deinit_screen_and_kbdr();
X (void)kill(getpid(), SIGSTOP);
X init_screen_and_kbdr();
X#ifdef sgi
X (void)signal(SIGTSTP, (void (*)(int ...)) termstop);
X#else
X (void)signal(SIGTSTP, termstop);
X#endif
X#ifdef SIGWINCH
X#ifdef sgi
X (void)signal(SIGWINCH, (void (*)(int ...)) winch);
X#else
X (void)signal(SIGWINCH, winch);
X#endif
X#endif
X
X //
X // window size may have changed
X //
X#ifdef TIOCGWINSZ
X int oCO = columns(), oLI = rows();
X struct winsize w;
X if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_row > 0)
X LI = w.ws_row;
X if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_col > 0)
X CO = w.ws_col;
X if (oCO != columns() || oLI != rows())
X win_size_changed = 1;
X else
X redisplay();
X#else
X redisplay();
X#endif
X}
X#endif /*SIGTSTP*/
X
X
X//
X// Clears all except the last two lines. Leaves the cursor at home.
X//
X
Xvoid clear_display_area (void)
X{
X cursor_home();
X for (int i = 0; i < rows() - 2; i++)
X {
X clear_to_end_of_line();
X cursor_down();
X }
X cursor_home();
X}
X
X
X//
X// Scroll screen up one. Must have DL or SF.
X//
X
Xvoid scroll_screen_up_one (void)
X{
X if (DL)
X {
X cursor_home();
X output_string_capability(DL, rows());
X }
X else
X {
X move_cursor(rows()-1, 0);
X output_string_capability(SF, rows());
X }
X if (DB)
X clear_message_line();
X}
X
X
X//
X// Scroll screen down one. Must have AL or SR.
X//
X
Xvoid scroll_screen_down_one (void)
X{
X cursor_home();
X
X if (AL)
X output_string_capability(AL, rows());
X else
X output_string_capability(SR, rows());
X if (DA)
X clear_to_end_of_line();
X}
X
X
X//
X// scroll screen up n. Must have DLN, DL or SF.
X//
X
Xvoid scroll_screen_up_N (int n)
X{
X if (DLN)
X {
X cursor_home();
X output_string_capability(tgoto(DLN, 0, n), rows());
X }
X else if (DL)
X {
X cursor_home();
X for (int i = 0; i < n; i++)
X output_string_capability(DL, rows());
X }
X else
X {
X move_cursor(rows()-1, 0);
X for (int i = 0; i < n; i++)
X output_string_capability(SF, rows());
X }
X if (DB)
X clear_to_end_of_screen(rows()-n);
X}
X
X
X//
X// scroll screen down n. Must have ALN, AL or SR.
X//
X
Xvoid scroll_screen_down_N (int n)
X{
X cursor_home();
X int i;
X if (ALN)
X output_string_capability(tgoto(ALN, 0, n), rows());
X else if (AL)
X for (i = 0; i < n; i++)
X output_string_capability(AL, rows());
X else
X for (i = 0; i < n; i++)
X output_string_capability(SR, rows());
X if (DA)
X {
X for (i = 0; i < n; i++)
X {
X clear_to_end_of_line();
X cursor_down();
X }
X cursor_home();


X }
X}
X
X
X//

X// Clears screen from line y to the bottom.
X//
X
Xvoid clear_to_end_of_screen (int y)
X{
X move_cursor(y, 0);
X if (CD)
X output_string_capability(DL, rows()-y);
X else
X for (int i = 0; i < rows()-y; i++)
X {
X clear_to_end_of_line();
X putchar('\n');


X }
X}
X
X
X//

X// Update a line on the screen.
X//
X// oldline is what is currently on the screen in row y.
X// newline is what we want on the screen in row y.
X//
X// We make a good attempt to optimize the output of characters to
X// the screen. We want to display newline on the screen,
X// assuming oldline is what is currently displayed. This
X// will be "good" if oldline and newline are quite similar.
X// That is to say, this should only be called when there is an
X// expectation that oldline and newline are "almost" the same.
X//
X
Xvoid update_screen_line (const char *oldline, const char *newline, int y)
X{
X if (strcmp(oldline, newline) == 0)
X return;
X
X size_t olen = strlen(oldline);
X size_t nlen = strlen(newline);
X size_t len = olen < nlen ? olen : nlen;
X
X //
X // Never display more than columns characters.
X //
X int chop = 0; // do we need to chop off the tail?
X if (len > columns())
X {
X chop = 1;
X len = columns();
X }
X
X char *equal = new char[len];
X
X //
X // How similar are the two strings?
X //
X int differences = 0;
X for (int i = 0; i < len; i++)
X equal[i] = 1;
X for (i = 0; i < len; i++)
X if (oldline[i] != newline[i])
X {
X differences++;
X equal[i] = 0;
X }
X
X if (differences > columns()/2)
X {
X //
X // We just display the new line.
X //
X clear_to_end_of_line();
X (void)fputs(newline, stdout);
X delete [] equal;
X
X return;
X }
X
X if (!OS)
X {
X //
X // We can just overwrite the old with the new.
X //
X int last = -2; // position of last character written
X for (i = 0; i < len; i++)
X {
X if (equal[i]) continue;
X if (i - 1 != last) move_cursor(y, i);
X (i == len - 1 && chop) ? putchar('!') : putchar(newline[i]);
X last = i;
X }
X if (nlen > olen)
X {
X //
X // Have more characters to output.
X //
X chop = len > columns();
X move_cursor(y, i);
X for (i = (int)len; i < nlen && i < columns(); i++)
X (i == columns()-1 && chop) ? putchar('!') : putchar(newline[i]);
X }
X else if (nlen < olen)
X {
X move_cursor(y, i);
X clear_to_end_of_line();


X }
X }
X else
X {
X //

X // We can not overwrite. Truncate at first difference.
X //
X for (i = 0; i < len; i++) if (!equal[i]) break;
X move_cursor(y, i);
X clear_to_end_of_line();
X for (; i < nlen && i < columns(); i++)
X i == columns()-1 ? putchar('!') : putchar(newline[i]);
X }
X
X delete [] equal;
X}
END_OF_FILE
if test 20128 -ne `wc -c <'display.C'`; then
echo shar: \"'display.C'\" unpacked with wrong size!
fi
# end of 'display.C'
fi
if test -f 'commands.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'commands.h'\"
else
echo shar: Extracting \"'commands.h'\" \(1718 characters\)
sed "s/^X//" >'commands.h' <<'END_OF_FILE'
X#ifndef COMMANDS_H
X#define COMMANDS_H
X
X#include "classes.h"
X#include "keys.h"
X
Xextern void change_sort_order (DirList *dl);
Xextern void chgrp_file (DirList *dl, const char *file, const char *group);
Xextern void chmod_current_file (DirList *dl);
Xextern void compress_current_file (DirList *dl);
Xextern void copy_current_file (DirList *dl);
Xextern void cursor_down_one_line (DirList* dl);
Xextern void cursor_up_one_line (DirList* dl);
Xextern void delete_current_file (DirList *dl);
Xextern void edit_current_file (DirList *dl, const KeyMap& keymap);
Xextern void edit_prompted_for_directory (DirList *dl, const KeyMap& keymap);
Xextern void goto_first (DirList *dl);
Xextern void goto_last (DirList *dl);
Xextern void help (DirList *dl);
Xextern void initialize_helpfile (const KeyMap& keymap);
Xextern void link_current_file (DirList *dl);
Xextern void output_version_string (DirList* dl);
Xextern void page_current_file (DirList *dl, const KeyMap& keymap);
Xextern void print_current_file (DirList *dl);
Xextern int pop_a_directory(DirList*& dl);
Xextern void quit (DirList*);
Xextern void rename_current_file (DirList *dl);
Xextern void reread_current_directory (DirList * &dlr);
Xextern void scroll_up_full_window (DirList *dl);
Xextern void scroll_down_full_window (DirList *dl);
Xextern void scroll_up_half_window (DirList *dl);
Xextern void scroll_down_half_window (DirList *dl, int freshen = 1);
Xextern void search_forward (DirList *dl);
Xextern void search_backward (DirList *dl);
Xextern void shell_command (DirList *dl);
Xextern void symlink_current_file (DirList *dl);
Xextern void unzip_file (DirList *dl, const char *file);
Xextern void uncompress_current_file (DirList *dl);
Xextern void zip_current_file (DirList *dl);
X
X#endif
END_OF_FILE
if test 1718 -ne `wc -c <'commands.h'`; then
echo shar: \"'commands.h'\" unpacked with wrong size!
fi
# end of 'commands.h'
fi
echo shar: End of archive 4 \(of 7\).
cp /dev/null ark4isdone

Kevin Braunsdorf

unread,
Oct 1, 1993, 7:05:24 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 69
Archive-name: dired-2.0/05

#! /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 7)."
# Contents: version.h utilities.C
# Wrapped by lijewski@xtesoc2 on Wed Sep 29 08:11:59 1993


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'\" \(292 characters\)
sed "s/^X//" >'version.h' <<'END_OF_FILE'
X//
X// version.h - where our version number is defined
X//
X// $Id: version.h,v 1.13 1993/09/29 12:01:53 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X

X#ifndef _VERSION_H
X#define _VERSION_H
X
X#include "dired.h"
X
Xconst char *const version = "Dired Version 2.1";
X
X#endif
END_OF_FILE
if test 292 -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'\" \(45404 characters\)
sed "s/^X//" >'utilities.C' <<'END_OF_FILE'
X//
X// utilities.C - utility functions
X//
X// $Id: utilities.C,v 1.17 1993/09/19 20:28:52 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X
X

X#include <ctype.h>
X#include <errno.h>
X#include <pwd.h>
X#include <signal.h>


X#include <stdlib.h>
X#include <string.h>
X

X#ifdef _IBMR2
X#include <sys/access.h>
X#endif
X
X#include <sys/stat.h>
X#include <sys/types.h>
X#include <sys/wait.h>
X
X#ifdef COMPLETION
X#ifdef apollo
X#include <sys/dir.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#define DIRENT struct direct
X#define S_ISDIR(m) ((m&S_IFMT) == S_IFDIR)
X#define S_ISREG(m) ((m&S_IFMT) == S_IFREG)
X#define S_ISLNK(m) ((m&S_IFMT) == S_IFLNK)
Xextern
X{
X char* getcwd (char *str,int len);
X int getuid(void);
X long strtol(const char *, char **, int);
X int read(int, const char *, int);
X pid_t fork(void);
X int close(int);
X}
X#else
X#include <dirent.h>
X#define DIRENT struct dirent
X#endif /*apollo*/
X#endif /*COMPLETION*/
X
X#ifndef apollo
X#include <unistd.h>
X#endif
X
X#ifndef R_OK
X#include <sys/file.h>


X#endif
X
X#include "String.h"
X#include "dired.h"
X#include "display.h"

X#include "keys.h"
X
X
X//
X// Keys used when in a prompt.
X//
X
Xconst char KEY_CTL_L = '\f'; // repaint screen - CTR-L
Xconst char KEY_TAB = '\t'; // TAB performs filename completion
Xconst char KEY_BKSP = '\b'; // backspace works as expected in a prompt
Xconst char KEY_CR = '\r'; // carriage return
Xconst char KEY_DEL = 127; // ASCII DELETE works like backspace in prompt
Xconst char KEY_ABORT = 0x7; // should be '\a' but for broken compilers ...
Xconst char KEY_CTL_U = 0x15; // delete complete prompt
Xconst char KEY_CTL_W = 0x17; // delete word
X
X
X//
X// Expects a string of the form "~ ...". Returns a new string in
X// volatile storage with the user's home directory in place of the ~.
X// The user's home directory is always appended in the form:
X// "/usr/staff/mjlx"; a slash is not added to the end of the home
X// directory string. Returns the original string if we cannot get
X// the user's home directory. The user should not attempt to delete
X// the return value.
X//
X
Xconst char *expand_tilde (const char *str)
X{
X if (*str != '~')
X return str;
X
X String string(str);
X
X static String expansion;
X
X //
X // Null out `expansion'.
X //
X expansion = "";
X
X if (string == "~" || (string[0] == '~' && string[1] == '/'))
X {
X //
X // Replace "~" by "$HOME".
X //
X static char *home = getenv("HOME");
X if (home == 0)
X {
X struct passwd *user = getpwuid(getuid());
X if (user == NULL)
X return str;
X home = user->pw_dir;
X }
X expansion += home;
X expansion += (str + 1);


X }
X else
X {
X //

X // Assume we have a string of the form "~user" or "~user/...".
X // We must try to find the home directory of the given user.
X //
X String nstr(str+1);
X char* slash = strchr((const char*)nstr, '/');
X if (slash)
X *slash = 0;
X struct passwd *user = getpwnam((const char*)nstr);
X if (user == NULL)
X return str;
X expansion += user->pw_dir;
X if (slash)
X {
X expansion += "/";
X expansion += (slash + 1);
X }
X }
X
X return expansion;
X}
X
X
X//
X// A specialized implementation of the ANSI C function strstr
X// using Boyer-Moore substring searching. Is really fast when
X// the same substring is used for many searches.
X//
X
Xchar* BMstrstr (const char* str, const char* substr)
X{
X const int DIM = 128;
X static int table[DIM];
X static char* old_substr;
X static int gap;
X
X if (str == 0 || substr == 0)
X return 0;
X
X int string_length = strlen(str), substr_length = strlen(substr);
X
X if (old_substr == 0 || strcmp(substr, old_substr))
X {
X int len = substr_length;
X delete [] old_substr;
X old_substr = ::new char[substr_length + 1];
X (void)strcpy(old_substr, substr);
X for (int i = 0; i < DIM; i++)
X table[i] = substr_length;
X for (const char* p = substr; *p; p++)
X table[*p] = --len;
X char endchar = substr[substr_length - 1];
X for (gap = 1, p = substr+substr_length-2; p != substr; gap++, p--)
X if (endchar == *p)
X break;
X }
X
X int index = substr_length - 1;
X
X while (index < string_length)
X {
X if (table[str[index]] == 0)
X {
X if (strncmp(&str[index-substr_length+1],substr,substr_length) == 0)
X return (char*)&str[index-substr_length+1];
X index += gap;
X }
X else
X index += table[str[index]];


X }
X return 0;
X}

X
X
X//
X// Returns a pointer to the start of a line read from fp, or the null
X// pointer if we hit eof or get an error from fgets. Exits if new fails.
X// Strips the newline from the line. 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)
X 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//

X// This routine tries to determine the full pathname of the current
X// directory. The pointer returned is new storage. If the beginning
X// of the directory matches "/tmp_mnt" -- the default automounter
X// directory -- we strip it off.
X//
X
Xchar *current_directory (void)
X{
X const int chunksize = 50;
X int size = chunksize;
X char *dir = new char[size];
X
X while (getcwd(dir, size) == 0)
X {
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 }
X

X const char* AutomountDir = "/tmp_mnt";
X int AutodirLen = strlen(AutomountDir);
X if (strncmp(dir, AutomountDir, AutodirLen) == 0)
X {
X //
X // Strip automounter prefix.
X //
X char* ndir = new char[strlen(dir) + 1 - AutodirLen];
X (void) strcpy(ndir, dir + AutodirLen);
X delete [] dir;
X dir = ndir;
X }
X
X return dir;
X}
X
X
X//
X// 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)
X cursor_wrap();
X }
X else
X {
X (void)fputs(str, stdout);
X if (!AM || XN)
X cursor_wrap();


X }
X}
X
X
X//

X// Get a long listing of the given directory. Returns 0 if we got other
X// than a "memory exhausted" error.
X//
X
XDirList *get_directory_listing (char *dirname)
X{
X message("Reading directory ... ");


X
X String cmd(ls_cmd[the_sort_order()]);

X cmd += dirname;
X cmd += " 2>/dev/null";
X
X FILE *fp = popen(cmd, "r");


X if (fp == 0)

X 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
X while ((line = fgetline(fp)) != 0)
X directory->add(new DirLine(&line));
X
X message("Reading directory ... done");
X
X
X if (!(feof(fp) && !ferror(fp)))
X directory = 0;
X
X (void) pclose(fp);
X return directory;
X}
X
X
X//
X// Constructs a null-terminated vector of the words in line into
X// tokens. The vector and its elements are in volatile storage
X// which we manage here. The return value is the number of non-null
X// tokens in the vector.
X//
X
Xint tokenize (const char *line, const char *separators, char**& tokens)
X{
X //
X // Since strtok modifies its argument, we use a copy of line.
X //
X static char *newline; // volatile storage of vector elements
X delete [] newline;
X newline = new char[strlen(line) + 1];
X (void)strcpy(newline, line);
X
X const int chunksize = 5; // chunksize to grow by
X int size = chunksize; // total size of vector
X int nwords = 0; // number of words in vector
X static char **words; // volatile storage for the word pointers
X delete [] words;
X words = new char*[chunksize];
X
X if ((words[nwords++] = strtok(newline, separators)) == 0)
X return 0;
X else
X {
X while (words[nwords++] = strtok(0, separators))
X if (nwords == size)
X {
X //
X // Grow words.
X //
X char **newspace = new char*[size += chunksize];
X memcpy(newspace, words, nwords * sizeof(char*));
X delete [] words;
X words = newspace;
X }
X tokens = words;
X }
X
X return nwords - 1; // minus one since we do not count the final NULL
X}
X
X
X//
X// Takes a string of ASCII characters, "backslash" escapes, "numeric"
X// escapes and "^X" sequences, and converts it into a string of the exact
X// ASCII characters themselves. The encoded string into returned in a
X// static buffer, so if you want to save the string you must make a copy
X// of it. Returns the NULL string if there was an error.
X//
X
Xconst char* encode (char* command)
X{
X if (!command)
X return 0;
X
X static const char* const EscapeMap = "abfnrtv";
X static const char* const EscapeValue = "\a\b\f\n\r\t\v";
X static const char* const ControlMap = "@abcdefghijklmnopqrstuvwxyz[\\]^_";
X
X static char* buffer; // our static storage
X static int buflen;
X
X if (strlen(command) > buflen)
X {
X delete [] buffer;
X buflen = strlen(command);
X buffer = ::new char[buflen + 1];
X }
X
X char* bufptr = buffer;
X
X for (; *command != '\0'; buffer++)
X {
X switch (*command)
X {
X case '^':
X if (command[1] == 0)
X //
X // A terminating '^' by itself.
X //
X *buffer = '^';
X else if (*++command == '?')
X //
X // ASCII DEL character.
X //
X *buffer = 0x7f;
X else
X {
X char c = *command;
X if (isupper(c)) c = tolower(c);
X
X const char* ctrl = strchr(ControlMap, c);
X if (ctrl == 0)
X {
X //
X // Invalid control sequence.
X //
X buffer = bufptr;
X return 0;
X }
X *buffer = ctrl - ControlMap;
X }
X ++command;
X break;
X case '\\': // Escapes.
X const char* ctrl;
X if (*++command == 0)
X //
X // A terminating '\' by itself.
X //
X *buffer = '\\';
X else if (isdigit(*command))
X {
X // Numeric escapes we allow for:
X //
X // octal \0nnn
X // hex \0xnn
X // decimal \nnn
X //
X long number = strtol(command, &command, 0);
X if (255 < number)
X {
X buffer = bufptr;
X return 0;
X }
X *buffer = (char) number;
X }
X else if ((ctrl = strchr(EscapeMap, *command)))
X {
X *buffer = EscapeValue[ctrl - EscapeMap];
X ++command;
X }
X else
X *buffer = *command++;
X break;
X default: // A literal character.
X *buffer = *command++;
X break;
X }
X }
X *buffer = 0;
X
X return buffer = bufptr;
X}
X
X
X//
X// Decodes a string of ASCII characters, making them printable.
X// Returns the printable string in volatile (static) storage,
X// so if you want to save the string, you must make your own copy
X// of it.
X//
X
Xconst char* decode (const char* str)
X{
X static char* buf;
X static char* bufptr = buf;
X static int buflen;
X
X if (2 * strlen(str) > buflen)
X {
X delete [] bufptr;
X buflen = 2 * strlen(str);
X bufptr = buf = ::new char[buflen + 1];
X }
X
X buf = bufptr;
X
X for (; *str; str++)
X {
X if (isprint(*str))
X *buf++ = *str;
X else if (iscntrl(*str))
X {
X *buf++ = '^';
X *buf++ = char('A' + *str - 1);
X }
X }
X
X *buf = 0;
X
X return bufptr;
X}
X
X
X//
X// Prints error message so it can be read. These are the error
X// functions we call once we have initialized the display.
X//
X
Xvoid error (const char *str)


X{
X move_to_message_line();
X clear_to_end_of_line();

X deinit_screen_and_kbdr();
X (void) printf(str);
X putchar('\n');
X exit(EXIT_FAILURE);
X}
X
X
Xvoid error (const char *fmt, int index)


X{
X move_to_message_line();
X clear_to_end_of_line();

X deinit_screen_and_kbdr();
X (void) printf(fmt, index);
X putchar('\n');
X exit(EXIT_FAILURE);
X}
X
X
Xvoid error (const char *fmt, const char *file, int line)


X{
X move_to_message_line();
X clear_to_end_of_line();

X deinit_screen_and_kbdr();
X (void) printf(fmt, file, line);
X putchar('\n');
X exit(EXIT_FAILURE);
X}
X
X
Xvoid error (const char *fmt, const char *file, int line, const char *str)


X{
X move_to_message_line();
X clear_to_end_of_line();

X deinit_screen_and_kbdr();
X (void) printf(fmt, file, line, str);
X putchar('\n');
X exit(EXIT_FAILURE);
X}
X
X
X//
X// This routine concatenates the two strings into the modeline. The
X// modeline is displayed in standout mode if possible. We never put
X// more than columns characters into the modeline. The modeline is
X// the penultimate line on the terminal screen. It does not synch
X// the display. If head == tail == 0, we just display the old modeline.
X// This happens 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 that it is 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//
X// Returns non-zero if a directory, otherwise 0. Also returns zero on error.
X//
X
Xint is_directory (const char *dir)
X{
X struct stat stbuf;
X if (stat(dir, &stbuf) < 0)
X return 0;
X return S_ISDIR(stbuf.st_mode);
X}
X
X
X//
X// Returns non-zero if a regular file, otherwise 0. Also returns zero on error.
X// We consider symlinks regular, if the implementation supports them.
X//
X
Xint is_regular_file (const char *file)
X{
X struct stat buf;
X if (stat(file, &buf) < 0)
X return 0;
X return S_ISREG(buf.st_mode)
X#ifndef NO_SYMLINKS
X || S_ISLNK(buf.st_mode)
X#endif


X ;
X}
X
X
X//

X// Returns non-zero if we have read and execute permission on the
X// directory, otherwise 0. 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//
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 its
X// tail to its head until we hit some whitespace. This presumes
X// that filenames do not contain whitespace. The one special case
X// to worry about is if the file is a symbolic link. In that case we will
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 = BMstrstr(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))
X --tmp;
X
X return tmp - line->line() + 1;
X}
X
X
X//
X// Returns the filename of the current line of the DirList in volatile storage.
X// If we have a symbolic link, we return the link not 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 = BMstrstr(file, " -> ");
X if (result)
X *result = 0;
X
X return file;
X}
X
X
X//
X// This routine redisplays the DirList at the top of our stack. It
X// assumes that the physical screen has become corrupted, clearing
X// each line before writing to it.
X//
X
Xvoid redisplay (void)
X{
X DirList *dl = dir_stack->top();
X
X DirLine *ln = dl->firstLine();
X
X clear_display_area();
X

X for (int i = 0; i < rows() - 2 && ln; i++, ln = ln->next())
X display_string(ln->line(), ln->length());
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//
X// forward declaration
X//
Xstatic void adjust_window (void);
X
X//
X// Displays the message in standout mode and waits until a character
X// is typed. Deals with SIGWINCH 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//
X// Reads a character from the keyboard. It only returns when it has
X// successfully read a character. So if we get suspended while waiting
X// to read a character, the signal handler will redisplay the screen and
X// we will still be here waiting to read a character. This is only used
X// by the main command loop so that we know that redisplay will do all
X// the necessary redisplay operations.
X//
X
Xchar read_from_keybd (void)
X{
X char c;
X
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//
X// Executes command using exec. Returns 1 if the exec went OK,
X// otherwise it returns 0. If closem is true, which is the default,
X// 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:
X return 0;
X case 0:
X if (closem)
X {
X (void)close(0);
X (void)close(1);
X (void)close(2);
X }
X execvp(file, argv);
X //
X // Exec failed.
X //
X exit(1);
X default:
X#ifdef NOWAITPID
X while (wait(&status) != pid) ;
X#else
X waitpid(pid, &status, 0);
X#endif
X return status == 0 ? 1 : 0;


X }
X}
X
X
X//

X// Execute the passed command using system. If prompt == 1, which is
X// the default, we prompt for a key before returning. We use system so that
X// shell and environment variables will be expanded.
X//
X
Xvoid exec_with_system (const char *cmd, int prompt)
X{
X deinit_screen_and_kbdr();
X unset_signals();
X
X system(cmd);


X
X set_signals();
X setraw();
X

X if (prompt)


X eat_a_character("Press Any Key to Continue");
X

X enter_cursor_addressing_mode();
X enter_visual_mode();
X enable_keypad();
X synch_display();
X}
X
X

X#ifdef SIGWINCH
X


X#include <sys/ioctl.h>
X
X#ifdef sgi
Xextern "C" int ioctl (int, int, ...);
X#endif
X

X//
X// Called to adjust our window after getting a SIGWINCH.
X//
X
Xstatic void adjust_window (void)
X{
X#ifdef TIOCGWINSZ


X struct winsize w;
X if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_row > 0)
X LI = w.ws_row;
X if (ioctl(fileno(stdout), TIOCGWINSZ, (char *)&w) == 0 && w.ws_col > 0)
X CO = w.ws_col;
X

X DirList* dl = dir_stack->top();
X //
X // Is current line still on the screen?
X //
X if (dl->savedYPos() >= rows()-2)
X {
X dl->setCurrLine(dir_stack->top()->firstLine());
X dl->saveYXPos(0, goal_column(dir_stack->top()));
X }
X
X //
X // need to adjust lastLine
X //
X DirLine *ln = dl->firstLine();
X for (int i = 0; i < rows()-2 && ln; i++, ln = ln->next())
X ;


X if (ln)
X dl->setLast(ln->prev());
X else
X dl->setLast(dl->tail());

X#endif
X}
X#endif
X
X
X#ifdef COMPLETION
X
X//
X// This is the sort routine we pass to qsort for sorting the
X// 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//
X// Read directory dir for any completions of prefix. Returns zero on
X// error; if prefix is null; if there are no completions; or if the
X// completion is the prefix itself. Else it returns a string in
X// volatile space containing the longest possible completion of prefix,
X// minus the 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)
X return 0;
X
X char **completions = new char*[tablesize * sizeof(char *)];
X
X 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 have got a match.
X //
X completions[matches] = new char[strlen(entry->d_name) + 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 memcpy(newtable, completions, (tablesize-chunksize)*sizeof(char*));
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 have tested have this improperly prototyped to void,
X // that it is 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 {
X if (c != completions[j][i])
X {
X stop = 1;
X break;
X }
X }
X if (stop)
X 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++)
X 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++)
X delete [] completions[i];
X delete [] completions;
X
X return completion;
X}
X
X#endif /*COMPLETION*/
X
X
X//
X// Deletes one char backward while in a prompt. Beeps if there are no
X// character to delete.
X//
X
Xstatic void dispose_of_char (size_t& pos, size_t& space_available, int msglen,
X char *response, const char *msg)
X{
X if (pos == 0)
X {
X ding();
X return;
X }
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 had previously shifted the
X // response to the left. Now we must shift the response to the right.
X //
X clear_message_line();
X response[pos] = 0;
X if (pos + msglen < columns())
X {
X //
X // Output message and response-to-date.
X //
X (void) fputs(msg, stdout);
X (void) fputs(response, stdout);
X space_available = columns() - pos - msglen;
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 }
X synch_display();
X}
X

X
X//
X// Displays msg prompt and then collects the response.
X// The keys of the response are echoed as they are 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;
X// Backspace and DELETE work as expected.
X// ^U deletes word preceding the cursor.
X// ^W delete the complete response.
X// Carriage return indicates the end of response.
X// Non-graphical characters are ignored.
X// If do_completion != 0, with do_completon == 0 by default,
X// we do filename completion on the TAB character.
X// The response is kept in volatile storage, so if the client
X// 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; // number of characters written to message line
X const char *abort_msg = "(C-g to Abort) ";
X String nmsg(abort_msg);
X nmsg += msg;
X size_t msglen = nmsg.length();
X
X move_to_message_line(); // it will have been already cleared by get_key
X
X if (msglen < columns())
X {
X (void) fputs(nmsg, stdout);
X written = msglen;


X }
X else
X {
X //

X // Leave space for columns/2 + 1 characters.
X //
X (void) fputs((const char *)nmsg + (msglen-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 + msglen < 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 - msglen;
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 are
X // adding characters as they are 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:
X {
X //
X // Perform filename completion.
X //
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;
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 do not
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 is 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 is relative to our current directory.
X //
X String head(current_directory);
X head += "/";
X head += 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 have 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
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 {
X DirList* dl = dir_stack->top();


X move_cursor(dl->savedYPos(), dl->savedXPos());

X message("Aborted");


X return 0;
X }
X

X case KEY_DEL:
X case KEY_BKSP: // back up one character
X dispose_of_char(pos, space_available, msglen, response, nmsg);
X break;
X case KEY_CTL_U: // clear line redisplay prompt
X if (pos + msglen >= columns())
X {
X //
X // nmsg is not visible
X //
X clear_message_line();
X if (msglen < columns())
X {
X (void) fputs(nmsg, stdout);
X written = msglen;
X }
X else
X {
X (void) fputs((const char*)nmsg +
X (msglen-columns()/2 + 1), stdout);
X written = columns()/2 - 1;
X }
X delete [] response;
X response = new char[space_available + 1];


X }
X else
X {
X //

X // msg is visible
X //
X written = msglen < columns() ? msglen : columns()/2 - 1;
X move_cursor(rows() - 1, written);
X clear_to_end_of_line();
X }
X synch_display();
X space_available = columns() - written;
X pos = 0;
X break;
X case KEY_CTL_W: // clear word
X {
X int leading_blanks = response[pos-1] == ' ' ||
X response[pos-1] == '\t';
X do
X {
X if (response[pos-1] != ' ' && response[pos-1] != '\t')
X leading_blanks = 0;
X
X if (!leading_blanks && (response[pos-1] == ' ' ||
X response[pos-1] == '\t'))
X break;
X
X dispose_of_char(pos, space_available, msglen,
X response, nmsg);
X }
X while (pos > 0);
X }
X break;
X default: ding(); break; // ignore other characters
X }


X }
X}
X
X
X//

X// Returns the number of lines in the DirList currently displayed
X// 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 ;
X return i;
X}
X
X
X//
X// Prints a message on the last line of the screen. It is up to the
X// calling process to put the cursor back where it belongs. Synchs
X// the display. It can 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 has exactly
X// one % into which the str will be substituted as in the ?printf functions.
X//
X
Xint message_window_dirty = 0;
X
Xvoid message (const char *fmt, const char *str)
X{
X String msg; // the complete message to be output
X
X clear_message_line();
X
X if (str)
X {
X const char *token = strchr(fmt, '%');
X if (token == 0)
X //
X // This should not happen. But if it does, let us
X // just print the format fmt.
X //
X msg = fmt;
X else
X {
X msg = String(fmt, token - fmt);
X msg += str;
X msg += token + 1;
X }
X }
X else
X msg = fmt;
X
X if (msg.length() < columns())
X (void)fputs(msg, stdout);
X else
X (void)printf("%*.*s", columns() - 1, columns() - 1, (const char *)msg);
X
X synch_display();
X message_window_dirty = 1;
X}
X
X
X//
X// Called both on receipt of certain signals.
X//
X
XSIGFUNC(exit_nicely) { deinit_screen_and_kbdr(); exit(1); }
X
X//
X// Prints the initial listing screen. Called by dired and read_commands
X// when rereading the current directory. Adjusts firstLine, lastLine
X// and currLine.
X//
X
Xvoid initial_listing (DirList *dl)
X{
X DirLine *ln = dl->head();
X dl->setFirst(ln);
X dl->setCurrLine(ln);
X
X clear_display_area();
X
X for (int i = 0; i < rows()-2 && ln; ln = ln->next(), i++)


X display_string(ln->line(), ln->length());
X
X if (ln)
X dl->setLast(ln->prev());
X else
X dl->setLast(dl->tail());
X}
X

X
X#ifdef SIGWINCH
X
X//
X// Set flag indicating window size changed.
X//
X
XSIGFUNC(winch)
X{
X#ifdef sgi
X (void) signal(SIGWINCH, (void (*)(int ...)) SIG_IGN);
X#else
X (void) signal(SIGWINCH, SIG_IGN);
X#endif
X win_size_changed = 1;
X#ifdef sgi
X (void) signal(SIGWINCH, (void (*)(int ...)) winch);


X#else
X (void) signal(SIGWINCH, winch);
X#endif
X}

X
X#endif
X
X
X//

X// Set up our signal handlers.
X//
X
Xvoid set_signals (void)
X{
X#ifdef sgi
X (void) signal(SIGHUP, (void (*)(int ...)) exit_nicely);
X (void) signal(SIGINT, (void (*)(int ...)) exit_nicely);
X (void) signal(SIGQUIT, (void (*)(int ...)) exit_nicely);
X (void) signal(SIGTERM, (void (*)(int ...)) exit_nicely);
X#ifdef SIGTSTP
X (void) signal(SIGTSTP, (void (*)(int ...)) termstop);
X#endif
X#ifdef SIGWINCH
X (void) signal(SIGWINCH, (void (*)(int ...)) winch);
X#endif
X#else
X (void) signal(SIGHUP, exit_nicely);
X (void) signal(SIGINT, exit_nicely);
X (void) signal(SIGQUIT, exit_nicely);
X (void) signal(SIGTERM, exit_nicely);
X#ifdef SIGTSTP
X (void) signal(SIGTSTP, termstop);
X#endif
X#ifdef SIGWINCH
X (void) signal(SIGWINCH, winch);
X#endif
X#endif
X}
X
X
X//
X// Set signals back to defaults.
X//
X
Xvoid unset_signals (void)
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//

X// Shifts the current line in DirList left until 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//
X// 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}

END_OF_FILE
if test 45404 -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 7\).
cp /dev/null ark5isdone

Kevin Braunsdorf

unread,
Oct 1, 1993, 7:22:46 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 70
Archive-name: dired-2.0/06

#! /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 6 (of 7)."
# Contents: String.C String.h GStack.C GStack.h Queue.C


# Wrapped by lijewski@xtesoc2 on Wed Sep 29 08:11:59 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH

if test -f 'String.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'String.C'\"
else
echo shar: Extracting \"'String.C'\" \(3998 characters\)
sed "s/^X//" >'String.C' <<'END_OF_FILE'
X//
X// String.C - implementation of reference counted string class.
X//
X// $Id: String.C,v 1.7 1993/07/22 16:53:27 lijewski Exp $
X//
X// Copyright 1991, 1992, 1993 by Mike Lijewski


X//
X
X#include <new.h>
X#include <stdio.h>
X#include <string.h>
X

X#include "String.h"
X
X
Xextern void error (const char *fmt, int index); // For index range errors.
X
XString::String (void) { p = new StringRep(); }
X
XString::String (const char *s, size_t len) { p = new StringRep(s,len); }
X
XString::String (char **s) { p = new StringRep(s, ::strlen(*s)); }
X
XString::String (const char *s) { p = new StringRep(s); }
X
XString::String (char** s, size_t slen) { p = new StringRep(s, slen); }
X
X
XStringRep::StringRep (const char *s)
X{
X len = ::strlen(s);
X rep = ::new char[len + 1];
X ::strcpy(rep, s);
X count = 1;
X}
X
X
XStringRep::StringRep (const char *s, size_t slen)
X{
X rep = ::new char[slen + 1];
X ::strncpy(rep, s, slen);
X rep[slen] = 0; // Force stringification.
X len = ::strlen(rep); // Force len == true length.
X count = 1;
X}
X
X
XString StringRep::operator+ (const String& s) const
X{
X size_t slen = s.length() + length();
X char *buf = ::new char[slen + 1];
X ::strcpy(buf, rep);
X ::strcpy(buf + length(), s.p->rep);
X return String(&buf, slen);
X}
X
X
X//
X// The definition of the head of the freelist that StringRep::operator new
X// uses to dole out StringReps efficiently.
X//
X
XStringRep* StringRep::freeList;
X
Xvoid* StringRep::operator new (size_t size)
X{
X if (size != sizeof(StringRep)) return ::new char[size];
X
X StringRep *s = freeList;
X
X if (s)
X freeList = s->next;


X else
X {
X //

X // We rely on a freestore exception catcher to catch
X // heap allocation failures.
X //

X StringRep *block = (StringRep*)::new char[chunksize*sizeof(StringRep)];
X


X for (int i = 0; i < chunksize - 1; i++)

X block[i].next = (StringRep *)&block[i + 1];
X block[chunksize - 1].next = 0;
X s = block;


X freeList = &block[1];
X }

X return s;
X}
X
X
Xvoid StringRep::operator delete(void *object)
X{
X StringRep *s = (StringRep *)object;
X s->next = freeList;
X freeList = s;
X}
X
X
XString::~String (void) { if (--p->count <= 0) delete p; }
X
X
XString& String::operator= (const String& rhs)
X{
X rhs.p->count++;
X if (--p->count <= 0) delete p;
X p = rhs.p;
X return *this;
X}
X
X
Xvoid String::operator+= (const String& rhs)
X{
X size_t slen = p->length() + rhs.length();
X char *buf = ::new char[slen + 1];
X (void)strcpy(buf, p->rep);
X (void)strcpy(buf + p->length(), rhs.p->rep);
X if (p->count == 1)
X {
X delete [] p->rep;
X p->rep = buf;
X p->len = slen;
X }
X else
X operator=(String(&buf, slen));
X}
X
X
Xvoid String::operator+= (const char *rhs)
X{
X size_t slen = p->length() + ::strlen(rhs);
X char *buf = ::new char[slen + 1];
X ::strcpy(buf, p->rep);
X ::strcpy(buf + p->length(), rhs);
X if (p->count == 1)
X {
X delete [] p->rep;
X p->rep = buf;
X p->len = slen;
X }
X else
X operator=(String(&buf, slen));
X}
X
X
Xvoid String::range_error (int index) const
X{
X ::error("range error: %d out of bounds", index);
X exit(1);
X}
X
X
X//
X// This operator WILL allow a constant String on the lhs of an = sign
X// to have the char at index changed. Use in this fashion at your own risk.
X//
XSBHelper String::operator[] (int index) const
X{
X if (index < 0 || index >= length())
X range_error(index);
X String* str = (String*)this;
X return SBHelper(*str, index);
X}
X
X
Xchar SBHelper::operator= (char c)
X{
X if (str.p->count == 1)
X //
X // Only one reference to our String. Just assign the character to
X // the appropriate place. Note that String::operator[] does the
X // range checking.
X //
X str.p->rep[index] = c;


X else
X {
X //

X // We have to uniquify our str.
X //
X str = String(str.p->rep);
X str.p->rep[index] = c;
X }
X return c;
X}
END_OF_FILE
if test 3998 -ne `wc -c <'String.C'`; then
echo shar: \"'String.C'\" unpacked with wrong size!
fi
# end of 'String.C'
fi
if test -f 'String.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'String.h'\"
else
echo shar: Extracting \"'String.h'\" \(4421 characters\)
sed "s/^X//" >'String.h' <<'END_OF_FILE'
X//
X// String.h - a reference counted string class.
X//
X// $Id: String.h,v 1.5 1993/06/20 01:51:53 lijewski Exp $


X//
X// Copyright (c) 1991, 1992, 1993 by Mike Lijewski.
X//
X

X#ifndef DIRED__STRING_H
X#define DIRED__STRING_H
X
X#include <stddef.h>


X#include <stdlib.h>
X#include <string.h>
X
X

X//
X// A Simple reference counted string class. It is implemented as an
X// Envelope-Letter abstaction with String being the envelope and StringRep
X// being the letter.
X//
X
Xclass String;
Xclass SBHelper;
X
Xclass StringRep
X{
X public:
X
X friend class String;
X friend class SBHelper;
X
X StringRep ();
X StringRep (const char *s);
X StringRep (const char *s, size_t slen);
X StringRep (char** r, size_t slen);
X ~StringRep ();
X
X static StringRep *freeList; // we manage our own storage
X enum { chunksize = 50 }; // # of StringReps to allocate at a time
X void *operator new (size_t size);
X void operator delete (void *object);
X
X int operator!= (const char *rhs) const;
X int operator== (const char *rhs) const;
X int operator!= (const StringRep& rhs) const;
X int operator== (const StringRep& rhs) const;
X
X String operator+ (const String& s) const;
X
X size_t length () const;
X
X private:
X
X //
X // Disable these two methods
X //
X StringRep (const StringRep&);
X StringRep& operator= (const StringRep &);
X
X union {
X char* rep;
X StringRep* next;
X };
X size_t len;
X int count;
X};
X
X
Xclass String
X{
X public:
X
X friend class StringRep;
X friend class SBHelper;
X
X String ();
X String (const String& s);
X String (const char *s);
X String (const char *s, size_t len);
X String (char **s);
X String (char** s, size_t slen);
X ~String ();
X
X String& operator= (const String& rhs);
X
X int operator== (const char *rhs) const;
X int operator== (const String& rhs) const;
X int operator!= (const char *rhs) const;
X int operator!= (const String& rhs) const;
X
X String operator+ (const String &rhs) const;
X friend String operator+ (const char *lhs, const String& rhs);
X
X void operator+= (const String &rhs);
X void operator+= (const char *rhs);
X
X operator const char * () const;
X SBHelper operator[] (int index) const;
X size_t length () const;
X
X private:
X
X void range_error (int index) const;
X
X StringRep* p;
X};
X
X
X//
X// This class is a helper class used by String::operator[] to distinguish
X// between applications of operator[] on the lhs and rhs of "=" signs.
X//
X
Xclass SBHelper
X{
X public:
X
X SBHelper (String &s, int i);
X SBHelper (const SBHelper& s);
X char operator= (char c);
X operator int () const;
X
X private:
X
X void operator= (const SBHelper&); // disallow this method
X
X String& str;
X int index;
X};
X
X
X//
X// StringRep inlines.
X//
X
Xinline StringRep::StringRep () { rep = ::new char[1]; len=0; *rep=0; count=1; }
X
Xinline StringRep::StringRep (char** r, size_t slen) {rep=*r;len=slen;count=1;}
X
Xinline StringRep::~StringRep () { delete [] rep; }
X
Xinline int StringRep::operator!= (const char *rhs) const
X{
X return strcmp(rep, rhs);
X}
X
Xinline int StringRep::operator== (const char *rhs) const
X{
X return strcmp(rep, rhs) == 0;
X}
X
Xinline int StringRep::operator!= (const StringRep& rhs) const
X{
X return strcmp(rep, rhs.rep);
X}
X
Xinline int StringRep::operator== (const StringRep& rhs) const
X{
X return strcmp(rep, rhs.rep) == 0;
X}
X
Xinline size_t StringRep::length () const { return len; }
X
X
X//
X// String inlines.
X//
X
Xinline String::String (const String& s) { p = s.p; p->count++; }
X
Xinline int String::operator== (const char *rhs) const { return *p == rhs; }
X
Xinline int String::operator== (const String& rhs) const {return *p==*(rhs.p);}
X
Xinline int String::operator!= (const char *rhs) const { return *p != rhs; }
X
Xinline int String::operator!= (const String& rhs) const {return *p!=*(rhs.p);}
X
Xinline String String::operator+ (const String &rhs) const { return *p + rhs; }
X
Xinline String operator+ (const char *lhs, const String& rhs)
X{
X return rhs + String(lhs);
X}
X
Xinline String::operator const char * () const { return p->rep; }
X
Xinline size_t String::length () const { return p->len; }
X
X
X//
X// SBHelper inlines.
X//
X
Xinline SBHelper::SBHelper (String& s, int i) : str(s), index(i) { };
X
Xinline SBHelper::SBHelper (const SBHelper& s) : str(s.str), index(s.index) { };
X
Xinline SBHelper::operator int () const { return str.p->rep[index]; }
X
X#endif /* DIRED_STRING_H */
END_OF_FILE
if test 4421 -ne `wc -c <'String.h'`; then
echo shar: \"'String.h'\" unpacked with wrong size!
fi
# end of 'String.h'
fi
if test -f 'GStack.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'GStack.C'\"
else
echo shar: Extracting \"'GStack.C'\" \(6579 characters\)
sed "s/^X//" >'GStack.C' <<'END_OF_FILE'
X//
X// GStack.C - implementation of a fast and memory efficient
X// generic stack class which stores pointers to objects.
X// The user must subclass to make a version specific to
X// the type of object they wish to store. Details can be
X// found in the test program at the bottom of this file.
X// This should use templates, to bad so many people still
X// don't have them.
X//
X// $Id: GStack.C,v 1.8 1993/06/20 01:51:53 lijewski Exp $
X//
X// Copyright 1991, 1992, 1993 by Mike Lijewski.
X//
X


X#include <stdlib.h>
X#include <string.h>
X

X#include "GStack.h"
X
X
XGStack::GStack (void)
X{
X blocks = 0;
X index = -1;
X offset = 0;
X nentries = size = 0;
X}
X
X
XGStack::~GStack (void)
X{
X for (int i = 0; i <= index; i++)
X delete [] blocks[i];
X delete [] blocks;
X}
X
X
XGStack::GStack (const GStack& rhs)
X{
X index = rhs.index;
X offset = rhs.offset;
X nentries = rhs.nentries;
X size = rhs.size;
X
X blocks = ::new void**[size];
X
X for (int i = 0; i <= index; i++)
X {
X blocks[i] = ::new void*[BlockSize];
X memcpy(blocks[i], rhs.blocks[i], BlockSize * sizeof(void*));
X }
X}
X
X
Xvoid GStack::operator= (const GStack& rhs)
X{
X if (this == &rhs) return;
X
X offset = rhs.offset;
X nentries = rhs.nentries;
X
X int minindex = index < rhs.index ? index : rhs.index;
X
X if (size < rhs.size)
X {
X void*** nblocks;
X //
X // We must allocate more space for void** pointers.
X //
X nblocks = ::new void**[rhs.size];
X //
X // Reuse as many of the previous blocks as possible.
X //
X memcpy(nblocks, blocks, (minindex + 1) * sizeof(void**));
X
X if (minindex == rhs.index)
X {
X //
X // Delete extraneous blocks.
X //
X for (int i = minindex + 1; i <= index; i++)
X delete [] blocks[i];


X }
X else
X {
X //

X // Must allocate new blocks.
X //
X for (int i = minindex + 1; i <= rhs.index; i++)
X nblocks[i] = ::new void*[BlockSize];
X }
X
X size = rhs.size;
X delete [] blocks;
X blocks = nblocks;
X }
X else
X {
X if (minindex == rhs.index)
X {
X //
X // Delete extraneous blocks.
X //
X for (int i = minindex + 1; i <= index; i++)
X delete [] blocks[i];


X }
X else
X {
X //

X // Must allocate new blocks.
X //
X for (int i = minindex + 1; i <= rhs.index; i++)
X blocks[i] = ::new void*[BlockSize];
X }
X }
X
X index = rhs.index;
X
X //
X // Now copy over the void* pointers.
X //
X for (int i = 0; i <= index; i++)
X memcpy(blocks[i], rhs.blocks[i], BlockSize * sizeof(void*));
X}
X
X
Xvoid GStack::push (void* object)
X{
X nentries++;
X
X if (offset == 0)
X {
X //
X // Must add a new block.
X //
X if (++index == 0 || index == size)
X {
X //
X // Must allocate room for more block pointers.
X //
X void*** nblocks = ::new void**[size += ArraySize];
X memcpy(nblocks, blocks, index * sizeof(void**));
X delete [] blocks;
X blocks = nblocks;
X }
X blocks[index] = ::new void*[BlockSize];
X }
X blocks[index][offset] = object;
X //
X // Adjust offset.
X //
X if (++offset == BlockSize) offset = 0;
X}
X
X
Xvoid* GStack::pop (void)
X{
X void* object = 0;
X
X if (nentries)
X {
X nentries--;
X
X if (offset == 0) offset = BlockSize;
X
X object = blocks[index][offset-1];
X
X if (--offset == 0)
X //
X // We can delete a block.
X //
X delete [] blocks[index--];
X }
X
X return object;
X}
X
X
X#ifdef TEST
X
X#include <iostream.h>
X#include <stdio.h> /* for sprintf */
X#include <new.h>
X#include <string.h>
X#include <sys/types.h>
X#include <sys/time.h>
X
X
Xclass StringStack : public GStack
X{
X public:
X void push (char* str) { GStack::push(str); }
X char* pop (void) { return (char*)GStack::pop(); }
X};
X
X
Xstatic void free_store_exhausted()
X{
X cerr << "exiting, free store exhausted ...";


X exit(1);
X}
X
X

Xstatic double elapsed_seconds(struct timeval& tp)
X{
X static struct timezone tzp;
X static struct timeval otp;
X otp = tp;
X gettimeofday(&tp, &tzp);
X return (tp.tv_sec - otp.tv_sec) + (tp.tv_usec - otp.tv_usec)/1000000.0;
X}
X
X
Xstatic int equal(StringStack& s1, StringStack& s2)
X{
X if (s1.entries() != s2.entries()) return 0;
X int rc = 1;
X for (int i = 0, max = (int)s1.entries(); i < max; i++)
X {
X if (strcmp(s1.pop(), s2.pop()))
X {
X rc = 0;
X break;
X }
X }
X return rc;
X}
X
X
Xint main()
X{
X set_new_handler(free_store_exhausted);
X
X StringStack s1;
X
X for (int i = 0; i < 33; i++)
X {
X char* name = new char[12];
X (void)sprintf(name, "%-10.10d", i);
X s1.push(name);
X }
X
X StringStack s2 = s1;
X
X cout << "test1: stacks are " << (equal(s1, s2) ? "equal\n" : "not equal\n");
X
X for (i = 0; i < 57; i++)
X {
X char* name = new char[12];
X (void)sprintf(name, "%-10.10d", i);
X s1.push(name);
X }
X
X s2 = s1;
X
X cout << "test2: stacks are " << (equal(s1, s2) ? "equal\n" : "not equal\n");
X
X const int count = 1000;
X
X struct timeval t;
X t.tv_sec = 0; // to keep CenterLine quiet
X t.tv_usec = 0; // ditto
X
X elapsed_seconds(t); // initialize timer
X
X srandom(13); // Initialize the random number generator.
X
X for (i = 0; i < count; i++)
X {
X unsigned long number = (unsigned long)random();
X
X switch (number & 01)
X {
X case 0:
X {
X //
X // Push the number onto the stack.
X //
X char* name = new char[12];
X (void)sprintf(name, "%-10.10d", i);
X cout << "pushing " << name << "\n";
X s1.push(name);
X break;
X }
X case 1:
X {
X //
X // Pop the stack.
X //
X char* object = s1.pop();
X cout << "popping " << (object == 0 ? "NULL" : object) << "\n";


X break;
X }
X }
X }

X
X cout << "count = " << count << ", seconds = " << elapsed_seconds(t) << endl;
X
X s2 = s1;
X
X cout << "test3: stacks are " << (equal(s1, s2) ? "equal\n" : "not equal\n");


X
X return 0;
X}
X

X#endif
END_OF_FILE
if test 6579 -ne `wc -c <'GStack.C'`; then
echo shar: \"'GStack.C'\" unpacked with wrong size!
fi
# end of 'GStack.C'
fi
if test -f 'GStack.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'GStack.h'\"
else
echo shar: Extracting \"'GStack.h'\" \(1405 characters\)
sed "s/^X//" >'GStack.h' <<'END_OF_FILE'
X//
X// GStack.h - implementation of a fast and memory efficient
X// generic stack class which stores pointers to objects.
X// The user must subclass to make a version specific to
X// the type of object they wish to store. Details can be
X// found in the test program appended to GStack.C.
X//
X// $Id: GStack.h,v 1.4 1993/06/20 01:51:53 lijewski Exp $
X//
X// Copyright 1991, 1992, 1993 by Mike Lijewski.
X//
X
X#ifndef GENERIC_STACK_H
X#define GENERIC_STACK_H
X
X
Xclass GStack
X{
X public:
X
X GStack (void);
X GStack (const GStack&);
X ~GStack (void);
X
X void operator= (const GStack&);
X
X void push (void* object);
X void* pop (void);
X
X void* top () const;
X
X unsigned long entries (void) const;
X
X int isEmpty (void) const;
X
X private:
X
X enum { BlockSize = 10, ArraySize = 5 };
X
X void*** blocks;
X int index; // index into a block of arrays of void*s
X int offset; // offset into block for push and pop
X unsigned long size; // number of allocated block pointers
X unsigned long nentries; // number of entries in GStack
X};
X
Xinline unsigned long GStack::entries (void) const { return nentries; }
X
Xinline int GStack::isEmpty (void) const { return nentries == 0; }
X
Xinline void* GStack::top () const
X{
X return isEmpty() ? 0 : blocks[index][offset==0 ? BlockSize-1 : offset-1];
X}
X
X#endif /*GENERIC_STACK_H*/
END_OF_FILE
if test 1405 -ne `wc -c <'GStack.h'`; then
echo shar: \"'GStack.h'\" unpacked with wrong size!
fi
# end of 'GStack.h'
fi
if test -f 'Queue.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Queue.C'\"
else
echo shar: Extracting \"'Queue.C'\" \(4766 characters\)
sed "s/^X//" >'Queue.C' <<'END_OF_FILE'
X//
X// Queue.C - queue for constant-sized objects. The objects must have a
X// working copy constructor as well as a default constructor.
X// The objects are stored in a dynamic array. Note that all
X// builtin types satisfy these criterion. If your compiler
X// supports templates compile with -DTEMPLATES. Otherwise,
X// I use typedefs which must be hand set.
X//
X//
X// $Id: Queue.C,v 1.8 1993/06/20 18:51:21 lijewski Exp $
X//
X// Copyright 1991, 1992, 1993 by Mike Lijewski.
X//
X
X#include <iostream.h>
X#include <math.h>


X#include <stdlib.h>
X#include <string.h>
X

X#include "Queue.h"
X
X#ifdef TEMPLATES
X#ifdef TEST
X
Xvoid error (const char* msg)
X{
X cerr << msg << "\n";
X}
X
X
Xextern "C"
X{
X double sin(double);
X double cos(double);
X}
X
X
Xmain (void)
X{
X //
X // char queue test
X //
X char* alphabet = "abcdefghijklmnopqrstuvwxyz";
X
X Queue<char> qc(10);
X
X qc.stats();
X
X for (int i = 0; i < 10; i++)
X {
X for (char* p = alphabet; *p; p++)
X qc.insert(*p);
X }
X
X int entries = qc.entries();
X for (i = 0; i < entries/2; i++)
X cout << qc.get();
X cout << endl;
X
X qc.stats();
X
X Queue<char> qc2 = qc;
X
X qc2.stats();
X
X for (i = 0; i < 10; i++)
X {
X for (char* p = alphabet; *p; p++)
X qc2.insert(*p);
X }
X
X qc = qc2;
X
X while (!qc.isEmpty())
X cout << qc.get();
X cout << endl;
X
X qc2.stats();
X
X qc.stats();
X
X //
X // double queue test
X //
X
X Queue<double> qd(10);
X
X qd.stats();
X
X for (i = 0; i < 10; i++)
X {
X for (int j = 0; j < 20; j++)
X qd.insert(j * sin(j +.123));
X }
X
X entries = qd.entries();
X cout << "qd contains " << entries << " doubles\n";
X
X for (i = 0; i < entries/2; i++)
X cout << qd.get() << " ";
X cout << endl;
X
X qd.stats();
X
X Queue<double> qd2 = qd;
X
X qd2.stats();
X
X for (i = 0; i < 10; i++)
X {
X for (int j = 0; j < 20; j++)
X qd2.insert(j * cos(j +.123));
X }
X
X qd = qd2;
X
X while (!qd.isEmpty())
X cout << qd.get() << " ";
X cout << endl;
X
X qd2.stats();
X qd.stats();
X}
X#endif /*TEST&&TEMPLATES*/
X
X#else /*!TEMPLATES*/
X
X
XQueue::Queue (int capacity_estimate)
X{
X head = tail = 0;
X rawdata = ::new T[(size = capacity_estimate)];
X}
X
X
XQueue::Queue (const Queue& q)
X{
X head = q.head;
X tail = q.tail;
X size = q.size;
X rawdata = ::new T[size];
X memcpy(rawdata, q.data(), size * sizeof(T));
X}
X
X
XQueue& Queue::operator= (const Queue& q)
X{
X if (this != &q)
X {
X delete [] rawdata;
X head = q.head;
X tail = q.tail;
X size = q.size;
X rawdata = ::new T[size];
X memcpy(rawdata, q.data(), size * sizeof(T));
X }
X return *this;
X}
X
X
Xvoid Queue::insert (T object)
X{
X if (tail == size)
X {
X if (head == 0)
X {
X T* nrawdata = ::new T[(size *= 2)];
X memcpy(nrawdata, rawdata, entries() * sizeof(T));
X delete [] rawdata;
X rawdata = nrawdata;
X }
X else
X {
X#ifdef MEMMOVE
X memmove(rawdata, &rawdata[head], entries() * sizeof(T));
X#else
X for (int i = 0; i < entries(); i++)
X rawdata[i] = rawdata[head + i];
X#endif
X tail -= head;
X head = 0;
X }
X }
X rawdata[tail++] = object;
X}
X
X
Xvoid Queue::error (void)
X{
X ::error("nothing to get()\n");
X}
X
X#ifdef TEST
X
Xvoid error (const char* msg)
X{
X cerr << msg << "\n";
X}
X
Xextern "C"
X{
X double sin(double);
X double cos(double);
X}
X
Xmain (void)
X{
X //
X // char queue test
X //
X char* alphabet = "abcdefghijklmnopqrstuvwxyz";
X
X Queue q(10);
X
X for (int i = 0; i < 10; i++)
X {
X for (char* p = alphabet; *p; p++)
X q.insert(*p);
X }
X
X int entries = q.entries();
X for (i = 0; i < entries; i++)
X cout << q.get();
X cout << endl;
X
X Queue q2 = q;
X
X for (i = 0; i < 10; i++)
X {
X for (char* p = alphabet; *p; p++)
X q2.insert(*p);
X }
X
X q = q2;
X
X while (!q.isEmpty())
X cout << q.get();
X cout << endl;
X
X#if 0
X //
X // double queue test
X //
X
X Queue q(10);
X
X for (int i = 0; i < 10; i++)
X {
X for (int j = 0; j < 20; j++)
X q.insert(j * sin(j +.123));
X }
X
X int entries = q.entries();
X cout << "q contains " << entries << " doubles\n";
X
X for (i = 0; i < entries; i++)
X cout << q.get() << " ";
X cout << endl;
X
X Queue q2 = q;
X
X for (i = 0; i < 10; i++)
X {
X for (int j = 0; j < 20; j++)
X q.insert(j * cos(j +.123));
X }
X
X q = q2;
X
X while (!q.isEmpty())
X cout << q.get() << " ";
X cout << endl;
X#endif
X}
X
X#endif /*!TEMPLATES&&TEST*/
X
X#endif /*!TEMPLATES*/
END_OF_FILE
if test 4766 -ne `wc -c <'Queue.C'`; then
echo shar: \"'Queue.C'\" unpacked with wrong size!
fi
# end of 'Queue.C'
fi
echo shar: End of archive 6 \(of 7\).
cp /dev/null ark6isdone

Kevin Braunsdorf

unread,
Oct 1, 1993, 7:45:44 PM10/1/93
to
Submitted-by: Michael J Lijewski <lije...@rosserv.gsfc.nasa.gov>
Posting-number: Volume 3, Issue 71
Archive-name: dired-2.0/07

#! /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 7 (of 7)."
# Contents: Queue.h keys.C ChangeLog INSTALL MANIFEST README


# Wrapped by lijewski@xtesoc2 on Wed Sep 29 08:11:59 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH

if test -f 'Queue.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Queue.h'\"
else
echo shar: Extracting \"'Queue.h'\" \(3143 characters\)
sed "s/^X//" >'Queue.h' <<'END_OF_FILE'
X//
X// Queue.h - queue for constant-sized objects. The objects must have a


X// working copy constructor as well as a default constructor.
X// The objects are stored in a dynamic array. Note that all

X// builtin types satisfy these criteria. If your compiler


X// supports templates compile with -DTEMPLATES. Otherwise,
X// I use typedefs which must be hand set.
X//

X// $Id: Queue.h,v 1.6 1993/06/20 18:51:21 lijewski Exp $


X//
X// Copyright 1991, 1992, 1993 by Mike Lijewski.
X//
X
X

X#ifndef QUEUE_H
X#define QUEUE_H
X
X
X#include <iostream.h>


X#include <stdlib.h>
X#include <string.h>
X

X#include "dired.h"
X
X
X#ifdef TEMPLATES
Xtemplate<class T> class Queue
X#else
X//
X// Change this typedef so that the type of object you want to
X// store in the queue is referred to as TYPE.
X//
Xtypedef char T;
X
Xclass Queue
X#endif
X{
X public:
X
X Queue (int initial_size);
X Queue(const Queue& rhs);
X
X ~Queue () { delete [] rawdata; }
X
X Queue& operator= (const Queue& rhs);
X
X T get () { if (isEmpty()) error(); return rawdata[head++]; }
X
X void insert (T object);
X
X int entries () const { return tail - head; }
X const T* data () const { return &rawdata[head]; }
X int isEmpty () const { return entries() == 0; }
X
X#ifdef TEST
X void stats (void);
X#endif
X
X protected:
X
X void error (void);
X
X int size;
X int head;
X int tail;
X T* rawdata;
X};
X
X
X//
X// If your compiler supports templates.
X//
X#ifdef TEMPLATES
X
X
Xtemplate<class T> Queue<T>::Queue (int initial_size)


X{
X head = tail = 0;

X size = initial_size;


X rawdata = ::new T[size];
X}

X
X
Xtemplate<class T> Queue<T>::Queue (const Queue<T>& q)
X{
X head = 0;
X tail = q.entries();


X size = q.size;
X rawdata = ::new T[size];

X memcpy(rawdata, q.data(), q.entries() * sizeof(T));
X}
X
X
Xtemplate<class T> Queue<T>& Queue<T>::operator= (const Queue<T>& q)


X{
X if (this != &q)
X {
X delete [] rawdata;

X head = 0;
X tail = q.entries();


X size = q.size;
X rawdata = ::new T[size];

X memcpy(rawdata, q.data(), q.entries() * sizeof(T));


X }
X return *this;
X}
X
X

Xtemplate<class T> void Queue<T>::insert (T object)


X{
X if (tail == size)
X {
X if (head == 0)
X {

X size *= 2;
X T* nrawdata = ::new T[size];


X memcpy(nrawdata, rawdata, entries() * sizeof(T));
X delete [] rawdata;
X rawdata = nrawdata;
X }
X else
X {
X#ifdef MEMMOVE
X memmove(rawdata, &rawdata[head], entries() * sizeof(T));
X#else
X for (int i = 0; i < entries(); i++)
X rawdata[i] = rawdata[head + i];
X#endif
X tail -= head;
X head = 0;
X }
X }
X rawdata[tail++] = object;
X}
X
X

Xtemplate<class T> void Queue<T>::error (void)


X{
X ::error("nothing to get()\n");
X}
X
X

X#ifdef TEST
Xtemplate<class T> void Queue<T>::stats (void)
X{
X cerr << "entries = " << entries() << "\n"
X << "size = " << size << "\n"
X << "head = " << head << "\n"
X << "tail = " << tail << "\n";
X}
X#endif /*TEST*/
X
X
X#endif /*TEMPLATES*/
X
X
X#endif /*QUEUE_H*/
END_OF_FILE
if test 3143 -ne `wc -c <'Queue.h'`; then
echo shar: \"'Queue.h'\" unpacked with wrong size!
fi
# end of 'Queue.h'
fi
if test -f 'keys.C' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'keys.C'\"
else
echo shar: Extracting \"'keys.C'\" \(11294 characters\)
sed "s/^X//" >'keys.C' <<'END_OF_FILE'
X//
X// keys.C - contains implementation of virtual keyboard for dired.
X//
X// $Id: keys.C,v 1.12 1993/06/20 19:01:50 lijewski Exp $
X//
X// Copyright (c) 1993 by Mike Lijewski.
X//
X
X#include <ctype.h>


X#include <stdio.h>
X#include <string.h>
X

X#include "Queue.h"
X#include "String.h"
X#include "dired.h"
X#include "keys.h"
X
X
Xstatic Command Commands[] =
X{
X { CURSOR_DOWN, "cursor_down" },
X { CURSOR_UP, "cursor_up" },
X { PAGE_UP, "page_up" },
X { PAGE_DOWN, "page_down" },
X { HALFPAGE_UP, "halfpage_up" },
X { HALFPAGE_DOWN, "halfpage_down" },
X { TOP_OF_FILE, "top_of_file" },
X { BOTTOM_OF_FILE, "bottom_of_file" },
X { EDIT_FILE, "edit_file" },
X { PAGE_FILE, "page_file" },
X { COPY_FILE, "copy_file" },
X { DELETE_FILE, "delete_file" },
X { RENAME_FILE, "rename_file" },
X { COMPRESS_FILE, "compress_file" },
X { EDIT_DIRECTORY, "edit_directory" },
X { CHGRP_FILE, "chgrp_file" },
X { HELP, "help" },
X { LINK_FILE, "link_file" },
X { CHMOD_FILE, "chmod_file" },
X { CHANGE_SORT_ORDER, "change_sort_order" },
X { PRINT_FILE, "print_file" },
X { REREAD_DIRECTORY, "reread_directory" },
X#ifndef NO_SYMLINKS
X { SYMLINK_FILE, "symlink_file" },
X#endif
X { GUNZIP_FILE, "gunzip_file" },
X { UNCOMPRESS_FILE, "uncompress_file" },
X { SEARCH_FORWARD, "search_forward" },
X { SEARCH_BACKWARD, "search_backward" },
X { SHELL_COMMAND, "shell_command" },
X { VERSION, "version" },
X { GZIP_FILE, "gzip_file" },
X { REDISPLAY, "redisplay" },
X { POP_THIS_DIRECTORY, "pop_this_directory" },
X { QUIT, "quit" }
X};
X
X
Xconst int NCommands = sizeof(Commands) / sizeof(Commands[0]);
X
X
Xconst char* Help[UNKNOWN] =
X{
X "Forward one line.",
X "Backward one line.",
X "Forward one window.",
X "Backward one window.",
X "Forward one half-window.",
X "Backward one half-window.",
X "Go to first line of listing.",
X "Go to last line of listing.",
X "Edit the current file (w/ $EDITOR) or directory.",
X "View current file with $PAGER (default `more').",
X "Copy current file - prompts for destination file.",
X "Delete current file - prompts for affirmation.",
X "Rename current file.",
X "Compress current file.",
X "Prompt for and edit a directory.",
X "Change the group of the current file.",
X "Display this help.",
X "Link current file to another file.",
X "Change the mode of the current file.",
X "Prompt for a new sorting order (a, c, t, or u).",
X "Print current file with $DIREDPRT (default `lpr').",
X "Rereads the current directory and updates the display.",
X#ifndef NO_SYMLINKS
X "Create symbolic link to current file.",
X#endif
X "Unzip current file using GNU unzip (gunzip).",
X "Uncompress current file.",
X "Search forward for string.",
X "Search backward for string.",
X "Runs a shell command.",
X "Print out version string.",
X "Zip current file using GNU zip (gzip).",
X "Repaint screen.",
X "Back up directory tree if possible, else quit.",
X "Exit immediately."
X};
X
X
X//
X// Implementation of KeyMap.
X//
X
XKeyMap::KeyMap (void)
X{
X map = ::new EncodedCommand*[(size = NCommands)];
X nentries = 0;
X}
X
X
XKeyMap::~KeyMap (void)
X{
X for (int i = 0; i < nentries; i++) delete map[i];
X delete [] map;
X}
X
X
XKeyMap::KeyMap (const KeyMap& k)
X{
X nentries = k.nentries;
X map = ::new EncodedCommand*[(size = k.size)];
X for (int i = 0; i < nentries; i++)
X map[i] = new EncodedCommand(k.map[i]->keycode, k.map[i]->sequence);
X}
X
X
XKeyMap& KeyMap::operator= (const KeyMap& k)
X{
X if (this != &k)
X {
X for (int i = 0; i < nentries; i++) delete map[i];
X nentries = k.nentries;
X map = ::new EncodedCommand*[(size = k.size)];
X for (i = 0; i < nentries; i++)
X map[i] = ::new EncodedCommand(k.map[i]->keycode, k.map[i]->sequence);


X }
X return *this;
X}
X
X

Xvoid KeyMap::add (CommandCode code, const char* seq)
X{
X if (!seq) return;
X
X if (nentries == size)
X {
X EncodedCommand** nmap = ::new EncodedCommand*[(size += NCommands)];
X memcpy(nmap, map, entries() * sizeof(EncodedCommand*));
X delete [] map;
X map = nmap;
X }
X map[nentries++] = ::new EncodedCommand(code, seq);
X}
X
X
Xvoid KeyMap::add (CommandCode code, const String& seq)
X{
X if (nentries == size)
X {
X EncodedCommand** nmap = ::new EncodedCommand*[(size += NCommands)];
X memcpy(nmap, map, entries() * sizeof(EncodedCommand*));
X delete [] map;
X map = nmap;
X }
X map[nentries++] = ::new EncodedCommand(code, seq);
X}
X
X
X//
X// Returns the CommandCode code for the command; UNKNOWN if it is not a valid
X// command.
X//
X
Xstatic CommandCode command_to_keycode (const char* name)
X{
X CommandCode rc = UNKNOWN;
X for (int i = 0; i < NCommands; i++)
X if (!strcmp(Commands[i].name,name))
X {
X rc = Commands[i].keycode;
X break;


X }
X return rc;
X}
X
X

X//
X// Build the default keymap.
X//
X
Xstatic KeyMap& default_keymap (void)
X{
X static KeyMap keymap;
X
X keymap.add(CURSOR_DOWN, encode("j"));
X keymap.add(CURSOR_DOWN, encode("n"));
X keymap.add(CURSOR_DOWN, encode("\\0x20"));
X keymap.add(CURSOR_DOWN, encode("^N"));
X keymap.add(CURSOR_DOWN, encode("\r"));
X keymap.add(CURSOR_UP, encode("k"));
X keymap.add(CURSOR_UP, encode("p"));
X keymap.add(CURSOR_UP, encode("^P"));
X keymap.add(CURSOR_UP, encode("^Y"));
X keymap.add(PAGE_UP, encode("^F"));
X keymap.add(PAGE_UP, encode("^V"));
X keymap.add(PAGE_DOWN, encode("^B"));
X keymap.add(PAGE_DOWN, encode("^[v"));
X keymap.add(HALFPAGE_UP, encode("^D"));
X keymap.add(HALFPAGE_DOWN, encode("^U"));
X keymap.add(TOP_OF_FILE, encode("<"));
X keymap.add(TOP_OF_FILE, encode("^[<"));
X keymap.add(BOTTOM_OF_FILE, encode(">"));
X keymap.add(BOTTOM_OF_FILE, encode("^[>"));
X keymap.add(EDIT_FILE, encode("e"));
X keymap.add(EDIT_FILE, encode("f"));
X keymap.add(PAGE_FILE, encode("m"));
X keymap.add(PAGE_FILE, encode("v"));
X keymap.add(COPY_FILE, encode("c"));
X keymap.add(DELETE_FILE, encode("d"));
X keymap.add(RENAME_FILE, encode("r"));
X keymap.add(COMPRESS_FILE, encode("C"));
X keymap.add(EDIT_DIRECTORY, encode("E"));
X keymap.add(CHGRP_FILE, encode("G"));
X keymap.add(HELP, encode("?"));
X keymap.add(HELP, encode("H"));
X keymap.add(LINK_FILE, encode("L"));
X keymap.add(CHMOD_FILE, encode("M"));
X keymap.add(CHANGE_SORT_ORDER, encode("O"));
X keymap.add(PRINT_FILE, encode("P"));
X keymap.add(REREAD_DIRECTORY, encode("g"));
X keymap.add(REREAD_DIRECTORY, encode("R"));
X#ifndef NO_SYMLINKS
X keymap.add(SYMLINK_FILE, encode("S"));
X#endif
X keymap.add(GUNZIP_FILE, encode("u"));
X keymap.add(UNCOMPRESS_FILE, encode("U"));
X keymap.add(SEARCH_FORWARD, encode("/"));
X keymap.add(SEARCH_BACKWARD, encode("\\"));
X keymap.add(SHELL_COMMAND, encode("!"));
X keymap.add(VERSION, encode("V"));
X keymap.add(GZIP_FILE, encode("z"));
X keymap.add(REDISPLAY, encode("^L"));
X keymap.add(POP_THIS_DIRECTORY, encode("q"));
X keymap.add(QUIT, encode("Q"));
X
X return keymap;
X}
X
X
Xvoid initialize_keymap_from_file (KeyMap& keymap, const char* file)
X{
X //
X // We look for file and ~/file, in that order.
X // If neither exist, we use the default keymap.
X //
X FILE* fp = fopen(file, "r");


X if (fp == 0)

X {
X char* home = getenv("HOME");
X if (home)
X {
X String pathname(home);
X if (pathname[pathname.length() - 1] != '/')


X pathname += "/";
X pathname += file;

X fp = fopen(pathname, "r");


X if (fp == 0)

X {
X keymap = default_keymap();
X return;
X }
X }
X else
X {
X keymap = default_keymap();


X return;
X }
X }
X

X char** tokens;
X for (char* line = fgetline(fp); line; line = fgetline(fp))
X {
X if (tokenize(line, " \t", tokens) > 1 && tokens[0][0] != '#')
X {
X //
X // Layout of ".diredrc":
X //
X // [ \t]*commandname[ \t]+encoding
X //
X CommandCode code = command_to_keycode(tokens[0]);
X
X if (code != UNKNOWN)
X {
X const char* encoding = encode(tokens[1]);
X if (encoding == 0)
X {
X //
X // Problem building encoded command.
X //
X delete [] line;
X continue;
X }
X keymap.add(code, encoding);
X }
X }
X delete [] line;
X }
X (void) fclose(fp);
X}
X
X
XCommandCode get_command (const KeyMap& keymap)
X{
X#ifdef TEMPLATES
X static Queue<char> q(5);
X#else
X static Queue q(5);
X#endif
X
X char c = read_from_keybd();
X
X if (c <keymap[0]->sequence[0] || c >keymap[keymap.entries()-1]->sequence[0])
X return UNKNOWN;
X else
X q.insert(c);
X
X int submatch, match = 0, match_index;
X
X do
X {
X submatch = 0;
X
X int index, cmp, lo = 0, hi = keymap.entries() - 1;
X
X while (lo <= hi)
X {
X index = (lo + hi) / 2;
X if ((cmp = strncmp(keymap[index]->sequence, q.data(), q.entries())) == 0)
X {
X if (keymap[index]->sequence.length() == q.entries())
X {
X match_index = index;
X match = 1;
X }
X else
X {
X submatch = 1;
X q.insert(read_from_keybd());
X }
X break;
X }
X else if (cmp < 0)
X lo = index + 1;
X else
X hi = index - 1;
X }
X }
X while (submatch);
X
X CommandCode rc = match ? keymap[match_index]->keycode : UNKNOWN;


X
X int entries = q.entries();

X for (int i = 0; i < entries; i++)
X //
X // Remove the characters.
X //
X (void) q.get();


X
X return rc;
X}
X

X#ifdef TEST
X
Xstatic const char* keycode_to_command (CommandCode key)
X{
X for (int i = 0; i < NCommands; i++)
X if (Commands[i].keycode == key)
X return Commands[i].name;
X}
X
Xmain()
X{
X KeyMap k;
X
X initialize_keymap_from_file(k, "junk");
X
X cout << k.entries() << " entries in the keymap for `junk'\r\n";
X
X for (int i = 0; i < k.entries(); i++)
X cout << decode(k[i]->sequence) << "\n";


X
X return 0;
X}
X

X#endif /*TEST*/


X
X
X
X
END_OF_FILE

if test 11294 -ne `wc -c <'keys.C'`; then
echo shar: \"'keys.C'\" unpacked with wrong size!
fi
# end of 'keys.C'
fi
if test -f 'ChangeLog' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'ChangeLog'\"
else
echo shar: Extracting \"'ChangeLog'\" \(13037 characters\)
sed "s/^X//" >'ChangeLog' <<'END_OF_FILE'
X1.2 to 1.3
X---------
X
Xo No longer remove `commands.C' on `make clean'.
X
Xo Fixed typo in MANIFEST.
X
Xo Added `SHELL = /bin/sh' to Makefile.
X
Xo Added list of machines on which `dired' has been successfully
X built and tested to INSTALL.
X
Xo Added a `#ifndef __GNUG__ ... #endif' around the prototypes of the
X termcap(3) functions to get around some inconsistencies in the way
X early versions of g++ declared the termcap(3) functions. As g++ is
X set up, the termcap funnctions are in std.h, which seems to be
X always included by the compiler, so we don't need to declare them
X for g++ anyhow.
X
Xo Found and fixed a problem with doing a popen(ls -al dirname) where
X dirname is a symbolic link. The solution is to instead do a
X popen(ls -al -L dirname) - force `ls' to follow symbolic links.
X
Xo made `v' and 'm' edit directories if the given file is a directory,
X instead of insisting that it can only page through regular files.
X That is, we edit if a directory and page if a regular file.
X
Xo removed the `-?' option.
X
Xo added `-u' and `-c' options to sort by access time and inode-change
X time to the already present `-t' option to sort by modification time.
X
Xo added the `O' command to query for and set a sorting order.
X
Xo Removed THANKS file.
X
Xo Added a command `E' to edit a prompted-for directory. This command
X interprets a `~' as the first character in the directory as the home
X directory of the user.
X
Xo Simplified the README.
X
X1.3 to 1.4
X----------
X
Xo Fixed the problem of the cursor position not being updated when we
X do a forward search from the last line.
X
Xo Documented that a response of either `y' or `Y' to the `d' command
X is considered affirmative.
X
Xo The screen is now cleared and the cursor positioned in the bottom
X left-hand corner of the screen, when a file is edited. Most
X fullscreen editors handle this themselves. This fix is so that
X non-fullscreen editors such as `ed' will work.
X
Xo Ctl-G now properly aborts out of the `E' command.
X
Xo Added a bit more documentation on the `M' command.
X
Xo Modified the copy command `c' so that if the first character of the
X file to which we are copying is a `~', the full pathname of the


X user's home directory will be substituted for the `~'.
X

Xo Fixed a problem with how the `!' command echos the command and then
X prints the output of the command. The intent is that the command
X line we're executing is echoed and then the following lines contain
X the output of the command, as `vi' does it.
X
X
Xo Am now tracking CWD and PWD, if either are defined, so that
X commands which depend on these variables will work correctly. In
X particular, this means that using emacs as ones editor works
X correctly. Personally, I feel the fact that emacs will use CWD or
X PWD, instead of using getcwd(), is an error.
X
Xo Corrected a problem with the cursor not being properly positioned
X when the last file in the window is deleted.
X
X1.4 to 1.5
X---------
X
Xo Fixed a glitch in dired.h which was tickled when compiling with g++
X on a machine for which -DNO_STRSTR was defined. Found this when I
X got dired working on an old MicroVax running Ultrix.
X
Xo Closed a small memory leak in dired.C relating to not reclaiming
X space previously passed to putenv() when tracking CWD and PWD in
X dired().
X
Xo Am no longer appending `-L' to the `ls' commands when the system
X supports symbolic links. This way, when you're editing a directory
X containing symbolic links, you actually see the `link -> file'.
X Users seem to prefer this to accessing the linked file transparently.
X
Xo Closed memory leak in command1.C relating to not releasing the
X response from prompt() in edit_prompted_for_directory().
X
Xo I've come up with an easier way to deal with symbolic links
X which has lead to a little simplification of the code.
X In particular, I've been able to remove edit_directory() and
X in_same_directory() from command1.C and full_path_name() from utilities.C.
X
Xo Added a compile-time define NEED_LG, which when defined in the
X Makefile, informs `dired' that `ls' needs the `-lg' flags in a long
X listing in order to display both owner and group of the files.
X This way all users should be able to see both the owner and group of
X the listed files. Modified INSTALL to NEED_LG also.
X
Xo Closed a tiny memory leak due to my use of putenv(). The putenv()
X documentation says that the memory used by strings "NAME=value1" is
X not recovered when a new "NAME=value2" is added; consequenty, I've
X started deleting the old string if I added it. This all arises
X since I'm now tracking CWD and PWD.
X
Xo Beefed up the login in the `c' command so that we don't get more
X than one file with the same name displayed on the screen.
X
Xo Modified the `g' command to update directories in a "smart" manner.
X It now places the cursor back on the file it started out on if that
X file still exists; otherwise it tries to place it "close" to where
X it was.
X
Xo Modified `dired.1' and `dired.lpr' to reflect the changes to the `g'
X and `c' commands.
X
Xo Now mention in the manpage that searches without a search string,
X repeat the previous search in that direction.
X
X1.5 to 1.6
X---------
X
Xo Am now ringing the terminal's bell when an unknown key sequence is
X input.
X
Xo The UP and DOWN arrow keys now work as expected.
X
Xo Removed the `D' command. It has proven to be too easy to abuse.
X
Xo The `E' and `c' commands now support filename completion when the
X TAB key is pressed from within the prompt asking for the file to
X copy to or the directory to edit.
X
Xo Am now updating the display when I do a `c' command which overwrites
X another file in the window.
X
Xo Am now appending the string "(C-g to Abort)" to all prompts, so that
X those users who don't RTFM can get out of prompts :-).
X
Xo The help screen now scrolls forward (SPACE) and backward (BACKSPACE).
X
Xo Replaced printf()s with fputs()s wherever possible.
X
Xo Added a second `const char *' argument to message(), which defaults
X to 0. This allows me to treat message() as either printing a simple
X string, or as a simple printf() mechanism where it replaces the `%'
X in the first string, with the second string.
X
Xo The modeline algorithm now updates the modeline in a "smart" manner
X intended to minimize the total number of characters output to the screen.
X
Xo Replaced getchar() with read().
X
Xo The `L', `S' and `r' commands now expand a `~' to the user's home
X directory, if the `~' is the first character of the new filename.
X
Xo Removed all possible system()s and instead do my own fork()ing and
X exec()ing where it makes sense.
X
Xo Modified reread_current_directory() to take a reference to a pointer
X instead of a pointer to a pointer.
X
Xo Modified get_current_directory() to return new'd storage. Also
X changed it's name to current_directory(). Modified get_file_name()
X to use its own volatile storage instead of forcing its clients to
X delete memory. Made prompt(), completion() and expand_tilde also
X return volatile storage.
X
Xo Plugged a memory leak in get_directory_listing() in utilities.C.
X
Xo Added class specific operators new() and delete() for the DirLine
X class. This is both a speed and space efficiency when directories
X have many files in them.
X
Xo Put all the globals into one file for easy reference.
X
Xo Fixed a problem with deleting the last line on the screen, which
X has more lines following it.
X
X1.6 to 1.7
X---------
X
Xo Developed and integrated in a simple reference counted string package.
X
Xo Fixed a problem with the directory name in the modeline not being
X updated in certain cases.
X
Xo Added -DSIGINTERRUPT to Makefile and INSTALL as well as adding the
X appropriate code to dired.C to call siginterrupt(2) when needed. I
X rely on read(2) being interrupted by signals to deal appropriately
X with SIGTSTP and SIGWINCH.
X
Xo Added LINES and COLUMNS to the list of environment variables in the
X manpage which dired consults.
X
Xo Now update the listing line after paging through a file. This way,
X if we're viewing files based on access or inode-modification time, the
X directory listing will reflect our reality, though it won't be ordered
X properly.
X
Xo Purify'd the code. Purify is a really nice product.
X
Xo Inlined those member functions which didn't significantly bloat the
X code.
X
X1.7 to 1.8
X---------
X
Xo removed setbuf for setvbuf -- stdout is still fully buffered, but
X now I let the standard I/O choose the buffer and the buffer size.
X
Xo added a bit more information to some of the error messages in
X command[12].C.
X
Xo added a define OLDDELETE to Makefile and INSTALL which means that
X your compiler doesn't understand the 'delete []' syntax. I then
X integrated this into the source code.
X
Xo according to POSIX.1 the second argument to the execv() functions is
X a (char *const *); my code is now cognizant of this fact.
X
Xo forced the cursor to return to its previous position when aborting
X out of a shell command.
X
Xo ran c++-tame-comments from the latest c++-mode.el on each .C file.
X This is where all the backslashes in the comments come from.
X
Xo added 1992 to Copyright notice.
X
Xo did some reformatting to meet new formatting requirements I've imposed on
X myself.
X
Xo got rid of <stdarg.h> function error() by overloading error for the
X specific cases I needed.
X
Xo added workaround to bug in g++ 2.2.2
X
Xo changed "clean" target in Makefile to not delete files ending in
X '~'. A new target "realclean" does though remove them as well as
X removing command.C, forcing it to be regenerated at next make.
X
Xo am now conditionally compiling code which depends on SIGTSTP for
X those poor souls who don't have job control.
X
X
X
X1.8 to 1.9
X---------
X
Xo fixed the problem with dired seemingly clearing the screen on exit on
X those kinds of terminals which normally redisplay what was on the screen
X before full-screen programs were invoked. Xterm work this way. Now
X dired is a nice citizen.
X
Xo fixed a problem with renaming one directory to another.
X
Xo added a copy constructor to SBHelper.
X
Xo fixed problem in dired.C related to not checking for null pointer when
X reading options.
X
Xo made DELETE behave the same as BACKSPACE when in a prompt.
X
Xo added workaround to missing waitpid(2).
X
Xo added patches for SunOS 4.0.2.
X
Xo added String(const char*, int) which works similarly to strncpy() and
X integrated it in.
X
Xo added HP/Apollo patches.
X
Xo I've switched to RCS from SCCS.
X
Xo removed all "#include <libc.h>"s.
X
Xo removed all references to <osfcn.h>
X
Xo changed <sys/errno.h> to <errno.h> as per POSIX.
X
Xo made ^W delete word preceding cursor and ^U delete the complete
X response while in a prompt.
X
Xo integrated in a patch from Tom McConnell (tmcc...@sedona.intel.com)
X which did the following:
X
X o Added more machines to INSTALL.
X
X o Added GNU zip/unzip support.
X
X o Moved `message("Compressing ... ")' in front of `if (!system(cmd))'.
X Otherwise you do not know if the machine is busy on a long compress.
X Also for uncompress, zip, and unzip.
X
X o Added `NO_D_NAMLEN' to support dirent struct's with no `d_namlen'
X members. Modified utilities.C to support this.
X
X o Added `$(HDR)' as dependancies for `dired' target in Makefile.
X
Xo replaced all C multi-line comments with C++ comments.
X
Xo separated out my reference counted String class into its own files.
X
Xo merged in a fast and memory efficient Generic Stack class.
X
Xo implemented a virtual keyboard so that users can map the available
X commands to the key sequence of their choosing.
X
X1.9 to 2.0
X---------
X
Xo I now check for conflicts in the built keymap.
X
Xo we can now run dired on a list of directories
X
Xo fixed problem with SPACE not working.
X
Xo speed up key processing.
X
Xo fixed small problem with `q' command.
X
Xo added support for PgUp, PgDn, Home and End keys on keyboards which
X support them; very few keyboards do though :-(.
X
Xo added my own specialized version of strstr called BMstrstr which uses
X Boyer-Moore string matching. This was to speed up the searches for
X " -> "; the symlink search.
X
Xo help now works with mapped keys.
X
Xo removed all anachronistic constructs as found by CC +p.
X
Xo made a valiant attempt to shut up most of the extra warnings
X from CC +w.
X
Xo applied patch from Tom McConnell to Makefile which modified how I do
X installs.
X
Xo added cast to a stat(2) call - seems people can't get their header
X files to properly prototype stat(2).
X
Xo removed an extraneous extern from the definition of Help.
X
Xo beefed up expand_tilde() in utilities.C
X
Xo fixed the orientation of PAGE_UP and PAGE_DOWN.
X
Xo now strip off default automounter directory from directory names.
X
Xo is_regular_file() now knows about symlinks.
X
Xo fixed a couple things g++ didn't like.
X
Xo made code compile with ObjectCenter 2.0
X
Xo changed gzip prefix from .z to .gz
X
Xo templatized Queue class. Will use if -DTEMPLATES is defined.
X
X2.0 to 2.1
X----------
X
Xo applied strcpy() trick to String class member functions.
X
Xo applied apollo patch from Thomas Dickey.
X
Xo ported to SGI Indigo.
X
Xo applied patches from comp.sources.reviewed.
END_OF_FILE
if test 13037 -ne `wc -c <'ChangeLog'`; then
echo shar: \"'ChangeLog'\" unpacked with wrong size!
fi
# end of 'ChangeLog'
fi
if test -f 'INSTALL' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'INSTALL'\"
else
echo shar: Extracting \"'INSTALL'\" \(7056 characters\)
sed "s/^X//" >'INSTALL' <<'END_OF_FILE'
X
XDired is written in C++, so you must have a C++ compiler. It runs
Xonly in the UNIX environment for the time being. If you would like to
Xport it to another OS, say VMS, please contact me. It has been
Xsuccessfully built and tested in the following environments:
X
XSolbourne running OS/MP 4.1B (SunOS 4.1.2) with gcc-2.3.3, libg++-2.3
XSun Sparc running SunOS 4.1.1 with Cfront 2.0, 2.1 & 3.0 and g++ 2.1, 2.4
XSun Sparc running SunOS 4.0.3 with Cfront 2.1
XSun Sparc running SunOS 4.0.2 with g++ 2.2.2
XSun Sparc2 running SunOS 4.0.3 with g++ 2.0
XIBM 3090 running AIX/370 with Cfront 2.0
XIBM RS/6000 running AIX 3.1.5 with Cfront 2.1, g++ 2.1
XIBM RS/6000 running AIX 3.2.2 with gcc-2.3.3, libg++-2.3
XHP9000/400 running HPUX 7.05 with Cfront 2.1
XHP9000/425 running HPUX 7.05 with Cfront 2.1
XHP9000/720 running HPUX 8.05 with Cfront 2.1
XMicroVax II running Ultrix-32 V3.0 (rev 64) running g++ 1.39
XBull DPX/2300 (SysV) with gcc-2.1
XNCR 3350 running SVR4.02 with gcc-2.3.3, libg++-2.3
XDEC MIPS running Ultrix 4.2 with g++ 2.2.2
XHP/Apollo running SR10.3.4 with Domain/C++ 2.1.0
XSilicon Graphics Indigo with Cfront 3.0
X
XIn order to build dired, a few lines in the Makefile may need to be
Xmodified. The line
X
XCC =
X
Xis used to define the name of your C++ compiler.
X
X ex. You have some version of Cfront
X --
X CC = CC
X
X ex. you have GNU g++
X --
X CC = g++
X
XThe line
X
XCFLAGS =
X
Xis where system-specific preprocessor defines are put. Six
Xpreprocessor defines, settable in 'Makefile', are the only things one
Xshould need to modify before typing 'make' to build dired. Direct
Xfrom the Makefile we have:
X


X# Add -DNO_SYMLINKS if your OS doesn't support symbolic links. If
X# you don't define this, it is assumed that your OS supports symbolic
X# links.
X#
X# Add -DMEMMOVE if your machine has memmove(3).
X#

X# Add -DTEMPLATES if your compiler supports templates.
X#
X# Add -DL_AND_G if your version of `ls' needs the `-lg' flags in
X# order to display both the owner and group of files in a long


X# listing. Typically, BSD-based OSs need this and SYSV ones don't.
X#
X# Add -DCOMPLETION if you want to enable tab-completion on the `E'
X# and `c' commands. As currently written this assumes that you have
X# the POSIX directory routines in <dirent.h>. In particular, I use
X# opendir(), readdir() and closedir().
X#
X# Add -DSIGINTERRUPT if you need to call siginterrupt(2) in order to
X# guarantee that signals will interrupt slow system calls. If you
X# don't have siginterrupt(2), you most certainly don't need it. Even
X# if you have it you may not need it, though defining -DSIGINTERRUPT
X# in this case should be a no-op. The best bet is to define this if
X# you have siginterrupt(2). Note: Suns need this.
X#
X# Add -DNOWAITPID if you don't have waitpid(2).
X

X ex. you don't have symbolic links
X --
X CFLAGS = -DNO_SYMLINKS
X
X ex. you have <dirent.h> and you need the -lg flags to show groups in `ls'
X --
X CFLAGS = -DCOMPLETION -DL_AND_G
X
XYou should also add -O to CFLAGS, unless you really don't trust the
Xoptimization phase of your compiler.
X
XThe line
X
XTERMFLAGS =
X
Xis used to set which type of terminal control your OS uses. From the
XMakefile:
X


X# Those flags needed to compile in the type of terminal
X# control you have. Use -DTERMIOS if you have <termios.h>, the POSIX
X# terminal control. Use -DTERMIO if you have <termio.h>, the SYSV
X# terminal control. Otherwise, the default assumes you have <sgtty.h>,
X# the BSD terminal control.
X#
X# If you choose to use -DTERMIOS and have problems, try -DTERMIO. On
X# at least two systems I've tried, the vendor hasn't had all the
X# include files set up correctly to include <unistd.h> together with

X# <osfcn.h> among others.
X
X ex. on a SYSV-based system
X --
X TERMFLAGS = -DTERMIO
X
X ex. on a POSIX system
X --
X TERMFLAGS = -DTERMIOS
X
X ex. on a BSD-based system
X --
X TERMFLAGS =
X
XTo control the screen, dired uses the termcap(3) library. The line
X
XLIBS =
X
Xis where you set what library needs to be linked with dired in order
Xto use the termcap functionality. From the Makefile:
X


X# -ltermcap on BSD-like systems
X# -ltermlib on SYSV-like systems
X# -lcurses on systems w/o the above libraries
X

X ex. on a SYSV-based system
X --
X LIBS = -ltermlib
X
X ex. on a BSD-based system
X --
X LIBS = -ltermcap
X
X ex. on an IBM RS/6000
X --
X LIBS = -lcurses
X
XOnce you've edited Makefile, type 'make'. Hopefully, the make will
Xcomplete with no problems and you will have a working 'dired'. Then
Xmove the executable 'dired' to a suitable binary directory.
X
XUnfortunately, from experience, it appears that C++ code is much more
Xdifficult to port than C code. The main problem seems to be header
Xfiles. Since every function must be prototyped before it is used, one
Xnecessarily includes many system include files to properly prototype
Xfunctions, especially in an application such as dired which uses a
Xfair number of system services and library functions. When one starts
Xincluding many include files, the inconsistencies of the files becomes
Xapparent. The most common "bug" is when two different include files
Xprototype a function differently. C++ compilers consider this as a hard
Xerror. The only thing to be done in this situation is to fix the
Xheader file(s) and continue with the build process.
X
XAnother common problem is a header file which doesn't prototype a
Xfunction when in fact it should. In this case your best bet is to
Xmanually add the prototype to 'dired.h'.
X
XAnother more fundamental problem with include files is that they are
Xincompletely or inconsistently standardized. ANSI C dictates the
Xcontents of only fifteen include files which are meant to cover the C
Xlibrary. In order to use a function not covered by ANSI C, which, by
Xnecessity, will include all operating system specific functions, one
Xmust have some other scheme for deciding what file(s) to include.
XWhere ANSI C stops, dired development has followed the rules proposed
Xby POSIX 1003.1 as regards which file to include to get the prototype
Xof some system-specific function. Not all systems follow or even
Xpurport to follow the POSIX standard.
X
XThe one place where this POSIX strategy may bite you are the S_ISREG
Xand S_ISDIR macros. These are an attempt by POSIX to hide the
Xmachinations traditionally done with the stat structure in order to
Xdecide upon the type of a file. If your machine doesn't have these
Xmacros, you'll need to modify two functions in utilities.C in a manner
Xappropriate to you machine/OS combination. Please send me any diffs
Xso I can incorporate them into the base release.
X
XIf nothing else, attempting to compile dired will probably point out a
Xnumber of deficiencies in the implementation of your header files.
XPersevere and report all bugs to your vendor.
X
X
XMike Lijewski (W) 301-513-0697 (H) 301-982-5461
XGoddard Space Flight Center
XINTERNET: lije...@rosserv.gsfc.nasa.gov
XSMAIL: 446 Ridge Rd. Apt. 3, Greenbelt, MD 20770
END_OF_FILE
if test 7056 -ne `wc -c <'INSTALL'`; then
echo shar: \"'INSTALL'\" unpacked with wrong size!
fi
# end of 'INSTALL'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(1291 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
XChangeLog lists changes from one version to the next
XGStack.C implementation of generic stack class
XGStack.h declaration of generic stack class
XINSTALL how to install this critter
XMANIFEST you're reading it
XMakefile controls the build
XQueue.C implementation of queue class
XQueue.h declaration of queue class
XREADME a little bit about 'dired'
XString.C implementation of reference counting string class
XString.h declaration of reference counting string class
Xclasses.C definitions of some of our class member functions
Xclasses.h declarations of the classes we use
Xcommand1.C some commands from main command loop
Xcommand2.C remainder of main loop commands and the loop itself
Xcommands.h declarations of functions exported by commands.C
Xdired.1 a man page
Xdired.C the 'main' program
Xdired.h included by all source code
Xdired.lpr a line-printable version of 'dired.1'
Xdisplay.C contains code controlling the display using termcap(3)
Xdisplay.h declarations and inlines for display.C
Xkeys.C implementation of virtual keyboard
Xkeys.h definitions of all the keyboard keys we acknowledge
Xutilities.C some utility functions
Xversion.h the patch level
END_OF_FILE
if test 1291 -ne `wc -c <'MANIFEST'`; then
echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'README' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1541 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
XDired - A Directory Editor
X
XWHAT IS IT?
X
XDired is a directory editor modelled after Dired Mode of GNU Emacs,
Xbut targetted for non-emacs users, designed for the UNIX environment.
XIt implements a window into the current directory consisting of lines
Xof the form:
X
X -rw------- 1 mjlx staff 2002 Apr 11 15:17 c++-motif
X -rw------- 1 mjlx staff 51399 Aug 11 1990 c++browser.tar.Z
X -rw------- 1 mjlx staff 970 Mar 14 18:44 callback
X drwx--x--x 2 mjlx staff 384 Jun 21 16:32 cfront
X -rw------- 1 mjlx staff 2152 Dec 17 1990 cfront-sun4.1.bug.Z
X drwx------ 3 mjlx staff 2048 Aug 1 13:04 dired
X -rw------- 1 mjlx staff 2033 Jan 25 1991 link-bug.C.Z
X
XThe filename in the line containing the cursor is known as the current
Xfile. There are numerous of commands available for operating on the
Xcurrent file. These include copy, delete, edit, view, chmod, chgrp,
Xcompress, rename and uncompress. Hence by simply positioning the
Xcursor on the appropriate filename, one can do many of the "usual"
XUNIX operations on the files in a directory without bothering to
Xspelling out the command and filename each time. If you edit a
Xdirectory, you get a directory listing of that directory. In this
Xmanner it is possible to walk up and down a directory tree from within
Xdired.
X
X
XAUTHOR:
X
XMike Lijewski (W) 301-513-0697 (H) 301-982-5461
XGoddard Space Flight Center
XINTERNET: lije...@rosserv.gsfc.nasa.gov
XSMAIL: 446 Ridge Rd. Apt. 3, Greenbelt, MD 20770
END_OF_FILE
if test 1541 -ne `wc -c <'README'`; then
echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
echo shar: End of archive 7 \(of 7\).
cp /dev/null ark7isdone

0 new messages