made CRYPT an #ifdef
[unix-history] / usr / src / usr.bin / ex / ex_io.c
CommitLineData
7c4625ef 1/* Copyright (c) 1980 Regents of the University of California */
1807bf33 2static char *sccsid = "@(#)ex_io.c 4.3 %G%";
544cd46c
MH
3#include "ex.h"
4#include "ex_argv.h"
5#include "ex_temp.h"
6#include "ex_tty.h"
7#include "ex_vis.h"
8
9/*
d266c416 10 * File input/output, source, preserve and recover
544cd46c
MH
11 */
12
13/*
14 * Following remember where . was in the previous file for return
15 * on file switching.
16 */
44232d5b
MH
17int altdot;
18int oldadot;
544cd46c 19bool wasalt;
d266c416 20short isalt;
544cd46c
MH
21
22long cntch; /* Count of characters on unit io */
44232d5b 23#ifndef VMUNIX
544cd46c 24short cntln; /* Count of lines " */
44232d5b
MH
25#else
26int cntln;
27#endif
544cd46c
MH
28long cntnull; /* Count of nulls " */
29long cntodd; /* Count of non-ascii characters " */
30
31/*
32 * Parse file name for command encoded by comm.
33 * If comm is E then command is doomed and we are
34 * parsing just so user won't have to retype the name.
35 */
36filename(comm)
37 int comm;
38{
39 register int c = comm, d;
40 register int i;
41
42 d = getchar();
43 if (endcmd(d)) {
44 if (savedfile[0] == 0 && comm != 'f')
45 error("No file|No current filename");
46 CP(file, savedfile);
d266c416
MH
47 wasalt = (isalt > 0) ? isalt-1 : 0;
48 isalt = 0;
544cd46c 49 oldadot = altdot;
d266c416
MH
50 if (c == 'e' || c == 'E')
51 altdot = lineDOT();
544cd46c
MH
52 if (d == EOF)
53 ungetchar(d);
54 } else {
55 ungetchar(d);
56 getone();
57 eol();
58 if (savedfile[0] == 0 && c != 'E' && c != 'e') {
59 c = 'e';
60 edited = 0;
61 }
62 wasalt = strcmp(file, altfile) == 0;
63 oldadot = altdot;
64 switch (c) {
65
66 case 'f':
67 edited = 0;
68 /* fall into ... */
69
70 case 'e':
71 if (savedfile[0]) {
72 altdot = lineDOT();
73 CP(altfile, savedfile);
74 }
75 CP(savedfile, file);
76 break;
77
78 default:
79 if (file[0]) {
80 if (c != 'E')
81 altdot = lineDOT();
82 CP(altfile, file);
83 }
84 break;
85 }
86 }
87 if (hush && comm != 'f' || comm == 'E')
88 return;
89 if (file[0] != 0) {
90 lprintf("\"%s\"", file);
91 if (comm == 'f') {
d266c416
MH
92 if (value(READONLY))
93 printf(" [Read only]");
544cd46c
MH
94 if (!edited)
95 printf(" [Not edited]");
96 if (tchng)
97 printf(" [Modified]");
98 }
99 flush();
100 } else
101 printf("No file ");
102 if (comm == 'f') {
103 if (!(i = lineDOL()))
104 i++;
105 printf(" line %d of %d --%ld%%--", lineDOT(), lineDOL(),
106 (long) 100 * lineDOT() / i);
107 }
108}
109
110/*
111 * Get the argument words for a command into genbuf
112 * expanding # and %.
113 */
114getargs()
115{
116 register int c;
117 register char *cp, *fp;
118 static char fpatbuf[32]; /* hence limit on :next +/pat */
119
120 pastwh();
121 if (peekchar() == '+') {
122 for (cp = fpatbuf;;) {
123 c = *cp++ = getchar();
124 if (cp >= &fpatbuf[sizeof(fpatbuf)])
125 error("Pattern too long");
126 if (c == '\\' && isspace(peekchar()))
127 c = getchar();
128 if (c == EOF || isspace(c)) {
129 ungetchar(c);
130 *--cp = 0;
131 firstpat = &fpatbuf[1];
132 break;
133 }
134 }
135 }
136 if (skipend())
137 return (0);
138 CP(genbuf, "echo "); cp = &genbuf[5];
139 for (;;) {
140 c = getchar();
141 if (endcmd(c)) {
142 ungetchar(c);
143 break;
144 }
145 switch (c) {
146
147 case '\\':
d266c416 148 if (any(peekchar(), "#%|"))
544cd46c
MH
149 c = getchar();
150 /* fall into... */
151
152 default:
153 if (cp > &genbuf[LBSIZE - 2])
154flong:
155 error("Argument buffer overflow");
156 *cp++ = c;
157 break;
158
159 case '#':
160 fp = altfile;
161 if (*fp == 0)
162 error("No alternate filename@to substitute for #");
163 goto filexp;
164
165 case '%':
166 fp = savedfile;
167 if (*fp == 0)
168 error("No current filename@to substitute for %%");
169filexp:
170 while (*fp) {
171 if (cp > &genbuf[LBSIZE - 2])
172 goto flong;
173 *cp++ = *fp++;
174 }
175 break;
176 }
177 }
178 *cp = 0;
179 return (1);
180}
181
182/*
183 * Glob the argument words in genbuf, or if no globbing
184 * is implied, just split them up directly.
185 */
186glob(gp)
187 struct glob *gp;
188{
189 int pvec[2];
190 register char **argv = gp->argv;
191 register char *cp = gp->argspac;
192 register int c;
193 char ch;
194 int nleft = NCARGS;
195
196 gp->argc0 = 0;
197 if (gscan() == 0) {
198 register char *v = genbuf + 5; /* strlen("echo ") */
199
200 for (;;) {
201 while (isspace(*v))
202 v++;
203 if (!*v)
204 break;
205 *argv++ = cp;
206 while (*v && !isspace(*v))
207 *cp++ = *v++;
208 *cp++ = 0;
209 gp->argc0++;
210 }
211 *argv = 0;
212 return;
213 }
214 if (pipe(pvec) < 0)
215 error("Can't make pipe to glob");
216 pid = fork();
217 io = pvec[0];
218 if (pid < 0) {
219 close(pvec[1]);
220 error("Can't fork to do glob");
221 }
222 if (pid == 0) {
223 int oerrno;
224
225 close(1);
226 dup(pvec[1]);
227 close(pvec[0]);
887e3e0d
MH
228 close(2); /* so errors don't mess up the screen */
229 open("/dev/null", 1);
544cd46c
MH
230 execl(svalue(SHELL), "sh", "-c", genbuf, 0);
231 oerrno = errno; close(1); dup(2); errno = oerrno;
232 filioerr(svalue(SHELL));
233 }
234 close(pvec[1]);
235 do {
236 *argv = cp;
237 for (;;) {
238 if (read(io, &ch, 1) != 1) {
239 close(io);
240 c = -1;
241 } else
242 c = ch & TRIM;
243 if (c <= 0 || isspace(c))
244 break;
245 *cp++ = c;
246 if (--nleft <= 0)
247 error("Arg list too long");
248 }
249 if (cp != *argv) {
250 --nleft;
251 *cp++ = 0;
252 gp->argc0++;
253 if (gp->argc0 >= NARGS)
254 error("Arg list too long");
255 argv++;
256 }
257 } while (c >= 0);
258 waitfor();
259 if (gp->argc0 == 0)
887e3e0d 260 error("No match");
544cd46c
MH
261}
262
263/*
264 * Scan genbuf for shell metacharacters.
265 * Set is union of v7 shell and csh metas.
266 */
267gscan()
268{
269 register char *cp;
270
271 for (cp = genbuf; *cp; cp++)
272 if (any(*cp, "~{[*?$`'\"\\"))
273 return (1);
274 return (0);
275}
276
277/*
278 * Parse one filename into file.
279 */
280getone()
281{
282 register char *str;
283 struct glob G;
284
285 if (getargs() == 0)
286 error("Missing filename");
287 glob(&G);
288 if (G.argc0 > 1)
289 error("Ambiguous|Too many file names");
290 str = G.argv[G.argc0 - 1];
291 if (strlen(str) > FNSIZE - 4)
292 error("Filename too long");
293samef:
294 CP(file, str);
295}
296
297/*
298 * Read a file from the world.
299 * C is command, 'e' if this really an edit (or a recover).
300 */
301rop(c)
302 int c;
303{
304 register int i;
305 struct stat stbuf;
306 short magic;
307
308 io = open(file, 0);
309 if (io < 0) {
310 if (c == 'e' && errno == ENOENT)
311 edited++;
312 syserror();
313 }
314 if (fstat(io, &stbuf))
315 syserror();
316 switch (stbuf.st_mode & S_IFMT) {
317
318 case S_IFBLK:
319 error(" Block special file");
320
321 case S_IFCHR:
322 if (isatty(io))
323 error(" Teletype");
324 if (samei(&stbuf, "/dev/null"))
325 break;
326 error(" Character special file");
327
328 case S_IFDIR:
329 error(" Directory");
330
331 case S_IFREG:
1807bf33 332#ifdef CRYPT
d266c416
MH
333 if (xflag)
334 break;
1807bf33 335#endif
544cd46c
MH
336 i = read(io, (char *) &magic, sizeof(magic));
337 lseek(io, 0l, 0);
338 if (i != sizeof(magic))
339 break;
340 switch (magic) {
341
342 case 0405:
343 case 0407:
344 case 0410:
345 case 0411:
346 error(" Executable");
347
348 case 0177545:
349 case 0177555:
350 error(" Archive");
351
352 default:
353 if (magic & 0100200)
354 error(" Non-ascii file");
355 break;
356 }
357 }
358 if (c == 'r')
359 setdot();
360 else
361 setall();
887e3e0d 362 if (FIXUNDO && inopen && c == 'r')
544cd46c
MH
363 undap1 = undap2 = dot + 1;
364 rop2();
365 rop3(c);
366}
367
368rop2()
369{
370
371 deletenone();
372 clrstats();
373 ignore(append(getfile, addr2));
374}
375
376rop3(c)
377 int c;
378{
379
380 if (iostats() == 0 && c == 'e')
381 edited++;
382 if (c == 'e') {
383 if (wasalt || firstpat) {
384 register line *addr = zero + oldadot;
385
386 if (addr > dol)
387 addr = dol;
388 if (firstpat) {
389 globp = (*firstpat) ? firstpat : "$";
390 commands(1,1);
391 firstpat = 0;
392 } else if (addr >= one) {
393 if (inopen)
394 dot = addr;
395 markpr(addr);
396 } else
397 goto other;
398 } else
399other:
400 if (dol > zero) {
401 if (inopen)
402 dot = one;
403 markpr(one);
404 }
887e3e0d
MH
405 if(FIXUNDO)
406 undkind = UNDNONE;
544cd46c
MH
407 if (inopen) {
408 vcline = 0;
409 vreplace(0, LINES, lineDOL());
410 }
411 }
412 if (laste) {
7c4625ef
MH
413#ifdef VMUNIX
414 tlaste();
415#endif
544cd46c
MH
416 laste = 0;
417 sync();
418 }
419}
420
421/*
422 * Are these two really the same inode?
423 */
424samei(sp, cp)
425 struct stat *sp;
426 char *cp;
427{
428 struct stat stb;
429
430 if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
431 return (0);
432 return (sp->st_ino == stb.st_ino);
433}
434
435/* Returns from edited() */
436#define EDF 0 /* Edited file */
437#define NOTEDF -1 /* Not edited file */
438#define PARTBUF 1 /* Write of partial buffer to Edited file */
439
440/*
441 * Write a file.
442 */
443wop(dofname)
444bool dofname; /* if 1 call filename, else use savedfile */
445{
446 register int c, exclam, nonexist;
447 line *saddr1, *saddr2;
448 struct stat stbuf;
449
450 c = 0;
451 exclam = 0;
452 if (dofname) {
453 if (peekchar() == '!')
454 exclam++, ignchar();
455 ignore(skipwh());
456 while (peekchar() == '>')
457 ignchar(), c++, ignore(skipwh());
458 if (c != 0 && c != 2)
459 error("Write forms are 'w' and 'w>>'");
460 filename('w');
461 } else {
887e3e0d
MH
462 if (savedfile[0] == 0)
463 error("No file|No current filename");
544cd46c
MH
464 saddr1=addr1;
465 saddr2=addr2;
466 addr1=one;
467 addr2=dol;
468 CP(file, savedfile);
469 if (inopen) {
470 vclrech(0);
471 splitw++;
472 }
473 lprintf("\"%s\"", file);
474 }
475 nonexist = stat(file, &stbuf);
476 switch (c) {
477
478 case 0:
d266c416
MH
479 if (!exclam && (!value(WRITEANY) || value(READONLY)))
480 switch (edfile()) {
544cd46c
MH
481
482 case NOTEDF:
483 if (nonexist)
484 break;
485 if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
486 if (samei(&stbuf, "/dev/null"))
487 break;
488 if (samei(&stbuf, "/dev/tty"))
489 break;
490 }
491 io = open(file, 1);
492 if (io < 0)
493 syserror();
494 if (!isatty(io))
495 serror(" File exists| File exists - use \"w! %s\" to overwrite", file);
496 close(io);
497 break;
498
d266c416
MH
499 case EDF:
500 if (value(READONLY))
501 error(" File is read only");
502 break;
503
544cd46c 504 case PARTBUF:
d266c416
MH
505 if (value(READONLY))
506 error(" File is read only");
544cd46c
MH
507 error(" Use \"w!\" to write partial buffer");
508 }
509cre:
44232d5b 510/*
544cd46c 511 synctmp();
44232d5b 512*/
544cd46c
MH
513#ifdef V6
514 io = creat(file, 0644);
515#else
516 io = creat(file, 0666);
517#endif
518 if (io < 0)
519 syserror();
520 if (hush == 0)
521 if (nonexist)
522 printf(" [New file]");
523 else if (value(WRITEANY) && edfile() != EDF)
524 printf(" [Existing file]");
525 break;
526
527 case 2:
528 io = open(file, 1);
529 if (io < 0) {
530 if (exclam || value(WRITEANY))
531 goto cre;
532 syserror();
533 }
534 lseek(io, 0l, 2);
535 break;
536 }
537 putfile();
538 ignore(iostats());
539 if (c != 2 && addr1 == one && addr2 == dol) {
540 if (eq(file, savedfile))
541 edited = 1;
542 sync();
543 }
544 if (!dofname) {
545 addr1 = saddr1;
546 addr2 = saddr2;
547 }
548}
549
550/*
551 * Is file the edited file?
552 * Work here is that it is not considered edited
553 * if this is a partial buffer, and distinguish
554 * all cases.
555 */
556edfile()
557{
558
559 if (!edited || !eq(file, savedfile))
560 return (NOTEDF);
561 return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
562}
563
544cd46c
MH
564/*
565 * Extract the next line from the io stream.
566 */
567static char *nextip;
568
569getfile()
570{
571 register short c;
572 register char *lp, *fp;
573
574 lp = linebuf;
575 fp = nextip;
576 do {
577 if (--ninbuf < 0) {
578 ninbuf = read(io, genbuf, LBSIZE) - 1;
579 if (ninbuf < 0) {
580 if (lp != linebuf) {
d266c416 581 lp++;
544cd46c
MH
582 printf(" [Incomplete last line]");
583 break;
584 }
585 return (EOF);
586 }
1807bf33 587#ifdef CRYPT
544cd46c 588 fp = genbuf;
d266c416
MH
589 while(fp < &genbuf[ninbuf]) {
590 if (*fp++ & 0200) {
591 if (kflag)
592 crblock(perm, genbuf, ninbuf+1,
593cntch);
594 break;
595 }
596 }
1807bf33 597#endif
d266c416
MH
598 fp = genbuf;
599 cntch += ninbuf+1;
544cd46c
MH
600 }
601 if (lp >= &linebuf[LBSIZE]) {
602 error(" Line too long");
603 }
604 c = *fp++;
605 if (c == 0) {
606 cntnull++;
607 continue;
608 }
609 if (c & QUOTE) {
610 cntodd++;
611 c &= TRIM;
612 if (c == 0)
613 continue;
614 }
615 *lp++ = c;
616 } while (c != '\n');
544cd46c
MH
617 *--lp = 0;
618 nextip = fp;
619 cntln++;
620 return (0);
621}
622
623/*
624 * Write a range onto the io stream.
625 */
626putfile()
627{
628 line *a1;
629 register char *fp, *lp;
630 register int nib;
631
632 a1 = addr1;
633 clrstats();
634 cntln = addr2 - a1 + 1;
635 if (cntln == 0)
636 return;
637 nib = BUFSIZ;
638 fp = genbuf;
639 do {
640 getline(*a1++);
641 lp = linebuf;
642 for (;;) {
643 if (--nib < 0) {
644 nib = fp - genbuf;
1807bf33 645#ifdef CRYPT
d266c416
MH
646 if(kflag)
647 crblock(perm, genbuf, nib, cntch);
1807bf33 648#endif
544cd46c
MH
649 if (write(io, genbuf, nib) != nib) {
650 wrerror();
651 }
652 cntch += nib;
653 nib = BUFSIZ - 1;
654 fp = genbuf;
655 }
656 if ((*fp++ = *lp++) == 0) {
657 fp[-1] = '\n';
658 break;
659 }
660 }
661 } while (a1 <= addr2);
662 nib = fp - genbuf;
1807bf33 663#ifdef CRYPT
d266c416
MH
664 if(kflag)
665 crblock(perm, genbuf, nib, cntch);
1807bf33 666#endif
544cd46c
MH
667 if (write(io, genbuf, nib) != nib) {
668 wrerror();
669 }
670 cntch += nib;
671}
672
673/*
674 * A write error has occurred; if the file being written was
675 * the edited file then we consider it to have changed since it is
676 * now likely scrambled.
677 */
678wrerror()
679{
680
681 if (eq(file, savedfile) && edited)
682 change();
683 syserror();
684}
685
686/*
687 * Source command, handles nested sources.
688 * Traps errors since it mungs unit 0 during the source.
689 */
d266c416
MH
690short slevel;
691short ttyindes;
544cd46c
MH
692
693source(fil, okfail)
694 char *fil;
695 bool okfail;
696{
697 jmp_buf osetexit;
698 register int saveinp, ointty, oerrno;
544cd46c
MH
699
700 signal(SIGINT, SIG_IGN);
701 saveinp = dup(0);
702 if (saveinp < 0)
703 error("Too many nested sources");
d266c416
MH
704 if (slevel <= 0)
705 ttyindes = saveinp;
544cd46c
MH
706 close(0);
707 if (open(fil, 0) < 0) {
708 oerrno = errno;
709 setrupt();
710 dup(saveinp);
711 close(saveinp);
712 errno = oerrno;
713 if (!okfail)
714 filioerr(fil);
715 return;
716 }
717 slevel++;
718 ointty = intty;
719 intty = isatty(0);
720 oprompt = value(PROMPT);
721 value(PROMPT) &= intty;
722 getexit(osetexit);
723 setrupt();
724 if (setexit() == 0)
725 commands(1, 1);
726 else if (slevel > 1) {
727 close(0);
728 dup(saveinp);
729 close(saveinp);
730 slevel--;
731 resexit(osetexit);
732 reset();
733 }
734 intty = ointty;
735 value(PROMPT) = oprompt;
736 close(0);
737 dup(saveinp);
738 close(saveinp);
739 slevel--;
740 resexit(osetexit);
741}
742
743/*
744 * Clear io statistics before a read or write.
745 */
746clrstats()
747{
748
749 ninbuf = 0;
750 cntch = 0;
751 cntln = 0;
752 cntnull = 0;
753 cntodd = 0;
754}
755
756/*
757 * Io is finished, close the unit and print statistics.
758 */
759iostats()
760{
761
762 close(io);
763 io = -1;
764 if (hush == 0) {
765 if (value(TERSE))
766 printf(" %d/%D", cntln, cntch);
767 else
768 printf(" %d line%s, %D character%s", cntln, plural((long) cntln),
769 cntch, plural(cntch));
770 if (cntnull || cntodd) {
771 printf(" (");
772 if (cntnull) {
773 printf("%D null", cntnull);
774 if (cntodd)
775 printf(", ");
776 }
777 if (cntodd)
778 printf("%D non-ASCII", cntodd);
779 putchar(')');
780 }
781 noonl();
782 flush();
783 }
784 return (cntnull != 0 || cntodd != 0);
785}