Fixed indentation.
[AROS.git] / workbench / c / Dir.c
blobff4b88aef950a24f38d1c536165f0b2d5a2ede13
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Dir CLI command
6 Lang: English
7 */
9 #define MAX_PATH_LEN 512
11 #define USE_SOFTLINKCHECK 0
13 #include <exec/devices.h>
14 #include <exec/io.h>
15 #include <exec/memory.h>
16 #include <exec/semaphores.h>
17 #include <dos/dos.h>
18 #include <dos/exall.h>
19 #include <dos/datetime.h>
20 #include <utility/tagitem.h>
21 #include <utility/utility.h>
23 #include <aros/asmcall.h>
24 #include <aros/debug.h>
26 #include <proto/exec.h>
27 #include <proto/dos.h>
28 #include <proto/utility.h>
30 #include <string.h>
31 #include <stdlib.h>
33 /******************************************************************************
36 NAME
38 Dir [(dir | pattern)] [OPT A | I | D | F] [ALL] [DIRS] [FILES] [INTER]
41 SYNOPSIS
43 DIR,OPT/K,ALL/S,DIRS/S,FILES/S,INTER/S
45 LOCATION
49 FUNCTION
51 DIR displays the file or directory contained in the current or
52 specified directory. Directories get listed first, then in alphabetical
53 order, the files are listed in two columns. Pressing CTRL-C aborts the
54 directory listing.
57 INPUTS
59 ALL -- Display all subdirectories and their files recursively.
60 DIRS -- Display only directories.
61 FILES -- Display only files.
62 INTER -- Enter interactive mode.
64 Interactive listing mode stops after each name to display
65 a question mark at which you can enter commands. These
66 commands are:
68 Return -- Goto the next file or directory.
69 E/ENTER -- Enters a directory.
70 DEL/DELETE -- Delete a file or an empty directory.
71 C/COM -- Let the file or directory be the input of
72 a DOS command (which specified after the C or
73 COM or specified separately later).
74 Q/QUIT -- Quit interactive mode.
75 B/BACK -- Go back one directory level.
77 RESULT
79 NOTES
81 EXAMPLE
83 BUGS
85 SEE ALSO
87 INTERNALS
89 HISTORY
91 XY.11.2000 SDuvan added pattern matching support and support for
92 FILES/S, DIRS/S and OPT/K and some support for
93 INTER/S. Complete interactive support is still missing.
95 ******************************************************************************/
97 #define VERSTAG "\0$VER: Dir 50.8 (27.11.2004)"
99 const TEXT version[] = VERSTAG;
101 struct table
103 char **entries;
104 int num;
105 int max;
109 struct data
111 struct ExecBase *SysBase;
112 struct DosLibrary *DOSBase;
113 struct Library *UtilityBase;
114 int g_indent;
117 /* FIXME: Remove these #define xxxBase hacks
118 Do not use this in new code !
120 #define SysBase data->SysBase
121 #define DOSBase data->DOSBase
122 #define UtilityBase data->UtilityBase
123 #define g_indent data->g_indent
126 /* Prototypes */
128 static
129 LONG doPatternDir(STRPTR dirPat, BOOL all, BOOL doDirs, BOOL doFiles,
130 BOOL inter, struct data *data);
131 static
132 LONG doDir(STRPTR dir, BOOL all, BOOL dirs, BOOL files, BOOL inter, struct data *data);
134 static
135 void showline(char *fmt, IPTR *args, struct data *data);
136 static
137 void maybeShowline(char *format, IPTR *args, BOOL doIt, BOOL inter, struct data *data);
138 static
139 void maybeShowlineCR(char *format, IPTR *args, BOOL doIt, BOOL inter, struct data *data);
141 static
142 int CheckDir(BPTR lock, struct ExAllData *ead, ULONG eadSize,
143 struct ExAllControl *eac, struct table *dirs,
144 struct table *files, struct data *data);
147 #define INTERARG_TEMPLATE "E=ENTER/S,B=BACK/S,DEL=DELETE/S,Q=QUIT/S,C=COM/S,COMMAND"
149 enum
151 INTERARG_ENTER = 0,
152 INTERARG_BACK,
153 INTERARG_DELETE,
154 INTERARG_QUIT,
155 INTERARG_COM,
156 INTERARG_COMMAND,
157 NOOFINTERARGS
160 #define ARG_TEMPLATE "DIR,OPT/K,ALL/S,DIRS/S,FILES/S,INTER/S"
162 enum
164 ARG_DIR = 0,
165 ARG_OPT,
166 ARG_ALL,
167 ARG_DIRS,
168 ARG_FILES,
169 ARG_INTER
172 __startup static AROS_ENTRY(int, Start,
173 AROS_UFHA(char *, argstr, A0),
174 AROS_UFHA(ULONG, argsize, D0),
175 struct ExecBase *, sBase)
177 AROS_USERFUNC_INIT
179 struct data _data, *data = &_data;
180 struct RDArgs *rda;
181 IPTR args[] = { (IPTR)NULL, (IPTR)NULL, (IPTR)FALSE, (IPTR)FALSE, (IPTR)FALSE, (IPTR)FALSE };
183 LONG error = RETURN_FAIL;
185 SysBase = sBase;
186 if ((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37)))
188 if ((UtilityBase=OpenLibrary("utility.library",37)))
190 error = RETURN_ERROR;
192 rda = ReadArgs(ARG_TEMPLATE, args, NULL);
194 if (rda != NULL)
196 STRPTR dir = (STRPTR)args[ARG_DIR];
197 STRPTR opt = (STRPTR)args[ARG_OPT];
198 BOOL all = (BOOL)args[ARG_ALL];
199 BOOL dirs = (BOOL)args[ARG_DIRS];
200 BOOL files = (BOOL)args[ARG_FILES];
201 BOOL inter = (BOOL)args[ARG_INTER];
203 LONG iswild;
205 g_indent = 0;
207 /* Convert the OPT arguments (if any) into the regular switches */
208 if (opt != NULL)
210 while (*opt != 0)
212 switch (ToUpper(*opt))
214 case 'D':
215 dirs = TRUE;
216 break;
218 case 'F':
219 files = TRUE;
220 break;
222 case 'A':
223 all = TRUE;
224 break;
226 case 'I':
227 inter = TRUE;
228 break;
230 default:
231 Printf("%lc option ignored\n", *opt);
232 break;
234 opt++;
238 if(dir == NULL)
240 dir = "";
241 iswild = 0;
243 else
245 ULONG toklen = strlen(dir) * 2;
246 STRPTR pattok = AllocMem(toklen, MEMF_PUBLIC | MEMF_ANY);
247 iswild = ParsePattern(dir, pattok, toklen);
248 FreeMem(pattok, toklen);
251 if(!files && !dirs)
253 files = TRUE;
254 dirs = TRUE;
258 if (iswild == 1)
260 error = doPatternDir(dir, all, dirs, files, inter, data);
262 else
264 error = doDir(dir, all, dirs, files, inter, data);
267 if (error != RETURN_OK)
269 LONG ioerr = IoErr();
270 switch (ioerr)
272 case ERROR_NO_MORE_ENTRIES:
273 ioerr = 0;
274 break;
275 case ERROR_OBJECT_WRONG_TYPE:
276 Printf("%s is not a directory\n", (IPTR)dir);
277 ioerr = ERROR_DIR_NOT_FOUND;
278 break;
279 default:
280 Printf("Could not get information for %s\n", (IPTR)dir);
282 PrintFault(ioerr, NULL);
285 FreeArgs(rda);
287 else
289 PrintFault(IoErr(), NULL);
292 CloseLibrary((struct Library *)UtilityBase);
294 CloseLibrary((struct Library *)DOSBase);
297 return error;
299 AROS_USERFUNC_EXIT
302 static
303 int AddEntry(struct table *table, char *entry, struct data *data)
305 char *dup;
306 int len;
308 if (table->num == table->max)
310 int new_max = table->max + 128;
311 char **new_entries;
313 new_entries = AllocVec(sizeof(char *) * new_max, MEMF_ANY);
315 if (new_entries == NULL)
316 return 0;
318 if (table->num)
320 CopyMemQuick(table->entries, new_entries,
321 sizeof(char *) * table->num);
322 FreeVec(table->entries);
325 table->entries = new_entries;
326 table->max = new_max;
329 len = strlen(entry) + 1;
330 if ((dup = AllocVec(len,MEMF_ANY)))
332 strcpy(dup,entry);
334 else
336 return 0;
338 table->entries[table->num++] = dup;
340 return 1;
344 static __inline
345 int mystrcasecmp(const char *s1, const char *s2)
347 int a, b;
351 a = *s1++;
352 b = *s2++;
354 if (a >= 'a' && a <= 'z') a -= 'a' - 'A';
355 if (b >= 'a' && b <= 'z') b -= 'a' - 'A';
357 a -= b;
358 if (a) return (int) a;
360 } while (b);
362 return 0;
365 static
366 int compare_strings(const void * s1, const void * s2)
368 return mystrcasecmp(*(char **)s1, *(char **)s2);
372 static
373 void maybeShowlineCR(char *format, IPTR *args, BOOL doIt, BOOL inter, struct data *data)
375 maybeShowline(format, args, doIt, inter, data);
377 if (!inter)
379 PutStr("\n");
385 static
386 void maybeShowline(char *format, IPTR *args, BOOL doIt, BOOL inter, struct data *data)
388 if(doIt)
390 showline(format, args, data);
392 #if 0
393 if(inter)
395 struct ReadArgs *rda;
397 IPTR interArgs[NOOFINTERARGS] = { (IPTR)FALSE,
398 (IPTR)FALSE,
399 (IPTR)FALSE,
400 (IPTR)FALSE,
401 (IPTR)FALSE,
402 NULL };
404 rda = ReadArgs(INTERARG_TEMPLATE, interArgs, NULL);
406 if (rda != NULL)
408 if (interArgs[ARG_ENTER])
410 return c_Enter;
412 else if (interArgs[ARG_BACK])
414 return c_Back;
416 else if (interArgs[ARG_DELETE])
418 return c_Delete;
420 else if (interArgs[ARG_QUIT])
422 return c_Quit;
424 else if (interArgs[ARG_COM])
426 return c_Com;
428 else if (interArgs[ARG_COMMAND] != NULL)
430 command =
431 return c_Command;
435 #endif
441 static
442 void showline(char *fmt, IPTR *args, struct data *data)
444 int t;
446 for (t = 0; t < g_indent; t++)
447 PutStr(" ");
449 VPrintf(fmt, args);
452 // Returns TRUE if all lines shown, FALSE if broken by SIGBREAKF_CTRL_C
453 static
454 BOOL showfiles(struct table *files, BOOL inter, struct data *data)
456 IPTR argv[2];
457 ULONG t;
459 qsort(files->entries, files->num, sizeof(char *),compare_strings);
461 for (t = 0; t < files->num; t += 2)
463 argv[0] = (IPTR)(files->entries[t]);
464 argv[1] = (IPTR)(t + 1 < files->num ? files->entries[t+1] : "");
466 if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
468 SetIoErr(ERROR_BREAK);
469 return FALSE;
472 maybeShowlineCR(" %-32.s %s", argv, TRUE, inter, data);
474 return TRUE;
477 static
478 BOOL showdir(char *dirName, BOOL inter, struct data *data)
480 IPTR argv[1] = {(IPTR)dirName};
481 maybeShowlineCR("%s (dir)", argv, TRUE, inter, data);
482 return TRUE;
485 static
486 LONG doPatternDir(STRPTR dirPat, BOOL all, BOOL doDirs, BOOL doFiles, BOOL inter, struct data *data)
488 struct table files;
489 struct AnchorPath *ap; /* Matching structure */
491 LONG match; /* Loop variable */
492 LONG error = RETURN_FAIL;
494 LONG ioerr = 0;
496 files.entries = NULL;
497 files.max = 0;
498 files.num = 0;
500 ap = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath) + MAX_PATH_LEN, MEMF_CLEAR);
502 if(ap != NULL)
504 ap->ap_Strlen = MAX_PATH_LEN;
505 ap->ap_BreakBits = SIGBREAKF_CTRL_C;
507 if ((match = MatchFirst(dirPat, ap)) == 0)
509 error = RETURN_OK;
510 g_indent++;
511 for (;;)
513 #if USE_SOFTLINKCHECK
514 if (SoftlinkDODIR(ap, TRUE, FALSE, DOSBase))
515 #else /* USE_SOFTLINKCHECK */
516 if (ap->ap_Info.fib_DirEntryType > 0)
517 #endif /* USE_SOFTLINKCHECK */
519 if (doDirs)
521 BPTR l = Lock(ap->ap_Buf, SHARED_LOCK);
522 if (l != BNULL)
524 UBYTE name[512];
525 name[0] = 0;
526 NameFromLock(l, name, 512);
527 UnLock(l);
528 showdir(name, inter, data);
531 if (all)
533 error = doDir(ap->ap_Buf, all, doDirs, doFiles, inter, data);
536 else if (doFiles)
538 if (!AddEntry(&files, ap->ap_Info.fib_FileName, data))
540 ioerr = ERROR_NO_FREE_STORE;
541 error = RETURN_FAIL;
542 break;
545 ioerr = IoErr();
547 if (error != RETURN_OK)
549 break;
551 ap->ap_Strlen = MAX_PATH_LEN;
552 match = MatchNext(ap);
553 if (match != 0)
554 break;
556 g_indent--;
557 if (error == RETURN_OK && files.num != 0)
559 if (!showfiles(&files, inter, data))
561 error = RETURN_FAIL;
565 MatchEnd(ap);
567 else
569 ioerr = match;
570 error = RETURN_FAIL;
572 FreeVec(ap);
575 SetIoErr(ioerr);
577 return error;
583 static
584 LONG doDir(STRPTR dir, BOOL all, BOOL doDirs, BOOL doFiles, BOOL inter, struct data *data)
586 BPTR lock;
587 static UBYTE buffer[4096];
588 struct ExAllControl *eac;
589 LONG error = RETURN_OK;
591 struct table dirs;
592 struct table files;
594 dirs.entries = files.entries = NULL;
595 dirs.max = files.max = 0;
596 dirs.num = files.num = 0;
598 lock = Lock(dir, SHARED_LOCK);
600 if (lock != BNULL)
602 eac = AllocDosObject(DOS_EXALLCONTROL, NULL);
604 if (eac != NULL)
606 int t;
608 eac->eac_LastKey = 0;
610 error = CheckDir(lock, (struct ExAllData *)buffer, sizeof(buffer), eac, &dirs, &files, data);
611 FreeDosObject(DOS_EXALLCONTROL, eac);
613 if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
615 SetIoErr(ERROR_BREAK);
616 error = RETURN_FAIL;
619 if (error == 0 && doDirs)
621 if (dirs.num != 0)
623 g_indent++;
625 //qsort(dirs.entries, dirs.num, sizeof(char *), compare_strings);
627 // Output the directories
628 for (t = 0; t < dirs.num; t++)
630 STRPTR dirname = dirs.entries[t];
632 // Recurse into subdirectories if "ALL" was specified by the user
633 if (all)
635 char *newpath;
636 int len;
637 int pathlen = strlen(dir);
639 len = pathlen + strlen(dirs.entries[t]) + 2;
640 newpath = AllocVec(len, MEMF_ANY);
642 if (newpath != NULL)
644 CopyMem(dir, newpath, pathlen + 1);
645 if (AddPart(newpath, dirs.entries[t], len))
647 if (doDirs)
648 showdir(dirname, inter, data);
649 error = doDir(newpath, all, doDirs, doFiles, inter, data);
651 else
653 SetIoErr(ERROR_LINE_TOO_LONG);
654 error = RETURN_ERROR;
656 FreeVec(newpath);
658 else
660 SetIoErr(ERROR_NO_FREE_STORE);
661 error = RETURN_FAIL;
663 if (error != RETURN_OK)
664 break;
666 else if (doDirs)
668 showdir(dirname, inter, data);
671 g_indent--;
675 // Output the files
676 if (error == RETURN_OK && (files.num != 0 && doFiles))
678 if (!showfiles(&files, inter, data))
680 error = RETURN_FAIL;
685 if (dirs.num != 0)
687 for (t = 0; t < dirs.num; t++)
689 FreeVec(dirs.entries[t]);
692 if (dirs.entries)
694 FreeVec(dirs.entries);
698 if (files.num != 0)
700 for (t = 0; t < files.num; t++)
702 FreeVec(files.entries[t]);
705 if (files.entries)
707 FreeVec(files.entries);
712 else
714 SetIoErr(ERROR_NO_FREE_STORE);
715 error = RETURN_FAIL;
718 UnLock(lock);
720 else
722 #if USE_SOFTLINKCHECK
724 struct DevProc *dvp;
725 LONG ioerr;
727 error = RETURN_FAIL;
728 ioerr = IoErr();
730 if (ioerr == ERROR_OBJECT_NOT_FOUND &&
731 (dvp = GetDeviceProc(dir, NULL)))
733 if (ReadLink(dvp->dvp_Port, dvp->dvp_Lock, dir, buffer, sizeof(buffer) - 1) > 0)
735 buffer[sizeof(buffer) - 1] = '\0';
737 Printf("Warning: Skipping dangling softlink %s -> %s\n",
738 (LONG) dir, (LONG) buffer);
740 error = RETURN_OK;
743 FreeDeviceProc(dvp);
746 SetIoErr(ioerr);
748 #else /* USE_SOFTLINKCHECK */
750 error = RETURN_FAIL;
752 #endif /* USE_SOFTLINKCHECK */
755 return error;
759 static
760 int CheckDir(BPTR lock, struct ExAllData *ead, ULONG eadSize,
761 struct ExAllControl *eac, struct table *dirs,
762 struct table *files, struct data *data)
764 int error = RETURN_OK;
765 BOOL loop;
766 struct ExAllData *oldEad = ead;
770 ead = oldEad;
771 loop = ExAll(lock, ead, eadSize, ED_COMMENT, eac);
773 if(!loop && IoErr() != ERROR_NO_MORE_ENTRIES)
775 error = RETURN_ERROR;
776 break;
779 if(eac->eac_Entries != 0)
784 #if USE_SOFTLINKCHECK
785 if (ead->ed_Type == ST_SOFTLINK)
787 BPTR dirlock, l;
789 dirlock = CurrentDir(lock);
790 l = Lock(ead->ed_Name, ACCESS_READ);
791 CurrentDir(dirlock);
793 if (l)
795 UBYTE _fib[sizeof(struct FileInfoBlock) + 3];
796 struct FileInfoBlock *fib = (APTR) (((IPTR) _fib + 3) & ~3);
798 if (Examine(l, fib))
800 ead->ed_Type = fib->fib_DirEntryType;
801 //ead->ed_Size = fib->fib_Size;
804 UnLock(l);
807 #endif /* USE_SOFTLINKCHECK */
809 if (!AddEntry(ead->ed_Type > 0 ? dirs : files,ead->ed_Name, data))
811 loop = 0;
812 error = RETURN_FAIL;
813 SetIoErr(ERROR_NO_FREE_STORE);
814 break;
817 ead = ead->ed_Next;
819 while(ead != NULL);
822 while((loop) && (error == RETURN_OK));
824 return error;