Hint added.
[AROS.git] / workbench / c / Dir.c
blobd8a2002f3c915ef0a851606f084518c7700436c7
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 B/BACK -- Go back one directory level.
76 DEL/DELETE -- Delete a file or an empty directory.
77 T/TYPE -- Display content of a file.
78 C/COM -- Let the file or directory be the input of
79 a DOS command (which specified after the C or
80 COM or specified separately later).
81 Q/QUIT -- Quit interactive mode.
83 RESULT
85 NOTES
87 EXAMPLE
89 BUGS
91 Interactive mode isn't fully working. It only walks stepwise
92 through the directory.
94 SEE ALSO
96 INTERNALS
98 ******************************************************************************/
100 struct table
102 char **entries;
103 int num;
104 int max;
107 /* Global variables */
108 int g_indent;
109 struct UtilityBase *UtilityBase;
111 /* Prototypes */
113 static
114 LONG doPatternDir(CONST_STRPTR dirPat, BOOL all, BOOL doDirs, BOOL doFiles,
115 BOOL inter);
116 static
117 LONG doDir(CONST_STRPTR dir, BOOL all, BOOL dirs, BOOL files, BOOL inter);
119 static
120 void showline(char *fmt, RAWARG args);
121 static
122 void maybeShowline(char *format, RAWARG args, BOOL doIt, BOOL inter);
123 static
124 void maybeShowlineCR(char *format, RAWARG args, BOOL doIt, BOOL inter);
126 static
127 int CheckDir(BPTR lock, struct ExAllData *ead, ULONG eadSize,
128 struct ExAllControl *eac, struct table *dirs,
129 struct table *files);
131 static
132 TEXT* getLine(CONST_STRPTR prompt, TEXT *buffer, LONG buflen);
134 #define INTERARG_TEMPLATE "E=ENTER/S,B=BACK/S,T=TYPE/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_TYPE,
142 INTERARG_QUIT,
143 INTERARG_COM,
144 INTERARG_COMMAND,
145 NOOFINTERARGS
148 AROS_SH6(Dir, 50.10,
149 AROS_SHA(CONST_STRPTR, ,DIR , , NULL),
150 AROS_SHA(CONST_STRPTR, ,OPT , /K , NULL),
151 AROS_SHA(BOOL, ,ALL , /S , FALSE),
152 AROS_SHA(BOOL, ,DIRS , /S , FALSE),
153 AROS_SHA(BOOL, ,FILES, /S , FALSE),
154 AROS_SHA(BOOL, ,INTER, /S , FALSE)
157 AROS_SHCOMMAND_INIT
159 LONG error = RETURN_FAIL;
160 CONST_STRPTR dir = SHArg(DIR);
161 CONST_STRPTR opt = SHArg(OPT);
162 BOOL all = SHArg(ALL);
163 BOOL dirs = SHArg(DIRS);
164 BOOL files = SHArg(FILES);
165 BOOL inter = SHArg(INTER);
167 LONG iswild;
169 g_indent = 0;
171 UtilityBase = (struct UtilityBase *)OpenLibrary("utility.library", 37);
172 if (!UtilityBase)
173 return error;
175 /* Convert the OPT arguments (if any) into the regular switches */
176 if (opt != NULL)
178 while (*opt != 0)
180 switch (ToUpper(*opt))
182 case 'D':
183 dirs = TRUE;
184 break;
186 case 'F':
187 files = TRUE;
188 break;
190 case 'A':
191 all = TRUE;
192 break;
194 case 'I':
195 inter = TRUE;
196 break;
198 default:
199 Printf("%lc option ignored\n", *opt);
200 break;
202 opt++;
206 if(dir == NULL)
208 dir = "";
209 iswild = 0;
211 else
213 ULONG toklen = strlen(dir) * 2;
214 STRPTR pattok = AllocMem(toklen, MEMF_PUBLIC | MEMF_ANY);
215 iswild = ParsePattern(dir, pattok, toklen);
216 FreeMem(pattok, toklen);
219 if(!files && !dirs)
221 files = TRUE;
222 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
259 static
260 int AddEntry(struct table *table, char *entry)
262 char *dup;
263 int len;
265 if (table->num == table->max)
267 int new_max = table->max + 128;
268 char **new_entries;
270 new_entries = AllocVec(sizeof(char *) * new_max, MEMF_ANY);
272 if (new_entries == NULL)
273 return 0;
275 if (table->num)
277 CopyMemQuick(table->entries, new_entries,
278 sizeof(char *) * table->num);
279 FreeVec(table->entries);
282 table->entries = new_entries;
283 table->max = new_max;
286 len = strlen(entry) + 1;
287 if ((dup = AllocVec(len,MEMF_ANY)))
289 strcpy(dup,entry);
291 else
293 return 0;
295 table->entries[table->num++] = dup;
297 return 1;
301 static __inline
302 int mystrcasecmp(const char *s1, const char *s2)
304 int a, b;
308 a = *s1++;
309 b = *s2++;
311 if (a >= 'a' && a <= 'z') a -= 'a' - 'A';
312 if (b >= 'a' && b <= 'z') b -= 'a' - 'A';
314 a -= b;
315 if (a) return (int) a;
317 } while (b);
319 return 0;
322 static
323 int compare_strings(const void * s1, const void * s2)
325 return mystrcasecmp(*(char **)s1, *(char **)s2);
329 static
330 void maybeShowlineCR(char *format, RAWARG args, BOOL doIt, BOOL inter)
332 maybeShowline(format, args, doIt, inter);
334 if (!inter)
336 PutStr("\n");
341 static
342 void maybeShowline(char *format, RAWARG args, BOOL doIt, BOOL inter)
344 if (doIt)
346 showline(format, args);
348 if (inter)
350 struct RDArgs *inter_rdargs = AllocDosObject(DOS_RDARGS, NULL);
351 if (inter_rdargs)
353 IPTR interArgs[NOOFINTERARGS] = { (IPTR)FALSE,
354 (IPTR)FALSE,
355 (IPTR)FALSE,
356 (IPTR)FALSE,
357 (IPTR)FALSE,
358 (IPTR)FALSE,
359 (IPTR)NULL };
360 TEXT buffer[80];
361 memset(inter_rdargs, 0, sizeof *inter_rdargs);
363 if (getLine(" ? ", buffer, sizeof buffer))
365 inter_rdargs->RDA_Source.CS_Buffer = buffer;
366 inter_rdargs->RDA_Source.CS_Length = sizeof buffer;
368 if (ReadArgs(INTERARG_TEMPLATE, interArgs, inter_rdargs))
370 #if 0
371 if (interArgs[ARG_ENTER])
373 return c_Enter;
375 else if (interArgs[ARG_BACK])
377 return c_Back;
379 else if (interArgs[ARG_DELETE])
381 return c_Delete;
383 else if (interArgs[ARG_QUIT])
385 return c_Quit;
387 else if (interArgs[ARG_COM])
389 return c_Com;
391 else if (interArgs[ARG_COMMAND] != NULL)
393 command =
394 return c_Command;
396 #endif
397 FreeArgs(inter_rdargs);
400 FreeDosObject(DOS_RDARGS, inter_rdargs);
407 static
408 void showline(char *fmt, RAWARG args)
410 int t;
412 for (t = 0; t < g_indent; t++)
413 PutStr(" ");
415 VPrintf(fmt, args);
419 // Returns TRUE if all lines shown, FALSE if broken by SIGBREAKF_CTRL_C
420 static
421 BOOL showfiles(struct table *files, BOOL inter)
423 CONST_STRPTR argv[2];
424 ULONG t;
426 qsort(files->entries, files->num, sizeof(char *),compare_strings);
428 if (inter)
430 for (t = 0; t < files->num; t++)
432 argv[0] = files->entries[t];
434 if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
436 SetIoErr(ERROR_BREAK);
437 return FALSE;
440 maybeShowlineCR(" %s", (RAWARG)argv, TRUE, inter);
443 else
445 for (t = 0; t < files->num; t += 2)
447 argv[0] = files->entries[t];
448 argv[1] = t + 1 < files->num ? files->entries[t+1] : "";
450 if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
452 SetIoErr(ERROR_BREAK);
453 return FALSE;
456 maybeShowlineCR(" %-32.s %s", (RAWARG)argv, TRUE, inter);
459 return TRUE;
463 static
464 BOOL showdir(char *dirName, BOOL inter)
466 maybeShowlineCR("%s (dir)", (RAWARG)&dirName, TRUE, inter);
467 return TRUE;
471 static
472 LONG doPatternDir(CONST_STRPTR dirPat, BOOL all, BOOL doDirs, BOOL doFiles, BOOL inter)
474 struct table files;
475 struct AnchorPath *ap; /* Matching structure */
477 LONG match; /* Loop variable */
478 LONG error = RETURN_FAIL;
480 LONG ioerr = 0;
482 files.entries = NULL;
483 files.max = 0;
484 files.num = 0;
486 ap = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath) + MAX_PATH_LEN, MEMF_CLEAR);
488 if(ap != NULL)
490 ap->ap_Strlen = MAX_PATH_LEN;
491 ap->ap_BreakBits = SIGBREAKF_CTRL_C;
493 if ((match = MatchFirst(dirPat, ap)) == 0)
495 error = RETURN_OK;
496 g_indent++;
497 for (;;)
499 #if USE_SOFTLINKCHECK
500 if (SoftlinkDODIR(ap, TRUE, FALSE, DOSBase))
501 #else /* USE_SOFTLINKCHECK */
502 if (ap->ap_Info.fib_DirEntryType > 0)
503 #endif /* USE_SOFTLINKCHECK */
505 if (doDirs)
507 BPTR l = Lock(ap->ap_Buf, SHARED_LOCK);
508 if (l != BNULL)
510 UBYTE name[512];
511 name[0] = 0;
512 NameFromLock(l, name, 512);
513 UnLock(l);
514 showdir(name, inter);
517 if (all)
519 error = doDir(ap->ap_Buf, all, doDirs, doFiles, inter);
522 else if (doFiles)
524 if (!AddEntry(&files, ap->ap_Info.fib_FileName))
526 ioerr = ERROR_NO_FREE_STORE;
527 error = RETURN_FAIL;
528 break;
531 ioerr = IoErr();
533 if (error != RETURN_OK)
535 break;
537 ap->ap_Strlen = MAX_PATH_LEN;
538 match = MatchNext(ap);
539 if (match != 0)
540 break;
542 g_indent--;
543 if (error == RETURN_OK && files.num != 0)
545 if (!showfiles(&files, inter))
547 error = RETURN_FAIL;
551 MatchEnd(ap);
553 else
555 ioerr = match;
556 error = RETURN_FAIL;
558 FreeVec(ap);
561 SetIoErr(ioerr);
563 return error;
567 static
568 LONG doDir(CONST_STRPTR dir, BOOL all, BOOL doDirs, BOOL doFiles, BOOL inter)
570 BPTR lock;
571 static UBYTE buffer[4096];
572 struct ExAllControl *eac;
573 LONG error = RETURN_OK;
575 struct table dirs;
576 struct table files;
578 dirs.entries = files.entries = NULL;
579 dirs.max = files.max = 0;
580 dirs.num = files.num = 0;
582 lock = Lock(dir, SHARED_LOCK);
584 if (lock != BNULL)
586 eac = AllocDosObject(DOS_EXALLCONTROL, NULL);
588 if (eac != NULL)
590 int t;
592 eac->eac_LastKey = 0;
594 error = CheckDir(lock, (struct ExAllData *)buffer, sizeof(buffer), eac, &dirs, &files);
595 FreeDosObject(DOS_EXALLCONTROL, eac);
597 if (SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
599 SetIoErr(ERROR_BREAK);
600 error = RETURN_FAIL;
603 if (error == 0 && doDirs)
605 if (dirs.num != 0)
607 g_indent++;
609 //qsort(dirs.entries, dirs.num, sizeof(char *), compare_strings);
611 // Output the directories
612 for (t = 0; t < dirs.num; t++)
614 STRPTR dirname = dirs.entries[t];
616 // Recurse into subdirectories if "ALL" was specified by the user
617 if (all)
619 char *newpath;
620 int len;
621 int pathlen = strlen(dir);
623 len = pathlen + strlen(dirs.entries[t]) + 2;
624 newpath = AllocVec(len, MEMF_ANY);
626 if (newpath != NULL)
628 CopyMem(dir, newpath, pathlen + 1);
629 if (AddPart(newpath, dirs.entries[t], len))
631 if (doDirs)
632 showdir(dirname, inter);
633 error = doDir(newpath, all, doDirs, doFiles, inter);
635 else
637 SetIoErr(ERROR_LINE_TOO_LONG);
638 error = RETURN_ERROR;
640 FreeVec(newpath);
642 else
644 SetIoErr(ERROR_NO_FREE_STORE);
645 error = RETURN_FAIL;
647 if (error != RETURN_OK)
648 break;
650 else if (doDirs)
652 showdir(dirname, inter);
655 g_indent--;
659 // Output the files
660 if (error == RETURN_OK && (files.num != 0 && doFiles))
662 if (!showfiles(&files, inter))
664 error = RETURN_FAIL;
669 if (dirs.num != 0)
671 for (t = 0; t < dirs.num; t++)
673 FreeVec(dirs.entries[t]);
676 if (dirs.entries)
678 FreeVec(dirs.entries);
682 if (files.num != 0)
684 for (t = 0; t < files.num; t++)
686 FreeVec(files.entries[t]);
689 if (files.entries)
691 FreeVec(files.entries);
696 else
698 SetIoErr(ERROR_NO_FREE_STORE);
699 error = RETURN_FAIL;
702 UnLock(lock);
704 else
706 #if USE_SOFTLINKCHECK
708 struct DevProc *dvp;
709 LONG ioerr;
711 error = RETURN_FAIL;
712 ioerr = IoErr();
714 if (ioerr == ERROR_OBJECT_NOT_FOUND &&
715 (dvp = GetDeviceProc(dir, NULL)))
717 if (ReadLink(dvp->dvp_Port, dvp->dvp_Lock, dir, buffer, sizeof(buffer) - 1) > 0)
719 buffer[sizeof(buffer) - 1] = '\0';
721 Printf("Warning: Skipping dangling softlink %s -> %s\n",
722 (LONG) dir, (LONG) buffer);
724 error = RETURN_OK;
727 FreeDeviceProc(dvp);
730 SetIoErr(ioerr);
732 #else /* USE_SOFTLINKCHECK */
734 error = RETURN_FAIL;
736 #endif /* USE_SOFTLINKCHECK */
739 return error;
743 static
744 int CheckDir(BPTR lock, struct ExAllData *ead, ULONG eadSize,
745 struct ExAllControl *eac, struct table *dirs,
746 struct table *files)
748 int error = RETURN_OK;
749 BOOL loop;
750 struct ExAllData *oldEad = ead;
754 ead = oldEad;
755 loop = ExAll(lock, ead, eadSize, ED_COMMENT, eac);
757 if(!loop && IoErr() != ERROR_NO_MORE_ENTRIES)
759 error = RETURN_ERROR;
760 break;
763 if(eac->eac_Entries != 0)
768 #if USE_SOFTLINKCHECK
769 if (ead->ed_Type == ST_SOFTLINK)
771 BPTR dirlock, l;
773 dirlock = CurrentDir(lock);
774 l = Lock(ead->ed_Name, ACCESS_READ);
775 CurrentDir(dirlock);
777 if (l)
779 UBYTE _fib[sizeof(struct FileInfoBlock) + 3];
780 struct FileInfoBlock *fib = (APTR) (((IPTR) _fib + 3) & ~3);
782 if (Examine(l, fib))
784 ead->ed_Type = fib->fib_DirEntryType;
785 //ead->ed_Size = fib->fib_Size;
788 UnLock(l);
791 #endif /* USE_SOFTLINKCHECK */
793 if (!AddEntry(ead->ed_Type > 0 ? dirs : files,ead->ed_Name))
795 loop = 0;
796 error = RETURN_FAIL;
797 SetIoErr(ERROR_NO_FREE_STORE);
798 break;
801 ead = ead->ed_Next;
803 while(ead != NULL);
806 while((loop) && (error == RETURN_OK));
808 return error;
812 static
813 TEXT* getLine(CONST_STRPTR prompt, TEXT *buffer, LONG buflen)
815 if (prompt)
817 PutStr(prompt);
819 Flush(Output());
820 return FGets(Input(), buffer, buflen);