* 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.11 (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 " */
* 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");
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 (hush
&& comm
!= 'f' || comm
== 'E')
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 */
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 */
execl(svalue(SHELL
), "sh", "-c", genbuf
, 0);
oerrno
= errno
; close(1); dup(2); errno
= oerrno
;
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.
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.
switch (stbuf
.st_mode
& S_IFMT
) {
error(" Block special file");
if (samei(&stbuf
, "/dev/null"))
error(" Character special file");
i
= read(io
, (char *) &magic
, sizeof(magic
));
case 0405: /* data overlay on exec */
case 0407: /* unshared */
case 0410: /* shared text */
case 0411: /* separate I/D */
case 0413: /* 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.
/* C/70 has a 10 bit byte */
/* Everybody else has an 8 bit byte */
error(" Non-ascii file");
if (value(READONLY
) && denied
) {
if ((stbuf
.st_mode
& 0222) == 0 || access(file
, 2) < 0) {
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 (!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");
else if (value(WRITEANY
) && edfile() != EDF
)
printf(" [Existing file]");
if (exclam
|| value(WRITEANY
))
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
, bsize
) - 1;
printf(" [Incomplete last line]");
while(fp
< &genbuf
[ninbuf
]) {
crblock(perm
, genbuf
, ninbuf
+1,
if (lp
>= &linebuf
[LBSIZE
]) {
* Write a range onto the io stream.
if (fstat(io
, &statb
) < 0)
bsize
= statb
.st_blksize
;
crblock(perm
, genbuf
, nib
, cntch
);
if (write(io
, genbuf
, nib
) != nib
) {
if ((*fp
++ = *lp
++) == 0) {
crblock(perm
, genbuf
, nib
, cntch
);
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.
printf(" %d/%D", cntln
, cntch
);
printf(" %d line%s, %D character%s", cntln
, plural((long) cntln
),
printf("%D null", cntnull
);
printf("%D non-ASCII", cntodd
);
return (cntnull
!= 0 || cntodd
!= 0);
/* It's so wonderful how we all speak the same language... */
char *index(), *rindex();
if (!( ( (beg
[-3] == ' ' || beg
[-3] == '\t')
|| ( (beg
[-3] == ' ' || beg
[-3] == '\t')
&& beg
[-1] == 'i'))) return;
strncpy(cmdbuf
, beg
+1, sizeof cmdbuf
);
end
= rindex(cmdbuf
, ':');