/* Copyright (c) 1979 Regents of the University of California */
* File input/output, unix escapes, source, filtering 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");
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
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");
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");
if (G
.argv
[0][0] == '+') {
firstln
= getn(G
.argv
[0] + 1);
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).
wasalt
= 2, oldadot
= firstln
, firstln
= 0;
if (c
== 'e' && errno
== ENOENT
)
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
));
error(" Non-ascii file");
undap1
= undap2
= dot
+ 1;
ignore(append(getfile
, addr2
));
if (iostats() == 0 && c
== 'e')
register line
*addr
= zero
+ oldadot
;
if (inopen
|| wasalt
== 2)
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 */
register int c
, exclam
, nonexist
;
while (peekchar() == '>')
ignchar(), c
++, ignore(skipwh());
error("Write forms are 'w' and 'w>>'");
nonexist
= stat(file
, &stbuf
);
if (!exclam
&& !value(WRITEANY
)) switch (edfile()) {
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(" 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
);
* First part of a shell escape,
* parse the line, expanding # and % and ! and printing if implied.
char printub
, puxb
[UXBSIZE
+ sizeof (int)];
if (c
== '\n' || c
== EOF
)
error("Incomplete shell escape command@- use 'shell' to get a shell");
if (any(peekchar(), "%#!"))
if (up
>= &uxb
[UXBSIZE
]) {
error("Command too long");
error("No previous command@to substitute for !");
error("No alternate filename@to substitute for #");
error("No filename@to substitute for %%");
} while (c
== '|' || !endcmd(c
));
if (warn
&& hush
== 0 && chng
&& xchng
!= chng
&& value(WARN
)) {
printf(mesg("[No write]|[No write since last change]"));
error("No previous command@to repeat");
* Do the real work for execution of a shell escape.
* Mode is like the number passed to open system calls
* and indicates filtering. If input is implied, newstdin
* must have been setup already.
unixex(opt
, up
, newstdin
, mode
)
if ((mode
& 1) && pipe(pvec
) < 0) {
/* Newstdin should be io so it will be closed */
error("Can't make pipe for filter");
error("No more processes");
signal(SIGQUIT
, oldquit
);
execl(svalue(SHELL
), "sh", opt
, up
, (char *) 0);
printf("No %s!\n", svalue(SHELL
));
* Wait for the command to complete.
* F is for restoration of tty mode if from open/visual.
* C flags suppression of printing.
if (!inopen
&& c
&& hush
== 0) {
* Setup a pipeline for the filtration implied by mode
* which is like a open number. If input is required to
* the filter, then a child editor is created to write it.
* If output is catch it from io which is created by unixex.
register int lines
= lineDOL();
error("Can't make pipe");
error("No more processes");
f
= unixex("-c", uxb
, (mode
& 2) ? pvec
[0] : 0, mode
);
ignore(append(getfile
, addr2
));
* Set up to do a recover, getting io to be a pipe from
error(" Can't make pipe for recovery");
error(" Can't fork to execute recovery");
execl(EXRECOVER
, "exrecover", svalue(DIRECTORY
), file
, (char *) 0);
error(" No recovery routine");
* Wait for the process (pid an external) to complete.
while (rpid
!= pid
&& rpid
!= -1);
status
= (status
>> 8) & 0377;
* The end of a recover operation. If the process
* exits non-zero, force not edited; otherwise force
if (pid
== rpid
&& status
!= 0)
* Extract the next line from the io stream.
ninbuf
= read(io
, genbuf
, LBSIZE
) - 1;
printf(" [Incomplete last line]");
if (lp
>= &linebuf
[LBSIZE
]) {
* Write a range onto the io stream.
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.
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);