re-order some parts of the code so that the msg and rect are only allocated once.
[AROS.git] / workbench / c / Info.c
blob5b7628483e6b2ea6cb3ec43ee6ffd435f92655c4
1 /*
2 Copyright © 1995-2019, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Info Cli Command
6 Lang: English
7 */
9 /******************************************************************************
12 NAME
14 Info
16 SYNOPSIS
18 DISKS/S, VOLS=VOLUMES/S, ALL/S, BLOCKS/S, DEVICES/M
20 LOCATION
24 FUNCTION
26 Show information on file system devices and volumes. When given no
27 arguments, information on all devices and volumes found in the system
28 is displayed. If information is wanted only for some specific devices,
29 these names may be given as arguments.
31 INPUTS
33 DISKS -- show information on file system devices
34 VOLS -- show information on volumes
35 ALL -- show information on bad devices or volumes
36 BLOCKS -- show additional block size and usage information
37 DEVICES -- device names to show information about
39 RESULT
41 NOTES
43 EXAMPLE
45 Info
47 Unit Size Used Free Full Errs State Type Name
48 Harddisk: 964.1M 776.7M 187.4M 81% 0 read/write OFS AROS
49 RAM: 8.0M 7.1M 7.1M 12% 0 read/write OFS Ram Disk
51 BUGS
53 SEE ALSO
55 INTERNALS
57 The original source showed that AROS version of ReadArgs() handles
58 the /M switch with zero arguments differently from AmigaOS. While AROS
59 returns an array where the first pointer is NULL, AmigaOS just returns
60 NULL.
62 HISTORY
64 16.11.2000 SDuvan -- converted to AROS
65 23.12.2000 SDuvan -- changed semantics and updated
66 (now fully functional)
67 17.02.2005 Joe Fenton -- fixed 64bit calculation
69 Based on the original by:
70 © 1997-1998 by Stephan Rupprecht
71 All rights resevered
72 ******************************************************************************/
74 #define DEBUG 0
75 #include <aros/debug.h>
77 #include <dos/dos.h>
78 #include <dos/dosextens.h>
79 #include <dos/filehandler.h>
80 #include <exec/memory.h>
81 #include <libraries/locale.h>
83 #include <proto/dos.h>
84 #include <proto/exec.h>
85 #include <proto/utility.h>
86 #include <proto/locale.h>
87 #include <proto/alib.h>
89 #include <string.h>
91 /* Prototypes */
93 ULONG ComputeKBytes(ULONG a, ULONG b);
94 void FmtProcedure(struct Hook *hook, char a, struct Locale *locale);
95 ULONG ExtUDivMod32(ULONG a, ULONG b, ULONG *mod);
96 void doInfo();
99 const TEXT VersionStr[] = "$VER: Info 41.2 (26.05.2012)";
101 struct Catalog *cat;
102 struct Locale *loc = NULL;
103 ULONG MaxLen;
105 APTR Pool;
108 /* catalog string id:s */
109 enum
111 UNIT,
112 DEVTITLE,
113 DISKSTITLE,
114 DEVFMTSTR,
115 DATEFMTSTR,
116 READONLY,
117 READWRITE,
118 VALIDATING,
119 MOUNTEDSTR,
120 SMALLNUMFMT,
121 BIGNUMFMT,
122 VOLNAMEFMTSTR,
123 BLOCKSSTR
127 struct InfoDosNode
129 struct InfoDosNode *Next;
130 ULONG IsVolume;
131 ULONG DosType;
132 struct MsgPort *Task;
133 struct DateStamp VolumeDate;
134 TEXT Name[108];
137 struct InfoDosNode *head = NULL;
140 struct DiskTypeList
141 { ULONG id;
142 STRPTR str;
145 struct DiskTypeList dtl[] =
147 { ID_DOS_DISK, "OFS" },
148 { ID_FFS_DISK, "FFS" },
149 { ID_INTER_DOS_DISK, "OFS-INT" },
150 { ID_INTER_FFS_DISK, "FFS-INT" },
151 { ID_FASTDIR_DOS_DISK, "OFS-DC" },
152 { ID_FASTDIR_FFS_DISK, "FFS-DC" },
154 { ID_DOS_muFS_DISK, "muFS OFS" },
155 { ID_FFS_muFS_DISK, "muFS FFS" },
156 { ID_INTER_DOS_muFS_DISK, "muFS OFS-INT" },
157 { ID_INTER_FFS_muFS_DISK, "muFS FFS-INT" },
158 { ID_FASTDIR_DOS_muFS_DISK, "muFS OFS-DC" },
159 { ID_FASTDIR_FFS_muFS_DISK, "muFS FFS-DC" },
161 { ID_MSDOS_DISK, "MS-DOS" },
162 { ID_ACD0_DISK, "CDFS" },
163 { ID_CACHECDFS_DISK, "CDFS" },
164 { ID_ASIMCDFS_DISK, "CDFS" },
165 { ID_NOT_REALLY_DOS, "NO DOS" },
166 { ID_MAC_DISK2, "MAC" },
167 { ID_MNX1_DISK, "Minix" },
168 { ID_QL5A_DISK, "QL720k" },
169 { ID_QL5B_DISK, "QL1.4M" },
170 { ID_CPM_DISK, "CP/M" },
171 { ID_ZXS3_DISK, "+3Dos" },
172 { ID_ZXS0_DISK, "Disciple" },
173 { ID_ZXS1_DISK, "UniDos" },
174 { ID_ZXS2_DISK, "SamDos" },
175 { ID_ZXS4_DISK, "Opus" },
176 { ID_P2A0_DISK, "NETWORK" },
178 { ID_FAT12_DISK, "FAT12" },
179 { ID_FAT16_DISK, "FAT16" },
180 { ID_FAT32_DISK, "FAT32" },
182 { ID_SFS_BE_DISK, "SFS-BE" },
183 { ID_SFS_LE_DISK, "SFS-LE" },
185 { ID_PFS_DISK, "PFS" },
187 { ID_EXT2_DISK, "EXT2" },
188 { 0L, 0L }
192 /****************************************************************************/
194 int UtilityBase_version = 0;
195 int LocaleBase_version = 0;
197 int __nocommandline;
199 int main(void)
201 static struct TagItem loctags[] = { { OC_Version, 1 },
202 { TAG_END , 0 } };
203 cat = OpenCatalogA(NULL, "info_com.catalog", loctags);
204 loc = OpenLocale(NULL);
206 D(bug("Calling doInfo()\n"));
208 doInfo();
210 CloseLocale(loc);
211 CloseCatalog(cat);
213 return RETURN_OK; /* TODO: Fix this */
217 CONST_STRPTR GetStrFromCat(ULONG id, CONST_STRPTR def)
219 if(cat != NULL)
221 def = GetCatalogStr(cat, id, def);
224 return def;
228 void VLPrintf(ULONG id, CONST_STRPTR def, ...)
230 CONST_STRPTR format = GetStrFromCat(id, def);
231 AROS_SLOWSTACKFORMAT_PRE_USING(def, format);
232 VPrintf(format, AROS_SLOWSTACKFORMAT_ARG(def));
233 AROS_SLOWSTACKFORMAT_POST(def);
237 BOOL myMatchPatternNoCase(STRPTR *array, STRPTR str)
239 if(*array != NULL)
241 while(*array != NULL)
243 UBYTE matchstr[128];
244 UBYTE name[32];
245 UBYTE *p = *array++;
246 UBYTE len = strlen(p);
248 if(p[len - 1] != ':')
250 CopyMem(p, name, len);
251 name[len] = ':';
252 name[len + 1] = 0;
253 p = name;
256 if(ParsePatternNoCase(p, matchstr, sizeof(matchstr)) != -1)
258 if(MatchPatternNoCase(matchstr, str))
260 return TRUE;
265 return FALSE;
268 return TRUE;
272 BOOL ScanDosList(STRPTR *filter)
274 struct InfoDosNode *idn = 0L;
275 struct DosList *ndl, *dl;
276 STRPTR *strray = NULL, dummy = NULL;
277 BOOL err = FALSE;
279 D(bug("Entered ScanDosList()\n"));
281 if (filter == NULL) filter = &dummy;
283 if(*filter != NULL)
285 strray = AllocPooled(Pool, sizeof(STRPTR)*MAX_MULTIARGS);
287 if(strray != NULL)
289 STRPTR *p = filter;
290 LONG i = 0;
292 while(*p)
293 strray[i++] = *p++;
295 while(i < MAX_MULTIARGS)
296 strray[i++] = NULL;
298 else
299 return FALSE;
302 /* lock list of devices & vols */
303 dl = ndl = LockDosList(LDF_ASSIGNS | LDF_VOLUMES | LDF_DEVICES | LDF_READ);
305 if(strray != NULL)
307 STRPTR *p = strray;
309 while(*p)
310 p++;
312 while((ndl = NextDosEntry(ndl, LDF_ASSIGNS | LDF_VOLUMES | LDF_READ)) != NULL)
314 TEXT name[108];
315 STRPTR taskName = NULL; /* Initialized to avoid a warning */
317 __sprintf(name, "%b:", ndl->dol_Name);
319 if ((ndl->dol_Type > DLT_VOLUME) || !(myMatchPatternNoCase(strray, name)))
321 continue;
324 switch (ndl->dol_Type)
326 case DLT_VOLUME:
327 taskName = ((struct Task *)ndl->dol_Task->mp_SigTask)->tc_Node.ln_Name;
329 D(bug("Found volume %s\n", taskName));
330 break;
332 case DLT_DIRECTORY:
334 struct AssignList *al = ndl->dol_misc.dol_assign.dol_List;
336 taskName = ((struct Task *)((struct FileLock *)BADDR(ndl->dol_Lock))->fl_Task->mp_SigTask)->tc_Node.ln_Name;
338 D(bug("Found directory %s\n", taskName));
340 while(al != NULL)
342 *p++ = ""; // TODO!!! ((struct Task *)((struct FileLock *)BADDR(al->al_Lock))->fl_Task->mp_SigTask)->tc_Node.ln_Name;
343 al = al->al_Next;
346 break;
349 *p++ = taskName;
352 else
353 strray = filter;
355 ndl = dl;
357 while((ndl = NextDosEntry(ndl, LDF_VOLUMES | LDF_DEVICES | LDF_READ)) != NULL)
359 UBYTE len = 0;
360 UBYTE type = ndl->dol_Type;
361 UBYTE name[108];
363 /* do not start non-started handlers or open CON: or RAW: windows */
364 if(type == DLT_DEVICE && !ndl->dol_Task)
365 continue;
367 __sprintf(name, "%b:", ndl->dol_Name);
368 D(bug("Found name %s\n", name));
370 if((type == DLT_DEVICE) && (myMatchPatternNoCase(strray, name) == FALSE))
372 D(int i);
374 D(bug("Failure! -- name = %s, strray = %p\n", name, (void *)strray));
376 D(for (i = 0; strray[i] != NULL; i++)
378 bug("Strray %i = %s\n", i, strray[i]);
381 continue;
384 idn = (struct InfoDosNode *)AllocPooled(Pool, sizeof(struct InfoDosNode));
386 if(idn == NULL)
388 err = TRUE;
389 break;
392 idn->Task = ndl->dol_Task;
393 idn->IsVolume = type == DLT_VOLUME;
395 while((idn->Name[len] = name[len]))
396 len++;
398 if(type == DLT_VOLUME)
400 idn->VolumeDate = ((struct DeviceList *)ndl)->dl_VolumeDate;
401 idn->Name[len - 1] = '\0'; /* remove ':' */
403 else
405 BPTR ptr = ndl->dol_misc.dol_handler.dol_Startup;
406 struct FileSysStartupMsg *fssm = NULL;
408 if (IsFileSystem(idn->Name))
410 // Only filesystems have a valid FileSysStartupMsg
411 fssm = (struct FileSysStartupMsg *)BADDR(ptr);
414 idn->DosType = ID_DOS_DISK;
416 // DLT_DEVICE
417 if (len > MaxLen)
418 MaxLen = len;
420 if (fssm)
422 struct DosEnvec *de;
423 de = (struct DosEnvec *)BADDR(fssm->fssm_Environ);
425 if (de && (de->de_TableSize & 0xffffff00) == 0)
426 if (de->de_DosType)
427 idn->DosType = de->de_DosType;
431 /* kinda insert sort */
433 struct InfoDosNode *work = head;
434 struct InfoDosNode *prev = NULL;
436 while((work != NULL) && (Stricmp(idn->Name, work->Name) > 0))
438 prev = work;
439 work = work->Next;
442 if(prev != NULL)
443 prev->Next = idn;
444 else
445 head = idn;
447 idn->Next = work;
451 /* unlock list of devices and volumes */
452 UnLockDosList(LDF_ASSIGNS | LDF_VOLUMES | LDF_DEVICES | LDF_READ);
454 // strray freed at DeletePool
456 return !err;
460 void PrintNum(ULONG num)
462 /* MBytes ? */
463 if(num > 1023)
465 ULONG x, xx;
466 char fmt = 'M';
468 /* GBytes ? */
469 if(num > 0xfffff)
471 num >>= 10;
472 fmt = 'G';
475 num = ExtUDivMod32(UMult32(num, 100) >> 10, 100, &x);
477 /* round */
478 x = ExtUDivMod32(x, 10, &xx);
480 if(xx > 4)
482 if(++x > 9)
484 x = 0;
485 num++;
489 VLPrintf(BIGNUMFMT, "%5ld.%ld%lc", num, x, fmt);
491 else
493 VLPrintf(SMALLNUMFMT, "%7ldK", num);
498 STRPTR GetFSysStr(ULONG DiskType)
500 struct DiskTypeList *dtlptr = dtl;
502 STRPTR ptr = NULL;
504 do {
505 if(dtlptr->id == DiskType)
507 ptr = dtlptr->str;
508 break;
510 } while(*((ULONG *)dtlptr++));
512 if(ptr == NULL)
514 static TEXT buffer[5];
516 ptr = (STRPTR)buffer;
517 *((ULONG *)ptr) = AROS_LONG2BE(DiskType);
519 if(ptr[3] < ' ')
520 ptr[3] += '0';
522 ptr[4] = '\0';
525 return ptr;
529 enum
531 ARG_DISKS,
532 ARG_VOLS,
533 ARG_ALL,
534 ARG_BLOCKS,
535 ARG_DEVS,
536 NOOFARGS
540 void doInfo()
542 struct RDArgs *rdargs;
543 struct Process *proc;
544 struct Window *win;
545 struct InfoDosNode *idn;
547 struct InfoData *id = AllocVec(sizeof(struct InfoData), MEMF_ANY);
549 IPTR args[] = { (IPTR)FALSE,
550 (IPTR)FALSE,
551 (IPTR)FALSE,
552 (IPTR)FALSE,
553 (IPTR)NULL };
555 CONST_STRPTR unit = GetStrFromCat(UNIT, "Unit");
557 if(id == NULL)
559 PrintFault(ERROR_NO_FREE_STORE, NULL);
560 return;
563 Pool = CreatePool(MEMF_ANY, 1024, 1024);
565 if(Pool == NULL)
567 PrintFault(ERROR_NO_FREE_STORE, NULL);
568 return; /* ??? */
571 D(bug("Calling ReadArgs()\n"));
573 /* read arguments */
574 rdargs = ReadArgs("DISKS/S,VOLS=VOLUMES/S,ALL/S,BLOCKS/S,DEVICES/M",
575 args, NULL);
577 if(rdargs != NULL)
579 BOOL disks = (BOOL)args[ARG_DISKS];
580 BOOL vols = (BOOL)args[ARG_VOLS];
581 BOOL showall = (BOOL)args[ARG_ALL];
582 BOOL blocks = (BOOL)args[ARG_BLOCKS];
583 STRPTR *devs = (STRPTR *)args[ARG_DEVS];
585 if (devs && (*devs == NULL)) devs = NULL;
587 /* If nothing is specified, show everything we got */
588 if(devs == NULL && !disks && !vols)
590 vols = TRUE;
591 disks = TRUE;
594 /* check pattern strings */
595 if(devs != NULL)
597 STRPTR *p = devs;
599 while(*p != NULL)
601 TEXT matchstr[128];
603 if(ParsePatternNoCase(*p, matchstr, sizeof(matchstr)) == -1)
605 PrintFault(IoErr(), *p);
606 goto end;
609 p++;
613 /* avoid requesters */
614 proc = (struct Process *)FindTask(NULL);
615 win = (struct Window *)proc->pr_WindowPtr;
616 proc->pr_WindowPtr = (struct Window *)~0;
618 MaxLen = strlen(unit);
620 D(bug("Calling ScanDosList()\n"));
622 /* scan doslist */
623 if(ScanDosList(devs))
625 CONST_STRPTR dstate[3] = { GetStrFromCat(READONLY, "read only"),
626 GetStrFromCat(VALIDATING, "validating"),
627 GetStrFromCat(READWRITE, "read/write") };
628 STRPTR datetimeFmt = NULL;
629 BOOL first = TRUE;
630 TEXT nfmtstr[16];
631 TEXT buf[64];
633 D(bug("Printing stuff\n"));
635 /* get datetimefmt string */
636 if(loc && (GetVar("info_datetime", buf, sizeof(buf), 0L) > 0L))
638 datetimeFmt = buf;
641 /* calc format string for 'Unit' */
642 __sprintf(nfmtstr, "%%-%lds", MaxLen);
644 /* show device infomation */
645 if(devs != NULL || disks || !vols)
647 for(idn = head; idn; idn = idn->Next)
649 BPTR lock;
650 STRPTR name = idn->Name;
652 D(bug("Got name = %s\n", name));
654 if(!idn->IsVolume && IsFileSystem(name))
656 BOOL gotinfo = FALSE;
657 /* if first device to print, print title */
658 if(first || blocks)
660 if(!first)
661 Printf("\n");
663 D(bug("Printing device\n"));
665 VLPrintf(~0, nfmtstr, unit);
666 VLPrintf(DEVTITLE, " Size Used Free Full Errs State Type Name\n");
668 first = FALSE;
671 VLPrintf(~0, nfmtstr, name);
673 D(bug("Locking \"%s\"\n", name));
674 lock = Lock(name, SHARED_LOCK);
676 D(bug("Lock = %p\n", (APTR)lock));
678 if(lock != BNULL)
680 D(bug("Got lock on %s\n", name));
682 if(Info(lock, id) == DOSTRUE)
684 D(bug("Calling NameFromLock()\n"));
686 if(NameFromLock(lock, name, 108L))
688 LONG len = strlen(name) - 1;
690 if(name[len] == ':')
692 name[len] = '\0';
696 gotinfo = TRUE;
698 UnLock(lock);
700 } else if (idn->Task) {
701 name = NULL;
702 D(bug("Calling ACTION_DISK_INFO\n"));
703 if (DoPkt(idn->Task, ACTION_DISK_INFO, (SIPTR)MKBADDR(id), (SIPTR)BNULL, (SIPTR)BNULL, (SIPTR)BNULL, (SIPTR)BNULL)) {
704 gotinfo = TRUE;
708 if (gotinfo) {
709 ULONG x, y;
711 D(bug("Got info on %s\n", name));
713 if (id->id_DiskType == ID_NO_DISK_PRESENT) {
714 VLPrintf(~0, " No disk present\n");
715 } else if (id->id_DiskType == ID_NOT_REALLY_DOS) {
716 VLPrintf(~0, " Not a DOS disk\n");
717 } else if (id->id_DiskType == ID_UNREADABLE_DISK) {
718 VLPrintf(~0, " Unreadable disk\n");
719 } else {
720 x = ComputeKBytes(id->id_NumBlocks, id->id_BytesPerBlock);
721 y = ComputeKBytes(id->id_NumBlocksUsed, id->id_BytesPerBlock);
723 PrintNum(x);
724 PrintNum(y);
725 PrintNum(x - y);
727 if(x > 0xfffff)
729 x >>= 10;
730 y >>= 10;
733 if(x)
735 x = ExtUDivMod32(UDivMod32(UMult32(y, 1000), x), 10, &y);
737 if(y > 4)
738 x++;
740 else
741 x = 0;
743 // y = ((struct DeviceList *)BADDR(id->id_VolumeNode))->dl_DiskType;
745 // if(!y)
746 y = id->id_DiskType;
748 if((idn->DosType & ID_DOS_DISK) != ID_DOS_DISK)
749 y = idn->DosType;
752 VLPrintf(DEVFMTSTR, "%4ld%% %4ld %-11s%-8s%s\n",
753 x, id->id_NumSoftErrors,
754 ((id->id_DiskState >= ID_WRITE_PROTECTED) && (id->id_DiskState <= ID_VALIDATED)) ? (const char *)dstate[id->id_DiskState - ID_WRITE_PROTECTED] : (const char *)"",
755 GetFSysStr(y),
756 name);
759 if(blocks)
761 VLPrintf(BLOCKSSTR,
762 "\nTotal blocks: %-10ld Blocks used: %ld\n"
763 " Blocks free: %-10ld Blocksize: %ld\n",
764 id->id_NumBlocks,
765 id->id_NumBlocksUsed,
766 id->id_NumBlocks-id->id_NumBlocksUsed,
767 id->id_BytesPerBlock);
771 else
773 D(bug("Info failure\n"));
774 VLPrintf(~0, "\n");
778 LONG err = IoErr();
780 /* just ignore PIPEFS */
781 if (err == ERROR_ACTION_NOT_KNOWN)
782 if (strcmp(name, "PIPEFS:") == 0)
783 err = 0;
785 if (err && showall)
787 VLPrintf(~0, nfmtstr, name);
788 PrintFault(err, NULL);
795 /* show volumes */
796 if(vols || (!devs && !disks))
798 if(!first)
799 PutStr("\n");
801 VLPrintf(DISKSTITLE, "Volumes\n");
803 /* find the longest volume name */
804 for(MaxLen = 15, idn = head; idn; idn = idn->Next)
806 if(idn->IsVolume)
808 LONG len = strlen(idn->Name);
810 if(len > MaxLen)
811 MaxLen = len;
815 __sprintf(nfmtstr, "%%-%lds%%-10s", MaxLen+1);
817 for(idn = head; idn; idn = idn->Next)
819 if(idn->IsVolume)
821 VLPrintf(VOLNAMEFMTSTR, nfmtstr, idn->Name,
822 idn->Task ? (const char *)GetStrFromCat(MOUNTEDSTR, "[Mounted]") : (const char *)"");
824 if(idn->VolumeDate.ds_Days != 0)
826 if(datetimeFmt)
828 UBYTE datestr[128];
829 static struct Hook hook;
831 memset(&hook, 0, sizeof(struct Hook));
833 hook.h_SubEntry = (HOOKFUNC)FmtProcedure;
834 hook.h_Data = datestr;
836 FormatDate(loc, datetimeFmt, &idn->VolumeDate, &hook);
838 PutStr(datestr);
840 else
842 TEXT StrDay[LEN_DATSTRING];
843 TEXT StrDate[LEN_DATSTRING];
844 TEXT StrTime[LEN_DATSTRING];
846 struct DateTime dt;
848 dt.dat_Flags = DTF_SUBST;
849 dt.dat_Format = FORMAT_DOS;
850 dt.dat_StrDay = StrDay;
851 dt.dat_StrDate = StrDate;
852 dt.dat_StrTime = StrTime;
853 dt.dat_Stamp = idn->VolumeDate;
855 if(DateToStr(&dt))
857 if(Strnicmp(StrDate, StrDay, strlen(StrDay)) == 0)
859 dt.dat_Flags = 0L;
860 DateToStr(&dt);
863 VLPrintf(DATEFMTSTR, "created %.3s, %-10s %s", StrDay, StrDate, StrTime);
868 PutStr("\n");
873 else
875 PrintFault( ERROR_NO_FREE_STORE, NULL);
878 /* reset window pointer of our process */
879 proc->pr_WindowPtr = win;
881 /* free args */
882 FreeArgs(rdargs);
886 end: /* free allocated memory */
887 FreeVec(id);
888 DeletePool(Pool);
892 ULONG ComputeKBytes(ULONG a, ULONG b)
894 // UQUAD result = UMult64(a, b);
896 UQUAD result = (UQUAD)a * b;
898 return (ULONG)(result >> 10);
902 void FmtProcedure(struct Hook *hook, char a, struct Locale *locale)
904 *((STRPTR)hook->h_Data) = a;
905 hook->h_Data = (STRPTR) hook->h_Data + 1;
909 ULONG ExtUDivMod32(ULONG a, ULONG b, ULONG *mod)
911 *mod = a % b;
913 return a/b;