Already setup SysBase->ThisTask during PrepareExecBase.
[AROS.git] / workbench / c / Assign.c
blob61cd047b278afe741cf64f6c208f31c5a9d27c94
1 /*
3 (C) 1995-2011 The AROS Development Team
4 (C) 2002-2005 Harry Sintonen
5 (C) 2005-2007 Pavel Fedin
6 $Id$
8 Desc: Assign CLI command
9 Lang: English
12 #define AROS_ALMOST_COMPATIBLE
14 #ifdef __AROS__
15 #include <aros/asmcall.h>
16 #include <proto/arossupport.h>
17 #include <aros/debug.h>
18 #else
19 #include <clib/debug_protos.h>
20 #endif
22 #include <exec/types.h>
23 #include <exec/lists.h>
24 #include <exec/memory.h>
25 #include <exec/execbase.h>
26 #include <dos/dos.h>
27 #include <dos/dosextens.h>
28 #include <dos/exall.h>
29 #include <utility/tagitem.h>
30 #include <proto/dos.h>
31 #include <proto/exec.h>
33 #include <string.h>
35 #define DEBUG_ASSIGN(x)
37 /******************************************************************************
40 NAME
42 Assign [(name):] [{(target)}] [LIST] [EXISTS] [DISMOUNT] [DEFER]
43 [PATH] [ADD] [REMOVE] [VOLS] [DIRS] [DEVICES]
45 SYNOPSIS
47 NAME, TARGET/M, LIST/S, EXISTS/S, DISMOUNT/S, DEFER/S, PATH/S, ADD/S,
48 REMOVE/S, VOLS/S, DIRS/S, DEVICES/S
50 LOCATION
54 FUNCTION
56 ASSIGN creates a reference to a file or directory. The reference
57 is a logical device name which makes it very convenient to specify
58 assigned objects using the reference instead of their paths.
60 If the NAME and TARGET arguments are given, ASSIGN assigns the
61 given logical name to the specified target. If the NAME given is
62 already assigned to a file or directory the new target replaces the
63 previous target. A colon must be included after the NAME argument.
65 If only the NAME argument is given, any assigns to that NAME are
66 removed. If no arguments whatsoever are given, all logical
67 assigns are listed.
69 INPUTS
71 NAME -- the name that should be assigned to a file or directory
72 TARGET -- one or more files or directories to assign the NAME to
73 LIST -- list all assigns made
74 EXISTS -- display the target of the specified assign. If NAME is
75 not assigned, set the condition flag to WARN
76 DISMOUNT -- remove the volume or device NAME from the dos list
77 DEFER -- make an ASSIGN to a path or directory that not need to
78 exist at the time of assignment. The first time the
79 NAME is referenced the NAME is bound to the object
80 PATH -- path is assigned with a non-binding assign. This means
81 that the assign is re-evaluated each time a reference
82 to NAME is done. Like for DEFER, the path doesn't have
83 to exist when the ASSIGN command is executed
84 ADD -- don't replace an assign but add another object for a
85 NAME (multi-assigns)
86 REMOVE -- remove an ASSIGN
87 VOLS -- show assigned volumes if in LIST mode
88 DIRS -- show assigned directories if in LIST mode
89 DEVICES -- show assigned devices if in LIST mode
92 RESULT
94 NOTES
96 EXAMPLE
98 BUGS
100 SEE ALSO
102 INTERNALS
104 The assign command has many switches. This together with the somewhat
105 messy handling of DosList:s by dos.library makes the operation rather
106 complicated.
108 There are some fundamental building blocks that defines the semantics
109 of the Assign command.
111 Only one of the switches ADD, REMOVE, PATH and DEFER may be specified.
113 If EXISTS is specified, only the name parameter is important.
115 The implementation is split up in two fundamental procedures.
117 doAssign() -- make [a number of] assigns
118 showAssigns() -- show the available assigns
119 checkAssign() -- check if a particular assign exists
121 ******************************************************************************/
123 #ifndef __AROS__
124 #define AROS_BSTR_strlen(s) *((UBYTE *)BADDR(s))
125 #endif
127 #ifdef __MORPHOS__
128 #define AROS_ASMSYMNAME(s) (&s)
130 static const int __abox__ = 1;
131 static const char version[] = "\0$VER: Assign unofficial 50.9 (18.10.07) © AROS" ;
132 #else
133 static const char version[] = "\0$VER: Assign 50.9 (18.10.07) © AROS" ;
134 #endif
136 struct localdata
138 struct ExecBase *ld_SysBase;
139 struct DosLibrary *ld_DOSBase;
140 struct MinList ld_DeferList;
143 /* FIXME: Remove these #define xxxBase hacks
144 Do not use this in new code !
146 #define SysBase ld->ld_SysBase
147 #define DOSBase ld->ld_DOSBase
148 #define DeferList ld->ld_DeferList
151 /* Prototypes */
153 static int Main(struct ExecBase *sBase);
154 static int checkAssign(struct localdata *ld, STRPTR name);
155 static int doAssign(struct localdata *ld, STRPTR name, STRPTR *target, BOOL dismount, BOOL defer, BOOL path,
156 BOOL add, BOOL remove);
157 static void showAssigns(struct localdata *ld, BOOL vols, BOOL dirs, BOOL devices);
158 static int removeAssign(struct localdata *ld, STRPTR name);
159 static STRPTR GetFullPath(struct localdata *ld, BPTR lock);
161 static void _DeferPutStr(struct localdata *ld, CONST_STRPTR str);
162 static void _DeferVPrintf(struct localdata *ld, CONST_STRPTR fmt, IPTR *args);
163 static void _DeferFlush(struct localdata *ld, BPTR fh);
165 #define DeferPutStr(str) _DeferPutStr(ld,str)
166 #define DeferPrintf(fmt,...) \
167 DEBUG_ASSIGN(kprintf(fmt, __VA_ARGS__);) \
168 do { IPTR __args[] = {0 , AROS_PP_VARIADIC_CAST2IPTR(__VA_ARGS__) }; \
169 _DeferVPrintf(ld, fmt, __args + 1); } while (0)
170 #define DeferFlush(fh) _DeferFlush(ld,fh)
173 static
174 const UBYTE template[] =
175 "NAME,"
176 "TARGET/M,"
177 "LIST/S,"
178 "EXISTS/S,"
179 "DISMOUNT/S,"
180 "DEFER/S,"
181 "PATH/S,"
182 "ADD/S,"
183 "REMOVE/S,"
184 "VOLS/S,"
185 "DIRS/S,"
186 "DEVICES/S";
188 struct ArgList
190 STRPTR name;
191 STRPTR *target;
192 IPTR list;
193 IPTR exists;
194 IPTR dismount;
195 IPTR defer;
196 IPTR path;
197 IPTR add;
198 IPTR remove;
199 IPTR vols;
200 IPTR dirs;
201 IPTR devices;
204 AROS_ENTRY(__startup static int, Start,
205 AROS_UFHA(char *, argstr, A0),
206 AROS_UFHA(ULONG, argsize, D0),
207 struct ExecBase *, sBase)
209 AROS_USERFUNC_INIT
210 return Main(sBase);
211 AROS_USERFUNC_EXIT
214 static int Main(struct ExecBase *sBase)
216 struct localdata _ld, *ld = &_ld;
217 struct RDArgs *readarg;
218 struct ArgList arglist;
219 struct ArgList *MyArgList = &arglist;
220 int error = RETURN_OK;
222 SysBase = sBase;
223 DOSBase = (struct DosLibrary *) OpenLibrary("dos.library",37);
224 if (DOSBase)
226 memset(&arglist, 0, sizeof(arglist));
228 NEWLIST(&DeferList);
230 readarg = ReadArgs(template, (IPTR *)MyArgList, NULL);
231 if (readarg)
233 /* Verify mutually exclusive args
235 if ((MyArgList->add!=0) + (MyArgList->remove!=0) + (MyArgList->path!=0) + (MyArgList->defer!=0) > 1)
237 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
238 FreeArgs(readarg);
239 CloseLibrary((struct Library *) DOSBase);
240 return RETURN_FAIL;
243 /* Check device name
245 if (MyArgList->name)
247 char *pos;
249 /* Correct assign name construction? The rule is that the device name
250 * should end with a colon at the same time as no other colon may be
251 * in the name.
253 pos = strchr(MyArgList->name, ':');
254 if (!pos || pos[1])
256 Printf("Invalid device name %s\n", (IPTR)MyArgList->name);
257 FreeArgs(readarg);
258 CloseLibrary((struct Library *) DOSBase);
259 return RETURN_FAIL;
263 /* If the EXISTS keyword is specified, we only care about NAME */
264 if (MyArgList->exists)
266 error = checkAssign(ld, MyArgList->name);
267 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error));
269 else if (MyArgList->name)
271 /* If a NAME is specified, our primary task is to add or
272 remove an assign */
274 error = doAssign(ld, MyArgList->name, MyArgList->target, MyArgList->dismount, MyArgList->defer,
275 MyArgList->path, MyArgList->add, MyArgList->remove);
276 DEBUG_ASSIGN(Printf("doassign error %ld\n",error));
277 if (MyArgList->list)
279 /* With the LIST keyword, the current assigns will be
280 displayed also when (after) making an assign */
282 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
285 else
287 /* If no NAME was given, we just show the current assigns
288 as specified by the user (VOLS, DIRS, DEVICES) */
290 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
293 FreeArgs(readarg);
296 CloseLibrary((struct Library *) DOSBase);
299 DEBUG_ASSIGN(Printf("error %ld\n", error));
301 return error;
305 static
306 void showAssigns(struct localdata *ld, BOOL vols, BOOL dirs, BOOL devices)
308 ULONG lockBits = LDF_READ;
309 struct DosList *dl;
311 /* If none of the parameters are specified, everything should be
312 displayed */
313 if (!(vols || dirs || devices))
315 vols = TRUE;
316 dirs = TRUE;
317 devices = TRUE;
320 lockBits |= vols ? LDF_VOLUMES : 0;
321 lockBits |= dirs ? LDF_ASSIGNS : 0;
322 lockBits |= devices ? LDF_DEVICES : 0;
324 dl = LockDosList(lockBits);
326 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
327 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
330 if (vols)
332 struct DosList *tdl = dl;
334 DeferPutStr("Volumes:\n");
336 /* Print all mounted volumes */
337 while ((tdl = NextDosEntry(tdl, LDF_VOLUMES)))
339 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
343 if (dirs)
345 struct DosList *tdl = dl;
346 int count;
348 DeferPutStr("\nDirectories:\n");
350 /* Print all assigned directories */
351 while ((tdl = NextDosEntry(tdl, LDF_ASSIGNS)))
353 DeferPrintf("%b ", tdl->dol_Name);
355 for (count = 14 - AROS_BSTR_strlen(tdl->dol_Name); count > 0; count--)
357 DeferPutStr(" ");
360 switch (tdl->dol_Type)
362 case DLT_LATE:
363 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
364 break;
366 case DLT_NONBINDING:
367 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
368 break;
370 default:
372 STRPTR dirName; /* For NameFromLock() */
373 struct AssignList *nextAssign; /* For multiassigns */
375 dirName = GetFullPath(ld, tdl->dol_Lock);
377 if (dirName)
379 DeferPutStr(dirName);
380 FreeVec(dirName);
382 DeferPutStr("\n");
384 nextAssign = tdl->dol_misc.dol_assign.dol_List;
386 while (nextAssign)
388 dirName = GetFullPath(ld, nextAssign->al_Lock);
390 if (dirName)
392 DeferPrintf(" + %s\n", (IPTR)dirName);
393 FreeVec(dirName);
396 nextAssign = nextAssign->al_Next;
400 break;
402 } /* while (NextDosEntry()) */
405 if (devices)
407 struct DosList *tdl = dl;
408 int count = 0; /* Used to make sure that as most 5 entries are printed per line */
410 DeferPutStr("\nDevices:\n");
412 /* Print all assigned devices */
413 while ((tdl = NextDosEntry(tdl, LDF_DEVICES)))
415 DeferPrintf("%b%lc", tdl->dol_Name, ++count % 5 ? ' ' : '\n');
418 if (count % 5)
420 DeferPutStr("\n");
424 UnLockDosList(lockBits);
426 DeferFlush(Output());
430 static
431 STRPTR GetFullPath(struct localdata *ld, BPTR lock)
433 STRPTR buf; /* Pointer to the memory allocated for the string */
434 ULONG size; /* Holder of the (growing) size of the string */
436 for (size = 512; ; size += 512)
438 buf = AllocVec(size, MEMF_ANY);
439 if (!buf)
441 break;
444 if (NameFromLock(lock, buf, size))
446 return buf;
449 FreeVec(buf);
451 if (IoErr() != ERROR_LINE_TOO_LONG)
453 break;
457 return NULL;
461 static
462 int doAssign(struct localdata *ld, STRPTR name, STRPTR *target, BOOL dismount, BOOL defer, BOOL path,
463 BOOL add, BOOL remove)
465 STRPTR colon;
466 BPTR lock = BNULL;
467 int i;
469 int error = RETURN_OK;
470 LONG ioerr = 0;
471 BOOL cancel = FALSE;
472 BOOL success = TRUE;
474 if (dismount)
476 #ifdef __AROS__
477 /* TODO: AROS currently doesn't support packet handlers directly
478 and we currently don't support shutting down IOFS handlers */
479 success = DOSFALSE;
480 ioerr = ERROR_ACTION_NOT_KNOWN;
481 #else
482 struct MsgPort *dp;
483 struct Process *tp;
485 tp=(struct Process *)FindTask(NULL);
486 tp->pr_WindowPtr = (APTR)-1;
487 dp = DeviceProc(name);
488 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp));
489 if (dp)
491 success = DoPkt(dp,ACTION_DIE,0,0,0,0,0);
492 ioerr = IoErr();
493 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success));
495 #endif
498 colon = strchr(name, ':');
500 *colon = '\0'; /* Remove trailing colon; name[] is changed! */
502 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name));
504 /* This is a little bit messy... We first remove the 'name' assign
505 * and later in the loop the target assigns.
508 if (dismount)
510 if ((!success) && (ioerr == ERROR_ACTION_NOT_KNOWN))
512 struct DosList *dl;
513 struct DosList *fdl;
515 DEBUG_ASSIGN(PutStr("Removing device node\n"));
516 dl = LockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
518 fdl = FindDosEntry(dl, name, LDF_VOLUMES | LDF_DEVICES);
520 /* Note the ! for conversion to boolean value */
521 if (fdl)
523 success = RemDosEntry(fdl);
524 if (success)
525 FreeDosEntry(fdl);
526 else
527 ioerr = ERROR_OBJECT_IN_USE;
528 } else
529 ioerr = ERROR_OBJECT_NOT_FOUND;
531 UnLockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
534 else
536 if (target == NULL || *target == NULL)
538 error = removeAssign(ld, name);
539 if (error)
541 ioerr = IoErr();
542 cancel = TRUE;
547 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
548 error = success ? error : RETURN_WARN;
549 DEBUG_ASSIGN(Printf("error: %d\n", error));
551 // The Loop over multiple targets starts here
553 if (target)
555 for (i = 0; target[i]; i++)
557 cancel = FALSE;
559 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target[i]));
560 if (!(path || defer || dismount))
562 lock = Lock(target[i], SHARED_LOCK);
564 if (!lock)
566 Printf("Can't find %s\n", (IPTR)target[i]);
567 return RETURN_FAIL;
571 if (remove)
573 if (!RemAssignList(name, lock))
575 Printf("Can't subtract %s from %s\n", (IPTR)target[i], (IPTR)name);
576 error = RETURN_FAIL;
578 UnLock(lock);
580 else if (path)
582 if (!AssignPath(name, target[i]))
584 ioerr = IoErr();
585 error = RETURN_FAIL;
586 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error));
589 else if (add)
591 if (!AssignAdd(name, lock))
593 struct DosList *dl;
595 error = RETURN_FAIL;
596 ioerr = IoErr();
597 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error));
599 /* Check if the assign doesn't exist at all. If so, create it.
600 * This fix bug id 145. - Piru
602 dl = LockDosList(LDF_ASSIGNS | LDF_READ);
603 dl = FindDosEntry(dl, name, LDF_ASSIGNS);
604 UnLockDosList(LDF_ASSIGNS | LDF_READ);
606 if (!dl)
608 if (AssignLock(name, lock))
610 error = RETURN_OK;
611 lock = BNULL;
613 else
615 ioerr = IoErr();
616 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error));
620 if (lock)
621 UnLock(lock);
623 if (error && ioerr != ERROR_OBJECT_EXISTS)
625 Printf("Can't add %s to %s\n", (IPTR)target[i], (IPTR)name);
629 else if (defer)
631 if (!AssignLate(name, target[i]))
633 ioerr = IoErr();
634 UnLock(lock);
635 error = RETURN_FAIL;
636 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error));
639 else
641 /* If no extra parameters are specified we just do a regular
642 assign (replacing any possible previous assign with that
643 name. The case of target being NULL is taken care of above.
645 if (!AssignLock(name, lock))
647 ioerr = IoErr();
648 cancel = TRUE;
649 UnLock(lock);
650 error = RETURN_FAIL;
651 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error));
653 /* If there are several targets, the next ones have to be added. */
654 add = TRUE;
657 /* We break as soon as we get a serious error */
658 if (error >= RETURN_FAIL)
660 break;
663 } /* loop through all targets */
666 if (error)
668 if (ioerr == ERROR_OBJECT_EXISTS)
670 Printf("Can't %s %s\n", (IPTR)(cancel ? "cancel" : "assign"), (IPTR)name);
672 else
674 PrintFault(ioerr, NULL);
678 return error;
682 static
683 int removeAssign(struct localdata *ld, STRPTR name)
685 /* In case no target is given, the 'name' assign should be removed.
686 * The AmigaDOS semantics for this is apparently that the error
687 * code is never set even if the assign didn't exist.
690 if (!AssignLock(name, BNULL))
692 return RETURN_FAIL;
694 return RETURN_OK;
698 static
699 int checkAssign(struct localdata *ld, STRPTR name)
701 STRPTR colon;
702 struct DosList *dl;
703 int error = RETURN_OK;
705 if (!name)
706 name = "";
708 colon = strchr(name, ':');
709 if (colon)
711 *colon = '\0';
714 dl = LockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
716 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
717 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
720 dl = FindDosEntry(dl, name, LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES);
721 if (dl)
723 struct DosList *tdl = dl;
724 int count;
726 switch (dl->dol_Type)
728 case DLT_DEVICE:
729 DeferPrintf("%b\n", tdl->dol_Name);
730 break;
732 case DLT_VOLUME:
733 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
734 break;
736 case DLT_DIRECTORY:
737 case DLT_LATE:
738 case DLT_NONBINDING:
740 DeferPrintf("%b ", tdl->dol_Name);
742 for (count = 14 - *((UBYTE*)BADDR(tdl->dol_Name)); count > 0; count--)
744 DeferPutStr(" ");
747 switch (tdl->dol_Type)
749 case DLT_LATE:
750 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
751 break;
753 case DLT_NONBINDING:
754 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
755 break;
757 default:
759 STRPTR dirName; /* For NameFromLock() */
760 struct AssignList *nextAssign; /* For multiassigns */
762 dirName = GetFullPath(ld, tdl->dol_Lock);
764 if (dirName)
766 DeferPutStr(dirName);
767 FreeVec(dirName);
769 DeferPutStr("\n");
771 nextAssign = tdl->dol_misc.dol_assign.dol_List;
773 while (nextAssign)
775 dirName = GetFullPath(ld, nextAssign->al_Lock);
777 if (dirName)
779 DeferPrintf(" + %s\n", (IPTR)dirName);
780 FreeVec(dirName);
783 nextAssign = nextAssign->al_Next;
788 break;
791 else
793 DeferPrintf("%s: not assigned\n", (IPTR)name);
795 error = RETURN_WARN;
798 UnLockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
800 DeferFlush(Output());
802 if (colon)
803 *colon = ':';
805 return error;
809 /* Feferred printing routines - Piru
812 #define MAXDEFERBUF 4096
813 #define MAXOUTPUT 128
814 struct deferbufnode
816 struct MinList node;
817 LONG pos;
818 UBYTE buf[MAXDEFERBUF];
821 static void deferputch(UBYTE ch, struct localdata *ld)
823 struct deferbufnode *cur;
825 if (!ch)
826 return;
828 cur = (struct deferbufnode *) GetTail(&DeferList);
830 if (!cur || cur->pos >= MAXDEFERBUF)
832 cur = AllocMem(sizeof(struct deferbufnode), MEMF_ANY);
833 if (!cur)
834 return;
836 cur->pos = 0;
838 ADDTAIL(&DeferList, cur);
841 cur->buf[cur->pos] = ch;
842 cur->pos++;
845 static
846 void _DeferPutStr(struct localdata *ld, CONST_STRPTR str)
848 UBYTE c;
850 DEBUG_ASSIGN(kprintf(str));
851 while ((c = *str++))
853 deferputch(c, ld);
857 #ifdef __AROS__
858 AROS_UFH2S(void, deferputch_gate,
859 AROS_UFHA(UBYTE, ch, D0),
860 AROS_UFHA(struct localdata *, ld, A3))
862 AROS_USERFUNC_INIT
863 deferputch(ch, ld);
864 AROS_USERFUNC_EXIT
866 #endif
868 #ifdef __MORPHOS__
869 static
870 void deferputch_trampoline(void)
872 UBYTE ch = (UBYTE) REG_D0;
873 struct localdata *ld = (struct localdata *) REG_A3;
875 deferputch(ch, ld);
878 static
879 const struct EmulLibEntry deferputch_gate =
881 TRAP_LIBNR, 0, (void (*)(void)) deferputch_trampoline
883 #endif
885 static
886 void _DeferVPrintf(struct localdata *ld, CONST_STRPTR fmt, IPTR *args)
888 RawDoFmt(fmt, args, (void (*)(void))AROS_ASMSYMNAME(deferputch_gate), ld);
891 static
892 void _DeferFlush(struct localdata *ld, BPTR fh)
894 struct deferbufnode *node;
895 BOOL broken = FALSE;
897 Flush(fh);
899 while ((node = (struct deferbufnode *)REMHEAD(&DeferList)))
901 LONG offs = 0;
902 LONG left = node->pos;
904 while (!broken && left)
906 LONG len;
908 if (SetSignal(0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
910 broken = TRUE;
911 break;
914 len = left > MAXOUTPUT ? MAXOUTPUT : left;
916 Write(fh, node->buf + offs, len);
917 offs += len;
918 left -= len;
921 FreeMem(node, sizeof(struct deferbufnode));
924 Flush(fh);
926 if (broken)
928 PrintFault(ERROR_BREAK, NULL);