* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
static char *sccsid
= "@(#)ex_io.c 7.17.1.1 (Berkeley) %G%";
* File input/output, source, preserve and recover
* Following remember where . was in the previous file for return
long cntch
; /* Count of characters on unit io */
short cntln
; /* Count of lines " */
long cntnull
; /* Count of nulls " */
long cntodd
; /* Count of non-ascii characters " */
* The alternate, saved and current file are locked the extent of the
* time that they are active. If the saved file is exchanged
* with the alternate file, the file descriptors are exchanged
* and the lock is not released.
int io_savedfile
, io_altfile
, io_curr
;
int lock_savedfile
, lock_altfile
, lock_curr
;
* Parse file name for command encoded by comm.
* If comm is E then command is doomed and we are
* parsing just so user won't have to retype the name.
register int c
= comm
, d
;
if (savedfile
[0] == 0 && comm
!= 'f')
error("No file|No current filename");
if (io_curr
&& io_curr
!= io_savedfile
) close(io_curr
) ;
lock
= lock_curr
= lock_savedfile
;
wasalt
= (isalt
> 0) ? isalt
-1 : 0;
if (c
== 'e' || c
== 'E')
if (savedfile
[0] == 0 && c
!= 'E' && c
!= 'e') {
wasalt
= strcmp(file
, altfile
) == 0;
if (strcmp(file
,savedfile
) == 0) break ;
if (io_altfile
) close (io_altfile
) ;
io_altfile
= io_savedfile
;
lock_altfile
= lock_savedfile
;
lock_savedfile
= lock_curr
;
io_curr
= 0 ; lock
= lock_curr
= 0 ;
&& io_altfile
!= io_curr
) close (io_altfile
) ;
lock_altfile
= lock_curr
;
io_curr
= 0 ; lock
= lock_curr
= 0 ;
if (hush
&& comm
!= 'f' || comm
== 'E')
ex_printf(" [Read only]");
ex_printf(" [Not edited]");
ex_printf(" [Modified]");
ex_printf(" [Shared lock]") ;
else if (lock
== LOCK_EX
)
ex_printf(" [Exclusive lock]") ;
ex_printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
(long) 100 * lineDOT() / i
);
* Get the argument words for a command into genbuf
static char fpatbuf
[32]; /* hence limit on :next +/pat */
c
= *cp
++ = ex_getchar();
if (cp
>= &fpatbuf
[sizeof(fpatbuf
)])
error("Pattern too long");
if (c
== '\\' && isspace(peekchar()))
if (c
== EOF
|| isspace(c
)) {
CP(genbuf
, "echo "); cp
= &genbuf
[5];
if (any(peekchar(), "#%|"))
if (cp
> &genbuf
[LBSIZE
- 2])
error("Argument buffer overflow");
error("No alternate filename@to substitute for #");
error("No current filename@to substitute for %%");
if (cp
> &genbuf
[LBSIZE
- 2])
* Glob the argument words in genbuf, or if no globbing
* is implied, just split them up directly.
register char **argv
= gp
->argv
;
register char *cp
= gp
->argspac
;
register char *v
= genbuf
+ 5; /* strlen("echo ") */
while (*v
&& !isspace(*v
))
error("Can't make pipe to glob");
error("Can't fork to do glob");
close(2); /* so errors don't mess up the screen */
ignore(open("/dev/null", 1));
execl(svalue(SHELL
), "sh", "-c", genbuf
, 0);
if (read(io
, &ch
, 1) != 1) {
if (c
<= 0 || isspace(c
))
error("Arg list too long");
error("Arg list too long");
* Scan genbuf for shell metacharacters.
* Set is union of v7 shell and csh metas.
#ifndef vms /* Never have meta-characters in vms */
for (cp
= genbuf
; *cp
; cp
++)
if (any(*cp
, "~{[*?$`'\"\\"))
* Parse one filename into file.
error("Missing filename");
error("Ambiguous|Too many file names");
str
= G
.argv
[G
.argc0
- 1];
if (strlen(str
) > FNSIZE
- 4)
error("Filename too long");
* Read a file from the world.
* C is command, 'e' if this really an edit (or a recover).
static int ovro
; /* old value(READONLY) */
static int denied
; /* 1 if READONLY was set due to file permissions */
if (c
== 'e' && errno
== ENOENT
) {
* If the user just did "ex foo" he is probably
* creating a new file. Don't be an error, since
* this is ugly, and it screws up the + option.
ex_printf(" [New file]");
switch (stbuf
.st_mode
& S_IFMT
) {
error(" Block special file");
if (samei(&stbuf
, "/dev/null"))
error(" Character special file");
i
= read(io
, (char *)&head
, sizeof(head
));
(void)lseek(io
, 0L, L_SET
);
switch ((int)head
.a_magic
) {
case 0405: /* data overlay on exec */
case OMAGIC
: /* unshared */
case NMAGIC
: /* shared text */
case 0411: /* separate I/D */
case ZMAGIC
: /* VM/Unix demand paged */
case 0430: /* PDP-11 Overlay shared */
case 0431: /* PDP-11 Overlay sep I/D */
* We do not forbid the editing of portable archives
* because it is reasonable to edit them, especially
* if they are archives of text files. This is
* especially useful if you archive source files together
* and copy them to another system with ~%take, since
* the files sometimes show up munged and must be fixed.
char *bp
= (char *)&head
;
if ((u_char
)bp
[0] == (u_char
)'\037' &&
(u_char
)bp
[1] == (u_char
)'\235')
error(" Compressed file");
if (!strncmp(bp
, "!<arch>\n__.SYMDEF", 17)
|| !strncmp(bp
, "!<arch>\n", 8))
if (value(READONLY
) && denied
) {
if ((stbuf
.st_mode
& 0222) == 0 || access(file
, 2) < 0) {
ex_printf(" [Read only]");
* Attempt to lock the file. We use an sharable lock if reading
* the file, and an exclusive lock if editting a file.
* The lock will be released when the file is no longer being
* referenced. At any time, the editor can have as many as
* three files locked, and with different lock statuses.
* if this is either the saved or alternate file or current file,
* point to the appropriate descriptor and file lock status.
if (strcmp (file
,savedfile
) == 0) {
if (!io_savedfile
) io_savedfile
= dup(io
) ;
lp
= &lock_savedfile
; iop
= &io_savedfile
;
} else if (strcmp (file
,altfile
) == 0) {
if (!io_altfile
) io_altfile
= dup(io
) ;
lp
= &lock_altfile
; iop
= &io_altfile
;
/* throw away current lock, accquire new current lock */
if (io_curr
) close (io_curr
) ;
lp
= &lock_curr
; iop
= &io_curr
;
if (c
== 'r' || value(READONLY
) || *lp
== 0) {
/* if we have a lock already, don't bother */
/* try for a shared lock */
if (flock(*iop
, LOCK_SH
|LOCK_NB
) < 0
&& errno
== EWOULDBLOCK
) {
" [FILE BEING MODIFIED BY ANOTHER PROCESS]") ;
if ( c
!= 'r' && !value(READONLY
) && *lp
!= LOCK_EX
) {
/* if we are editting the file, upgrade to an exclusive lock. */
if (flock(*iop
, LOCK_EX
|LOCK_NB
) < 0 && errno
== EWOULDBLOCK
) {
ex_printf (" [File open by another process]") ;
if (FIXUNDO
&& inopen
&& c
== 'r')
undap1
= undap2
= dot
+ 1;
if (fstat(io
, &statb
) < 0)
bsize
= statb
.st_blksize
;
ignore(append(getfile
, addr2
));
* if the modeline variable is set,
* check the first and last five lines of the file
for (a
=first
; a
<=last
; a
++) {
if (a
==first
+5 && last
-first
> 10)
if (iostats() == 0 && c
== 'e')
if (wasalt
|| firstpat
) {
register line
*addr
= zero
+ oldadot
;
globp
= (*firstpat
) ? firstpat
: "$";
} else if (addr
>= one
) {
vreplace(0, LINES
, lineDOL());
* Are these two really the same inode?
if (stat(cp
, &stb
) < 0 || sp
->st_dev
!= stb
.st_dev
)
return (sp
->st_ino
== stb
.st_ino
);
/* Returns from edited() */
#define EDF 0 /* Edited file */
#define NOTEDF -1 /* Not edited file */
#define PARTBUF 1 /* Write of partial buffer to Edited file */
bool dofname
; /* if 1 call filename, else use savedfile */
register int c
, exclam
, nonexist
;
while (peekchar() == '>')
ignchar(), c
++, ignore(skipwh());
error("Write forms are 'w' and 'w>>'");
error("No file|No current filename");
nonexist
= stat(file
, &stbuf
);
* if this is either the saved or alternate file or current file,
* point to the appropriate descriptor and file lock status.
if (strcmp (file
,savedfile
) == 0) {
lp
= &lock_savedfile
; iop
= &io_savedfile
;
} else if (strcmp (file
,altfile
) == 0) {
lp
= &lock_altfile
; iop
= &io_altfile
;
lp
= &lock_curr
; iop
= &io_curr
;
if ((*iop
= open(file
, 1)) < 0) *iop
= 0 ;
if (!exclam
&& (!value(WRITEANY
) || value(READONLY
)))
if ((stbuf
.st_mode
& S_IFMT
) == S_IFCHR
) {
if (samei(&stbuf
, "/dev/null"))
if (samei(&stbuf
, "/dev/tty"))
serror(" File exists| File exists - use \"w! %s\" to overwrite", file
);
error(" File is read only");
error(" File is read only");
error(" Use \"w!\" to write partial buffer");
if (*iop
&& !*lp
!= LOCK_EX
&& !exclam
) {
* upgrade to a exclusive lock. if can't get, someone else
* has the exclusive lock. bitch to the user.
if (flock(*iop
, LOCK_EX
|LOCK_NB
) < 0 && errno
== EWOULDBLOCK
)
error (" File being modified by another process - use \"w!\" to write");
#ifdef vms /* to retain file protection modes on newer version of file */
chmod(file
, stbuf
.st_mode
& 0777);
ex_printf(" [New file]");
else if (value(WRITEANY
) && edfile() != EDF
)
ex_printf(" [Existing file]");
if (exclam
|| value(WRITEANY
))
if (!*iop
) *iop
= dup(io
) ;
if (*lp
!= LOCK_EX
&& !exclam
) {
* upgrade to a exclusive lock. if can't get,
* someone else has the exclusive lock.
if (flock(*iop
, LOCK_SH
|LOCK_NB
) < 0
" File being modified by another process - use \"w!>>\" to write");
if (flock(*iop
, LOCK_EX
|LOCK_NB
) >= 0)
if (c
!= 2 && addr1
== one
&& addr2
== dol
) {
* Is file the edited file?
* Work here is that it is not considered edited
* if this is a partial buffer, and distinguish
if (!edited
|| !eq(file
, savedfile
))
return (addr1
== one
&& addr2
== dol
? EDF
: PARTBUF
);
* Extract the next line from the io stream.
ninbuf
= read(io
, genbuf
, (int) bsize
) - 1;
ex_printf(" [Incomplete last line]");
if (lp
>= &linebuf
[LBSIZE
]) {
* Write a range onto the io stream.
if (fstat(io
, &statb
) < 0)
bsize
= statb
.st_blksize
;
if (write(io
, genbuf
, nib
) != nib
) {
if ((*fp
++ = *lp
++) == 0) {
if (write(io
, genbuf
, nib
) != nib
) {
* A write error has occurred; if the file being written was
* the edited file then we consider it to have changed since it is
if (eq(file
, savedfile
) && edited
)
* Source command, handles nested sources.
* Traps errors since it mungs unit 0 during the source.
register int saveinp
, ointty
, oerrno
;
error("Too many nested sources");
* Clear io statistics before a read or write.
* Io is finished, close the unit and print statistics.
ex_printf(" %d/%D", cntln
, cntch
);
ex_printf(" %d line%s, %D character%s", cntln
, plural((long) cntln
),
ex_printf("%D null", cntnull
);
ex_printf("%D non-ASCII", cntodd
);
return (cntnull
!= 0 || cntodd
!= 0);
char *index(), *rindex(), *strncpy();
if (!( ( (beg
[-3] == ' ' || beg
[-3] == '\t')
|| ( (beg
[-3] == ' ' || beg
[-3] == '\t')
&& beg
[-1] == 'i'))) return;
strncpy(cmdbuf
, beg
+1, sizeof cmdbuf
);
end
= rindex(cmdbuf
, ':');