added concrete implementations of putc(), getc(), getchar() and gets()
[tangerine.git] / workbench / c / Info.c
blob9e4d6909ba463bf2a7cca3012283bd91b7e8f679
1 /*
2 Copyright © 1995-2007, 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
22 Sys:C
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>
92 #define ID_MAC_DISK2 (0x4d414300L) /* MAC\0 - xfs mac disk */
93 #define ID_MNX1_DISK (0x4d4e5801L) /* MNX\1 - xfs minix disk */
94 #define ID_MNX2_DISK (0x4d4e5802L) /* MNX\2 - xfs minix disk */
95 #define ID_QL5A_DISK (0x514c3541L) /* QL5A - xfs ql 720k / ed disk */
96 #define ID_QL5B_DISK (0x514c3542L) /* QL5B - xfs ql 1440k disk */
97 #define ID_ZXS0_DISK (0x5a585300L) /* Spectrum Disciple - xfs */
98 #define ID_ZXS1_DISK (0x5a585301L) /* Spectrum UniDos - xfs */
99 #define ID_ZXS2_DISK (0x5a585302L) /* Spectrum SamDos - xfs */
100 #define ID_ZXS4_DISK (0x5a585304L) /* Spectrum Opus 180k - xfs */
101 #define ID_ARME_DISK (0x41524d44L) /* Archimedes - xfs */
102 #define ID_ARMD_DISK (0x41524d43L) /* Archimedes - xfs */
103 #define ID_CPM_DISK (0x43505c4dL) /* CP/M - xfs */
104 #define ID_ZXS3_DISK (0x5a585303L) /* ZXS\3 - Plus3Dos xfs */
105 #define ID_1541_DISK (0x31353431L) /* 1541 - xfs */
106 #define ID_1581_DISK (0x31353831L) /* 1581 - xfs */
107 #define ID_MAC_DISK (0x4d534800L) /* MSH\0 - CrossDos MACDisk ?! */
108 #define ID_ACD0_DISK (0x41434400L) /* ACD\0 - AmiCDFS disk */
109 #define ID_CDFS_DISK (0x43444653L) /* CDFS - AmiCDFS disk */
110 #define ID_CACHECDFS_DISK (0x43443031L)
111 #define ID_ASIMCDFS_DISK (0x662dabacL)
112 #define ID_PFS2_DISK (0x50465302L)
113 #define ID_PFS2_SCSI_DISK (0x50445300L)
114 #define ID_PFS2_muFS_DISK (0x6d755046L)
115 #define ID_FLOPPY_PFS_DISK (0x50465300L)
116 #define ID_P2A0_DISK (0x50324130L)
117 #define ID_AFS0_DISK (0x41465300L) /* AFS\0 */
118 #define ID_muFS_DISK (0x6d754653L) /* muFS - Mulituserfsys */
119 #define ID_FAT12_DISK (0x46415400L) /* FAT12 */
120 #define ID_FAT16_DISK (0x46415401L) /* FAT16 */
121 #define ID_FAT32_DISK (0x46415402L) /* FAT32 */
124 /* Prototypes */
126 ULONG ComputeKBytes(ULONG a, ULONG b);
127 void FmtProcedure(struct Hook *hook, char a, struct Locale *locale);
128 ULONG ExtUDivMod32(ULONG a, ULONG b, ULONG *mod);
129 void doInfo();
132 const TEXT VersionStr[] = "$VER: Info 41.1 (16.11.2000)";
134 struct Catalog *cat;
135 struct Locale *loc = NULL;
136 ULONG MaxLen;
138 APTR Pool;
141 /* catalog string id:s */
142 enum
144 UNIT,
145 DEVTITLE,
146 DISKSTITLE,
147 DEVFMTSTR,
148 DATEFMTSTR,
149 READONLY,
150 READWRITE,
151 VALIDATING,
152 MOUNTEDSTR,
153 SMALLNUMFMT,
154 BIGNUMFMT,
155 VOLNAMEFMTSTR,
156 BLOCKSSTR
160 struct InfoDosNode
162 struct InfoDosNode *Next;
163 ULONG IsVolume;
164 ULONG DosType;
165 struct MsgPort *Task;
166 struct DateStamp VolumeDate;
167 TEXT Name[108];
170 struct InfoDosNode *head = NULL;
173 struct DiskTypeList
174 { ULONG id;
175 STRPTR str;
178 struct DiskTypeList dtl[] =
180 { ID_DOS_DISK, "OFS" },
181 { ID_FFS_DISK, "FFS" },
182 { ID_INTER_DOS_DISK, "OFS-INT" },
183 { ID_INTER_FFS_DISK, "FFS-INT" },
184 { ID_FASTDIR_DOS_DISK, "OFS-DC" },
185 { ID_FASTDIR_FFS_DISK, "FFS-DC" },
186 { ID_MSDOS_DISK, "MS-DOS" },
187 { ID_ACD0_DISK, "CDFS" },
188 { ID_CACHECDFS_DISK, "CDFS" },
189 { ID_ASIMCDFS_DISK, "CDFS" },
190 { ID_NOT_REALLY_DOS, "NO DOS" },
191 { ID_MAC_DISK2, "MAC" },
192 { ID_MNX1_DISK, "Minix" },
193 { ID_QL5A_DISK, "QL720k" },
194 { ID_QL5B_DISK, "QL1.4M" },
195 { ID_CPM_DISK, "CP/M" },
196 { ID_ZXS3_DISK, "+3Dos" },
197 { ID_ZXS0_DISK, "Disciple " },
198 { ID_ZXS1_DISK, "UniDos" },
199 { ID_ZXS2_DISK, "SamDos" },
200 { ID_ZXS4_DISK, "Opus" },
201 { ID_P2A0_DISK, "NETWORK" },
202 { ID_FAT12_DISK, "FAT12" },
203 { ID_FAT16_DISK, "FAT16" },
204 { ID_FAT32_DISK, "FAT32" },
205 { 0L, 0L }
209 /****************************************************************************/
211 int UtilityBase_version = 0;
212 int LocaleBase_version = 0;
214 int __nocommandline;
216 int main(void)
218 static struct TagItem loctags[] = { { OC_Version, 1 },
219 { TAG_END , 0 } };
220 cat = OpenCatalogA(NULL, "info_com.catalog", loctags);
221 loc = OpenLocale(NULL);
223 D(bug("Calling doInfo()\n"));
225 doInfo();
227 CloseLocale(loc);
228 CloseCatalog(cat);
230 return RETURN_OK; /* TODO: Fix this */
234 CONST_STRPTR GetStrFromCat(ULONG id, CONST_STRPTR def)
236 if(cat != NULL)
238 def = GetCatalogStr(cat, id, def);
241 return def;
245 void LPrintf(ULONG id, CONST_STRPTR def, ...) __stackparm;
247 void LPrintf(ULONG id, CONST_STRPTR def, ...)
249 def = GetStrFromCat(id, def);
251 VPrintf(def, ((IPTR *)(&def))+1);
255 BOOL myMatchPatternNoCase(STRPTR *array, STRPTR str)
257 if(*array != NULL)
259 while(*array != NULL)
261 UBYTE matchstr[128];
262 UBYTE name[32];
263 UBYTE *p = *array++;
264 UBYTE len = strlen(p);
266 if(p[len - 1] != ':')
268 CopyMem(p, name, len);
269 name[len] = ':';
270 name[len + 1] = 0;
271 p = name;
274 if(ParsePatternNoCase(p, matchstr, sizeof(matchstr)) != -1)
276 if(MatchPatternNoCase(matchstr, str))
278 return TRUE;
283 return FALSE;
286 return TRUE;
290 BOOL ScanDosList(STRPTR *filter)
292 struct InfoDosNode *idn = 0L;
293 struct DosList *ndl, *dl;
294 STRPTR *strray = NULL, dummy = NULL;
295 BOOL err = FALSE;
297 D(bug("Entered ScanDosList()\n"));
299 if (filter == NULL) filter = &dummy;
301 if(*filter != NULL)
303 strray = AllocPooled(Pool, sizeof(STRPTR)*MAX_MULTIARGS);
305 if(strray != NULL)
307 STRPTR *p = filter;
308 LONG i = 0;
310 while(*p)
311 strray[i++] = *p++;
313 while(i < MAX_MULTIARGS)
314 strray[i++] = NULL;
316 else
317 return FALSE;
320 /* lock list of devices & vols */
321 dl = ndl = LockDosList(LDF_ASSIGNS | LDF_VOLUMES | LDF_DEVICES | LDF_READ);
323 if(strray != NULL)
325 STRPTR *p = strray;
327 while(*p)
328 p++;
330 while((ndl = NextDosEntry(ndl, LDF_ASSIGNS | LDF_VOLUMES | LDF_READ)) != NULL)
332 TEXT name[108];
333 STRPTR taskName = NULL; /* Initialized to avoid a warning */
335 __sprintf(name, "%s:", ndl->dol_Ext.dol_AROS.dol_DevName);
337 if ((ndl->dol_Type > DLT_VOLUME) || !(myMatchPatternNoCase(strray, name)))
339 continue;
342 switch (ndl->dol_Type)
344 case DLT_VOLUME:
345 taskName = ndl->dol_Ext.dol_AROS.dol_DevName; // ((struct Task *)ndl->dol_Task->mp_SigTask)->tc_Node.ln_Name;
347 D(bug("Found volume %s\n", taskName));
348 break;
350 case DLT_DIRECTORY:
352 struct AssignList *al = ndl->dol_misc.dol_assign.dol_List;
355 taskName = ndl->dol_Ext.dol_AROS.dol_DevName; // ((struct Task *)((struct FileLock *)BADDR(ndl->dol_Lock))->fl_Task->mp_SigTask)->tc_Node.ln_Name;
357 D(bug("Found directory %s\n", taskName));
359 while(al != NULL)
361 *p++ = ""; // TODO!!! ((struct Task *)((struct FileLock *)BADDR(al->al_Lock))->fl_Task->mp_SigTask)->tc_Node.ln_Name;
362 al = al->al_Next;
365 break;
368 *p++ = taskName;
371 else
372 strray = filter;
374 ndl = dl;
376 while((ndl = NextDosEntry(ndl, LDF_VOLUMES | LDF_DEVICES | LDF_READ)) != NULL)
378 UBYTE len = 0;
379 UBYTE type = ndl->dol_Type;
380 UBYTE name[108];
382 // if(((type == DLT_DEVICE))) // && (!ndl->dol_Task) TODO Check this!
383 // continue;
385 __sprintf(name, "%s:", ndl->dol_Ext.dol_AROS.dol_DevName);
387 D(bug("Found name %s\n", ndl->dol_Ext.dol_AROS.dol_DevName));
389 if((type == DLT_DEVICE) && (myMatchPatternNoCase(strray, name) == FALSE))
391 int i;
393 D(bug("Failure! -- name = %s, strray = %p\n", name, (void *)strray));
395 for (i = 0; strray[i] != NULL; i++)
397 D(bug("Strray %i = %s\n", i, strray[i]));
400 continue;
403 idn = (struct InfoDosNode *)AllocPooled(Pool, sizeof(struct InfoDosNode));
405 if(idn == NULL)
407 err = TRUE;
408 break;
411 // idn->Task = (struct MsgPort *)ndl->dol_Task;
412 idn->IsVolume = type == DLT_VOLUME;
414 while((idn->Name[len] = name[len]))
415 len++;
417 if(type == DLT_VOLUME)
419 idn->VolumeDate = ((struct DeviceList *)ndl)->dl_VolumeDate;
420 idn->Name[len - 1] = '\0'; /* remove ':' */
422 else
424 // struct FileSysStartupMsg *fssm = (struct FileSysStartupMsg *)BADDR(ndl->dol_misc.dol_handler.dol_Startup);
426 idn->DosType = ID_DOS_DISK;
428 // DLT_DEVICE
429 if(len > MaxLen)
430 MaxLen = len;
432 #if 0
433 if(TypeOfMem(fssm))
435 if(*(UBYTE *)fssm == 0 || *(UBYTE *)BADDR(fssm->fssm_Device) != 0)
437 struct DosEnvec *de = (struct DosEnvec *)BADDR(fssm->fssm_Environ);
439 if(de != NULL && (de->de_TableSize & 0xffffff00) == 0L)
441 if(de->de_DosType)
442 idn->DosType = de->de_DosType;
446 #endif
450 /* kinda insert sort */
452 struct InfoDosNode *work = head;
453 struct InfoDosNode *prev = NULL;
455 while((work != NULL) && (Stricmp(idn->Name, work->Name) > 0))
457 prev = work;
458 work = work->Next;
461 if(prev != NULL)
462 prev->Next = idn;
463 else
464 head = idn;
466 idn->Next = work;
470 /* unlock list of devices and volumes */
471 UnLockDosList(LDF_ASSIGNS | LDF_VOLUMES | LDF_DEVICES | LDF_READ);
473 // strray freed at DeletePool
475 return !err;
479 void PrintNum(ULONG num)
481 /* MBytes ? */
482 if(num > 1023)
484 ULONG x, xx;
485 char fmt = 'M';
487 /* GBytes ? */
488 if(num > 0xfffff)
490 num >>= 10;
491 fmt = 'G';
494 num = ExtUDivMod32(UMult32(num, 100) >> 10, 100, &x);
496 /* round */
497 x = ExtUDivMod32(x, 10, &xx);
499 if(xx > 4)
501 if(++x > 9)
503 x = 0;
504 num++;
508 LPrintf(BIGNUMFMT, "%5ld.%ld%lc", num, x, fmt);
510 else
512 LPrintf(SMALLNUMFMT, "%7ldK", num);
517 STRPTR GetFSysStr(ULONG DiskType)
519 struct DiskTypeList *dtlptr = dtl;
521 STRPTR ptr = NULL;
523 do {
524 if(dtlptr->id == DiskType)
526 ptr = dtlptr->str;
527 break;
529 } while(*((ULONG *)dtlptr++));
531 if(ptr == NULL)
533 static TEXT buffer[5];
535 ptr = (STRPTR)buffer;
536 *((ULONG *)ptr) = DiskType;
538 if(ptr[3] < ' ')
539 ptr[3] += '0';
541 ptr[4] = '\0';
544 return ptr;
548 enum
550 ARG_DISKS,
551 ARG_VOLS,
552 ARG_ALL,
553 ARG_BLOCKS,
554 ARG_DEVS,
555 NOOFARGS
559 void doInfo()
561 struct RDArgs *rdargs;
562 struct Process *proc;
563 struct Window *win;
564 struct InfoDosNode *idn;
566 static struct InfoData id;
568 IPTR args[] = { (IPTR)FALSE,
569 (IPTR)FALSE,
570 (IPTR)FALSE,
571 (IPTR)FALSE,
572 (IPTR)NULL };
574 CONST_STRPTR unit = GetStrFromCat(UNIT, "Unit");
576 Pool = CreatePool(MEMF_ANY, 1024, 1024);
578 if(Pool == NULL)
580 PrintFault(ERROR_NO_FREE_STORE, NULL);
581 return; /* ??? */
584 D(bug("Calling ReadArgs()\n"));
586 /* read arguments */
587 rdargs = ReadArgs("DISKS/S,VOLS=VOLUMES/S,ALL/S,BLOCKS/S,DEVICES/M",
588 args, NULL);
590 if(rdargs != NULL)
592 BOOL disks = (BOOL)args[ARG_DISKS];
593 BOOL vols = (BOOL)args[ARG_VOLS];
594 BOOL showall = (BOOL)args[ARG_ALL];
595 BOOL blocks = (BOOL)args[ARG_BLOCKS];
596 STRPTR *devs = (STRPTR *)args[ARG_DEVS];
598 if (devs && (*devs == NULL)) devs = NULL;
600 /* If nothing is specified, show everything we got */
601 if(devs == NULL && !disks && !vols)
603 vols = TRUE;
604 disks = TRUE;
607 /* check pattern strings */
609 if(devs != NULL)
611 STRPTR *p = devs;
613 while(*p != NULL)
615 TEXT matchstr[128];
617 if(ParsePatternNoCase(*p, matchstr, sizeof(matchstr)) == -1)
619 PrintFault(IoErr(), *p);
620 goto end;
623 p++;
627 /* avoid requesters */
628 proc = (struct Process *)FindTask(NULL);
629 win = (struct Window *)proc->pr_WindowPtr;
630 proc->pr_WindowPtr = (struct Window *)~0;
632 MaxLen = strlen(unit);
634 D(bug("Calling ScanDosList()\n"));
636 /* scan doslist */
637 if(ScanDosList(devs))
639 CONST_STRPTR dstate[3] = { GetStrFromCat(READONLY, "read only"),
640 GetStrFromCat(VALIDATING, "validating"),
641 GetStrFromCat(READWRITE, "read/write") };
642 STRPTR datetimeFmt = NULL;
643 BOOL first = TRUE;
644 TEXT nfmtstr[16];
645 TEXT buf[64];
647 D(bug("Printing stuff\n"));
649 /* get datetimefmt string */
650 if(loc && (GetVar("info_datetime", buf, sizeof(buf), 0L) > 0L))
652 datetimeFmt = buf;
655 /* calc format string for 'Unit' */
656 __sprintf(nfmtstr, "%%-%lds", MaxLen);
658 /* show device infomation */
659 if(devs != NULL || disks || !vols)
661 for(idn = head; idn; idn = idn->Next)
663 BPTR lock;
664 STRPTR name = idn->Name;
666 D(bug("Got name = %s\n", name));
668 if(!idn->IsVolume && IsFileSystem(name))
670 /* if first device to print, print title */
671 if(first || blocks)
673 if(!first)
674 Printf("\n");
676 D(bug("Printing device\n"));
678 LPrintf(~0, nfmtstr, unit);
679 LPrintf(DEVTITLE, " Size Used Free Full Errs State Type Name\n");
681 first = FALSE;
684 D(bug("Locking \"%s\"\n", name));
685 lock = Lock(name, SHARED_LOCK);
687 D(bug("Lock = %p\n", lock));
689 if(lock != NULL)
691 D(bug("Got lock on %s\n", name));
693 if(Info(lock, &id) == DOSTRUE)
695 ULONG x, y;
697 D(bug("Got info on %s\n", name));
699 LPrintf(~0, nfmtstr, name);
701 x = ComputeKBytes(id.id_NumBlocks, id.id_BytesPerBlock);
702 y = ComputeKBytes(id.id_NumBlocksUsed, id.id_BytesPerBlock);
704 PrintNum(x);
705 PrintNum(y);
706 PrintNum(x - y);
708 D(bug("Calling NameFromLock()\n"));
710 if(NameFromLock(lock, name, 108L))
712 LONG len = strlen(name) - 1;
714 if(name[len] == ':')
716 name[len] = '\0';
720 if(x > 0xfffff)
722 x >>= 10;
723 y >>= 10;
726 if(x)
728 x = ExtUDivMod32(UDivMod32(UMult32(y, 1000), x), 10, &y);
730 if(y > 4)
731 x++;
733 else
734 x = 0;
736 // y = ((struct DeviceList *)BADDR(id.id_VolumeNode))->dl_DiskType;
738 // if(!y)
739 y = id.id_DiskType;
741 if((idn->DosType & ID_DOS_DISK) != ID_DOS_DISK)
742 y = idn->DosType;
744 LPrintf(DEVFMTSTR, "%4ld%% %4ld %-11s%-8s%s\n",
745 x, id.id_NumSoftErrors,
746 ((id.id_DiskState >= ID_WRITE_PROTECTED) && (id.id_DiskState <= ID_VALIDATED)) ?
747 dstate[id.id_DiskState - ID_WRITE_PROTECTED] : (STRPTR)"", GetFSysStr(y), name);
749 if(blocks)
751 LPrintf(BLOCKSSTR,
752 "\nTotal blocks: %-10ld Blocks used: %ld\n"
753 " Blocks free: %-10ld Blocksize: %ld\n",
754 id.id_NumBlocks, id.id_NumBlocksUsed,
755 id.id_NumBlocks-id.id_NumBlocksUsed, id.id_BytesPerBlock );
758 else
759 D(bug("Info failure\n"));
761 UnLock(lock);
765 LONG err = IoErr();
767 if((err != 0) && showall)
769 LPrintf(~0, nfmtstr, name);
770 PrintFault(err, NULL);
777 /* show volumes */
778 if(vols || (!devs && !disks))
780 if(!first)
781 PutStr("\n");
783 LPrintf(DISKSTITLE, "Volumes\n");
785 for(MaxLen = 15, idn = head; idn; idn = idn->Next)
787 if(idn->IsVolume)
789 LONG len = strlen(idn->Name);
791 if(len > MaxLen)
792 MaxLen = len;
796 __sprintf(nfmtstr, "%%-%lds%%-10s", MaxLen+1);
798 for(idn = head; idn; idn = idn->Next)
800 if(idn->IsVolume)
802 LPrintf(VOLNAMEFMTSTR, nfmtstr, idn->Name,
803 GetStrFromCat(MOUNTEDSTR, "[Mounted]"));
804 // idn->Task ? GetStrFromCat(MOUNTEDSTR, "[Mounted]") : ""); TODO
806 if(datetimeFmt)
808 UBYTE datestr[128];
809 static struct Hook hook;
811 memset(&hook, 0, sizeof(struct Hook));
813 hook.h_SubEntry = (HOOKFUNC)FmtProcedure;
814 hook.h_Data = datestr;
816 FormatDate(loc, datetimeFmt, &idn->VolumeDate, &hook);
818 PutStr(datestr);
820 else
822 TEXT StrDay[LEN_DATSTRING];
823 TEXT StrDate[LEN_DATSTRING];
824 TEXT StrTime[LEN_DATSTRING];
826 struct DateTime dt;
828 dt.dat_Flags = DTF_SUBST;
829 dt.dat_Format = FORMAT_DOS;
830 dt.dat_StrDay = StrDay;
831 dt.dat_StrDate = StrDate;
832 dt.dat_StrTime = StrTime;
833 dt.dat_Stamp = idn->VolumeDate;
835 if(DateToStr(&dt))
837 if(Strnicmp(StrDate, StrDay, strlen(StrDay)) == 0)
839 dt.dat_Flags = 0L;
840 DateToStr(&dt);
843 LPrintf(DATEFMTSTR, "created %.3s, %-10s %s",
844 StrDay, StrDate, StrTime);
848 PutStr("\n");
853 else
855 PrintFault( ERROR_NO_FREE_STORE, NULL);
858 /* reset window pointer of our process */
859 proc->pr_WindowPtr = win;
861 /* free args */
862 FreeArgs(rdargs);
866 end: /* free allocated memory */
868 DeletePool(Pool);
872 ULONG ComputeKBytes(ULONG a, ULONG b)
874 // UQUAD result = UMult64(a, b);
876 UQUAD result = (UQUAD)a * b;
878 return (ULONG)(result >> 10);
882 void FmtProcedure(struct Hook *hook, char a, struct Locale *locale)
884 *((STRPTR)hook->h_Data) = a;
885 hook->h_Data = (STRPTR) hook->h_Data + 1;
889 ULONG ExtUDivMod32(ULONG a, ULONG b, ULONG *mod)
891 *mod = a % b;
893 return a/b;