revert between 56095 -> 55830 in arch
[AROS.git] / workbench / c / Assign.c
blob2d6426aa360347bc1105b04a0ac5c523e92c6a55
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[] __attribute__((used)) = "\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, ...);
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 _DeferVPrintf(ld, fmt, __VA_ARGS__);
169 #define DeferFlush(fh) _DeferFlush(ld,fh)
172 static
173 const UBYTE template[] =
174 "NAME,"
175 "TARGET/M,"
176 "LIST/S,"
177 "EXISTS/S,"
178 "DISMOUNT/S,"
179 "DEFER/S,"
180 "PATH/S,"
181 "ADD/S,"
182 "REMOVE/S,"
183 "VOLS/S,"
184 "DIRS/S,"
185 "DEVICES/S";
187 struct ArgList
189 STRPTR name;
190 STRPTR *target;
191 IPTR list;
192 IPTR exists;
193 IPTR dismount;
194 IPTR defer;
195 IPTR path;
196 IPTR add;
197 IPTR remove;
198 IPTR vols;
199 IPTR dirs;
200 IPTR devices;
203 __startup AROS_PROCH(Start, argstr, argsize, sBase)
205 AROS_PROCFUNC_INIT
206 return Main(sBase);
207 AROS_PROCFUNC_EXIT
210 static int Main(struct ExecBase *sBase)
212 struct localdata _ld, *ld = &_ld;
213 struct RDArgs *readarg;
214 struct ArgList arglist;
215 struct ArgList *MyArgList = &arglist;
216 int error = RETURN_OK;
218 SysBase = sBase;
219 DOSBase = (struct DosLibrary *) OpenLibrary("dos.library",37);
220 if (DOSBase)
222 memset(&arglist, 0, sizeof(arglist));
224 NEWLIST(&DeferList);
226 readarg = ReadArgs(template, (IPTR *)MyArgList, NULL);
227 if (readarg)
229 /* Verify mutually exclusive args
231 if ((MyArgList->add!=0) + (MyArgList->remove!=0) + (MyArgList->path!=0) + (MyArgList->defer!=0) > 1)
233 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
234 FreeArgs(readarg);
235 CloseLibrary((struct Library *) DOSBase);
236 return RETURN_FAIL;
239 /* Check device name
241 if (MyArgList->name)
243 char *pos;
245 /* Correct assign name construction? The rule is that the device name
246 * should end with a colon at the same time as no other colon may be
247 * in the name.
249 pos = strchr(MyArgList->name, ':');
250 if (!pos || pos[1])
252 Printf("Invalid device name %s\n", (IPTR)MyArgList->name);
253 FreeArgs(readarg);
254 CloseLibrary((struct Library *) DOSBase);
255 return RETURN_FAIL;
259 /* If the EXISTS keyword is specified, we only care about NAME */
260 if (MyArgList->exists)
262 error = checkAssign(ld, MyArgList->name);
263 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error));
265 else if (MyArgList->name)
267 /* If a NAME is specified, our primary task is to add or
268 remove an assign */
270 error = doAssign(ld, MyArgList->name, MyArgList->target, MyArgList->dismount, MyArgList->defer,
271 MyArgList->path, MyArgList->add, MyArgList->remove);
272 DEBUG_ASSIGN(Printf("doassign error %ld\n",error));
273 if (MyArgList->list)
275 /* With the LIST keyword, the current assigns will be
276 displayed also when (after) making an assign */
278 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
281 else
283 /* If no NAME was given, we just show the current assigns
284 as specified by the user (VOLS, DIRS, DEVICES) */
286 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
289 FreeArgs(readarg);
292 CloseLibrary((struct Library *) DOSBase);
295 DEBUG_ASSIGN(Printf("error %ld\n", error));
297 return error;
301 static
302 void showAssigns(struct localdata *ld, BOOL vols, BOOL dirs, BOOL devices)
304 ULONG lockBits = LDF_READ;
305 struct DosList *dl;
307 /* If none of the parameters are specified, everything should be
308 displayed */
309 if (!(vols || dirs || devices))
311 vols = TRUE;
312 dirs = TRUE;
313 devices = TRUE;
316 lockBits |= vols ? LDF_VOLUMES : 0;
317 lockBits |= dirs ? LDF_ASSIGNS : 0;
318 lockBits |= devices ? LDF_DEVICES : 0;
320 dl = LockDosList(lockBits);
322 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
323 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
326 if (vols)
328 struct DosList *tdl = dl;
330 DeferPutStr("Volumes:\n");
332 /* Print all mounted volumes */
333 while ((tdl = NextDosEntry(tdl, LDF_VOLUMES)))
335 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
339 if (dirs)
341 struct DosList *tdl = dl;
342 int count;
344 DeferPutStr("\nDirectories:\n");
346 /* Print all assigned directories */
347 while ((tdl = NextDosEntry(tdl, LDF_ASSIGNS)))
349 DeferPrintf("%b ", tdl->dol_Name);
351 for (count = 14 - AROS_BSTR_strlen(tdl->dol_Name); count > 0; count--)
353 DeferPutStr(" ");
356 switch (tdl->dol_Type)
358 case DLT_LATE:
359 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
360 break;
362 case DLT_NONBINDING:
363 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
364 break;
366 default:
368 STRPTR dirName; /* For NameFromLock() */
369 struct AssignList *nextAssign; /* For multiassigns */
371 dirName = GetFullPath(ld, tdl->dol_Lock);
373 if (dirName)
375 DeferPutStr(dirName);
376 FreeVec(dirName);
378 DeferPutStr("\n");
380 nextAssign = tdl->dol_misc.dol_assign.dol_List;
382 while (nextAssign)
384 dirName = GetFullPath(ld, nextAssign->al_Lock);
386 if (dirName)
388 DeferPrintf(" + %s\n", (IPTR)dirName);
389 FreeVec(dirName);
392 nextAssign = nextAssign->al_Next;
396 break;
398 } /* while (NextDosEntry()) */
401 if (devices)
403 struct DosList *tdl = dl;
404 int count = 0; /* Used to make sure that as most 5 entries are printed per line */
406 DeferPutStr("\nDevices:\n");
408 /* Print all assigned devices */
409 while ((tdl = NextDosEntry(tdl, LDF_DEVICES)))
411 DeferPrintf("%b%lc", tdl->dol_Name, ++count % 5 ? ' ' : '\n');
414 if (count % 5)
416 DeferPutStr("\n");
420 UnLockDosList(lockBits);
422 DeferFlush(Output());
426 static
427 STRPTR GetFullPath(struct localdata *ld, BPTR lock)
429 STRPTR buf; /* Pointer to the memory allocated for the string */
430 ULONG size; /* Holder of the (growing) size of the string */
432 for (size = 512; ; size += 512)
434 buf = AllocVec(size, MEMF_ANY);
435 if (!buf)
437 break;
440 if (NameFromLock(lock, buf, size))
442 return buf;
445 FreeVec(buf);
447 if (IoErr() != ERROR_LINE_TOO_LONG)
449 break;
453 return NULL;
457 static
458 int doAssign(struct localdata *ld, STRPTR name, STRPTR *target, BOOL dismount, BOOL defer, BOOL path,
459 BOOL add, BOOL remove)
461 STRPTR colon;
462 BPTR lock = BNULL;
463 int i;
465 int error = RETURN_OK;
466 LONG ioerr = 0;
467 BOOL cancel = FALSE;
468 BOOL success = TRUE;
470 if (dismount)
472 struct MsgPort *dp;
473 struct Process *tp;
475 tp=(struct Process *)FindTask(NULL);
476 tp->pr_WindowPtr = (APTR)-1;
477 dp = DeviceProc(name);
478 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp));
479 if (dp)
481 success = DoPkt(dp,ACTION_DIE,0,0,0,0,0);
482 ioerr = IoErr();
483 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success));
487 colon = strchr(name, ':');
489 *colon = '\0'; /* Remove trailing colon; name[] is changed! */
491 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name));
493 /* This is a little bit messy... We first remove the 'name' assign
494 * and later in the loop the target assigns.
497 if (dismount)
499 if ((!success) && (ioerr == ERROR_ACTION_NOT_KNOWN))
501 struct DosList *dl;
502 struct DosList *fdl;
504 DEBUG_ASSIGN(PutStr("Removing device node\n"));
505 dl = LockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
507 fdl = FindDosEntry(dl, name, LDF_VOLUMES | LDF_DEVICES);
509 /* Note the ! for conversion to boolean value */
510 if (fdl)
512 success = RemDosEntry(fdl);
513 if (success)
514 FreeDosEntry(fdl);
515 else
516 ioerr = ERROR_OBJECT_IN_USE;
517 } else
518 ioerr = ERROR_OBJECT_NOT_FOUND;
520 UnLockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
523 else
525 if (target == NULL || *target == NULL)
527 error = removeAssign(ld, name);
528 if (error)
530 ioerr = IoErr();
531 cancel = TRUE;
536 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
537 error = success ? error : RETURN_WARN;
538 DEBUG_ASSIGN(Printf("error: %d\n", error));
540 // The Loop over multiple targets starts here
542 if (target)
544 for (i = 0; target[i]; i++)
546 cancel = FALSE;
548 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target[i]));
549 if (!(path || defer || dismount))
551 lock = Lock(target[i], SHARED_LOCK);
553 if (!lock)
555 Printf("Can't find %s\n", (IPTR)target[i]);
556 return RETURN_FAIL;
560 if (remove)
562 if (!RemAssignList(name, lock))
564 Printf("Can't subtract %s from %s\n", (IPTR)target[i], (IPTR)name);
565 error = RETURN_FAIL;
567 UnLock(lock);
569 else if (path)
571 if (!AssignPath(name, target[i]))
573 ioerr = IoErr();
574 error = RETURN_FAIL;
575 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error));
578 else if (add)
580 if (!AssignAdd(name, lock))
582 struct DosList *dl;
584 error = RETURN_FAIL;
585 ioerr = IoErr();
586 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error));
588 /* Check if the assign doesn't exist at all. If so, create it.
589 * This fix bug id 145. - Piru
591 dl = LockDosList(LDF_ASSIGNS | LDF_READ);
592 dl = FindDosEntry(dl, name, LDF_ASSIGNS);
593 UnLockDosList(LDF_ASSIGNS | LDF_READ);
595 if (!dl)
597 if (AssignLock(name, lock))
599 error = RETURN_OK;
600 lock = BNULL;
602 else
604 ioerr = IoErr();
605 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error));
609 if (lock)
610 UnLock(lock);
612 if (error && ioerr != ERROR_OBJECT_EXISTS)
614 Printf("Can't add %s to %s\n", (IPTR)target[i], (IPTR)name);
618 else if (defer)
620 if (!AssignLate(name, target[i]))
622 ioerr = IoErr();
623 UnLock(lock);
624 error = RETURN_FAIL;
625 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error));
628 else
630 /* If no extra parameters are specified we just do a regular
631 assign (replacing any possible previous assign with that
632 name. The case of target being NULL is taken care of above.
634 if (!AssignLock(name, lock))
636 ioerr = IoErr();
637 cancel = TRUE;
638 UnLock(lock);
639 error = RETURN_FAIL;
640 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error));
642 /* If there are several targets, the next ones have to be added. */
643 add = TRUE;
646 /* We break as soon as we get a serious error */
647 if (error >= RETURN_FAIL)
649 break;
652 } /* loop through all targets */
655 if (error)
657 if (ioerr == ERROR_OBJECT_EXISTS)
659 Printf("Can't %s %s\n", (IPTR)(cancel ? "cancel" : "assign"), (IPTR)name);
661 else
663 PrintFault(ioerr, NULL);
667 return error;
671 static
672 int removeAssign(struct localdata *ld, STRPTR name)
674 /* In case no target is given, the 'name' assign should be removed.
675 * The AmigaDOS semantics for this is apparently that the error
676 * code is never set even if the assign didn't exist.
679 if (!AssignLock(name, BNULL))
681 return RETURN_FAIL;
683 return RETURN_OK;
687 static
688 int checkAssign(struct localdata *ld, STRPTR name)
690 STRPTR colon;
691 struct DosList *dl;
692 int error = RETURN_OK;
694 if (!name)
695 name = "";
697 colon = strchr(name, ':');
698 if (colon)
700 *colon = '\0';
703 dl = LockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
705 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
706 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
709 dl = FindDosEntry(dl, name, LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES);
710 if (dl)
712 struct DosList *tdl = dl;
713 int count;
715 switch (dl->dol_Type)
717 case DLT_DEVICE:
718 DeferPrintf("%b\n", tdl->dol_Name);
719 break;
721 case DLT_VOLUME:
722 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
723 break;
725 case DLT_DIRECTORY:
726 case DLT_LATE:
727 case DLT_NONBINDING:
729 DeferPrintf("%b ", tdl->dol_Name);
731 for (count = 14 - *((UBYTE*)BADDR(tdl->dol_Name)); count > 0; count--)
733 DeferPutStr(" ");
736 switch (tdl->dol_Type)
738 case DLT_LATE:
739 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
740 break;
742 case DLT_NONBINDING:
743 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
744 break;
746 default:
748 STRPTR dirName; /* For NameFromLock() */
749 struct AssignList *nextAssign; /* For multiassigns */
751 dirName = GetFullPath(ld, tdl->dol_Lock);
753 if (dirName)
755 DeferPutStr(dirName);
756 FreeVec(dirName);
758 DeferPutStr("\n");
760 nextAssign = tdl->dol_misc.dol_assign.dol_List;
762 while (nextAssign)
764 dirName = GetFullPath(ld, nextAssign->al_Lock);
766 if (dirName)
768 DeferPrintf(" + %s\n", (IPTR)dirName);
769 FreeVec(dirName);
772 nextAssign = nextAssign->al_Next;
777 break;
780 else
782 DeferPrintf("%s: not assigned\n", (IPTR)name);
784 error = RETURN_WARN;
787 UnLockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
789 DeferFlush(Output());
791 if (colon)
792 *colon = ':';
794 return error;
798 /* Feferred printing routines - Piru
801 #define MAXDEFERBUF 4096
802 #define MAXOUTPUT 128
803 struct deferbufnode
805 struct MinList node;
806 LONG pos;
807 UBYTE buf[MAXDEFERBUF];
810 static void deferputch(UBYTE ch, struct localdata *ld)
812 struct deferbufnode *cur;
814 if (!ch)
815 return;
817 cur = (struct deferbufnode *) GetTail(&DeferList);
819 if (!cur || cur->pos >= MAXDEFERBUF)
821 cur = AllocMem(sizeof(struct deferbufnode), MEMF_ANY);
822 if (!cur)
823 return;
825 cur->pos = 0;
827 ADDTAIL(&DeferList, cur);
830 cur->buf[cur->pos] = ch;
831 cur->pos++;
834 static
835 void _DeferPutStr(struct localdata *ld, CONST_STRPTR str)
837 UBYTE c;
839 DEBUG_ASSIGN(kprintf(str));
840 while ((c = *str++))
842 deferputch(c, ld);
846 #ifdef __AROS__
847 AROS_UFH2S(void, deferputch_gate,
848 AROS_UFHA(UBYTE, ch, D0),
849 AROS_UFHA(struct localdata *, ld, A3))
851 AROS_USERFUNC_INIT
852 deferputch(ch, ld);
853 AROS_USERFUNC_EXIT
855 #endif
857 #ifdef __MORPHOS__
858 static
859 void deferputch_trampoline(void)
861 UBYTE ch = (UBYTE) REG_D0;
862 struct localdata *ld = (struct localdata *) REG_A3;
864 deferputch(ch, ld);
867 static
868 const struct EmulLibEntry deferputch_gate =
870 TRAP_LIBNR, 0, (void (*)(void)) deferputch_trampoline
872 #endif
874 static
875 void _DeferVPrintf(struct localdata *ld, CONST_STRPTR fmt, ...)
877 AROS_SLOWSTACKFORMAT_PRE(fmt);
878 RawDoFmt(fmt, AROS_SLOWSTACKFORMAT_ARG(fmt), (void (*)(void))AROS_ASMSYMNAME(deferputch_gate), ld);
879 AROS_SLOWSTACKFORMAT_POST(fmt);
882 static
883 void _DeferFlush(struct localdata *ld, BPTR fh)
885 struct deferbufnode *node;
886 BOOL broken = FALSE;
888 Flush(fh);
890 while ((node = (struct deferbufnode *)REMHEAD(&DeferList)))
892 LONG offs = 0;
893 LONG left = node->pos;
895 while (!broken && left)
897 LONG len;
899 if (SetSignal(0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
901 broken = TRUE;
902 break;
905 len = left > MAXOUTPUT ? MAXOUTPUT : left;
907 Write(fh, node->buf + offs, len);
908 offs += len;
909 left -= len;
912 FreeMem(node, sizeof(struct deferbufnode));
915 Flush(fh);
917 if (broken)
919 PrintFault(ERROR_BREAK, NULL);