remember the lists current position when it updates
[AROS.git] / workbench / c / Dir.c
blobb07e8203325c53c571b12a70930f3f61a74d7153
1 /*
2 Copyright © 1995-2012, 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 #define SH_GLOBAL_SYSBASE 1
31 #define SH_GLOBAL_DOSBASE 1
33 #include <aros/shcommands.h>
35 #include <string.h>
36 #include <stdlib.h>
38 /******************************************************************************
41 NAME
43 Dir [(dir | pattern)] [OPT A | I | D | F] [ALL] [DIRS] [FILES] [INTER]
46 SYNOPSIS
48 DIR,OPT/K,ALL/S,DIRS/S,FILES/S,INTER/S
50 LOCATION
54 FUNCTION
56 DIR displays the file or directory contained in the current or
57 specified directory. Directories get listed first, then in alphabetical
58 order, the files are listed in two columns. Pressing CTRL-C aborts the
59 directory listing.
62 INPUTS
64 ALL -- Display all subdirectories and their files recursively.
65 DIRS -- Display only directories.
66 FILES -- Display only files.
67 INTER -- Enter interactive mode.
69 Interactive listing mode stops after each name to display
70 a question mark at which you can enter commands. These
71 commands are:
73 Return -- Goto the next file or directory.
74 E/ENTER -- Enters a directory.
75 DEL/DELETE -- Delete a file or an empty directory.
76 C/COM -- Let the file or directory be the input of
77 a DOS command (which specified after the C or
78 COM or specified separately later).
79 Q/QUIT -- Quit interactive mode.
80 B/BACK -- Go back one directory level.
82 RESULT
84 NOTES
86 EXAMPLE
88 BUGS
90 SEE ALSO
92 INTERNALS
94 HISTORY
96 XY.11.2000 SDuvan added pattern matching support and support for
97 FILES/S, DIRS/S and OPT/K and some support for
98 INTER/S. Complete interactive support is still missing.
100 ******************************************************************************/
102 struct table
104 char **entries;
105 int num;
106 int max;
109 /* Global variables */
110 int g_indent;
111 struct UtilityBase *UtilityBase;
113 /* Prototypes */
115 static
116 LONG doPatternDir(CONST_STRPTR dirPat, BOOL all, BOOL doDirs, BOOL doFiles,
117 BOOL inter);
118 static
119 LONG doDir(CONST_STRPTR dir, BOOL all, BOOL dirs, BOOL files, BOOL inter);
121 static
122 void showline(char *fmt, IPTR *args);
123 static
124 void maybeShowline(char *format, IPTR *args, BOOL doIt, BOOL inter);
125 static
126 void maybeShowlineCR(char *format, IPTR *args, BOOL doIt, BOOL inter);
128 static
129 int CheckDir(BPTR lock, struct ExAllData *ead, ULONG eadSize,
130 struct ExAllControl *eac, struct table *dirs,
131 struct table *files);
134 #define INTERARG_TEMPLATE "E=ENTER/S,B=BACK/S,DEL=DELETE/S,Q=QUIT/S,C=COM/S,COMMAND"
136 enum
138 INTERARG_ENTER = 0,
139 INTERARG_BACK,
140 INTERARG_DELETE,
141 INTERARG_QUIT,
142 INTERARG_COM,
143 INTERARG_COMMAND,
144 NOOFINTERARGS
147 AROS_SH6(Dir, 50.9,
148 AROS_SHA(CONST_STRPTR, ,DIR , , NULL),
149 AROS_SHA(CONST_STRPTR, ,OPT , /K , NULL),
150 AROS_SHA(BOOL, ,ALL , /S , FALSE),
151 AROS_SHA(BOOL, ,DIRS , /S , FALSE),
152 AROS_SHA(BOOL, ,FILES, /S , FALSE),
153 AROS_SHA(BOOL, ,INTER, /S , FALSE)
156 AROS_SHCOMMAND_INIT
158 LONG error = RETURN_FAIL;
159 CONST_STRPTR dir = SHArg(DIR);
160 CONST_STRPTR opt = SHArg(OPT);
161 BOOL all = SHArg(ALL);
162 BOOL dirs = SHArg(DIRS);
163 BOOL files = SHArg(FILES);
164 BOOL inter = SHArg(INTER);
166 LONG iswild;
168 g_indent = 0;
170 UtilityBase = (struct UtilityBase *)OpenLibrary("utility.library", 37);
171 if (!UtilityBase)
172 return error;
174 /* Convert the OPT arguments (if any) into the regular switches */
175 if (opt != NULL)
177 while (*opt != 0)
179 switch (ToUpper(*opt))
181 case 'D':
182 dirs = TRUE;
183 break;
185 case 'F':
186 files = TRUE;
187 break;
189 case 'A':
190 all = TRUE;
191 break;
193 case 'I':
194 inter = TRUE;
195 break;
197 default:
198 Printf("%lc option ignored\n", *opt);
199 break;
201 opt++;
205 if(dir == NULL)
207 dir = "";
208 iswild = 0;
210 else
212 ULONG toklen = strlen(dir) * 2;
213 STRPTR pattok = AllocMem(toklen, MEMF_PUBLIC | MEMF_ANY);
214 iswild = ParsePattern(dir, pattok, toklen);
215 FreeMem(pattok, toklen);
218 if(!files && !dirs)
220 files = TRUE;
221 dirs = TRUE;
225 if (iswild == 1)
227 error = doPatternDir(dir, all, dirs, files, inter);
229 else
231 error = doDir(dir, all, dirs, files, inter);
234 if (error != RETURN_OK)
236 LONG ioerr = IoErr();
237 switch (ioerr)
239 case ERROR_NO_MORE_ENTRIES:
240 ioerr = 0;
241 break;
242 case ERROR_OBJECT_WRONG_TYPE:
243 Printf("%s is not a directory\n", (IPTR)dir);
244 ioerr = ERROR_DIR_NOT_FOUND;
245 break;
246 default:
247 Printf("Could not get information for %s\n", (IPTR)dir);
249 PrintFault(ioerr, NULL);
252 CloseLibrary((struct Library *)UtilityBase);
253 return error;
255 AROS_SHCOMMAND_EXIT
258 static
259 int AddEntry(struct table *table, char *entry)
261 char *dup;
262 int len;
264 if (table->num == table->max)
266 int new_max = table->max + 128;
267 char **new_entries;
269 new_entries = AllocVec(sizeof(char *) * new_max, MEMF_ANY);
271 if (new_entries == NULL)
272 return 0;
274 if (table->num)
276 CopyMemQuick(table->entries, new_entries,
277 sizeof(char *) * table->num);
278 FreeVec(table->entries);
281 table->entries = new_entries;
282 table->max = new_max;
285 len = strlen(entry) + 1;
286 if ((dup = AllocVec(len,MEMF_ANY)))
288 strcpy(dup,entry);
290 else
292 return 0;
294 table->entries[table->num++] = dup;
296 return 1;
300 static __inline
301 int mystrcasecmp(const char *s1, const char *s2)
303 int a, b;
307 a = *s1++;
308 b = *s2++;
310 if (a >= 'a' && a <= 'z') a -= 'a' - 'A';
311 if (b >= 'a' && b <= 'z') b -= 'a' - 'A';
313 a -= b;
314 if (a) return (int) a;
316 } while (b);
318 return 0;
321 static
322 int compare_strings(const void * s1, const void * s2)
324 return mystrcasecmp(*(char **)s1, *(char **)s2);
328 static
329 void maybeShowlineCR(char *format, IPTR *args, BOOL doIt, BOOL inter)
331 maybeShowline(format, args, doIt, inter);
333 if (!inter)
335 PutStr("\n");
341 static
342 void maybeShowline(char *format, IPTR *args, BOOL doIt, BOOL inter)
344 if(doIt)
346 showline(format, args);
348 #if 0
349 if(inter)
351 struct ReadArgs *rda;
353 IPTR interArgs[NOOFINTERARGS] = { (IPTR)FALSE,
354 (IPTR)FALSE,
355 (IPTR)FALSE,
356 (IPTR)FALSE,
357 (IPTR)FALSE,
358 NULL };
360 rda = ReadArgs(INTERARG_TEMPLATE, interArgs, NULL);
362 if (rda != NULL)
364 if (interArgs[ARG_ENTER])
366 return c_Enter;
368 else if (interArgs[ARG_BACK])
370 return c_Back;
372 else if (interArgs[ARG_DELETE])
374 return c_Delete;
376 else if (interArgs[ARG_QUIT])
378 return c_Quit;
380 else if (interArgs[ARG_COM])
382 return c_Com;
384 else if (interArgs[ARG_COMMAND] != NULL)
386 command =
387 return c_Command;
391 #endif
397 static
398 void showline(char *fmt, IPTR *args)
400 int t;
402 for (t = 0; t < g_indent; t++)
403 PutStr(" ");
405 VPrintf(fmt, args);
408 // Returns TRUE if all lines shown, FALSE if broken by SIGBREAKF_CTRL_C
409 static
410 BOOL showfiles(struct table *files, BOOL inter)
412 IPTR argv[2];
413 ULONG t;
415 qsort(files->entries, files->num, sizeof(char *),compare_strings);
417 for (t = 0; t < files->num; t += 2)
419 argv[0] = (IPTR)(files->entries[t]);
420 argv[1] = (IPTR)(t + 1 < files->num ? files->entries[t+1] : "");
422 if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
424 SetIoErr(ERROR_BREAK);
425 return FALSE;
428 maybeShowlineCR(" %-32.s %s", argv, TRUE, inter);
430 return TRUE;
433 static
434 BOOL showdir(char *dirName, BOOL inter)
436 IPTR argv[1] = {(IPTR)dirName};
437 maybeShowlineCR("%s (dir)", argv, TRUE, inter);
438 return TRUE;
441 static
442 LONG doPatternDir(CONST_STRPTR dirPat, BOOL all, BOOL doDirs, BOOL doFiles, BOOL inter)
444 struct table files;
445 struct AnchorPath *ap; /* Matching structure */
447 LONG match; /* Loop variable */
448 LONG error = RETURN_FAIL;
450 LONG ioerr = 0;
452 files.entries = NULL;
453 files.max = 0;
454 files.num = 0;
456 ap = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath) + MAX_PATH_LEN, MEMF_CLEAR);
458 if(ap != NULL)
460 ap->ap_Strlen = MAX_PATH_LEN;
461 ap->ap_BreakBits = SIGBREAKF_CTRL_C;
463 if ((match = MatchFirst(dirPat, ap)) == 0)
465 error = RETURN_OK;
466 g_indent++;
467 for (;;)
469 #if USE_SOFTLINKCHECK
470 if (SoftlinkDODIR(ap, TRUE, FALSE, DOSBase))
471 #else /* USE_SOFTLINKCHECK */
472 if (ap->ap_Info.fib_DirEntryType > 0)
473 #endif /* USE_SOFTLINKCHECK */
475 if (doDirs)
477 BPTR l = Lock(ap->ap_Buf, SHARED_LOCK);
478 if (l != BNULL)
480 UBYTE name[512];
481 name[0] = 0;
482 NameFromLock(l, name, 512);
483 UnLock(l);
484 showdir(name, inter);
487 if (all)
489 error = doDir(ap->ap_Buf, all, doDirs, doFiles, inter);
492 else if (doFiles)
494 if (!AddEntry(&files, ap->ap_Info.fib_FileName))
496 ioerr = ERROR_NO_FREE_STORE;
497 error = RETURN_FAIL;
498 break;
501 ioerr = IoErr();
503 if (error != RETURN_OK)
505 break;
507 ap->ap_Strlen = MAX_PATH_LEN;
508 match = MatchNext(ap);
509 if (match != 0)
510 break;
512 g_indent--;
513 if (error == RETURN_OK && files.num != 0)
515 if (!showfiles(&files, inter))
517 error = RETURN_FAIL;
521 MatchEnd(ap);
523 else
525 ioerr = match;
526 error = RETURN_FAIL;
528 FreeVec(ap);
531 SetIoErr(ioerr);
533 return error;
539 static
540 LONG doDir(CONST_STRPTR dir, BOOL all, BOOL doDirs, BOOL doFiles, BOOL inter)
542 BPTR lock;
543 static UBYTE buffer[4096];
544 struct ExAllControl *eac;
545 LONG error = RETURN_OK;
547 struct table dirs;
548 struct table files;
550 dirs.entries = files.entries = NULL;
551 dirs.max = files.max = 0;
552 dirs.num = files.num = 0;
554 lock = Lock(dir, SHARED_LOCK);
556 if (lock != BNULL)
558 eac = AllocDosObject(DOS_EXALLCONTROL, NULL);
560 if (eac != NULL)
562 int t;
564 eac->eac_LastKey = 0;
566 error = CheckDir(lock, (struct ExAllData *)buffer, sizeof(buffer), eac, &dirs, &files);
567 FreeDosObject(DOS_EXALLCONTROL, eac);
569 if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
571 SetIoErr(ERROR_BREAK);
572 error = RETURN_FAIL;
575 if (error == 0 && doDirs)
577 if (dirs.num != 0)
579 g_indent++;
581 //qsort(dirs.entries, dirs.num, sizeof(char *), compare_strings);
583 // Output the directories
584 for (t = 0; t < dirs.num; t++)
586 STRPTR dirname = dirs.entries[t];
588 // Recurse into subdirectories if "ALL" was specified by the user
589 if (all)
591 char *newpath;
592 int len;
593 int pathlen = strlen(dir);
595 len = pathlen + strlen(dirs.entries[t]) + 2;
596 newpath = AllocVec(len, MEMF_ANY);
598 if (newpath != NULL)
600 CopyMem(dir, newpath, pathlen + 1);
601 if (AddPart(newpath, dirs.entries[t], len))
603 if (doDirs)
604 showdir(dirname, inter);
605 error = doDir(newpath, all, doDirs, doFiles, inter);
607 else
609 SetIoErr(ERROR_LINE_TOO_LONG);
610 error = RETURN_ERROR;
612 FreeVec(newpath);
614 else
616 SetIoErr(ERROR_NO_FREE_STORE);
617 error = RETURN_FAIL;
619 if (error != RETURN_OK)
620 break;
622 else if (doDirs)
624 showdir(dirname, inter);
627 g_indent--;
631 // Output the files
632 if (error == RETURN_OK && (files.num != 0 && doFiles))
634 if (!showfiles(&files, inter))
636 error = RETURN_FAIL;
641 if (dirs.num != 0)
643 for (t = 0; t < dirs.num; t++)
645 FreeVec(dirs.entries[t]);
648 if (dirs.entries)
650 FreeVec(dirs.entries);
654 if (files.num != 0)
656 for (t = 0; t < files.num; t++)
658 FreeVec(files.entries[t]);
661 if (files.entries)
663 FreeVec(files.entries);
668 else
670 SetIoErr(ERROR_NO_FREE_STORE);
671 error = RETURN_FAIL;
674 UnLock(lock);
676 else
678 #if USE_SOFTLINKCHECK
680 struct DevProc *dvp;
681 LONG ioerr;
683 error = RETURN_FAIL;
684 ioerr = IoErr();
686 if (ioerr == ERROR_OBJECT_NOT_FOUND &&
687 (dvp = GetDeviceProc(dir, NULL)))
689 if (ReadLink(dvp->dvp_Port, dvp->dvp_Lock, dir, buffer, sizeof(buffer) - 1) > 0)
691 buffer[sizeof(buffer) - 1] = '\0';
693 Printf("Warning: Skipping dangling softlink %s -> %s\n",
694 (LONG) dir, (LONG) buffer);
696 error = RETURN_OK;
699 FreeDeviceProc(dvp);
702 SetIoErr(ioerr);
704 #else /* USE_SOFTLINKCHECK */
706 error = RETURN_FAIL;
708 #endif /* USE_SOFTLINKCHECK */
711 return error;
715 static
716 int CheckDir(BPTR lock, struct ExAllData *ead, ULONG eadSize,
717 struct ExAllControl *eac, struct table *dirs,
718 struct table *files)
720 int error = RETURN_OK;
721 BOOL loop;
722 struct ExAllData *oldEad = ead;
726 ead = oldEad;
727 loop = ExAll(lock, ead, eadSize, ED_COMMENT, eac);
729 if(!loop && IoErr() != ERROR_NO_MORE_ENTRIES)
731 error = RETURN_ERROR;
732 break;
735 if(eac->eac_Entries != 0)
740 #if USE_SOFTLINKCHECK
741 if (ead->ed_Type == ST_SOFTLINK)
743 BPTR dirlock, l;
745 dirlock = CurrentDir(lock);
746 l = Lock(ead->ed_Name, ACCESS_READ);
747 CurrentDir(dirlock);
749 if (l)
751 UBYTE _fib[sizeof(struct FileInfoBlock) + 3];
752 struct FileInfoBlock *fib = (APTR) (((IPTR) _fib + 3) & ~3);
754 if (Examine(l, fib))
756 ead->ed_Type = fib->fib_DirEntryType;
757 //ead->ed_Size = fib->fib_Size;
760 UnLock(l);
763 #endif /* USE_SOFTLINKCHECK */
765 if (!AddEntry(ead->ed_Type > 0 ? dirs : files,ead->ed_Name))
767 loop = 0;
768 error = RETURN_FAIL;
769 SetIoErr(ERROR_NO_FREE_STORE);
770 break;
773 ead = ead->ed_Next;
775 while(ead != NULL);
778 while((loop) && (error == RETURN_OK));
780 return error;