1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /* Copyright (c) 1988 AT&T */
22 /* All Rights Reserved */
23 /*
24  * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
25  * Use is subject to license terms.
26  */
27 /*
28  * Copyright 2006-2020 J. Schilling
29  *
30  * @(#)filehand.c	1.8 20/09/06 J. Schilling
31  */
32 #if defined(sun)
33 #pragma ident "@(#)filehand.c 1.8 20/09/06 J. Schilling"
34 #endif
35 /*
36  * @(#)filehand.c 1.4 06/12/12
37  */
38 
39 #if defined(sun)
40 #pragma ident	"@(#)filehand.c"
41 #pragma ident	"@(#)sccs:lib/cassi/filehand.c"
42 #endif
43 
44 /* EMACS_MODES: c !fill tabstop=4 */
45 
46 /*
47  *	filehand -- Get a handle on file operations.
48  *
49  *
50  *
51  *	This file contains a number of routines which are useful in handling
52  *	files.  The operationd performed are defined in the filehand.h file,
53  *	as are the return codes.
54  */
55 
56 # include <defines.h>
57 # include <filehand.h>
58 
59 /* Debugging options. */
60 
61 # ifdef TRACE
62 extern FILE		*trace;
63 # define TR(W,X,Y,Z) fprintf (trace, W, X, Y, Z)
64 # else
65 # define TR(W,X,Y,Z) /* W X Y Z */
66 # endif
67 
68 # define READ "rb"
69 # define APP "ab"
70 # define BUFSIZE 1024
71 # define SAME 0							/* Same as strcmp. */
72 # define BEFORE 1						/* Almost same as strcmp. */
73 # define AFTER (-1)						/* Ditto. */
74 # define ERROR (-1)
75 # define OFILE1 1
76 # define OFILE2 2
77 # define ALLOC1 4
78 # define ALLOC2 8
79 # define ALLOC3 16
80 
81 # ifdef XALLOC
82 /* Use this for places where xalloc and xfree are used in the code which
83  * calls filehand routines. */
84 USXALLOC();
85 # endif
86 
87 static int ftrans	__PR((int opcode, char *file1, char *file2, char *area, int asize));
88 static int copyrest	__PR((FILE *fp1, FILE *fp2, char *place, int bsize));
89 static int getrec	__PR((FILE *file, char *buffer, int recsep, int rectype, int maxlen));
90 static int rec_cmp	__PR((char **args, char **fmatch, int recsep));
91 static int argchop	__PR((char *s, int fldsep, char **sargs));
92 
93 /*
94  *	ftrans -- Handle routine file transfer operations.
95  */
96 
97 static int
ftrans(opcode,file1,file2,area,asize)98 ftrans (opcode, file1, file2, area, asize)
99 int	opcode;				/* Choices are in filehand.h. */
100 char	*file1, *file2,			/* The files to handle. */
101 	*area;				/* Buffer area pointer. */
102 int	asize;				/* Of buffer area. */
103 {
104 	FILE	*fp1, *fp2;
105 	int		retcode;
106 
107 	TR("Ftrans: %d (%s) (%s)\n", opcode, file1, file2);
108 	TR("Ftrans: area=%d size=%d\n", area, asize, EMPTY);
109 
110 	if ((opcode == RENAME || opcode == CPY) &&
111 	  (fp2 = fopen (file2, READ)) != NULL) {
112 		if (fclose (fp2) == ERROR)
113 			return (RECURSE + DESTEXISTS);
114 		return (DESTEXISTS);
115 		}
116 
117 	if ((opcode == MOVE || opcode == COPYOVER)
118 	  && (fp2 = fopen (file2, READ)) != NULL) {
119 		if (fclose (fp2) == ERROR)
120 			return (RECURSE + LIVEDEST);
121 		if (unlink (file2) == ERROR)
122 			return (LIVEDEST);
123 		}
124 
125 	if ((fp1 = fopen (file1, READ)) == NULL)
126 		return (NOSOURCE);
127 
128 	if ((fp2 = fopen (file2, APP)) == NULL) {
129 		if (fclose (fp1) == ERROR)
130 			return (RECURSE + NODEST);
131 		return (NODEST);
132 		}
133 
134 	retcode = copyrest (fp1, fp2, area, asize);
135 	if (fclose (fp1) == ERROR)
136 		retcode = LIVESRC;
137 	if (fclose (fp2) == ERROR)
138 		retcode = NOCLOSE;
139 	if (retcode != DONE)
140 		return (retcode);
141 	if ((opcode == MOVE || opcode == RENAME) && unlink (file1) == ERROR)
142 		return (LIVESRC);
143 	TR("Ftrans: normal return\n", EMPTY, EMPTY, EMPTY);
144 	return (DONE);
145 }
146 
147 static int
copyrest(fp1,fp2,place,bsize)148 copyrest (fp1, fp2, place, bsize)		/* Copy the rest of a file. */
149 FILE	*fp1, *fp2;				/* Use these file pointers. */
150 char	*place;					/* A buffer to use. */
151 int	bsize;					/* Size of buffer. */
152 {
153 	char	*space;				/* Area being used. */
154 	unsigned count;						/* Of bytes read. */
155 	int		retcode = DONE;
156 
157 	TR("Copyrest: fp1=%d fp2=%d place=%d ", fp1, fp2, place);
158 	TR("size=%d\n", bsize, EMPTY, EMPTY);
159 
160 	if (place == EMPTY) {
161 		if (bsize <= 0)
162 			bsize = BUFSIZE;
163 		if ((space = (char *) malloc ((unsigned) bsize)) == EMPTY) {
164 			TR("Copyrest: no space\n", EMPTY, EMPTY, EMPTY);
165 			return (NOSPACE);
166 			}
167 		}
168 	else {
169 		if (bsize <= 0)
170 			return (BADSIZE);
171 		space = place;
172 		}
173 
174 	while ((count = fread (space, sizeof (char), (unsigned) bsize, fp1)) >
175 	  (unsigned) 0)
176 		if (fwrite (space, sizeof (char), count, fp2) != count) {
177 			retcode = COPYERROR;
178 			break;
179 			}
180 
181 	if (place == EMPTY)
182 		(void) free (space);
183 	TR("Copyrest: returns %d\n", retcode, EMPTY, EMPTY);
184 	return (retcode);
185 }
186 
187 static int
getrec(file,buffer,recsep,rectype,maxlen)188 getrec (file, buffer, recsep, rectype, maxlen)
189 FILE	*file;					/* The open file to read from. */
190 char	buffer[],				/* The buffer to read into. */
191 	recsep;					/* The character to break on. */
192 int	rectype,				/* FIXED or VARYING length. */
193 	maxlen;					/* Maximum record length. */
194 {
195 	int		count;						/* Of characters read. */
196 	int	lc;		/* must be int to hold EOF */
197 
198 	TR("Getrec: file=%d recsep=(%c) maxlen=%d", file, recsep, maxlen);
199 	TR(" rectype=%d buffer=%d\n", rectype, buffer, EMPTY);
200 
201 	if (rectype == FIXED) {
202 		TR("Getrec: FIXED record size\n", EMPTY, EMPTY, EMPTY);
203 		count = fread (buffer, sizeof (char), (unsigned) maxlen, file);
204 		if (count == 0)
205 			return (FILE1EOF);
206 		else if (count == maxlen)
207 			return (DONE);
208 		else
209 			return (SHORTREC);
210 		}
211 	else if (rectype == VARIED) {
212 		TR("Getrec: VARIED record size\n", EMPTY, EMPTY, EMPTY);
213 		count = 0;
214 		while ((lc = fgetc (file)) != EOF) {
215 			if ((char) lc == recsep) {
216 				buffer[count] = '\0';
217 				TR("Getrec: returns buffer=(%s)\n", buffer, EMPTY, EMPTY);
218 				return (DONE);
219 				}
220 			else {
221 				buffer[count++] = (char) lc;
222 				if (count >= maxlen)
223 					return (BIGREC);
224 				}
225 			}
226 		TR("Getrec: end of file\n", EMPTY, EMPTY, EMPTY);
227 		buffer[count] = '\0';
228 		return (FILE1EOF);
229 		}
230 	else
231 		return (BADTYPE);				/* Unknown record type. */
232 }
233 
234 static int
rec_cmp(args,fmatch,recsep)235 rec_cmp (args, fmatch, recsep)			/* Compare a record with fmatch. */
236 char	*args[], *fmatch[], recsep;
237 {
238 	int		i,							/* For looping. */
239 			cmpstat;					/* Comparison value. */
240 
241 	cmpstat = SAME;
242 	for (i = 0; fmatch[i] != EMPTY; i++) {
243 		cmpstat = strcmp (fmatch[i], args[i]);
244 		if (fmatch[i][0] == recsep || cmpstat == 0)
245 			cmpstat = SAME;
246 		else if (cmpstat > 0) {
247 			cmpstat = BEFORE;			/* fmatch must follow args. */
248 			/* That is, we could still match. */
249 			break;
250 			}
251 		else {
252 			cmpstat = AFTER;
253 			break;						/* The for loop. */
254 			}
255 		}
256 	TR("Rec_cmp: returns %d\n", cmpstat, EMPTY, EMPTY);
257 	return (cmpstat);
258 }
259 
260 static int
argchop(s,fldsep,sargs)261 argchop (s, fldsep, sargs)		/* Seperate s into its fields. */
262 char	*s,				/* The string to separate. */
263 	fldsep,				/* Seperates the fields. */
264 	**sargs;			/* Pointers to the fields. */
265 {
266 	int		i = 0,						/* For looping. */
267 			subs = 0;					/* Counter of fields. */
268 
269 	TR("Argchop: s=(%s) fldsep=(%c) sargs=%d\n", s, fldsep, sargs);
270 
271 	while (s[i] == fldsep || (fldsep == WHITE && (s[i] == ' '
272 	  || s[i] == '\t')))
273 		s[i++] = '\0';					/* Skip initial blank fields. */
274 
275 	while (s[i] != '\0') {
276 		if (s[i] == fldsep || (fldsep == WHITE && (s[i] == ' '
277 		  || s[i] == '\t')))
278 			s[i] = '\0';				/* Insert a string terminater. */
279 		else if ((i > 0 && s[i - 1] == '\0') || i == 0)
280 			sargs[subs++] = &s[i];		/* An argument begins here. */
281 		i++;
282 		}
283 	sargs[subs] = EMPTY;
284 # ifdef TRACE							/* Echo the fields. */
285 	for (i = 0; sargs[i] != EMPTY; i++)
286 		TR(".. %d (%s)\n", i, sargs[i], EMPTY);
287 # endif
288 	TR("Argchop: normal return\n", EMPTY, EMPTY, EMPTY);
289 	return (DONE);
290 }
291 
292 /*
293  *	sweep -- One shot file modification.  Execute the request and exit.
294  */
295 
296 int
sweep(opcode,file1,file2,recsep,fldsep,maxlen,fmatch,usrbuf,usrmatch,chop,comparef)297 sweep (opcode, file1, file2, recsep, fldsep, maxlen, fmatch, usrbuf, usrmatch,
298   chop, comparef)
299 int		opcode;							/* The operation to perform. */
300 char	*file1,							/* The search/source file. */
301 		*file2,							/* The scratch file.  (Required.) */
302 		recsep,							/* Record separator. */
303 		fldsep;							/* Field separator. */
304 int		maxlen;							/* Maximum length of record. */
305 char	*fmatch[],						/* Fields to match. */
306 		*usrbuf,						/* User's copy of matching record. */
307 		*usrmatch[];					/* Pointers to arguments. */
308 int		(*chop) __PR((char *, int, char **)),		/* Routine to separate the fields. */
309 		(*comparef) __PR((char **, char **, int));	/* Routine to compare records. */
310 {
311 	char	**arg = NULL,					/* Pointers to the fields. */
312 		*realrec = NULL,				/* A copy of what was actually read. */
313 		*hackrec = NULL;				/* A place to keep the fields. */
314 
315 	int		cmpstat,					/* Comparison status. */
316 			retcode,					/* Return code. */
317 			action = 0,					/* Record of what we have done. */
318 			rectype,					/* FIXED or VARIED record size. */
319 			i, j;						/* Looping variable. */
320 
321 	FILE	*fp1, *fp2 = NULL;				/* Pointers to file1 and file2. */
322 
323 	TR("Sweep: opcode=%d file1=(%s) file2=(%s)\n", opcode, file1, file2);
324 	TR(".. recsep=(%c) fldsep=(%c) maxlen=%d ", recsep, fldsep, maxlen);
325 	TR("usrbuf=%d usrmatch=%d chop=%d\n", usrbuf, usrmatch, chop);
326 	TR(".. usrbuf=(%s) compare=%d\n", usrbuf, comparef, EMPTY);
327 #ifdef TRACE
328 	TR(".. fmatch ..\n", EMPTY, EMPTY, EMPTY);
329 	for (i = 0; fmatch[i] != EMPTY; i++)
330 		TR("%d (%s)\n", i, fmatch[i], EMPTY);
331 #endif
332 
333 	if ((fp1 = fopen (file1, READ)) == NULL) {
334 		if (opcode == PUTNOW || (opcode & INSERT) > 0) {
335 			if ((fp1 = fopen (file1, APP)) == NULL)
336 				return (NOSOURCE);
337 			if (fclose (fp1) == ERROR)
338 				return (NOSOURCE);
339 			if ((fp1 = fopen (file1, READ)) == NULL)
340 				return (NOSOURCE);
341 			TR("Sweep: made a blank file1\n", EMPTY, EMPTY, EMPTY);
342 			opcode = PUTNOW;			/* Since buffers are unneeded. */
343 			}
344 		else
345 			return (NOSOURCE);
346 		}
347 
348 	if ((opcode & VERIFY) == 0) {
349 		if ((fp2 = fopen (file2, READ)) != NULL) {
350 			if (fclose (fp2) == ERROR || unlink (file2) == ERROR) {
351 				if (fclose (fp1) == ERROR)
352 					return (RECURSE + LIVEDEST);
353 				return (LIVEDEST);
354 				}
355 			}
356 		if ((fp2 = fopen (file2, APP)) == NULL) {
357 			if (fclose (fp1) == ERROR)
358 				return (RECURSE + NODEST);
359 			return (NODEST);
360 			}
361 		action += OFILE2;				/* Flag file2 as open. */
362 		}
363 
364 	action += OFILE1;					/* Flag file1 as open. */
365 
366 	TR("Sweep: file1%s open\n", ((action & OFILE2) > 0) ? " and file2" : "",
367 	  EMPTY, EMPTY);
368 
369 	if (opcode == PUTNOW)
370 		goto begin;						/* Because we need no allocations. */
371 
372 	/* At this point, we begin allocating the three areas that  this routine
373 	 * needs.  realrec will contain a copy of what was really read.  hackrec
374 	 * will provide space to store its individual fields.  arg will  provide
375 	 * space for the pointers the those fields.  The general scheme used for
376 	 * allocation and use of these areas is as follows:
377 	 *
378 	 *			Given:	usrbuf		--			usrbuf		--
379 	 *					usrmatch	usrmatch	--			--
380 	 *			---------------------------------------------------
381 	 *			arg		usrmatch/1	usrmatch/2	ALLOC3		ALLOC3
382 	 *			---------------------------------------------------
383 	 *			realrec	ALLOC1		ALLOC1		usrbuf/3	ALLOC1
384 	 *			---------------------------------------------------
385 	 *			hackrec	usrbuf/4	ALLOC2		ALLOC2		ALLOC2
386 	 *			---------------------------------------------------
387 	 *
388 	 * Note 1: On DELETE this  array will hold pointers to the actual fields
389 	 *  of the DELETED record.  On INSERT and REPLACE, it will hold pointers
390 	 *  into usrbuf which will be MEANINGLESS.  On VERIFY, it will also hold
391 	 *	pointers, but they are to the matched record.  On PUTNOW, this array
392 	 *	is unaffected.
393 	 *
394 	 * Note 2: On all operations, the pointers in this array will be useless
395 	 *	upon return.
396 	 *
397 	 * Note 3: On DELETE and VERIFY, this array will hold the actual  record
398 	 *	deleted or matched.  On INSERT or REPLACE, this record will hold the
399 	 *	actual  record  inserted  or  replaced (and is not used internally).
400 	 *	PUTNOW has no effect.
401 	 *
402 	 * Note 4: On DELETE and VERIFY, this array will hold the actual  record
403 	 *	deleted or matched.  On INSERT and REPLACE, this array will hold the
404 	 *	actual record inserted or replaced (in which case, the specification
405 	 *	of usrmatch will put only junk into that array, as in note 1).  This
406 	 *	area is not used internally for an INSERT or REPLACE.  This array is
407 	 *	not affected by PUTNOW.
408 	 */
409 
410 	if (maxlen <= 0) {
411 		retcode = BADSIZE;
412 		goto bye;
413 		}
414 
415 	retcode = NOSPACE;					/* All errors will be for this. */
416 
417 	if (usrbuf == EMPTY) {				/* We must allocate a buffer. */
418 		TR("Sweep: no usrbuf\n", EMPTY, EMPTY, EMPTY);
419 		if ((realrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
420 			goto bye;
421 		action += ALLOC1;				/* A buffer was allocated. */
422 		TR("Sweep: allocated (1) realrec=%d\n", realrec, EMPTY, EMPTY);
423 		if ((hackrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
424 			goto bye;
425 		action += ALLOC2;
426 		TR("Sweep: allocated (2) hackrec=%d\n", hackrec, EMPTY, EMPTY);
427 		if (usrmatch == (char**) NULL) { /* User provided no field pointers. */
428 			j = (maxlen / 2 + 1) * sizeof (char*);
429 			if ((arg = (char**) malloc ((unsigned) j)) == (char**) NULL)
430 				goto bye;
431 			action += ALLOC3;			/* Pointer space was allocated. */
432 			TR("Sweep: allocated (3) arg=%d\n", arg, EMPTY, EMPTY);
433 			}
434 		else
435 			arg = usrmatch;
436 		}
437 
438 	else {								/* A usrbuf area was specified */
439 		TR("Sweep: usrbuf given\n", EMPTY, EMPTY, EMPTY);
440 		if (usrmatch == (char**) NULL) {
441 			TR("Sweep: no usrmatch\n", EMPTY, EMPTY, EMPTY);
442 			if ((opcode & REPLACE) > 0 || (opcode & INSERT) > 0) {
443 				/* Alas, we cannot use the area. */
444 				if ((realrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
445 					goto bye;
446 				action += ALLOC1;
447 				TR("Sweep: allocated (4) realrec=%d\n", realrec, EMPTY, EMPTY);
448 				}
449 			else
450 				realrec = usrbuf;
451 			if ((hackrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
452 				goto bye;
453 			action += ALLOC2;
454 			TR("Sweep: allocated (5) hackrec=%d\n", hackrec, EMPTY, EMPTY);
455 			j = (maxlen / 2 + 1) * sizeof (char*);
456 			if ((arg = (char**) malloc ((unsigned) j)) == (char**) NULL)
457 				goto bye;
458 			action += ALLOC3;
459 			TR("Sweep: allocated (6) arg=%d\n", arg, EMPTY, EMPTY);
460 			}
461 		else {							/* We have usrbuf and usrmatch. */
462 			if ((opcode & REPLACE) > 0 || (opcode & INSERT) > 0) {
463 				if ((hackrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
464 					goto bye;
465 				action += ALLOC2;
466 				TR("Sweep: allocated (7) hackrec=%d\n", hackrec, EMPTY, EMPTY);
467 				}
468 			else
469 				hackrec = usrbuf;
470 			arg = usrmatch;
471 			if ((realrec = (char *) malloc ((unsigned) maxlen)) == EMPTY)
472 				goto bye;
473 			action += ALLOC1;
474 			TR("Sweep: allocated (8) realrec=%d\n", realrec, EMPTY, EMPTY);
475 			}
476 		}
477 
478 begin:
479 
480 	if (opcode == PUTNOW) {				/* Insert a message at beginning. */
481 		if ((action & OFILE2) > 0) {
482 			fprintf (fp2, "%s%c", usrbuf, recsep);
483 			retcode = DONE;
484 			goto bye;
485 			}
486 		else {
487 			retcode = NOTPUT;
488 			goto bye;
489 			}
490 		}
491 
492 	if (recsep == 0)
493 		rectype = FIXED;
494 	else
495 		rectype = VARIED;
496 
497 	while ((i = getrec (fp1, realrec, recsep, rectype, maxlen)) == DONE) {
498 
499 		/* Copy the buffer */
500 		strncpy (hackrec, realrec, (unsigned) maxlen);
501 
502 		/* Seperate the command into arguments. */
503 		if (chop == (int (*) __PR((char *, int, char **))) NULL)
504 			i = argchop (hackrec, fldsep, arg);
505 		else
506 			i = (*chop) (hackrec, fldsep, arg);
507 		if (i != DONE)
508 			break;						/* Something funny in record. */
509 
510 		/* Now, determine the alphabetic status of this record. */
511 		if (comparef == (int (*) __PR((char **, char **, int))) NULL)
512 			cmpstat = rec_cmp (arg, fmatch, recsep);
513 		else
514 			cmpstat = (*comparef) (arg, fmatch, recsep);
515 
516 		/* Now, decide to continue or not. */
517 		if ((opcode & SEQUENTIAL) == 0 && cmpstat != SAME)
518 			continue;
519 			/* We continue if the search is not sequential and the record
520 			 * did not match. */
521 
522 		if (cmpstat == SAME) {
523 			TR("Sweep: Enter SAME section\n", EMPTY, EMPTY, EMPTY);
524 			if ((opcode & INSERT) > 0)
525 				retcode = NOTNEW;
526 			else if ((opcode & VERIFY) > 0)
527 				retcode = FOUND;
528 			else {
529 				if ((opcode & REPLACE) > 0 && usrbuf != EMPTY)
530 					fprintf (fp2, "%s%c", usrbuf, recsep);
531 					/* And if this was a DELETE, nothing is printed. */
532 				retcode = DONE;
533 				}
534 			goto bye;
535 			}
536 
537 		else if (cmpstat == BEFORE) {
538 			TR("Sweep: Enter BEFORE section\n", EMPTY, EMPTY, EMPTY);
539 			/* Put the record into the "scratch" file. */
540 			if ((opcode & VERIFY) == 0)
541 				fprintf (fp2, "%s%c", realrec, recsep);
542 			}
543 
544 		else {							/* cmpstat == AFTER. */
545 			TR("Sweep: enter AFTER section\n", EMPTY, EMPTY, EMPTY);
546 			/* Match goes (or should have come) before this one. */
547 			if (opcode == SEQINSERT) {
548 				if (usrbuf != EMPTY)
549 					fprintf (fp2, "%s%c", usrbuf, recsep);
550 				/* Put in the INSERTed one, then replace the old one. */
551 				fprintf (fp2, "%s%c", realrec, recsep);
552 				retcode = DONE;
553 				}
554 			else
555 				/* We never get here on non-sequential searches. */
556 				retcode = NOTFOUND;
557 			goto bye;
558 			}
559 		}								/* End of while loop. */
560 	TR("Sweep: end of record loop\n", EMPTY, EMPTY, EMPTY);
561 
562 	if (i != FILE1EOF) {
563 		TR("Sweep: funny death\n", EMPTY, EMPTY, EMPTY);
564 		retcode = i;					/* So user knows about bad data. */
565 		goto bye;
566 		}
567 
568 	if (fclose (fp1) == ERROR)
569 		retcode = NOCLOSE;
570 	action -= OFILE1;
571 
572 	if (((opcode & VERIFY) | (opcode & REPLACE) | (opcode & DELETE)) > 0) {
573 		TR("Sweep: not there\n", EMPTY, EMPTY, EMPTY);
574 		if ((opcode & VERIFY) == 0) {
575 			if (fclose (fp2) == ERROR)
576 				retcode = RESETERR;
577 			action -= OFILE2;
578 			if (unlink (file2) == ERROR) {
579 				retcode = RESETERR;		/* Should never happen? */
580 				goto bye;
581 				}
582 			}
583 		retcode = NOTFOUND;
584 		goto bye;
585 		}
586 
587 	if ((opcode & INSERT) > 0) {
588 		TR("Sweep: insert tail record\n", EMPTY, EMPTY, EMPTY);
589 		if (usrbuf != EMPTY)
590 			fprintf (fp2, "%s%c", usrbuf, recsep);
591 		retcode = DONE;
592 		}
593 	else
594 		retcode = ABEND;				/* Should never happen? */
595 
596 bye:
597 
598 	TR("Sweep: closing retcode=%d action=%d\n", retcode, action, EMPTY);
599 	if (retcode == DONE) {				/* Successful on a file update. */
600 		if ((action & OFILE1) > 0 && (action & OFILE2) > 0)
601 			retcode = copyrest (fp1, fp2, EMPTY, BUFSIZE);
602 		}
603 
604 	if ((action & OFILE1) > 0) {
605 		if (fclose (fp1) == ERROR)
606 			retcode = NOCLOSE;
607 		TR("Sweep: file1 closed\n", EMPTY, EMPTY, EMPTY);
608 		}
609 	if ((action & OFILE2) > 0) {
610 		if (fclose (fp2) == ERROR)
611 			retcode = NOCLOSE;
612 		TR("Sweep: file2 closed\n", EMPTY, EMPTY, EMPTY);
613 		}
614 
615 	/* With the files closed, we now replace file1 with file2 (if needed). */
616 	if (retcode == DONE) {
617 		if ((retcode = ftrans (MOVE, file2, file1, EMPTY, BUFSIZE)) != DONE)
618 			retcode += RECURSE;
619 		TR("Sweep: final transfer\n", EMPTY, EMPTY, EMPTY);
620 		}
621 	if ((action & ALLOC1) > 0) {
622 		TR("Sweep: free realrec\n", EMPTY, EMPTY, EMPTY);
623 		free (realrec);
624 		}
625 	if ((action & ALLOC2) > 0) {
626 		TR("Sweep: free hackrec\n", EMPTY, EMPTY, EMPTY);
627 		free (hackrec);
628 		}
629 	if ((action & ALLOC3) > 0) {
630 		TR("Sweep: free arg\n", EMPTY, EMPTY, EMPTY);
631 		free ((char*) arg);
632 		}
633 	return (retcode);
634 }
635 
636