Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 7u83 1
/*
2
 * This code contains changes by
3
 *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
4
 *
5
 * Conditions 1, 2, and 4 and the no-warranty notice below apply
6
 * to these changes.
7
 *
8
 *
9
 * Copyright (c) 1980, 1993
10
 * 	The Regents of the University of California.  All rights reserved.
11
 *
12
 * Redistribution and use in source and binary forms, with or without
13
 * modification, are permitted provided that the following conditions
14
 * are met:
15
 * 1. Redistributions of source code must retain the above copyright
16
 *    notice, this list of conditions and the following disclaimer.
17
 * 2. Redistributions in binary form must reproduce the above copyright
18
 *    notice, this list of conditions and the following disclaimer in the
19
 *    documentation and/or other materials provided with the distribution.
20
 * 3. All advertising materials mentioning features or use of this software
21
 *    must display the following acknowledgement:
22
 * 	This product includes software developed by the University of
23
 * 	California, Berkeley and its contributors.
24
 * 4. Neither the name of the University nor the names of its contributors
25
 *    may be used to endorse or promote products derived from this software
26
 *    without specific prior written permission.
27
 *
28
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38
 * SUCH DAMAGE.
39
 *
40
 *
41
 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
42
 *
43
 * Redistribution and use in source and binary forms, with or without
44
 * modification, are permitted provided that the following conditions
45
 * are met:
46
 *   Redistributions of source code and documentation must retain the
47
 *    above copyright notice, this list of conditions and the following
48
 *    disclaimer.
49
 *   Redistributions in binary form must reproduce the above copyright
50
 *    notice, this list of conditions and the following disclaimer in the
51
 *    documentation and/or other materials provided with the distribution.
52
 *   All advertising materials mentioning features or use of this software
53
 *    must display the following acknowledgement:
54
 *      This product includes software developed or owned by Caldera
55
 *      International, Inc.
56
 *   Neither the name of Caldera International, Inc. nor the names of
57
 *    other contributors may be used to endorse or promote products
58
 *    derived from this software without specific prior written permission.
59
 *
60
 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
61
 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
62
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
63
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64
 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
65
 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
66
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
67
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
68
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
69
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
70
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
71
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72
 */
73
 
74
#ifndef	lint
75
#ifdef	DOSCCS
76
static char sccsid[] = "@(#)ex_io.c	1.40 (gritter) 2/17/05";
77
#endif
78
#endif
79
 
80
/* from ex_io.c	7.11.1.1 (Berkeley) 8/12/86 */
81
 
82
#include "ex.h"
83
#include "ex_argv.h"
84
#include "ex_temp.h"
85
#include "ex_tty.h"
86
#include "ex_vis.h"
87
 
88
/*
89
 * File input/output, source, preserve and recover
90
 */
91
 
92
/*
93
 * Following remember where . was in the previous file for return
94
 * on file switching.
95
 */
96
int	altdot;
97
int	oldadot;
98
bool	wasalt;
99
short	isalt;
100
 
101
long	cntch;			/* Count of characters on unit io */
102
#ifndef VMUNIX
103
short	cntln;			/* Count of lines " */
104
#else
105
int	cntln;
106
#endif
107
long	cntnull;		/* Count of nulls " */
108
#ifndef	BIT8
109
long	cntodd;			/* Count of non-ascii characters " */
110
#endif
111
 
112
/*
113
 * Parse file name for command encoded by comm.
114
 * If comm is E then command is doomed and we are
115
 * parsing just so user won't have to retype the name.
116
 */
117
void 
118
filename(int comm)
119
{
120
	register int c = comm, d;
121
	register int i;
122
 
123
	d = getchar();
124
	if (endcmd(d)) {
125
		if (savedfile[0] == 0 && comm != 'f')
126
			error(catgets(catd, 1, 72,
127
					"No file|No current filename"));
128
		CP(file, savedfile);
129
		wasalt = (isalt > 0) ? isalt-1 : 0;
130
		isalt = 0;
131
		oldadot = altdot;
132
		if (c == 'e' || c == 'E')
133
			altdot = lineDOT();
134
		if (d == EOF)
135
			ungetchar(d);
136
	} else {
137
		ungetchar(d);
138
		getone();
139
		eol();
140
		if (savedfile[0] == 0 && c != 'E' && c != 'e') {
141
			c = 'e';
142
			edited = 0;
143
		}
144
		wasalt = strcmp(file, altfile) == 0;
145
		oldadot = altdot;
146
		switch (c) {
147
 
148
		case 'f':
149
			edited = 0;
150
			/* fall into ... */
151
 
152
		case 'e':
153
			if (savedfile[0]) {
154
				altdot = lineDOT();
155
				CP(altfile, savedfile);
156
			}
157
			CP(savedfile, file);
158
			break;
159
 
160
		default:
161
			if (file[0]) {
162
				if (c != 'E')
163
					altdot = lineDOT();
164
				CP(altfile, file);
165
			}
166
			break;
167
		}
168
	}
169
	if (hush && comm != 'f' || comm == 'E')
170
		return;
171
	if (file[0] != 0) {
172
		lprintf("\"%s\"", file);
173
		if (comm == 'f') {
174
			if (value(READONLY))
175
				printf(catgets(catd, 1, 73, " [Read only]"));
176
			if (!edited)
177
				printf(catgets(catd, 1, 74, " [Not edited]"));
178
			if (tchng)
179
				printf(catgets(catd, 1, 75, " [Modified]"));
180
		}
181
		flush();
182
	} else
183
		printf(catgets(catd, 1, 76, "No file "));
184
	if (comm == 'f') {
185
		if (!(i = lineDOL()))
186
			i++;
187
		printf(catgets(catd, 1, 77,
188
			" line %d of %d --%ld%%--"), lineDOT(), lineDOL(),
189
		    (long) 100 * lineDOT() / i);
190
	}
191
}
192
 
193
/*
194
 * Get the argument words for a command into genbuf
195
 * expanding # and %.
196
 */
197
int 
198
getargs(void)
199
{
200
	register int c;
201
	register char *cp, *fp;
202
	static char fpatbuf[32];	/* hence limit on :next +/pat */
203
 
204
	pastwh();
205
	if (peekchar() == '+') {
206
		for (cp = fpatbuf;;) {
207
			c = *cp++ = getchar();
208
			if (cp >= &fpatbuf[sizeof(fpatbuf)])
209
				error(catgets(catd, 1, 78, "Pattern too long"));
210
			if (c == '\\' && isspace(peekchar()))
211
				c = getchar();
212
			if (c == EOF || isspace(c)) {
213
				ungetchar(c);
214
				*--cp = 0;
215
				firstpat = &fpatbuf[1];
216
				break;
217
			}
218
		}
219
	}
220
	if (skipend())
221
		return (0);
222
	CP(genbuf, "echo "); cp = &genbuf[5];
223
	for (;;) {
224
		c = getchar();
225
		if (endcmd(c)) {
226
			ungetchar(c);
227
			break;
228
		}
229
		switch (c) {
230
 
231
		case '\\':
232
			if (any(peekchar(), "#%|"))
233
				c = getchar();
234
			/* fall into... */
235
 
236
		default:
237
			if (cp > &genbuf[LBSIZE - 2])
238
flong:
239
				error(catgets(catd, 1, 79,
240
						"Argument buffer overflow"));
241
			*cp++ = c;
242
			break;
243
 
244
		case '#':
245
			fp = altfile;
246
			if (*fp == 0)
247
				error(catgets(catd, 1, 80,
248
				"No alternate filename@to substitute for #"));
249
			goto filexp;
250
 
251
		case '%':
252
			fp = savedfile;
253
			if (*fp == 0)
254
				error(catgets(catd, 1, 81,
255
				"No current filename@to substitute for %%"));
256
filexp:
257
			while (*fp) {
258
				if (cp > &genbuf[LBSIZE - 2])
259
					goto flong;
260
				*cp++ = *fp++;
261
			}
262
			break;
263
		}
264
	}
265
	*cp = 0;
266
	return (1);
267
}
268
 
269
/*
270
 * Scan genbuf for shell metacharacters.
271
 * Set is union of v7 shell and csh metas.
272
 */
273
int 
274
gscan(void)
275
{
276
	register char *cp;
277
 
278
	for (cp = genbuf; *cp; cp++)
279
		if (any(*cp, "~{[*?$`'\"\\"))
280
			return (1);
281
	return (0);
282
}
283
 
284
/*
285
 * Glob the argument words in genbuf, or if no globbing
286
 * is implied, just split them up directly.
287
 */
288
void 
289
gglob(struct glob *gp)
290
{
291
	int pvec[2];
292
	register char **argv = gp->argv;
293
	register char *cp = gp->argspac;
294
	register int c;
295
	char ch;
296
	int nleft = NCARGS;
297
 
298
	gp->argc0 = 0;
299
	if (gscan() == 0) {
300
		register char *v = genbuf + 5;		/* strlen("echo ") */
301
 
302
		for (;;) {
303
			while (isspace(*v&0377))
304
				v++;
305
			if (!*v)
306
				break;
307
			*argv++ = cp;
308
			while (*v && !isspace(*v&0377))
309
				*cp++ = *v++;
310
			*cp++ = 0;
311
			gp->argc0++;
312
		}
313
		*argv = 0;
314
		return;
315
	}
316
	if (pipe(pvec) < 0)
317
		error(catgets(catd, 1, 82, "Can't make pipe to glob"));
318
	pid = fork();
319
	io = pvec[0];
320
	if (pid < 0) {
321
		close(pvec[1]);
322
		error(catgets(catd, 1, 83, "Can't fork to do glob"));
323
	}
324
	if (pid == 0) {
325
		int oerrno;
326
 
327
		close(1);
328
		dup(pvec[1]);
329
		close(pvec[0]);
330
		close(2);	/* so errors don't mess up the screen */
331
		open("/dev/null", O_WRONLY);
332
		execl(svalue(SHELL), "sh", "-c", genbuf, (char *)0);
333
		oerrno = errno; close(1); dup(2); errno = oerrno;
334
		filioerr(svalue(SHELL));
335
	}
336
	close(pvec[1]);
337
	do {
338
		*argv = cp;
339
		for (;;) {
340
			if (read(io, &ch, 1) != 1) {
341
				close(io);
342
				c = -1;
343
			} else
344
				c = ch & TRIM;
345
			if (c <= 0 || isspace(c))
346
				break;
347
			*cp++ = c;
348
			if (--nleft <= 0)
349
				error(catgets(catd, 1, 84,
350
							"Arg list too long"));
351
		}
352
		if (cp != *argv) {
353
			--nleft;
354
			*cp++ = 0;
355
			gp->argc0++;
356
			if (gp->argc0 >= NARGS)
357
				error(catgets(catd, 1, 85,
358
							"Arg list too long"));
359
			argv++;
360
		}
361
	} while (c >= 0);
362
	waitfor();
363
	if (gp->argc0 == 0)
364
		error(catgets(catd, 1, 86, "No match"));
365
}
366
 
367
/*
368
 * Parse one filename into file.
369
 */
370
struct glob G;
371
void
372
getone(void)
373
{
374
	register char *str;
375
 
376
	if (getargs() == 0)
377
missing:
378
		error(catgets(catd, 1, 87, "Missing filename"));
379
	gglob(&G);
380
	if (G.argc0 > 1)
381
		error(catgets(catd, 1, 88, "Ambiguous|Too many file names"));
382
	if ((str = G.argv[G.argc0 - 1]) == NULL)
383
		goto missing;
384
	if (strlen(str) > FNSIZE - 4)
385
		error(catgets(catd, 1, 89, "Filename too long"));
386
/* samef: */
387
	CP(file, str);
388
}
389
 
390
/*
391
 * Are these two really the same inode?
392
 */
393
int 
394
samei(struct stat *sp, char *cp)
395
{
396
	struct stat stb;
397
 
398
	if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
399
		return (0);
400
	return (sp->st_ino == stb.st_ino);
401
}
402
 
403
/*
404
 * Read a file from the world.
405
 * C is command, 'e' if this really an edit (or a recover).
406
 */
407
void 
408
rop(int c)
409
{
410
	register int i;
411
	struct stat stbuf;
412
	char magic[4];
413
	static int ovro;	/* old value(READONLY) */
414
	static int denied;	/* 1 if READONLY was set due to file permissions */
415
 
416
	io = open(file, O_RDONLY);
417
	if (io < 0) {
418
		if (c == 'e' && errno == ENOENT) {
419
			edited++;
420
			/*
421
			 * If the user just did "ex foo" he is probably
422
			 * creating a new file.  Don't be an error, since
423
			 * this is ugly, and it screws up the + option.
424
			 *
425
			 * POSIX.2 specifies that this be done for all
426
			 * "edit" commands, not just for the first one.
427
			 */
428
			if (1 || !seenprompt) {
429
				printf(catgets(catd, 1, 90, " [New file]"));
430
				noonl();
431
				return;
432
			}
433
		}
434
		failed = 1;
435
		syserror();
436
	}
437
	if (fstat(io, &stbuf))
438
		syserror();
439
	switch (stbuf.st_mode & S_IFMT) {
440
 
441
	case S_IFBLK:
442
		error(catgets(catd, 1, 91, " Block special file"));
443
 
444
	case S_IFCHR:
445
		if (isatty(io))
446
			error(catgets(catd, 1, 92, " Teletype"));
447
		if (samei(&stbuf, "/dev/null"))
448
			break;
449
		error(catgets(catd, 1, 93, " Character special file"));
450
 
451
	case S_IFDIR:
452
		error(catgets(catd, 1, 94, " Directory"));
453
 
454
#ifdef	S_IFSOCK
455
	case S_IFSOCK:
456
		error(catgets(catd, 1, 95, " Socket"));
457
#endif
458
#ifdef	S_IFIFO
459
	case S_IFIFO:
460
		error(catgets(catd, 1, 96, " Named pipe"));
461
#endif
462
 
463
	case S_IFREG:
464
		/*
465
		 * The magics are checked byte-wise now to avoid
466
		 * endianness problems. Some quite old types
467
		 * were omitted.
468
		 *
469
		 * Feel free too add more magics here, but do not
470
		 * make this a copy of the `file' program.
471
		 *
472
		 * GR
473
		 */
474
		i = read(io, magic, sizeof(magic));
475
		lseek(io, (off_t) 0, SEEK_SET);
476
		if (i != sizeof(magic))
477
			break;
478
		switch (magic[0]&0377) {
479
 
480
		case 01:	/* big endian a.out */
481
			if (magic[1] != 05 && magic[1] != 07
482
				&& magic[1] != 010 && magic[1] != 011
483
				&& magic[1] != 013 && magic[1] != 030
484
				&& magic[1] != 031)
485
				break;
486
			goto is_exec;
487
		case 0314:	/* Linux/ia32 QMAGIC */
488
			if (magic[1] != 0 || magic[2] != 0144)
489
				break;
490
			goto is_exec;
491
		case 05:	/* data overlay on exec */
492
		case 07:	/* unshared */
493
		case 010:	/* shared text */
494
		case 011:	/* separate I/D */
495
		case 013:	/* VM/Unix demand paged */
496
		case 030:	/* PDP-11 Overlay shared */
497
		case 031:	/* PDP-11 Overlay sep I/D */
498
			if (magic[1] == 01)
499
is_exec:
500
				error(catgets(catd, 1, 97, " Executable"));
501
			break;
502
 
503
		case 037:
504
			switch (magic[1]&0377) {
505
			case 036:	/* pack */
506
			case 037:	/* compact */
507
			case 0235:	/* compress */
508
			case 0213:	/* gzip */
509
			/*
510
			 * We omit bzip2 here since it has
511
			 * an ASCII header.
512
			 */
513
				error(catgets(catd, 1, 98, " Compressed Data"));
514
			}
515
			break;
516
 
517
		case 0177:
518
			if (magic[1] == 'E' && magic[2] == 'L'
519
					&& magic[3] == 'F')
520
				error(catgets(catd, 1, 99, " ELF object"));
521
			break;
522
 
523
		default:
524
			break;
525
		}
526
#ifdef	notdef
527
		/*
528
		 * We do not forbid the editing of portable archives
529
		 * because it is reasonable to edit them, especially
530
		 * if they are archives of text files.  This is
531
		 * especially useful if you archive source files together
532
		 * and copy them to another system with ~%take, since
533
		 * the files sometimes show up munged and must be fixed.
534
		 */
535
		case 0177545:
536
		case 0177555:
537
			error(catgets(catd, 1, 100, " Archive"));
538
 
539
		default:
540
#ifdef mbb
541
			/* C/70 has a 10 bit byte */
542
			if (magic & 03401600)
543
#else
544
			/* Everybody else has an 8 bit byte */
545
			if (magic & 0100200)
546
#endif
547
				error(catgets(catd, 1, 101, " Non-ascii file"));
548
			break;
549
		}
550
#endif	/* notdef */
551
	}
552
	if (c != 'r') {
553
		if (value(READONLY) && denied) {
554
			value(READONLY) = ovro;
555
			denied = 0;
556
		}
557
		if ((stbuf.st_mode & 0222) == 0 || access(file, W_OK) < 0) {
558
			ovro = value(READONLY);
559
			denied = 1;
560
			value(READONLY) = 1;
561
		}
562
	}
563
	if (value(READONLY) && !hush) {
564
		printf(catgets(catd, 1, 102, " [Read only]"));
565
		flush();
566
	}
567
	if (c == 'r')
568
		setdot();
569
	else
570
		setall();
571
	if (FIXUNDO && inopen && c == 'r')
572
		undap1 = undap2 = addr1 + 1;
573
	rop2();
574
	rop3(c);
575
}
576
 
577
void 
578
rop2(void)
579
{
580
	line *first, *last, *a;
581
	struct stat statb;
582
 
583
	deletenone();
584
	clrstats();
585
	first = addr2 + 1;
586
	if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
587
		bsize = LBSIZE;
588
	else {
589
		bsize = statb.st_blksize;
590
		if (bsize <= 0)
591
			bsize = LBSIZE;
592
	}
593
	ignore(append(getfile, addr2));
594
	last = dot;
595
	/*
596
	 *	if the modelines variable is set,
597
	 *	check the first and last five lines of the file
598
	 *	for a mode line.
599
	 */
600
	if (value(MODELINES)) {
601
		for (a=first; a<=last; a++) {
602
			if (a==first+5 && last-first > 10)
603
				a = last - 4;
604
			getline(*a);
605
			checkmodeline(linebuf);
606
		}
607
	}
608
}
609
 
610
/*
611
 * Io is finished, close the unit and print statistics.
612
 */
613
int 
614
iostats(void)
615
{
616
 
617
	fsync(io);
618
	close(io);
619
	io = -1;
620
	if (hush == 0) {
621
		if (value(TERSE))
622
			printf(catgets(catd, 1, 103,
623
						" %d/%d"), cntln, (int)cntch);
624
		else
625
			printf(catgets(catd, 1, 104,
626
		" %d line%s, %d character%s"), cntln, plural((long) cntln),
627
			    (int)cntch, plural((int)cntch));
628
		if (cntnull
629
#ifndef	BIT8
630
				|| cntodd
631
#endif
632
				) {
633
			printf(catgets(catd, 1, 105, " ("));
634
			if (cntnull) {
635
				printf(catgets(catd, 1, 106,
636
						"%d null"), (int)cntnull);
637
#ifndef	BIT8
638
				if (cntodd)
639
					printf(catgets(catd, 1, 107, ", "));
640
#endif
641
			}
642
#ifndef	BIT8
643
			if (cntodd)
644
				printf(catgets(catd, 1, 108,
645
					"%d non-ASCII"), (int)cntodd);
646
#endif
647
			putchar(')');
648
		}
649
		noonl();
650
		flush();
651
	}
652
	return (cntnull != 0
653
#ifndef	BIT8
654
			|| cntodd != 0
655
#endif
656
			);
657
}
658
 
659
void
660
rop3(int c)
661
{
662
 
663
	if (iostats() == 0 && c == 'e')
664
		edited++;
665
	if (c == 'e') {
666
		if (wasalt || firstpat) {
667
			register line *addr = zero + oldadot;
668
 
669
			if (addr > dol)
670
				addr = dol;
671
			if (firstpat) {
672
				globp = (*firstpat) ? firstpat : "$";
673
				commands(1,1);
674
				firstpat = 0;
675
			} else if (addr >= one) {
676
				if (inopen)
677
					dot = addr;
678
				markpr(addr);
679
			} else
680
				goto other;
681
		} else
682
other:
683
			if (dol > zero) {
684
				if (inopen)
685
					dot = one;
686
				markpr(one);
687
			}
688
		if(FIXUNDO)
689
			undkind = UNDNONE;
690
		if (inopen) {
691
			vcline = 0;
692
			vreplace(0, TLINES, lineDOL());
693
		}
694
	}
695
}
696
 
697
/* Returns from edited() */
698
#define	EDF	0		/* Edited file */
699
#define	NOTEDF	-1		/* Not edited file */
700
#define	PARTBUF	1		/* Write of partial buffer to Edited file */
701
 
702
/*
703
 * Is file the edited file?
704
 * Work here is that it is not considered edited
705
 * if this is a partial buffer, and distinguish
706
 * all cases.
707
 */
708
int 
709
edfile(void)
710
{
711
 
712
	if (!edited || !eq(file, savedfile))
713
		return (NOTEDF);
714
	return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
715
}
716
 
717
/*
718
 * Write a file.
719
 */
720
void
721
wop(bool dofname)
722
/*bool dofname;	/\* if 1 call filename, else use savedfile */
723
{
724
	register int c, exclam, nonexist;
725
	line *saddr1 = NULL, *saddr2 = NULL;
726
	struct stat stbuf;
727
 
728
	c = 0;
729
	exclam = 0;
730
	if (dofname) {
731
		if (peekchar() == '!')
732
			exclam++, ignchar();
733
		ignore(skipwh());
734
		while (peekchar() == '>')
735
			ignchar(), c++, ignore(skipwh());
736
		if (c != 0 && c != 2)
737
			error(catgets(catd, 1, 109,
738
					"Write forms are 'w' and 'w>>'"));
739
		filename('w');
740
	} else {
741
		if (savedfile[0] == 0)
742
			error(catgets(catd, 1, 110,
743
					"No file|No current filename"));
744
		saddr1=addr1;
745
		saddr2=addr2;
746
		addr1=one;
747
		addr2=dol;
748
		CP(file, savedfile);
749
		if (inopen) {
750
			vclrech(0);
751
			splitw++;
752
		}
753
		lprintf("\"%s\"", file);
754
	}
755
	nonexist = stat(file, &stbuf);
756
	switch (c) {
757
 
758
	case 0:
759
		if (!exclam && (!value(WRITEANY) || value(READONLY)))
760
		switch (edfile()) {
761
 
762
		case NOTEDF:
763
			if (nonexist)
764
				break;
765
			if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
766
				if (samei(&stbuf, "/dev/null"))
767
					break;
768
				if (samei(&stbuf, "/dev/tty"))
769
					break;
770
			}
771
			io = open(file, O_WRONLY);
772
			if (io < 0)
773
				syserror();
774
			if (!isatty(io))
775
				serror(catgets(catd, 1, 111,
776
	" File exists| File exists - use \"w! %s\" to overwrite"), file);
777
			close(io);
778
			break;
779
 
780
		case EDF:
781
			if (value(READONLY))
782
				error(catgets(catd, 1, 112,
783
						" File is read only"));
784
			break;
785
 
786
		case PARTBUF:
787
			if (value(READONLY))
788
				error(catgets(catd, 1, 113,
789
						" File is read only"));
790
			error(catgets(catd, 1, 114,
791
				" Use \"w!\" to write partial buffer"));
792
		}
793
cre:
794
/*
795
		synctmp();
796
*/
797
		io = creat(file, 0666);
798
		if (io < 0)
799
			syserror();
800
		writing = 1;
801
		if (hush == 0)
802
			if (nonexist)
803
				printf(catgets(catd, 1, 115, " [New file]"));
804
			else if (value(WRITEANY) && edfile() != EDF)
805
				printf(catgets(catd, 1, 116,
806
							" [Existing file]"));
807
		break;
808
 
809
	case 2:
810
		io = open(file, O_WRONLY);
811
		if (io < 0) {
812
			if (exclam || value(WRITEANY))
813
				goto cre;
814
			syserror();
815
		}
816
		lseek(io, (off_t) 0, SEEK_END);
817
		break;
818
	}
819
	putfile(0);
820
	ignore(iostats());
821
	if (c != 2 && addr1 == one && addr2 == dol) {
822
		if (eq(file, savedfile))
823
			edited = 1;
824
		synced();
825
	}
826
	if (!dofname) {
827
		addr1 = saddr1;
828
		addr2 = saddr2;
829
	}
830
	writing = 0;
831
}
832
 
833
/*
834
 * Extract the next line from the io stream.
835
 */
836
char *nextip;
837
 
838
int
839
getfile(void)
840
{
841
	register short c;
842
	register char *lp, *fp;
843
 
844
	lp = linebuf;
845
	fp = nextip;
846
	do {
847
		if (--ninbuf < 0) {
848
			ninbuf = read(io, genbuf, bsize) - 1;
849
			if (ninbuf < 0) {
850
				if (lp != linebuf) {
851
					lp++;
852
					printf(catgets(catd, 1, 117,
853
						" [Incomplete last line]"));
854
					break;
855
				}
856
				return (EOF);
857
			}
858
			fp = genbuf;
859
			cntch += ninbuf+1;
860
		}
861
		if (lp >= &linebuf[LBSIZE]) {
862
			synced();
863
			error(catgets(catd, 1, 118, " Line too long"));
864
		}
865
		c = *fp++;
866
		if (c == 0) {
867
			cntnull++;
868
#ifndef	BIT8
869
			continue;
870
#else
871
			c = 0200;
872
#endif
873
		}
874
#ifndef	BIT8
875
		if (c & QUOTE) {
876
			cntodd++;
877
			c &= TRIM;
878
			if (c == 0)
879
				continue;
880
		}
881
#endif
882
		*lp++ = c;
883
	} while (c != '\n');
884
	*--lp = 0;
885
	nextip = fp;
886
	cntln++;
887
	return (0);
888
}
889
 
890
/*
891
 * Write a range onto the io stream.
892
 */
893
void
894
putfile(int isfilter)
895
{
896
	line *a1;
897
	register char *fp, *lp;
898
	register int nib;
899
	struct stat statb;
900
 
901
	a1 = addr1;
902
	clrstats();
903
	cntln = fixedzero ? 0 : addr2 - a1 + 1;
904
	if (cntln == 0 || fixedzero)
905
		return;
906
	if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
907
		bsize = LBSIZE;
908
	else {
909
		bsize = statb.st_blksize;
910
		if (bsize <= 0)
911
			bsize = LBSIZE;
912
	}
913
	nib = bsize;
914
	fp = genbuf;
915
	do {
916
		getline(*a1++);
917
		lp = linebuf;
918
		for (;;) {
919
			if (--nib < 0) {
920
				nib = fp - genbuf;
921
				if (write(io, genbuf, nib) != nib) {
922
					wrerror();
923
				}
924
				cntch += nib;
925
				nib = bsize - 1;
926
				fp = genbuf;
927
			}
928
			if ((*fp++ = *lp++) == 0) {
929
				fp[-1] = '\n';
930
				break;
931
			}
932
		}
933
	} while (a1 <= addr2);
934
	nib = fp - genbuf;
935
	if (write(io, genbuf, nib) != nib) {
936
		wrerror();
937
	}
938
	cntch += nib;
939
}
940
 
941
/*
942
 * A write error has occurred;  if the file being written was
943
 * the edited file then we consider it to have changed since it is
944
 * now likely scrambled.
945
 */
946
void
947
wrerror(void)
948
{
949
 
950
	if (eq(file, savedfile) && edited)
951
		change();
952
	syserror();
953
}
954
 
955
/*
956
 * Source command, handles nested sources.
957
 * Traps errors since it mungs unit 0 during the source.
958
 */
959
short slevel;
960
short ttyindes;
961
 
962
void
963
source(char *fil, bool okfail)
964
{
965
	JMP_BUF osetexit;
966
	register int saveinp, ointty, oerrno;
967
	char *saveglobp, *saveinput;
968
	char	saveinline[BUFSIZ];
969
	int savepeekc, savelastc;
970
 
971
	signal(SIGINT, SIG_IGN);
972
	saveinp = dup(0);
973
	savepeekc = peekc;
974
	savelastc = lastc;
975
	saveglobp = globp;
976
	saveinput = input;
977
	if (input)
978
		strcpy(saveinline, input);
979
	peekc = 0; lastc = 0; globp = 0; input = 0;
980
	if (saveinp < 0)
981
		error(catgets(catd, 1, 119, "Too many nested sources"));
982
	if (slevel <= 0)
983
		ttyindes = saveinp;
984
	close(0);
985
	if (open(fil, O_RDONLY) < 0) {
986
		oerrno = errno;
987
		setrupt();
988
		dup(saveinp);
989
		close(saveinp);
990
		input = saveinput;
991
		if (input)
992
			strcpy(input, saveinline);
993
		lastc = savelastc;
994
		errno = oerrno;
995
		if (!okfail)
996
			filioerr(fil);
997
		return;
998
	}
999
	slevel++;
1000
	ointty = intty;
1001
	intty = isatty(0);
1002
	oprompt = value(PROMPT);
1003
	value(PROMPT) &= intty;
1004
	getexit(osetexit);
1005
	setrupt();
1006
	if (setexit() == 0)
1007
		commands(1, 1);
1008
	else if (slevel > 1) {
1009
		close(0);
1010
		dup(saveinp);
1011
		close(saveinp);
1012
		input = saveinput;
1013
		if (input)
1014
			strcpy(input, saveinline);
1015
		lastc = savelastc;
1016
		slevel--;
1017
		resexit(osetexit);
1018
		reset();
1019
	}
1020
	intty = ointty;
1021
	value(PROMPT) = oprompt;
1022
	close(0);
1023
	dup(saveinp);
1024
	close(saveinp);
1025
	globp = saveglobp;
1026
	input = saveinput;
1027
	if (input)
1028
		strcpy(input, saveinline);
1029
	peekc = savepeekc;
1030
	lastc = savelastc;
1031
	slevel--;
1032
	resexit(osetexit);
1033
}
1034
 
1035
/*
1036
 * Clear io statistics before a read or write.
1037
 */
1038
void
1039
clrstats(void)
1040
{
1041
 
1042
	ninbuf = 0;
1043
	cntch = 0;
1044
	cntln = 0;
1045
	cntnull = 0;
1046
#ifndef	BIT8
1047
	cntodd = 0;
1048
#endif
1049
}
1050
 
1051
/* It's so wonderful how we all speak the same language... */
1052
# define index strchr
1053
# define rindex strrchr
1054
 
1055
void
1056
checkmodeline(char *lin)
1057
{
1058
	char *beg, *end;
1059
	char cmdbuf[BUFSIZ];
1060
 
1061
	beg = index(lin, ':');
1062
	if (beg == NULL)
1063
		return;
1064
	if (&beg[-2] < lin)
1065
		return;
1066
	if (!((beg[-2] == 'e' && beg[-1] == 'x')
1067
	     || (beg[-2] == 'v' && beg[-1] == 'i'))) return;
1068
	strncpy(cmdbuf, beg+1, sizeof cmdbuf);
1069
	end = rindex(cmdbuf, ':');
1070
	if (end == NULL)
1071
		return;
1072
	*end = 0;
1073
	globp = cmdbuf;
1074
	commands(1, 1);
1075
}
1076
 
1077
#ifdef	MB
1078
int
1079
mbtowi(int *cp, const char *s, size_t n)
1080
{
1081
	wchar_t	wc;
1082
	int	i;
1083
 
1084
	i = mbtowc(&wc, s, n);
1085
	if (i >= 0 && widthok(wc) && !(wc & 0x70000000))
1086
		*cp = wc;
1087
	else {
1088
		*cp = *s&0377 | INVBIT;
1089
		i = 1;
1090
	}
1091
	return i;
1092
}
1093
 
1094
int
1095
widthok(int c)
1096
{
1097
	return inopen ? wcwidth(c) <= 2 : 1;
1098
}
1099
#endif	/* MB */
1100
 
1101
int
1102
GETWC(char *mb)
1103
{
1104
	int	c, n;
1105
 
1106
	n = 1;
1107
	mb[0] = c = getchar();
1108
	mb[1] = '\0';
1109
#ifdef	MB
1110
	if (mb_cur_max > 1 && c & 0200 && c != EOF) {
1111
		int	m;
1112
		wchar_t	wc;
1113
		while ((m = mbtowc(&wc, mb, mb_cur_max)) < 0 && n<mb_cur_max) {
1114
			mb[n++] = c = getchar();
1115
			mb[n] = '\0';
1116
			if (c == '\n' || c == EOF)
1117
				break;
1118
		}
1119
		if (m != n || c & 0x70000000)
1120
			error("illegal multibyte sequence");
1121
		return wc;
1122
	} else
1123
#endif	/* MB */
1124
		return c;
1125
}