prism2.device: Compiler delint
[AROS.git] / workbench / c / Assign.c
blobfe7ca42e7a9937540ebe66279a17b3c2823d644f
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 __startup AROS_PROCH(Start, argstr, argsize, sBase)
206 AROS_PROCFUNC_INIT
207 return Main(sBase);
208 AROS_PROCFUNC_EXIT
211 static int Main(struct ExecBase *sBase)
213 struct localdata _ld, *ld = &_ld;
214 struct RDArgs *readarg;
215 struct ArgList arglist;
216 struct ArgList *MyArgList = &arglist;
217 int error = RETURN_OK;
219 SysBase = sBase;
220 DOSBase = (struct DosLibrary *) OpenLibrary("dos.library",37);
221 if (DOSBase)
223 memset(&arglist, 0, sizeof(arglist));
225 NEWLIST(&DeferList);
227 readarg = ReadArgs(template, (IPTR *)MyArgList, NULL);
228 if (readarg)
230 /* Verify mutually exclusive args
232 if ((MyArgList->add!=0) + (MyArgList->remove!=0) + (MyArgList->path!=0) + (MyArgList->defer!=0) > 1)
234 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
235 FreeArgs(readarg);
236 CloseLibrary((struct Library *) DOSBase);
237 return RETURN_FAIL;
240 /* Check device name
242 if (MyArgList->name)
244 char *pos;
246 /* Correct assign name construction? The rule is that the device name
247 * should end with a colon at the same time as no other colon may be
248 * in the name.
250 pos = strchr(MyArgList->name, ':');
251 if (!pos || pos[1])
253 Printf("Invalid device name %s\n", (IPTR)MyArgList->name);
254 FreeArgs(readarg);
255 CloseLibrary((struct Library *) DOSBase);
256 return RETURN_FAIL;
260 /* If the EXISTS keyword is specified, we only care about NAME */
261 if (MyArgList->exists)
263 error = checkAssign(ld, MyArgList->name);
264 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error));
266 else if (MyArgList->name)
268 /* If a NAME is specified, our primary task is to add or
269 remove an assign */
271 error = doAssign(ld, MyArgList->name, MyArgList->target, MyArgList->dismount, MyArgList->defer,
272 MyArgList->path, MyArgList->add, MyArgList->remove);
273 DEBUG_ASSIGN(Printf("doassign error %ld\n",error));
274 if (MyArgList->list)
276 /* With the LIST keyword, the current assigns will be
277 displayed also when (after) making an assign */
279 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
282 else
284 /* If no NAME was given, we just show the current assigns
285 as specified by the user (VOLS, DIRS, DEVICES) */
287 showAssigns(ld, MyArgList->vols, MyArgList->dirs, MyArgList->devices);
290 FreeArgs(readarg);
293 CloseLibrary((struct Library *) DOSBase);
296 DEBUG_ASSIGN(Printf("error %ld\n", error));
298 return error;
302 static
303 void showAssigns(struct localdata *ld, BOOL vols, BOOL dirs, BOOL devices)
305 ULONG lockBits = LDF_READ;
306 struct DosList *dl;
308 /* If none of the parameters are specified, everything should be
309 displayed */
310 if (!(vols || dirs || devices))
312 vols = TRUE;
313 dirs = TRUE;
314 devices = TRUE;
317 lockBits |= vols ? LDF_VOLUMES : 0;
318 lockBits |= dirs ? LDF_ASSIGNS : 0;
319 lockBits |= devices ? LDF_DEVICES : 0;
321 dl = LockDosList(lockBits);
323 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
324 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
327 if (vols)
329 struct DosList *tdl = dl;
331 DeferPutStr("Volumes:\n");
333 /* Print all mounted volumes */
334 while ((tdl = NextDosEntry(tdl, LDF_VOLUMES)))
336 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
340 if (dirs)
342 struct DosList *tdl = dl;
343 int count;
345 DeferPutStr("\nDirectories:\n");
347 /* Print all assigned directories */
348 while ((tdl = NextDosEntry(tdl, LDF_ASSIGNS)))
350 DeferPrintf("%b ", tdl->dol_Name);
352 for (count = 14 - AROS_BSTR_strlen(tdl->dol_Name); count > 0; count--)
354 DeferPutStr(" ");
357 switch (tdl->dol_Type)
359 case DLT_LATE:
360 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
361 break;
363 case DLT_NONBINDING:
364 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
365 break;
367 default:
369 STRPTR dirName; /* For NameFromLock() */
370 struct AssignList *nextAssign; /* For multiassigns */
372 dirName = GetFullPath(ld, tdl->dol_Lock);
374 if (dirName)
376 DeferPutStr(dirName);
377 FreeVec(dirName);
379 DeferPutStr("\n");
381 nextAssign = tdl->dol_misc.dol_assign.dol_List;
383 while (nextAssign)
385 dirName = GetFullPath(ld, nextAssign->al_Lock);
387 if (dirName)
389 DeferPrintf(" + %s\n", (IPTR)dirName);
390 FreeVec(dirName);
393 nextAssign = nextAssign->al_Next;
397 break;
399 } /* while (NextDosEntry()) */
402 if (devices)
404 struct DosList *tdl = dl;
405 int count = 0; /* Used to make sure that as most 5 entries are printed per line */
407 DeferPutStr("\nDevices:\n");
409 /* Print all assigned devices */
410 while ((tdl = NextDosEntry(tdl, LDF_DEVICES)))
412 DeferPrintf("%b%lc", tdl->dol_Name, ++count % 5 ? ' ' : '\n');
415 if (count % 5)
417 DeferPutStr("\n");
421 UnLockDosList(lockBits);
423 DeferFlush(Output());
427 static
428 STRPTR GetFullPath(struct localdata *ld, BPTR lock)
430 STRPTR buf; /* Pointer to the memory allocated for the string */
431 ULONG size; /* Holder of the (growing) size of the string */
433 for (size = 512; ; size += 512)
435 buf = AllocVec(size, MEMF_ANY);
436 if (!buf)
438 break;
441 if (NameFromLock(lock, buf, size))
443 return buf;
446 FreeVec(buf);
448 if (IoErr() != ERROR_LINE_TOO_LONG)
450 break;
454 return NULL;
458 static
459 int doAssign(struct localdata *ld, STRPTR name, STRPTR *target, BOOL dismount, BOOL defer, BOOL path,
460 BOOL add, BOOL remove)
462 STRPTR colon;
463 BPTR lock = BNULL;
464 int i;
466 int error = RETURN_OK;
467 LONG ioerr = 0;
468 BOOL cancel = FALSE;
469 BOOL success = TRUE;
471 if (dismount)
473 struct MsgPort *dp;
474 struct Process *tp;
476 tp=(struct Process *)FindTask(NULL);
477 tp->pr_WindowPtr = (APTR)-1;
478 dp = DeviceProc(name);
479 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp));
480 if (dp)
482 success = DoPkt(dp,ACTION_DIE,0,0,0,0,0);
483 ioerr = IoErr();
484 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success));
488 colon = strchr(name, ':');
490 *colon = '\0'; /* Remove trailing colon; name[] is changed! */
492 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name));
494 /* This is a little bit messy... We first remove the 'name' assign
495 * and later in the loop the target assigns.
498 if (dismount)
500 if ((!success) && (ioerr == ERROR_ACTION_NOT_KNOWN))
502 struct DosList *dl;
503 struct DosList *fdl;
505 DEBUG_ASSIGN(PutStr("Removing device node\n"));
506 dl = LockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
508 fdl = FindDosEntry(dl, name, LDF_VOLUMES | LDF_DEVICES);
510 /* Note the ! for conversion to boolean value */
511 if (fdl)
513 success = RemDosEntry(fdl);
514 if (success)
515 FreeDosEntry(fdl);
516 else
517 ioerr = ERROR_OBJECT_IN_USE;
518 } else
519 ioerr = ERROR_OBJECT_NOT_FOUND;
521 UnLockDosList(LDF_VOLUMES | LDF_DEVICES | LDF_WRITE);
524 else
526 if (target == NULL || *target == NULL)
528 error = removeAssign(ld, name);
529 if (error)
531 ioerr = IoErr();
532 cancel = TRUE;
537 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
538 error = success ? error : RETURN_WARN;
539 DEBUG_ASSIGN(Printf("error: %d\n", error));
541 // The Loop over multiple targets starts here
543 if (target)
545 for (i = 0; target[i]; i++)
547 cancel = FALSE;
549 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target[i]));
550 if (!(path || defer || dismount))
552 lock = Lock(target[i], SHARED_LOCK);
554 if (!lock)
556 Printf("Can't find %s\n", (IPTR)target[i]);
557 return RETURN_FAIL;
561 if (remove)
563 if (!RemAssignList(name, lock))
565 Printf("Can't subtract %s from %s\n", (IPTR)target[i], (IPTR)name);
566 error = RETURN_FAIL;
568 UnLock(lock);
570 else if (path)
572 if (!AssignPath(name, target[i]))
574 ioerr = IoErr();
575 error = RETURN_FAIL;
576 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error));
579 else if (add)
581 if (!AssignAdd(name, lock))
583 struct DosList *dl;
585 error = RETURN_FAIL;
586 ioerr = IoErr();
587 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error));
589 /* Check if the assign doesn't exist at all. If so, create it.
590 * This fix bug id 145. - Piru
592 dl = LockDosList(LDF_ASSIGNS | LDF_READ);
593 dl = FindDosEntry(dl, name, LDF_ASSIGNS);
594 UnLockDosList(LDF_ASSIGNS | LDF_READ);
596 if (!dl)
598 if (AssignLock(name, lock))
600 error = RETURN_OK;
601 lock = BNULL;
603 else
605 ioerr = IoErr();
606 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error));
610 if (lock)
611 UnLock(lock);
613 if (error && ioerr != ERROR_OBJECT_EXISTS)
615 Printf("Can't add %s to %s\n", (IPTR)target[i], (IPTR)name);
619 else if (defer)
621 if (!AssignLate(name, target[i]))
623 ioerr = IoErr();
624 UnLock(lock);
625 error = RETURN_FAIL;
626 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error));
629 else
631 /* If no extra parameters are specified we just do a regular
632 assign (replacing any possible previous assign with that
633 name. The case of target being NULL is taken care of above.
635 if (!AssignLock(name, lock))
637 ioerr = IoErr();
638 cancel = TRUE;
639 UnLock(lock);
640 error = RETURN_FAIL;
641 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error));
643 /* If there are several targets, the next ones have to be added. */
644 add = TRUE;
647 /* We break as soon as we get a serious error */
648 if (error >= RETURN_FAIL)
650 break;
653 } /* loop through all targets */
656 if (error)
658 if (ioerr == ERROR_OBJECT_EXISTS)
660 Printf("Can't %s %s\n", (IPTR)(cancel ? "cancel" : "assign"), (IPTR)name);
662 else
664 PrintFault(ioerr, NULL);
668 return error;
672 static
673 int removeAssign(struct localdata *ld, STRPTR name)
675 /* In case no target is given, the 'name' assign should be removed.
676 * The AmigaDOS semantics for this is apparently that the error
677 * code is never set even if the assign didn't exist.
680 if (!AssignLock(name, BNULL))
682 return RETURN_FAIL;
684 return RETURN_OK;
688 static
689 int checkAssign(struct localdata *ld, STRPTR name)
691 STRPTR colon;
692 struct DosList *dl;
693 int error = RETURN_OK;
695 if (!name)
696 name = "";
698 colon = strchr(name, ':');
699 if (colon)
701 *colon = '\0';
704 dl = LockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
706 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
707 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
710 dl = FindDosEntry(dl, name, LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES);
711 if (dl)
713 struct DosList *tdl = dl;
714 int count;
716 switch (dl->dol_Type)
718 case DLT_DEVICE:
719 DeferPrintf("%b\n", tdl->dol_Name);
720 break;
722 case DLT_VOLUME:
723 DeferPrintf("%b [Mounted]\n", tdl->dol_Name);
724 break;
726 case DLT_DIRECTORY:
727 case DLT_LATE:
728 case DLT_NONBINDING:
730 DeferPrintf("%b ", tdl->dol_Name);
732 for (count = 14 - *((UBYTE*)BADDR(tdl->dol_Name)); count > 0; count--)
734 DeferPutStr(" ");
737 switch (tdl->dol_Type)
739 case DLT_LATE:
740 DeferPrintf("<%s>\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
741 break;
743 case DLT_NONBINDING:
744 DeferPrintf("[%s]\n", (IPTR)tdl->dol_misc.dol_assign.dol_AssignName);
745 break;
747 default:
749 STRPTR dirName; /* For NameFromLock() */
750 struct AssignList *nextAssign; /* For multiassigns */
752 dirName = GetFullPath(ld, tdl->dol_Lock);
754 if (dirName)
756 DeferPutStr(dirName);
757 FreeVec(dirName);
759 DeferPutStr("\n");
761 nextAssign = tdl->dol_misc.dol_assign.dol_List;
763 while (nextAssign)
765 dirName = GetFullPath(ld, nextAssign->al_Lock);
767 if (dirName)
769 DeferPrintf(" + %s\n", (IPTR)dirName);
770 FreeVec(dirName);
773 nextAssign = nextAssign->al_Next;
778 break;
781 else
783 DeferPrintf("%s: not assigned\n", (IPTR)name);
785 error = RETURN_WARN;
788 UnLockDosList(LDF_DEVICES | LDF_ASSIGNS | LDF_VOLUMES | LDF_READ);
790 DeferFlush(Output());
792 if (colon)
793 *colon = ':';
795 return error;
799 /* Feferred printing routines - Piru
802 #define MAXDEFERBUF 4096
803 #define MAXOUTPUT 128
804 struct deferbufnode
806 struct MinList node;
807 LONG pos;
808 UBYTE buf[MAXDEFERBUF];
811 static void deferputch(UBYTE ch, struct localdata *ld)
813 struct deferbufnode *cur;
815 if (!ch)
816 return;
818 cur = (struct deferbufnode *) GetTail(&DeferList);
820 if (!cur || cur->pos >= MAXDEFERBUF)
822 cur = AllocMem(sizeof(struct deferbufnode), MEMF_ANY);
823 if (!cur)
824 return;
826 cur->pos = 0;
828 ADDTAIL(&DeferList, cur);
831 cur->buf[cur->pos] = ch;
832 cur->pos++;
835 static
836 void _DeferPutStr(struct localdata *ld, CONST_STRPTR str)
838 UBYTE c;
840 DEBUG_ASSIGN(kprintf(str));
841 while ((c = *str++))
843 deferputch(c, ld);
847 #ifdef __AROS__
848 AROS_UFH2S(void, deferputch_gate,
849 AROS_UFHA(UBYTE, ch, D0),
850 AROS_UFHA(struct localdata *, ld, A3))
852 AROS_USERFUNC_INIT
853 deferputch(ch, ld);
854 AROS_USERFUNC_EXIT
856 #endif
858 #ifdef __MORPHOS__
859 static
860 void deferputch_trampoline(void)
862 UBYTE ch = (UBYTE) REG_D0;
863 struct localdata *ld = (struct localdata *) REG_A3;
865 deferputch(ch, ld);
868 static
869 const struct EmulLibEntry deferputch_gate =
871 TRAP_LIBNR, 0, (void (*)(void)) deferputch_trampoline
873 #endif
875 static
876 void _DeferVPrintf(struct localdata *ld, CONST_STRPTR fmt, IPTR *args)
878 RawDoFmt(fmt, args, (void (*)(void))AROS_ASMSYMNAME(deferputch_gate), ld);
881 static
882 void _DeferFlush(struct localdata *ld, BPTR fh)
884 struct deferbufnode *node;
885 BOOL broken = FALSE;
887 Flush(fh);
889 while ((node = (struct deferbufnode *)REMHEAD(&DeferList)))
891 LONG offs = 0;
892 LONG left = node->pos;
894 while (!broken && left)
896 LONG len;
898 if (SetSignal(0, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
900 broken = TRUE;
901 break;
904 len = left > MAXOUTPUT ? MAXOUTPUT : left;
906 Write(fh, node->buf + offs, len);
907 offs += len;
908 left -= len;
911 FreeMem(node, sizeof(struct deferbufnode));
914 Flush(fh);
916 if (broken)
918 PrintFault(ERROR_BREAK, NULL);