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
#ifdef	__GNUC__
75
#define	UNUSED	__attribute__ ((unused))
76
#else
77
#define	UNUSED
78
#endif
79
 
80
#ifndef	lint
81
#ifdef	DOSCCS
82
char *copyright =
83
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
84
 All rights reserved.\n";
85
#endif
86
static char sccsid[] UNUSED = "@(#)exrecover.c	1.21 (gritter) 11/27/04";
87
#endif
88
 
89
/* from exrecover.c	7.9.2 (2.11BSD) 1996/10/26 */
90
 
91
#include <stdarg.h>
92
#ifdef	notdef	/* GR */
93
#include <stdio.h>	/* mjm: BUFSIZ: stdio = 512, VMUNIX = 1024 */
94
#undef	BUFSIZ		/* mjm: BUFSIZ different */
95
#undef	EOF		/* mjm: EOF and NULL effectively the same */
96
#undef	NULL
97
#else
98
#define	xstderr	(int*)0
99
typedef	int	xFILE;
100
extern void	perror(const char *);
101
extern int	vsprintf(char *, const char *, va_list);
102
#endif
103
 
104
#define	var
105
 
106
#include "ex.h"
107
#include "ex_temp.h"
108
#include "ex_tty.h"
109
#include <dirent.h>
110
#include <time.h>
111
 
112
#ifndef	MAXNAMLEN
113
#ifdef	FNSIZE
114
#define	MAXNAMLEN	FNSIZE
115
#else
116
#ifdef	NAME_MAX
117
#define	MAXNAMLEN	NAME_MAX
118
#else
119
#define	MAXNAMLEN	255
120
#endif
121
#endif
122
#endif
123
 
124
#define	TMP		"/var/tmp"
125
 
126
#ifdef	LANGMSG
127
nl_catd	catd;
128
#endif
129
 
130
char xstr[1];		/* make loader happy */
131
int tfile = -1;	/* ditto */
132
 
133
/*
134
 *
135
 * This program searches through the specified directory and then
136
 * the directory /usr/preserve looking for an instance of the specified
137
 * file from a crashed editor or a crashed system.
138
 * If this file is found, it is unscrambled and written to
139
 * the standard output.
140
 *
141
 * If this program terminates without a "broken pipe" diagnostic
142
 * (i.e. the editor doesn't die right away) then the buffer we are
143
 * writing from is removed when we finish.  This is potentially a mistake
144
 * as there is not enough handshaking to guarantee that the file has actually
145
 * been recovered, but should suffice for most cases.
146
 */
147
 
148
/*
149
 * Here we save the information about files, when
150
 * you ask us what files we have saved for you.
151
 * We buffer file name, number of lines, and the time
152
 * at which the file was saved.
153
 */
154
struct svfile {
155
	char	sf_name[FNSIZE + 1];
156
	int	sf_lines;
157
	char	sf_entry[MAXNAMLEN + 1];
158
	time_t	sf_time;
159
};
160
 
161
#define	ignorl(a)	a
162
 
163
/*
164
 * This directory definition also appears (obviously) in expreserve.c.
165
 * Change both if you change either.
166
 */
167
#ifdef	notdef
168
char	mydir[] =	"/usr/preserve";
169
#else
170
char	mydir[] =	"/var/preserve";
171
#endif
172
 
173
/*
174
 * Limit on the number of printed entries
175
 * when an, e.g. ``ex -r'' command is given.
176
 */
177
#define	NENTRY	50
178
 
179
char	nb[BUFSIZ];
180
int	vercnt;			/* Count number of versions of file found */
181
 
182
extern void error(char *, ...);
183
extern void listfiles(char *);
184
extern void enter(struct svfile *, char *, int);
185
extern int qucmp(struct svfile *, struct svfile *);
186
extern void findtmp(char *);
187
extern void searchdir(char *);
188
extern int yeah(char *);
189
extern int preserve(void);
190
extern void scrapbad(void);
191
extern void putfile(int);
192
extern void wrerror(void);
193
extern void clrstats(void);
194
extern void getline(line);
195
extern char *getblock(line, int);
196
extern void blkio(bloc, char *, ssize_t (*)(int, void *, size_t));
197
extern void syserror(void);
198
extern void xvfprintf(xFILE *, char *, va_list);
199
extern void xfprintf(xFILE *, char *, ...);
200
 
201
int 
202
main(int argc, char *argv[])
203
{
204
	register char *cp;
205
	register int b, i;
206
 
207
	/*
208
	 * Initialize the built-in memory allocator.
209
	 */
210
#ifdef	VMUNIX
211
	poolsbrk(0);
212
#endif
213
#ifdef	LANGMSG
214
	setlocale(LC_MESSAGES, "");
215
	catd = catopen(CATNAME, NL_CAT_LOCALE);
216
#endif
217
 
218
	/*
219
	 * Initialize as though the editor had just started.
220
	 */
221
	fendcore = (line *) sbrk(0);
222
	dot = zero = dol = fendcore;
223
	one = zero + 1;
224
	endcore = fendcore - 2;
225
	iblock = oblock = -1;
226
 
227
	/*
228
	 * If given only a -r argument, then list the saved files.
229
	 */
230
	if (argc == 2 && strcmp(argv[1], "-r") == 0) {
231
		listfiles(mydir);
232
		listfiles(TMP);
233
		exit(0);
234
	}
235
	if (argc != 3)
236
		error(catgets(catd, 2, 1,
237
			" Wrong number of arguments to exrecover"), 0);
238
 
239
	strcpy(file, argv[2]);
240
 
241
	/*
242
	 * Search for this file.
243
	 */
244
	findtmp(argv[1]);
245
 
246
	/*
247
	 * Got (one of the versions of) it, write it back to the editor.
248
	 */
249
	cp = ctime(&H.Time);
250
	cp[19] = 0;
251
	xfprintf(xstderr, catgets(catd, 2, 2, " [Dated: %s"), cp);
252
	xfprintf(xstderr, vercnt > 1 ? catgets(catd, 2, 3,
253
			", newest of %d saved]")
254
		: catgets(catd, 2, 4, "]"), vercnt);
255
	H.Flines++;
256
 
257
	/*
258
	 * Allocate space for the line pointers from the temp file.
259
	 */
260
	if ((char *) sbrk(H.Flines * sizeof (line)) == (char *) -1)
261
		/*
262
		 * Good grief.
263
		 */
264
		error(catgets(catd, 1, 5, " Not enough core for lines"), 0);
265
#ifdef DEBUG
266
	xfprintf(xstderr, "%d lines\n", H.Flines);
267
#endif
268
 
269
	/*
270
	 * Now go get the blocks of seek pointers which are scattered
271
	 * throughout the temp file, reconstructing the incore
272
	 * line pointers at point of crash.
273
	 */
274
	b = 0;
275
	while (H.Flines > 0) {
276
		ignorl(lseek(tfile, (off_t) ((blocks[b] & BLKMSK) * BUFSIZ),
277
					SEEK_SET));
278
		i = H.Flines < BUFSIZ / sizeof (line) ?
279
			H.Flines * sizeof (line) : BUFSIZ;
280
		if (read(tfile, (char *) dot, i) != i) {
281
			perror(nb);
282
			exit(1);
283
		}
284
		dot += i / sizeof (line);
285
		H.Flines -= i / sizeof (line);
286
		b++;
287
	}
288
	dot--; dol = dot;
289
 
290
	/*
291
	 * Sigh... due to sandbagging some lines may really not be there.
292
	 * Find and discard such.  This shouldn't happen much.
293
	 */
294
	scrapbad();
295
 
296
	/*
297
	 * Now if there were any lines in the recovered file
298
	 * write them to the standard output.
299
	 */
300
	if (dol > zero) {
301
		addr1 = one; addr2 = dol; io = 1;
302
		putfile(0);
303
	}
304
 
305
	/*
306
	 * Trash the saved buffer.
307
	 * Hopefully the system won't crash before the editor
308
	 * syncs the new recovered buffer; i.e. for an instant here
309
	 * you may lose if the system crashes because this file
310
	 * is gone, but the editor hasn't completed reading the recovered
311
	 * file from the pipe from us to it.
312
	 *
313
	 * This doesn't work if we are coming from an non-absolute path
314
	 * name since we may have chdir'ed but what the hay, noone really
315
	 * ever edits with temporaries in "." anyways.
316
	 */
317
	if (nb[0] == '/')
318
		ignore(unlink(nb));
319
 
320
	/*
321
	 * Adieu.
322
	 */
323
	exit(0);
324
}
325
 
326
/*
327
 * Print an error message (notably not in error
328
 * message file).  If terminal is in RAW mode, then
329
 * we should be writing output for "vi", so don't print
330
 * a newline which would screw up the screen.
331
 */
332
/*VARARGS2*/
333
void
334
error(char *str, ...)
335
{
336
	va_list ap;
337
 
338
	va_start(ap, str);
339
	xvfprintf(xstderr, str, ap);
340
	va_end(ap);
341
	tcgetattr(2, &tty);
342
	if (tty.c_lflag & ICANON)
343
		xfprintf(xstderr, "\n");
344
	exit(1);
345
}
346
 
347
void
348
listfiles(char *dirname)
349
{
350
	register DIR *dir;
351
	struct dirent *dirent;
352
	int ecount;
353
	register int f;
354
	char *cp;
355
	struct svfile *fp, svbuf[NENTRY];
356
 
357
	/*
358
	 * Open /usr/preserve, and go there to make things quick.
359
	 */
360
	dir = opendir(dirname);
361
	if (dir == NULL) {
362
		perror(dirname);
363
		return;
364
	}
365
	if (chdir(dirname) < 0) {
366
		perror(dirname);
367
		return;
368
	}
369
	xfprintf(xstderr, "%s:\n", dirname);
370
 
371
	/*
372
	 * Look at the candidate files in /usr/preserve.
373
	 */
374
	fp = &svbuf[0];
375
	ecount = 0;
376
	while ((dirent = readdir(dir)) != NULL) {
377
		if (dirent->d_name[0] != 'E')
378
			continue;
379
#ifdef DEBUG
380
		xfprintf(xstderr, "considering %s\n", dirent->d_name);
381
#endif
382
		/*
383
		 * Name begins with E; open it and
384
		 * make sure the uid in the header is our uid.
385
		 * If not, then don't bother with this file, it can't
386
		 * be ours.
387
		 */
388
		f = open(dirent->d_name, O_RDONLY);
389
		if (f < 0) {
390
#ifdef DEBUG
391
			xfprintf(xstderr, "open failed\n");
392
#endif
393
			continue;
394
		}
395
		if (read(f, (char *) &H, sizeof H) != sizeof H) {
396
#ifdef DEBUG
397
			xfprintf(xstderr, "culdnt read hedr\n");
398
#endif
399
			ignore(close(f));
400
			continue;
401
		}
402
		ignore(close(f));
403
		if (getuid() != H.Uid) {
404
#ifdef DEBUG
405
			xfprintf(xstderr, "uid wrong\n");
406
#endif
407
			continue;
408
		}
409
 
410
		/*
411
		 * Saved the day!
412
		 */
413
		enter(fp++, dirent->d_name, ecount);
414
		ecount++;
415
#ifdef DEBUG
416
		xfprintf(xstderr, "entered file %s\n", dirent->d_name);
417
#endif
418
	}
419
	ignore(closedir(dir));
420
 
421
	/*
422
	 * If any files were saved, then sort them and print
423
	 * them out.
424
	 */
425
	if (ecount == 0) {
426
		xfprintf(xstderr, catgets(catd, 2, 6, "No files saved.\n"));
427
		return;
428
	}
429
	qsort(&svbuf[0], ecount, sizeof svbuf[0], (int(*)()) qucmp);
430
	for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) {
431
		cp = ctime(&fp->sf_time);
432
		cp[10] = 0;
433
		xfprintf(xstderr, catgets(catd, 2, 7, "On %s at "), cp);
434
 		cp[16] = 0;
435
		xfprintf(xstderr, &cp[11]);
436
		xfprintf(xstderr, catgets(catd, 2, 8,
437
					" saved %d lines of file \"%s\"\n"),
438
		    fp->sf_lines, fp->sf_name);
439
	}
440
}
441
 
442
/*
443
 * Enter a new file into the saved file information.
444
 */
445
void
446
enter(struct svfile *fp, char *fname, int count)
447
{
448
	register char *cp, *cp2;
449
	register struct svfile *f, *fl;
450
	time_t curtime, itol();
451
 
452
	f = 0;
453
	if (count >= NENTRY) {
454
		/*
455
		 * My god, a huge number of saved files.
456
		 * Would you work on a system that crashed this
457
		 * often?  Hope not.  So lets trash the oldest
458
		 * as the most useless.
459
		 *
460
		 * (I wonder if this code has ever run?)
461
		 */
462
		fl = fp - count + NENTRY - 1;
463
		curtime = fl->sf_time;
464
		for (f = fl; --f > fp-count; )
465
			if (f->sf_time < curtime)
466
				curtime = f->sf_time;
467
		for (f = fl; --f > fp-count; )
468
			if (f->sf_time == curtime)
469
				break;
470
		fp = f;
471
	}
472
 
473
	/*
474
	 * Gotcha.
475
	 */
476
	fp->sf_time = H.Time;
477
	fp->sf_lines = H.Flines;
478
	cp2 = fp->sf_name, cp = savedfile;
479
	while (*cp2++ = *cp++);
480
	for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;)
481
		*cp2++ = *cp++;
482
	*cp2++ = 0;
483
}
484
 
485
/*
486
 * Do the qsort compare to sort the entries first by file name,
487
 * then by modify time.
488
 */
489
int
490
qucmp(struct svfile *p1, struct svfile *p2)
491
{
492
	register int t;
493
 
494
	if (t = strcmp(p1->sf_name, p2->sf_name))
495
		return(t);
496
	if (p1->sf_time > p2->sf_time)
497
		return(-1);
498
	return(p1->sf_time < p2->sf_time);
499
}
500
 
501
/*
502
 * Scratch for search.
503
 */
504
char	bestnb[BUFSIZ];		/* Name of the best one */
505
long	besttime;		/* Time at which the best file was saved */
506
int	bestfd;			/* Keep best file open so it dont vanish */
507
 
508
/*
509
 * Look for a file, both in the users directory option value
510
 * (i.e. usually /tmp) and in /usr/preserve.
511
 * Want to find the newest so we search on and on.
512
 */
513
void
514
findtmp(char *dir)
515
{
516
 
517
	/*
518
	 * No name or file so far.
519
	 */
520
	bestnb[0] = 0;
521
	bestfd = -1;
522
 
523
	/*
524
	 * Search /usr/preserve and, if we can get there, /tmp
525
	 * (actually the users "directory" option).
526
	 */
527
	searchdir(dir);
528
	if (chdir(mydir) == 0)
529
		searchdir(mydir);
530
	if (bestfd != -1) {
531
		/*
532
		 * Gotcha.
533
		 * Put the file (which is already open) in the file
534
		 * used by the temp file routines, and save its
535
		 * name for later unlinking.
536
		 */
537
		tfile = bestfd;
538
		strcpy(nb, bestnb);
539
		ignorl(lseek(tfile, (off_t) 0, SEEK_SET));
540
 
541
		/*
542
		 * Gotta be able to read the header or fall through
543
		 * to lossage.
544
		 */
545
		if (read(tfile, (char *) &H, sizeof H) == sizeof H)
546
			return;
547
	}
548
 
549
	/*
550
	 * Extreme lossage...
551
	 */
552
	error(catgets(catd, 2, 9, " File not found"), 0);
553
}
554
 
555
/*
556
 * Search for the file in directory dirname.
557
 *
558
 * Don't chdir here, because the users directory
559
 * may be ".", and we would move away before we searched it.
560
 * Note that we actually chdir elsewhere (because it is too slow
561
 * to look around in /usr/preserve without chdir'ing there) so we
562
 * can't win, because we don't know the name of '.' and if the path
563
 * name of the file we want to unlink is relative, rather than absolute
564
 * we won't be able to find it again.
565
 */
566
void
567
searchdir(char *dirname)
568
{
569
	struct dirent *dirent;
570
	register DIR *dir;
571
	/* char dbuf[BUFSIZ]; */
572
 
573
	dir = opendir(dirname);
574
	if (dir == NULL)
575
		return;
576
	while ((dirent = readdir(dir)) != NULL) {
577
		if (dirent->d_name[0] != 'E')
578
			continue;
579
		/*
580
		 * Got a file in the directory starting with E...
581
		 * Save a consed up name for the file to unlink
582
		 * later, and check that this is really a file
583
		 * we are looking for.
584
		 */
585
		ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent->d_name));
586
		if (yeah(nb)) {
587
			/*
588
			 * Well, it is the file we are looking for.
589
			 * Is it more recent than any version we found before?
590
			 */
591
			if (H.Time > besttime) {
592
				/*
593
				 * A winner.
594
				 */
595
				ignore(close(bestfd));
596
				bestfd = dup(tfile);
597
				besttime = H.Time;
598
				strcpy(bestnb, nb);
599
			}
600
			/*
601
			 * Count versions so user can be told there are
602
			 * ``yet more pages to be turned''.
603
			 */
604
			vercnt++;
605
		}
606
		ignore(close(tfile));
607
	}
608
	ignore(closedir(dir));
609
}
610
 
611
/*
612
 * Given a candidate file to be recovered, see
613
 * if its really an editor temporary and of this
614
 * user and the file specified.
615
 */
616
int
617
yeah(char *name)
618
{
619
 
620
	tfile = open(name, O_RDWR);
621
	if (tfile < 0)
622
		return (0);
623
	if (read(tfile, (char *) &H, sizeof H) != sizeof H) {
624
nope:
625
		ignore(close(tfile));
626
		return (0);
627
	}
628
	if (strcmp(savedfile, file))
629
		goto nope;
630
	if (getuid() != H.Uid)
631
		goto nope;
632
	/*
633
	 * This is old and stupid code, which
634
	 * puts a word LOST in the header block, so that lost lines
635
	 * can be made to point at it.
636
	 */
637
	ignorl(lseek(tfile, (off_t) (BUFSIZ*HBLKS-8), SEEK_SET));
638
	ignore(write(tfile, "LOST", 5));
639
	return (1);
640
}
641
 
642
int
643
preserve(void)
644
{
645
	return 0;
646
}
647
 
648
/*
649
 * Find the true end of the scratch file, and ``LOSE''
650
 * lines which point into thin air.  This lossage occurs
651
 * due to the sandbagging of i/o which can cause blocks to
652
 * be written in a non-obvious order, different from the order
653
 * in which the editor tried to write them.
654
 *
655
 * Lines which are lost are replaced with the text LOST so
656
 * they are easy to find.  We work hard at pretty formatting here
657
 * as lines tend to be lost in blocks.
658
 *
659
 * This only seems to happen on very heavily loaded systems, and
660
 * not very often.
661
 */
662
void
663
scrapbad(void)
664
{
665
	register line *ip;
666
	struct stat stbuf;
667
	off_t size, maxt;
668
	bbloc bno, cnt = 0, bad, was;
669
	char bk[BUFSIZ];
670
 
671
	ignore(fstat(tfile, &stbuf));
672
	size = stbuf.st_size;
673
	maxt = (size >> SHFT) | (BNDRY-1);
674
	bno = (maxt >> OFFBTS) & BLKMSK;
675
#ifdef DEBUG
676
	xfprintf(xstderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno);
677
#endif
678
 
679
	/*
680
	 * Look for a null separating two lines in the temp file;
681
	 * if last line was split across blocks, then it is lost
682
	 * if the last block is.
683
	 */
684
	while (bno > 0) {
685
		ignorl(lseek(tfile, (off_t) (BUFSIZ * (bno & BLKMSK)),
686
					SEEK_SET));
687
		cnt = read(tfile, (char *) bk, BUFSIZ);
688
		while (cnt > 0)
689
			if (bk[--cnt] == 0)
690
				goto null;
691
		bno--;
692
	}
693
null:
694
 
695
	/*
696
	 * Magically calculate the largest valid pointer in the temp file,
697
	 * consing it up from the block number and the count.
698
	 */
699
	maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1;
700
#ifdef DEBUG
701
	xfprintf(xstderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt);
702
#endif
703
 
704
	/*
705
	 * Now cycle through the line pointers,
706
	 * trashing the Lusers.
707
	 */
708
	was = bad = 0;
709
	for (ip = one; ip <= dol; ip++)
710
		if (*ip > maxt) {
711
#ifdef DEBUG
712
			xfprintf(xstderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt);
713
#endif
714
			if (was == 0)
715
				was = ip - zero;
716
			*ip = ((HBLKS*BUFSIZ)-8) >> SHFT;
717
		} else if (was) {
718
			if (bad == 0)
719
				xfprintf(xstderr, catgets(catd, 2, 10,
720
						" [Lost line(s):"));
721
			xfprintf(xstderr, catgets(catd, 2, 11,
722
						" %d"), was);
723
			if ((ip - 1) - zero > was)
724
				xfprintf(xstderr, catgets(catd, 2, 12, "-%d"),
725
						(int) ((ip - 1) - zero));
726
			bad++;
727
			was = 0;
728
		}
729
	if (was != 0) {
730
		if (bad == 0)
731
			xfprintf(xstderr, catgets(catd, 2, 13,
732
						" [Lost line(s):"));
733
		xfprintf(xstderr, catgets(catd, 2, 14, " %d"), was);
734
		if (dol - zero != was)
735
			xfprintf(xstderr, catgets(catd, 2, 15,
736
						"-%d"), (int) (dol - zero));
737
		bad++;
738
	}
739
	if (bad)
740
		xfprintf(xstderr, catgets(catd, 2, 16, "]"));
741
}
742
 
743
int	cntch, cntln, cntodd, cntnull;
744
 
745
/*
746
 * Following routines stolen mercilessly from ex.
747
 */
748
void
749
putfile(int unused)
750
{
751
	line *a1;
752
	register char *fp, *lp;
753
	register int nib;
754
 
755
	a1 = addr1;
756
	clrstats();
757
	cntln = addr2 - a1 + 1;
758
	if (cntln == 0)
759
		return;
760
	nib = BUFSIZ;
761
	fp = genbuf;
762
	do {
763
		getline(*a1++);
764
		lp = linebuf;
765
		for (;;) {
766
			if (--nib < 0) {
767
				nib = fp - genbuf;
768
				if (write(io, genbuf, nib) != nib)
769
					wrerror();
770
				cntch += nib;
771
				nib = MAXBSIZE - 1 /* 511 */;
772
				fp = genbuf;
773
			}
774
			if ((*fp++ = *lp++) == 0) {
775
				fp[-1] = '\n';
776
				break;
777
			}
778
		}
779
	} while (a1 <= addr2);
780
	nib = fp - genbuf;
781
	if (write(io, genbuf, nib) != nib)
782
		wrerror();
783
	cntch += nib;
784
}
785
 
786
void
787
wrerror(void)
788
{
789
 
790
	syserror();
791
}
792
 
793
void
794
clrstats(void)
795
{
796
 
797
	ninbuf = 0;
798
	cntch = 0;
799
	cntln = 0;
800
	cntnull = 0;
801
	cntodd = 0;
802
}
803
 
804
#define	READ	0
805
#define	WRITE	1
806
 
807
void
808
getline(line tl)
809
{
810
	register char *bp, *lp;
811
	register int nl;
812
 
813
	lp = linebuf;
814
	bp = getblock(tl, READ);
815
	nl = nleft;
816
	tl &= ~OFFMSK;
817
	while (*lp++ = *bp++)
818
		if (--nl == 0) {
819
			bp = getblock(tl += INCRMT, READ);
820
			nl = nleft;
821
		}
822
}
823
 
824
char *
825
getblock(line atl, int iof)
826
{
827
	register bbloc bno, off;
828
 
829
	bno = (atl >> OFFBTS) & BLKMSK;
830
	off = (atl << SHFT) & LBTMSK;
831
	if (bno >= NMBLKS)
832
		error(catgets(catd, 2, 17, " Tmp file too large"));
833
	nleft = BUFSIZ - off;
834
	if (bno == iblock) {
835
		ichanged |= iof;
836
		return (ibuff + off);
837
	}
838
	if (bno == oblock)
839
		return (obuff + off);
840
	if (iof == READ) {
841
		if (ichanged)
842
			blkio(iblock, ibuff, (ssize_t(*)())write);
843
		ichanged = 0;
844
		iblock = bno;
845
		blkio(bno, ibuff, (ssize_t(*)())read);
846
		return (ibuff + off);
847
	}
848
	if (oblock >= 0)
849
		blkio(oblock, obuff, (ssize_t(*)())write);
850
	oblock = bno;
851
	return (obuff + off);
852
}
853
 
854
void
855
blkio(bloc b, char *buf, ssize_t (*iofcn)(int, void *, size_t))
856
{
857
 
858
	lseek(tfile, (off_t) ((b & BLKMSK) * BUFSIZ), SEEK_SET);
859
	if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
860
		syserror();
861
}
862
 
863
void
864
syserror(void)
865
{
866
 
867
	dirtcnt = 0;
868
	write(2, " ", 1);
869
	error("%s", strerror(errno));
870
	exit(1);
871
}
872
 
873
/*
874
 * Must avoid stdio because expreserve uses sbrk to do memory
875
 * allocation and stdio uses malloc.
876
 */
877
/*
878
 * I do not know whether vsprintf() uses malloc() or not.
879
 * So this may be fail, too.
880
 */
881
void
882
xvfprintf(xFILE *fp, char *fmt, va_list ap)
883
{
884
	char buf[BUFSIZ];
885
 
886
	if (fp != xstderr)
887
		return;
888
	vsprintf(buf, fmt, ap);
889
	write(2, buf, strlen(buf));
890
}
891
 
892
void
893
xfprintf(xFILE *fp, char *fmt, ...)
894
{
895
	va_list ap;
896
 
897
	if (fp != xstderr)
898
		return;
899
	va_start(ap, fmt);
900
	xvfprintf(fp, fmt, ap);
901
	va_end(ap);
902
}