Some typo fixes etc.
[cake.git] / workbench / c / Assign.c
blob3aa520294f5b243757c887a5f63ce8e9aaf3a977
1 /*
3 (C) 1995-2008 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 #else
18 #include <clib/debug_protos.h>
19 #endif
21 #include <exec/types.h>
22 #include <exec/lists.h>
23 #include <exec/memory.h>
24 #include <exec/execbase.h>
25 #include <dos/dos.h>
26 #include <dos/dosextens.h>
27 #include <dos/exall.h>
28 #include <utility/tagitem.h>
29 #include <proto/dos.h>
30 #include <proto/exec.h>
32 #include <string.h>
34 #define DEBUG_ASSIGN(x)
36 /******************************************************************************
39 NAME
41 Assign [(name):] [{(target)}] [LIST] [EXISTS] [DISMOUNT] [DEFER]
42 [PATH] [ADD] [REMOVE] [VOLS] [DIRS] [DEVICES]
44 SYNOPSIS
46 NAME, TARGET/M, LIST/S, EXISTS/S, DISMOUNT/S, DEFER/S, PATH/S, ADD/S,
47 REMOVE/S, VOLS/S, DIRS/S, DEVICES/S
49 LOCATION
53 FUNCTION
55 ASSIGN creates a reference to a file or directory. The reference
56 is a logical device name which makes it very convenient to specify
57 assigned objects using the reference instead of their paths.
59 If the NAME and TARGET arguments are given, ASSIGN assigns the
60 given logical name to the specified target. If the NAME given is
61 already assigned to a file or directory the new target replaces the
62 previous target. A colon must be included after the NAME argument.
64 If only the NAME argument is given, any assigns to that NAME are
65 removed. If no arguments whatsoever are given, all logical
66 assigns are listed.
68 INPUTS
70 NAME -- the name that should be assigned to a file or directory
71 TARGET -- one or more files or directories to assign the NAME to
72 LIST -- list all assigns made
73 EXISTS -- display the target of the specified assign. If NAME is
74 not assigned, set the condition flag to WARN
75 DISMOUNT -- remove the volume or device NAME from the dos list
76 DEFER -- make an ASSIGN to a path or directory that not need to
77 exist at the time of assignment. The first time the
78 NAME is referenced the NAME is bound to the object
79 PATH -- path is assigned with a non-binding assign. This means
80 that the assign is re-evaluated each time a reference
81 to NAME is done. Like for DEFER, the path doesn't have
82 to exist when the ASSIGN command is executed
83 ADD -- don't replace an assign but add another object for a
84 NAME (multi-assigns)
85 REMOVE -- remove an ASSIGN
86 VOLS -- show assigned volumes if in LIST mode
87 DIRS -- show assigned directories if in LIST mode
88 DEVICES -- show assigned devices if in LIST mode
91 RESULT
93 NOTES
95 EXAMPLE
97 BUGS
99 SEE ALSO
101 INTERNALS
103 The assign command has many switches. This together with the somewhat
104 messy handling of DosList:s by dos.library makes the operation rather
105 complicated.
107 There are some fundamental building blocks that defines the semantics
108 of the Assign command.
110 Only one of the switches ADD, REMOVE, PATH and DEFER may be specified.
112 If EXISTS is specified, only the name parameter is important.
114 The implementation is split up in two fundamental procedures.
116 doAssign() -- make [a number of] assigns
117 showAssigns() -- show the available assigns
118 checkAssign() -- check if a particular assign exists
120 ******************************************************************************/
122 #ifndef __AROS__
123 #define AROS_BSTR_strlen(s) *((UBYTE *)BADDR(s))
124 #endif
126 #ifdef __MORPHOS__
127 #define AROS_ASMSYMNAME(s) (&s)
129 static const int __abox__ = 1;
130 static const char version[] = "\0$VER: Assign unofficial 50.9 (18.10.07) © AROS" ;
131 #else
132 static const char version[] = "\0$VER: Assign 50.9 (18.10.07) © AROS" ;
133 #endif
135 struct localdata
137 struct ExecBase *ld_SysBase;
138 struct DosLibrary *ld_DOSBase;
139 struct MinList ld_DeferList;
142 #define SysBase ld->ld_SysBase
143 #define DOSBase ld->ld_DOSBase
144 #define DeferList ld->ld_DeferList
147 /* Prototypes */
149 static int Main(struct ExecBase *sBase);
150 static int checkAssign(struct localdata *ld, STRPTR name);
151 static int doAssign(struct localdata *ld, STRPTR name, STRPTR *target, BOOL dismount, BOOL defer, BOOL path,
152 BOOL add, BOOL remove);
153 static void showAssigns(struct localdata *ld, BOOL vols, BOOL dirs, BOOL devices);
154 static int removeAssign(struct localdata *ld, STRPTR name);
155 static STRPTR GetFullPath(struct localdata *ld, BPTR lock);
157 static void _DeferPutStr(struct localdata *ld, CONST_STRPTR str);
158 static void _DeferVPrintf(struct localdata *ld, CONST_STRPTR fmt, IPTR *args);
159 static void _DeferFlush(struct localdata *ld, BPTR fh);
161 #define DeferPutStr(str) _DeferPutStr(ld,str)
162 #define DeferPrintf(fmt,args...) \
163 DEBUG_ASSIGN(kprintf(fmt, ## args);) \
164 do { IPTR __args[] = {0 , ## args}; _DeferVPrintf(ld, fmt, __args + 1); } while (0)
165 #define DeferFlush(fh) _DeferFlush(ld,fh)
168 static
169 const UBYTE template[] =
170 "NAME,"
171 "TARGET/M,"
172 "LIST/S,"
173 "EXISTS/S,"
174 "DISMOUNT/S,"
175 "DEFER/S,"
176 "PATH/S,"
177 "ADD/S,"
178 "REMOVE/S,"
179 "VOLS/S,"
180 "DIRS/S,"
181 "DEVICES/S";
183 struct ArgList
185 STRPTR name;
186 STRPTR *target;
187 IPTR list;
188 IPTR exists;
189 IPTR dismount;
190 IPTR defer;
191 IPTR path;
192 IPTR add;
193 IPTR remove;
194 IPTR vols;
195 IPTR dirs;
196 IPTR devices;
199 #ifdef __AROS__
200 AROS_UFH3(__startup static int, Start,
201 AROS_UFHA(char *, argstr, A0),
202 AROS_UFHA(ULONG, argsize, D0),
203 AROS_UFHA(struct ExecBase *, sBase, A6))
205 AROS_USERFUNC_INIT
206 return Main(sBase);
207 AROS_USERFUNC_EXIT
209 #else
210 int Start(void)
212 struct ExecBase *sBase;
214 sBase = *((struct ExecBase **) 4);
215 return Main(sBase);
217 #endif
219 static int Main(struct ExecBase *sBase)
221 struct localdata _ld, *ld = &_ld;
222 struct RDArgs *readarg;
223 struct ArgList arglist;
224 struct ArgList *MyArgList = &arglist;
225 int error = RETURN_OK;
227 SysBase = sBase;
228 DOSBase = (struct DosLibrary *) OpenLibrary("dos.library",37);
229 if (DOSBase)
231 memset(&arglist, 0, sizeof(arglist));
233 NEWLIST(&DeferList);
235 readarg = ReadArgs(template, (IPTR *)MyArgList, NULL);
236 if (readarg)
238 /* Verify mutually exclusive args
240 if ((MyArgList->add!=0) + (MyArgList->remove!=0) + (MyArgList->path!=0) + (MyArgList->defer!=0) > 1)
242 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
243 FreeArgs(readarg);
244 CloseLibrary((struct Library *) DOSBase);
245 return RETURN_FAIL;
248 /* Check device name
250 if (MyArgList->name)
252 char *pos;
254 /* Correct assign name construction? The rule is that the device name
255 * should end with a colon at the same time as no other colon may be
256 * in the name.
258 pos = strchr(MyArgList->name, ':');
259 if (!pos || pos[1])
261 Printf("Invalid device name %s\n", (IPTR)MyArgList->name);
262 FreeArgs(readarg);
263 CloseLibrary((struct Library *) DOSBase);
264 return RETURN_FAIL;
268 /* If the EXISTS keyword is specified, we only care about NAME */
269 if (MyArgList->exists)
271 error = checkAssign(ld, MyArgList->name);
272 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error));
274 else if (MyArgList->name)
276 /* If a NAME is specified, our primary task is to add or
277 remove an assign */
279 error = doAssign(ld, MyArgList->name, MyArgList->target, MyArgList->dismount, MyArgList->defer,
280 MyArgList->path, MyArgList->add, MyArgList->remove);
281 DEBUG_ASSIGN(Printf("doassign error %ld\n",error));
282 if (MyArgList->list)
284 /* With the LIST keyword, the current assigns will be
285 displayed also when (after) making an assign */
287 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
290 else
292 /* If no NAME was given, we just show the current assigns
293 as specified by the user (VOLS, DIRS, DEVICES) */
295 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
298 FreeArgs(readarg);
301 CloseLibrary((struct Library *) DOSBase);
304 DEBUG_ASSIGN(Printf("error %ld\n", error));
306 return error;
310 static
311 void showAssigns(struct localdata *ld, BOOL vols, BOOL dirs, BOOL devices)
313 ULONG lockBits = LDF_READ;
314 struct DosList *dl;
316 /* If none of the parameters are specified, everything should be
317 displayed */
318 if (!(vols || dirs || devices))
320 vols = TRUE;
321 dirs = TRUE;
322 devices = TRUE;
325 lockBits |= vols ? LDF_VOLUMES : 0;
326 lockBits |= dirs ? LDF_ASSIGNS : 0;
327 lockBits |= devices ? LDF_DEVICES : 0;
329 dl = LockDosList(lockBits);
331 #warning "FIXME: GetFullPath() breaks LockDosList()'s Forbid()!"
332 #warning "Note: This should be ok as long as we don't have ks 1.x compatibility."
334 if (vols)
336 struct DosList *tdl = dl;
338 DeferPutStr("Volumes:\n");
340 /* Print all mounted volumes */
341 while ((tdl = NextDosEntry(tdl, LDF_VOLUMES)))
343 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
347 if (dirs)
349 struct DosList *tdl = dl;
350 int count;
352 DeferPutStr("\nDirectories:\n");
354 /* Print all assigned directories */
355 while ((tdl = NextDosEntry(tdl, LDF_ASSIGNS)))
357 DeferPrintf("%b ", tdl->dol_Name);
359 for (count = 14 - AROS_BSTR_strlen(tdl->dol_Name); count > 0; count--)
361 DeferPutStr(" ");
364 switch (tdl->dol_Type)
366 case DLT_LATE:
367 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
368 break;
370 case DLT_NONBINDING:
371 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
372 break;
374 default:
376 STRPTR dirName; /* For NameFromLock() */
377 struct AssignList *nextAssign; /* For multiassigns */
379 dirName = GetFullPath(ld, tdl->dol_Lock);
381 if (dirName)
383 DeferPutStr(dirName);
384 FreeVec(dirName);
386 DeferPutStr("\n");
388 nextAssign = tdl->dol_misc.dol_assign.dol_List;
390 while (nextAssign)
392 dirName = GetFullPath(ld, nextAssign->al_Lock);
394 if (dirName)
396 DeferPrintf(" + %s\n", (IPTR)dirName);
397 FreeVec(dirName);
400 nextAssign = nextAssign->al_Next;
404 break;
406 } /* while (NextDosEntry()) */
409 if (devices)
411 struct DosList *tdl = dl;
412 int count = 0; /* Used to make sure that as most 5 entries are printed per line */
414 DeferPutStr("\nDevices:\n");
416 /* Print all assigned devices */
417 while ((tdl = NextDosEntry(tdl, LDF_DEVICES)))
419 DeferPrintf("%b%lc", tdl->dol_Name, ++count % 5 ? ' ' : '\n');
422 if (count % 5)
424 DeferPutStr("\n");
428 UnLockDosList(lockBits);
430 DeferFlush(Output());
434 static
435 STRPTR GetFullPath(struct localdata *ld, BPTR lock)
437 STRPTR buf; /* Pointer to the memory allocated for the string */
438 ULONG size; /* Holder of the (growing) size of the string */
440 for (size = 512; ; size += 512)
442 buf = AllocVec(size, MEMF_ANY);
443 if (!buf)
445 break;
448 if (NameFromLock(lock, buf, size))
450 return buf;
453 FreeVec(buf);
455 if (IoErr() != ERROR_LINE_TOO_LONG)
457 break;
461 return NULL;
465 static
466 int doAssign(struct localdata *ld, STRPTR name, STRPTR *target, BOOL dismount, BOOL defer, BOOL path,
467 BOOL add, BOOL remove)
469 STRPTR colon;
470 BPTR lock = NULL;
471 int i;
473 int error = RETURN_OK;
474 LONG ioerr = 0;
475 BOOL cancel = FALSE;
476 BOOL success = TRUE;
478 if (dismount)
480 #ifdef __AROS__
481 /* TODO: AROS currently doesn't support packet handlers directly
482 and we currently don't support shutting down IOFS handlers */
483 success = DOSFALSE;
484 ioerr = ERROR_ACTION_NOT_KNOWN;
485 #else
486 struct MsgPort *dp;
487 struct Process *tp;
489 tp=(struct Process *)FindTask(NULL);
490 tp->pr_WindowPtr = (APTR)-1;
491 dp = DeviceProc(name);
492 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp));
493 if (dp)
495 success = DoPkt(dp,ACTION_DIE,0,0,0,0,0);
496 ioerr = IoErr();
497 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success));
499 #endif
502 colon = strchr(name, ':');
504 *colon = '\0'; /* Remove trailing colon; name[] is changed! */
506 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name));
508 /* This is a little bit messy... We first remove the 'name' assign
509 * and later in the loop the target assigns.
512 if (dismount)
514 if ((!success) && (ioerr == ERROR_ACTION_NOT_KNOWN))
516 struct DosList *dl;
517 struct DosList *fdl;
519 DEBUG_ASSIGN(PutStr("Removing device node\n"));
520 dl = LockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
522 fdl = FindDosEntry(dl, name, LDF_VOLUMES | LDF_DEVICES);
524 /* Note the ! for conversion to boolean value */
525 if (fdl)
527 success = RemDosEntry(fdl);
528 if (success)
529 FreeDosEntry(fdl);
530 else
531 ioerr = ERROR_OBJECT_IN_USE;
532 } else
533 ioerr = ERROR_OBJECT_NOT_FOUND;
535 UnLockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
538 else
540 if (target == NULL || *target == NULL)
542 error = removeAssign(ld, name);
543 if (error)
545 ioerr = IoErr();
546 cancel = TRUE;
551 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
552 error = success ? error : RETURN_WARN;
553 DEBUG_ASSIGN(Printf("error: %d\n", error));
555 // The Loop over multiple targets starts here
557 if (target)
559 for (i = 0; target[i]; i++)
561 cancel = FALSE;
563 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target[i]));
564 if (!(path || defer || dismount))
566 lock = Lock(target[i], SHARED_LOCK);
568 if (!lock)
570 Printf("Can't find %s\n", (IPTR)target[i]);
571 return RETURN_FAIL;
575 if (remove)
577 if (!RemAssignList(name, lock))
579 Printf("Can't subtract %s from %s\n", (IPTR)target[i], (IPTR)name);
580 error = RETURN_FAIL;
582 UnLock(lock);
584 else if (path)
586 if (!AssignPath(name, target[i]))
588 ioerr = IoErr();
589 error = RETURN_FAIL;
590 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error));
593 else if (add)
595 if (!AssignAdd(name, lock))
597 struct DosList *dl;
599 error = RETURN_FAIL;
600 ioerr = IoErr();
601 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error));
603 /* Check if the assign doesn't exist at all. If so, create it.
604 * This fix bug id 145. - Piru
606 dl = LockDosList(LDF_ASSIGNS | LDF_READ);
607 dl = FindDosEntry(dl, name, LDF_ASSIGNS);
608 UnLockDosList(LDF_ASSIGNS | LDF_READ);
610 if (!dl)
612 if (AssignLock(name, lock))
614 error = RETURN_OK;
615 lock = NULL;
617 else
619 ioerr = IoErr();
620 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error));
624 if (lock)
625 UnLock(lock);
627 if (error && ioerr != ERROR_OBJECT_EXISTS)
629 Printf("Can't add %s to %s\n", (IPTR)target[i], (IPTR)name);
633 else if (defer)
635 if (!AssignLate(name, target[i]))
637 ioerr = IoErr();
638 UnLock(lock);
639 error = RETURN_FAIL;
640 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error));
643 else
645 /* If no extra parameters are specified we just do a regular
646 assign (replacing any possible previous assign with that
647 name. The case of target being NULL is taken care of above.
649 if (!AssignLock(name, lock))
651 ioerr = IoErr();
652 cancel = TRUE;
653 UnLock(lock);
654 error = RETURN_FAIL;
655 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error));
657 /* If there are several targets, the next ones have to be added. */
658 add = TRUE;
661 /* We break as soon as we get a serious error */
662 if (error >= RETURN_FAIL)
664 break;
667 } /* loop through all targets */
670 if (error)
672 if (ioerr == ERROR_OBJECT_EXISTS)
674 Printf("Can't %s %s\n", (IPTR)(cancel ? "cancel" : "assign"), (IPTR)name);
676 else
678 PrintFault(ioerr, NULL);
682 return error;
686 static
687 int removeAssign(struct localdata *ld, STRPTR name)
689 /* In case no target is given, the 'name' assign should be removed.
690 * The AmigaDOS semantics for this is apparently that the error
691 * code is never set even if the assign didn't exist.
694 if (!AssignLock(name, NULL))
696 return RETURN_FAIL;
698 return RETURN_OK;
702 static
703 int checkAssign(struct localdata *ld, STRPTR name)
705 STRPTR colon;
706 struct DosList *dl;
707 int error = RETURN_OK;
709 if (!name)
710 name = "";
712 colon = strchr(name, ':');
713 if (colon)
715 *colon = '\0';
718 dl = LockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
720 #warning "Note: GetFullPath() breaks LockDosList()'s Forbid()!"
721 #warning "Note: This should be ok as long as we don't have ks 1.x compatibility."
723 dl = FindDosEntry(dl, name, LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES);
724 if (dl)
726 struct DosList *tdl = dl;
727 int count;
729 switch (dl->dol_Type)
731 case DLT_DEVICE:
732 DeferPrintf("%b\n", tdl->dol_Name);
733 break;
735 case DLT_VOLUME:
736 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
737 break;
739 case DLT_DIRECTORY:
740 case DLT_LATE:
741 case DLT_NONBINDING:
743 DeferPrintf("%b ", tdl->dol_Name);
745 for (count = 14 - *((UBYTE*)BADDR(tdl->dol_Name)); count > 0; count--)
747 DeferPutStr(" ");
750 switch (tdl->dol_Type)
752 case DLT_LATE:
753 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
754 break;
756 case DLT_NONBINDING:
757 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
758 break;
760 default:
762 STRPTR dirName; /* For NameFromLock() */
763 struct AssignList *nextAssign; /* For multiassigns */
765 dirName = GetFullPath(ld, tdl->dol_Lock);
767 if (dirName)
769 DeferPutStr(dirName);
770 FreeVec(dirName);
772 DeferPutStr("\n");
774 nextAssign = tdl->dol_misc.dol_assign.dol_List;
776 while (nextAssign)
778 dirName = GetFullPath(ld, nextAssign->al_Lock);
780 if (dirName)
782 DeferPrintf(" + %s\n", (IPTR)dirName);
783 FreeVec(dirName);
786 nextAssign = nextAssign->al_Next;
791 break;
794 else
796 DeferPrintf("%s: not assigned\n", (IPTR)name);
798 error = RETURN_WARN;
801 UnLockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
803 DeferFlush(Output());
805 if (colon)
806 *colon = ':';
808 return error;
812 /* Feferred printing routines - Piru
815 #define MAXDEFERBUF 4096
816 #define MAXOUTPUT 128
817 struct deferbufnode
819 struct MinList node;
820 LONG pos;
821 UBYTE buf[MAXDEFERBUF];
824 static void deferputch(UBYTE ch, struct localdata *ld)
826 struct deferbufnode *cur;
828 if (!ch)
829 return;
831 cur = (struct deferbufnode *) GetTail(&DeferList);
833 if (!cur || cur->pos >= MAXDEFERBUF)
835 cur = AllocMem(sizeof(struct deferbufnode), MEMF_ANY);
836 if (!cur)
837 return;
839 cur->pos = 0;
841 ADDTAIL(&DeferList, cur);
844 cur->buf[cur->pos] = ch;
845 cur->pos++;
848 static
849 void _DeferPutStr(struct localdata *ld, CONST_STRPTR str)
851 UBYTE c;
853 DEBUG_ASSIGN(kprintf(str);)
854 while ((c = *str++))
856 deferputch(c, ld);
860 #ifdef __AROS__
861 AROS_UFH2(static void, deferputch_gate,
862 AROS_UFHA(UBYTE, ch, D0),
863 AROS_UFHA(struct localdata *, ld, A3))
865 AROS_USERFUNC_INIT
866 deferputch(ch, ld);
867 AROS_USERFUNC_EXIT
869 #endif
871 #ifdef __MORPHOS__
872 static
873 void deferputch_trampoline(void)
875 UBYTE ch = (UBYTE) REG_D0;
876 struct localdata *ld = (struct localdata *) REG_A3;
878 deferputch(ch, ld);
881 static
882 const struct EmulLibEntry deferputch_gate =
884 TRAP_LIBNR, 0, (void (*)(void)) deferputch_trampoline
886 #endif
888 static
889 void _DeferVPrintf(struct localdata *ld, CONST_STRPTR fmt, IPTR *args)
891 RawDoFmt(fmt, args, (void (*)(void))AROS_ASMSYMNAME(deferputch_gate), ld);
894 static
895 void _DeferFlush(struct localdata *ld, BPTR fh)
897 struct deferbufnode *node;
898 BOOL broken = FALSE;
900 Flush(fh);
902 while ((node = REMHEAD(&DeferList)))
904 LONG offs = 0;
905 LONG left = node->pos;
907 while (!broken && left)
909 LONG len;
911 if (SetSignal(0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
913 broken = TRUE;
914 break;
917 len = left > MAXOUTPUT ? MAXOUTPUT : left;
919 Write(fh, node->buf + offs, len);
920 offs += len;
921 left -= len;
924 FreeMem(node, sizeof(struct deferbufnode));
927 Flush(fh);
929 if (broken)
931 PrintFault(ERROR_BREAK, NULL);