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