Aros/Developer/Docs/Libraries/DOS
Introduction
[edit | edit source]A file-system controls the way files and folders are laid out on the disk, and handles all the details of creating, reading, writing and deleting those files. Without a file-system, your disk (or floppy, CD-ROM, USB key or SD card) is just a mass of raw unstructured data.
When you access a file, a file handler is created each time.
- dos.h contains Open, Close, Seek, Read, Write, etc.
- stdio.h contains fopen, fclose, fseek, fread, fwrite, etc.
All file routines go through dos.library even if you use fopen(), those "non native" library functions are just some implementation at standardising file access with c, if you follow the code they will use Aros operation systems calls at the end.
Same thing for malloc etc. they end up in exec.library AllocMem/AllocVec/AllocPooled or what ever.
One thing to note for is when you call some operation system function your code is actually running the OS-code, there is no behind-the-wall-supercode doing it for you. Your apps PC counter goes through the calls.
If you use standard c library calls then you will have to link it in the binary or the binary has to open the clib at runtime. You would not want to have clib when coding parts of the operation system (user apps can do what they want). If one is used, c library calls on operating system calls then it gets just messy (clib code would convert the c standard to native calls, etc.)
When you go to generic routines from a compiler's standard c library or a library of posix type functions, you usually see specific limitations when it comes to AROS (or Amiga) system stuff. They can have incompatible ideas of file permission bits, incompatible ideas of how to handle multiple root volumes or when looking up things that are actually "multi-path assigns" or use file notes which Amiga native app writers took advantage of. The standard libs don't understand the relationships between programs and icons in the Amiga workbench environment.
Standard libs programming can be oblivious to user frustration at getting a bunch of "insert volume foo: requesters", popping up on their screen or worse, on the workbench screen when the app is running on its own screen blocking the view of those requesters, because they were written with a unix shell semantic in mind, where error handling was done differently.
It is probably safe to stick to the AROS native functions names, even though a standard call is internally remapped.
Alloc a large chunk of memory (Get size with AvailMem and flag MEMF_LARGEST and allocate with AllocDosObject (used to be AllocMem), filesize get with Lock and Examine (ExAll)), open the file with Open and read in the data with Read, Open the source file with Open and save data with Write. For total RAM use MEMF_TOTAL.
An easy way to check for existing files is: get data of one directory with Examine/ExNext/ExAll and try for every file name an Lock() in the destination directory. To change a directory/create a directory CurrentDir and CreateDir are useful. The functions Rename and DeleteFile will be useful.
struct FileHandle { /* The next three are used with packet-based filesystems */ struct Message * fh_Link; /* exec message containing packet */ struct MsgPort * fh_Port; /* packet reply port */ struct MsgPort * fh_Type; /* port to send packets to */ UBYTE * fh_Buf; UBYTE * fh_Pos; UBYTE * fh_End; #ifdef AROS_DOS_PACKETS LONG fh_Funcs; #define fh_Func1 fh_Funcs LONG fh_Func2; LONG fh_Func3; LONG fh_Args; #define fh_Arg1 fh_Args LONG fh_Arg2; /* kept here until things stabilize */ ULONG fh_Size; ULONG fh_Flags; #else /* The following four fields have different names and a different function than their AmigaOS equivalents. The original names were: fh_Funcs/fh_Func1, fh_Func2, fh_Func3, fh_Args/fh_Arg1 and fh_Arg2 */ ULONG fh_Size; ULONG fh_Flags; /* see below */ /* This is a pointer to a filesystem handler. See <dos/filesystems.h> for more information. */ struct Device * fh_Device; /* SDuvan: Added this and removed the #if below. This field allows us to emulate packets -- specifically it makes it possible to implement the ***Pkt() functions */ SIPTR fh_CompatibilityHack; /* A private pointer to a device specific filehandle structure. See <dos/filesystems.h> for more information. */ struct Unit * fh_Unit; #endif };
The only fields which are referred to by external code are fh_Device and fh_Unit. They are used for sending direct IOFS requests. In fact fh_Size and fh_Flags can also be aliased to fh_Funcs and fh_Func2, but this would affect m68k port if they decide to implement these callbacks for some reason. It might be better if fields that might hold pointers were upgraded to SIPRs rather than APTRs. This will give better source-level compatibility with AmigaOS code, avoiding compiler warnings for example.
Previous implementation, for non AROS_DOS_PACKET case field fh_Size was at offset 24 and field fh_Flags was at offset 28. After your change, field fh_Size is at offset 44 and field fh_Flags is at offset 48. Above the FH structure before and after the change for reference.
Volumes
[edit | edit source]Info() function gets information about a volume in the system.
- lock—a lock on any file on the volume for which information should be supplied
- parameterBlock—pointer to an InfoData structure
and results with != 0 if the operation was successful, == 0 if the operation was not successful Boolean indicating success or failure. If success (!= 0) the 'parameterBlock' is filled with information about the volume.
/* returned by Info(), must be on a 4 byte boundary */ struct InfoData {
LONG id_NumSoftErrors; /* number of soft errors on disk */ LONG id_UnitNumber; /* Which unit disk is (was) mounted on (os1.3 only) */ LONG id_DiskState; /* ID_WRITE_PROTECTED, ID_VALIDATING, ID_VALIDATED */ LONG id_NumBlocks; /* Number of blocks on disk */ LONG id_NumBlocksUsed; /* Number of block in use */ LONG id_BytesPerBlock; LONG id_DiskType; /* (ID_NO_DISK_PRESENT, ID_UNREADABLE_DISK, ID_NOT_REALLY_DOS, ID_BUSY) Filesystem ID_DOS_DISK, ID_FFS_DISK, ID_INTER_DOS_DISK, ID_INTER_FFS_DISK, ID_FASTDIR_DOS_DISK, ID_FASTDIR_FFS_DISK, ID_LONGNAME_DOS_DISK, ID_LONGNAME_FFS_DISK, ID_MSDOS_DISK Console ID_CON, ID_RAWCON, ID_KICKSTART_DISK */ BPTR id_VolumeNode; /* BCPL pointer to volume node */ LONG id_InUse; /* Flag, zero if not in use */
}; /* InfoData */
to find the address of the CON:/RAW: window the shell is running in; documented very scantily in the AmigaDOS manuals is the fact that if you send a DiskInfo packet to your console handler, the id_VolumeNode field in the InfoData structure holds the desired window address.
#include
#include
#include
extern struct Library *OpenLibrary();
struct Library *IntuitionBase;
struct Window *wb_window;
wb_window = NULL;
#if RAW_CONSOLE
if ((IntuitionBase = OpenLibrary("intuition.library", 0L)) != NULL) {
BPTR iLock;
struct FileHandle *conF;
struct InfoData *infTemp;
extern long dos_packet();
iLock = (BPTR) Input();
conF = (struct FileHandle *) BADDR(iLock);
infTemp = (struct InfoData *) malloc(sizeof(struct InfoData));
if (infTemp == 0)
return;
/*
* Send the packet.
*/
if (!dos_packet(conF->fh_Type, ACTION_DISK_INFO,
((ULONG) infTemp) >> 2)) {
free((void *) infTemp);
return;
}
wb_window = (struct Window *) infTemp->id_VolumeNode;
}
#endif
Files
[edit | edit source]Open Close Files
[edit | edit source]Before you can do anything with a file you have to ask the operating system to "open it". This is because AROS needs to know what you are going to do with the file. Are you going to create a new file (MODE_NEWFILE) or are you going to work with an already opened file (MODE_OLDFILE). You open a file by calling the function Open():
For example, if you want to create a new file called "Highscore.dat" you write:
/* Try to open the file: */
file_handle = Open("Highscore.dat", MODE_NEWFILE );
/* Check if we have opened the file: */
if( file_handle == NULL )
/* Could NOT open file! */
Once you have finished reading/writing the file you need to close it. You do it by calling the function Close():
Close(file_handle);
Close all files you have opened!
/*
Copyright © 1995-2010, The AROS Development Team. All rights reserved.
$Id: filetest.c 37364 2011-03-04 22:40:55Z jmcmullan $
*/
#include <dos/dos.h>
#include <stdio.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/dos.h>
static void hexdump(UBYTE *data, LONG len)
{
LONG i;
for (i = 0; i < len; i++)
printf("%02X ", data[i]);
printf("\n");
}
int main(int argc, char **argv)
{
BPTR file;
UBYTE buffer[20];
LONG r1=0,r2=0,r3=0,r4=0;
char *name = "testfile";
if (argc > 1)
name = argv[1];
file=Open(name, MODE_NEWFILE);
if(file)
{
r1=Write(file,"hello, world\n",13);
r2=Seek(file,0,OFFSET_BEGINNING);
r3=Read(file,buffer,19);
r4=Close(file);
}
if(r3>=0)
buffer[r3]=0;
printf("Results: %d %d %d %d \'%s\'\n",(int)r1,(int)r2,(int)r3,(int)r4,buffer);
hexdump(buffer, r3);
return 0;
}
The use of any global FILE pointers are asking for trouble, so its best not to use them. Just declare their use in each function parsing if needed. global variables are a bad idea and will likely get you in trouble in the future if you get into the habit of using them without thinking about it. Make sure you understand variable scopes and their consequences before going on.
Read, Write and Seek Files
[edit | edit source]Once you have successfully opened a file you can start to read or write. AmigaDOS(TM) and AROS consider files to be a stream of bytes, and when you read or write something you need to specify how many bytes you want to read or write.
When you what to write to a file you use the function Write():
And to Seek
#include <stdio.h>
#include <dos/dos.h>
#include <stdlib.h>
#include <proto/dos.h>
int main()
{
FILE *fd;
char buffer[32];
int i;
BPTR file;
fd = fopen( "seek.txt", "wb" );
if ( !fd )
{
fprintf( stderr, "Could not write test file seek.txt\n" );
exit(-1);
}
fprintf( fd, "() does not work!\n" );
fclose(fd);
/* fseek() */
fd = fopen( "seek.txt", "rb" );
if ( !fd )
{
fprintf( stderr, "Could not open test file seek.txt\n" );
exit(-1);
}
i = fread( buffer, 1, 1, fd );
//printf("pos=%ld\n",ftell(fd));
i += fread( &buffer[1], 1, 6, fd );
if( i != 7 )
{
fprintf( stderr, "Wanted to fread() %d chars, but could only get %d!\n", 6, i-1 );
exit(-1);
}
fseek( fd, 4, SEEK_CUR );
i = fread( &buffer[7], 1, 11, fd );
buffer[7+i]=0;
printf( "fseek%s", buffer );
fclose(fd);
/* Seek() */
file = Open( "seek.txt", MODE_OLDFILE );
i = Read( file, buffer, 7 );
Seek( file, 4, OFFSET_CURRENT );
i += Read( file, &buffer[7], 11 );
buffer[i] = 0;
printf( "\nSeek%s", buffer );
return 0;
}
See here also.
#include "dos/dosextens.h"
extern struct FileHandle *Open()
main()
{
struct FileHandle *file_handle;
file_handle = Open("CON:10/10/500/150/New Window", MODE_NEWFILE);
Write (file_handle,"Hello World\n",13);
Delay(400);
Close(file_handle);
}
currentposition=Seek(file_handle,0, OFFSET_END); currentposition=Seek(file_handle,0, OFFSET_CURRENT); currentposition=Seek(file_handle,20, OFFSET_CURRENT); currentposition=Seek(file_handle,0, OFFSET_BEGINNING);
boolean_status=WaitForChar(file_handle,waiting_time);
Lock
[edit | edit source]flock calls DupLockFromFH() with filehandle that is opened in MODE_NEWFILE mode. It is not allowed to be duplicated. Can be fixed by replacing O_CREAT = NEWFILE with following: first create the file with MODE_NEWFILE, close it and then re-open it with MODE_OLDFILE. ChangeMode() is also possibility but it may not supported by all filesystems or it can be broken (UAE fs implementation CHANGE_FH variant was buggy until about two years ago. It is really rarely used in AOS programs).
Actually flock() calls NameFromFH() which itself calls DupLockFromFH(). So the first question is if NameFromFH() should fail on a handle opened with MODE_NEWFILE ? Does it on OS3.x ? NameFromFH() must work with MODE_NEWFILE opened filehandles. It is also mentioned in Guru Book that says AOS NameFromFH() internally uses ExamineFH() and ParentOfFH() to get the name string because DupLockFromFH() can't work if handle is MODE_NEWFILE.
It looks like it does something like this:
- parentlock = ParentOfFH(fh);
- Path string = NameFromLock(parentlock);
- Unlock(parentlock)
- Append file name to path string from ExamineFH(fh)
What are the technical reasons for not making DupLockFromFH() work on MODE_NEWFILE files? IIRC it's because MODE_NEWFILE uses an exclusive lock (on all filesystems, not just on AmberRAM).
Would there be any issue if we just made it work? UAE's filesystem would have to be reworked, but that's all about it, no? Wouldn't it break compatibility with AOS m68k filesystems? Yes. Exclusive lock duplication (which includes MODE_NEWFILE filehandles that have internal exclusive lock) attempts must fail. It is a documented behavior. NameFromFH() is now rewritten and works with exclusive filehandles.
SameLock()
dol_Name is a BCPL pointer to a BCPL string so you can't just print it with printf(). Note that you shouldn't do so anyway, not while you're holding a lock on the dos list. Better to copy the information you need into your own buffers and then print it after unlocking the dos list.
char buffer[256]; const uint8 *bstr = BADDR(dlPtr->dol_Name); memcpy(buffer, bstr+1, *bstr); buffer[*bstr] = 0;
You should never perform any DOS I/O with the doslist locked. Create an exec list of what you want, release the doslist, then print the list nodes out....
Assign steps thought the DOS list and displays all devices in the list. If you want to filter only those devices which have already been activated, you would have to write a new program which steps through the list (LockDosList / NextDosEntry) and check if dol_Task is filled (OS4 renamed it to dol_Port). The name is dol_Task (see dos/dosextens.h). It contains a pointer to a MsgPort. But note that this can change at any time. A device can be mounted but not activated at once. It is activated automatically when the user accesses it for the first time. This applies for example to RAM, SER, PAR and PRT.
Volume name
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
struct InfoData *info;
struct DosList *doslist;
char *name;
void main(void)
{
BPTR lock, mydoslist;
BOOL success;
lock=Lock("File",ACCESS_READ);
if(!lock) exit(20);
info=(struct InfoData*)AllocVec(sizeof(struct InfoData),MEMF_PUBLIC|MEMF_CLEAR);
success=Info(lock,info);
if(!success) exit(30);
doslist=(struct DosList*)((info->id_VolumeNode)<<2);
name=((struct Node*)(((struct MsgPort*)(doslist->dol_Task))->mp_SigTask))->ln_Name;
printf("%s:\n",name);
FreeVec(info);
}
#include <dos/dosextens.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <stdio.h>
#define MAXV 40
struct DE {
char name[256];
APTR task;
};
struct DE vols[MAXV], devs[MAXV];
void ReadDosList(struct DE *list, int *max, int type) {
struct DosList *dl;
int i = 0;
UBYTE *x;
if (!(dl = LockDosList(type))) {*max = 0; return;}
while ((dl = NextDosEntry(dl, type))) {
if (i >= MAXV) continue;
x = BADDR(dl->dol_Name);
CopyMem(&x[1], &list[i].name, x[0]);
list[i].name[x[0]] = 0;
list[i].task = dl->dol_Task;
i++;
}
UnLockDosList(type);
*max = i;
}
int main() {
int i, j, voln, devn;
STRPTR x;
SysBase = *((struct ExecBase **)4UL);
if ((DOSBase = OpenLibrary("dos.library", 37))) {
ReadDosList(&devs[0], &devn, LDF_DEVICES | LDF_READ);
ReadDosList(&vols[0], &voln, LDF_VOLUMES | LDF_READ);
CloseLibrary(DOSBase);
}
for (i = 0; i < voln; i++) {
// x = "o device?;
for (j = 0; j < devn; j++)
if (vols[i].task == devs[j].task) x = devs[j].name;
printf("%s -> %s\n", vols[i].name, x);
}
return 0;
}
To translate the WBArg into a full path you can do:
TEXT fullpath[1024]; NameFromLock(WBArg->wa_Lock, fullpath, sizeof(fullpath)); AddPart(fullpath, WBArg->wa_Name, sizeof(fullpath));
However, it's usually more efficient to:
BPTR oldcd, file;
oldcd = CurrentDir(WBArg->wa_Lock);
file = Open(WBArg->wa_Name, MODE_OLDFILE);
CurrentDir(oldcd);
if (file != 0) {
/* ... */
Close(file);
}
Examine Files or Directories
[edit | edit source]The newest method require ExAll which replaced Examine() and ExNext(). As ExAll() scans a directory (having been locked earlier), it an ExAllData structure into the buffer for each entry.
Examples - Find,
The old method required Examine(). You give the function a pointer to a "lock" on the file or directory you want to examine, and it will set up a FileInfoBlock structure for you.
/*
Copyright © 1995-2002, The AROS Development Team. All rights reserved.
$Id: ExNext.c 33409 2010-05-31 13:45:26Z mazze $
Test ExNext() function
*/
#include <proto/dos.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <dos/exall.h>
#include <dos/rdargs.h>
#include <dos/var.h>
#include <exec/memory.h>
#include <exec/types.h>
#include <utility/tagitem.h>
#include <stdio.h>
#include <aros/debug.h>
#define ARG_TEMPLATE "DIRECTORY"
#define TOTAL_ARGS 1
static const char version[] = "$VER: ExNext 41.1 (30.01.2000)\n";
int main(int argc, char *argv[])
{
struct RDArgs * rda;
IPTR * args[TOTAL_ARGS] = { NULL };
int Return_Value;
BPTR lock;
Return_Value = RETURN_OK;
rda = ReadArgs(ARG_TEMPLATE, (IPTR *)args, NULL);
if (rda)
{
if (!args[0])
lock = Lock("", ACCESS_READ);
else
lock = Lock((STRPTR)args[0], ACCESS_READ);
if (lock)
{
struct FileInfoBlock * FIB;
BOOL success;
FIB = AllocVec(sizeof(struct FileInfoBlock), MEMF_CLEAR);
if (FIB)
{
success = Examine(lock, FIB);
kprintf("calling ExNext()...\n");
success = ExNext(lock, FIB);
kprintf("called ExNext()\n");
while (success != DOSFALSE)
{
/* don't show dirs */
if (FIB->fib_DirEntryType < 0)
printf("%s\n",FIB->fib_FileName);
else
printf("%s (not a file)\n", FIB->fib_FileName);
kprintf("calling ExNext()...\n");
success = ExNext(lock, FIB);
kprintf("called ExNext()\n");
}
FreeVec(FIB);
}
UnLock(lock);
}
else
{
PrintFault(IoErr(), "ExNext");
Return_Value = RETURN_FAIL;
}
}
else
{
PrintFault(IoErr(), "ExNext");
Return_Value = RETURN_ERROR;
}
if (rda)
FreeArgs(rda);
return (Return_Value);
} /* main */
Is it safe to use memory allocated with AllocMem with the dos function Examine and use fib_SIZEOF as the size, or is it better to use AllocDosObject instead? AllocMem is safe for all currently existing AmigaOS versions but AllocDosObject is better for future compatibility. Note that AllocDosObject does not exist in Kickstart 1.3 and below, so for these OS versions you need to use AllocMem.
struct FileInfoBlock fib; BPTR lock = Lock(filename, ACCESS_READ); if (lock) { Examine(lock, &fib); UnLock(lock); }
On PowerPC stack is always long-word-aligned and you are not forced to use AllocDosObject() or AllocMem(). If coding for AmigaOS/68k you must allocate long word-aligned FIB.
TEXT buf[500];
struct FileInfoBlock *fib = (APTR)&buf;
stccpy(buf, "PROGDIR:", sizeof(buf));
AddPart(buf, "Image.png", sizeof(buf)); // Buf is passed to dos.library
fh = Open(buf, MODE_OLDFILE); // Again buf is passed to dos.library
ExamineFH(fh, &fib); // Yet again same buf is passed to dos.library
Close(fh);
FileInfoBlock is not a system structure. It is just pointer to a buffer used to retrieve data. If you still insist FIB must be allocated using AllocDosObject() (why?) you should also use system routines to allocate space for names passed to Lock(), Open(), etc.
As you see this idea to use AllocDosObject() to allocate FIB a bad idea. For other structures like ExAllControl it is needed because structure must be initialized properly before passed to dos.library. The only real reason to use AllocDosObject() is long word alignment requirement in AmigaOS (but not in MorphOS or AROS). In old days it was source of many bugs because developers did not know about long word alignment restrictions resulting in random behaviour/memory trashing.
void CreateQPort(struct MsgPort *port)
{
port->mp_Node.ln_Type = NT_MSGPORT;
port->mp_Flags = PA_SIGNAL;
if ((BYTE) (port->mp_SigBit = AllocSignal(-1)) == -1)
{
port->mp_SigBit = SIGB_SINGLE;
SetSignal(0, SIGF_SINGLE);
}
port->mp_SigTask = FindTask(NULL); // SysBase->ThisTask would be faster here
NEWLIST(&port->mp_MsgList);
}
void DeleteQPort(struct MsgPort *port)
{
if (port->mp_SigBit == SIGB_SINGLE)
{
SetSignal(0, SIGF_SINGLE);
}
else
{
FreeSignal(port->mp_SigBit);
}
}
How can I return the date in format DDMMYYYY in string format with this ? dos.library/DateToStr()
#include <dos/dos.h>
#include <proto/dos.h>
#include <dos/datetime.h>
int main(void)
{
struct DateTime curr;
char day[LEN_DATSTRING];
char time[LEN_DATSTRING];
char date[LEN_DATSTRING];
struct DateStamp stamp;
curr.dat_Format = FORMAT_DOS;
curr.dat_Flags = 0;
curr.dat_StrDay = day;
curr.dat_StrDate = date;
curr.dat_StrTime = time;
DateStamp(&curr.dat_Stamp);
DateToStr(&curr);
Printf("Current time: %s, %s, %s\n", day, date, time);
BPTR fh = Open("__TEST__", MODE_NEWFILE);
if (fh != NULL)
{
struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
if (fib != NULL)
{
if (ExamineFH(fh, fib))
{
curr.dat_Stamp = fib->fib_Date;
DateToStr(&curr);
Printf("File modification time: %s, %s, %s\n", day, date, time);
}
else
PrintFault(IoErr(), "Examine failed");
Printf("Waiting 5 seconds\n");
Delay(5*50);
DateStamp(&stamp);
Printf("Calling SetFileDate\n");
if(SetFileDate("__TEST__", &stamp)) {
if (ExamineFH(fh, fib))
{
curr.dat_Stamp = fib->fib_Date;
DateToStr(&curr);
Printf("New file modification time: %s, %s, %s\n", day, date, time);
}
else
PrintFault(IoErr(), "Examine failed");
}
else
PrintFault(IoErr(), "SetFileDate");
FreeDosObject(DOS_FIB, fib);
}
else
PrintFault(IoErr(), "Couldn't alloc FileInfoBlock");
Close(fh);
DeleteFile("__TEST__");
}
else
PrintFault(IoErr(), "Couldn't create file");
return 0;
}
Protect Files And Directories
[edit | edit source]You can protect files and directory from being accidentally deleted or changed. You do it by calling the function SetProtection() which will alter the protection bits as desired in the FIB FileInfoBlock struct.
#include <proto/dos.h>
#include <stdio.h>
int main(void)
{
TEXT buffer[512];
BPTR fh = Open("__TEST__", MODE_NEWFILE);
if (fh != NULL)
{
if (NameFromFH(fh, buffer, 512))
{
printf("got name: %s\n", buffer);
}
else
{
printf("namefromlock failed. ioerr = %ld\n", IoErr());
}
Close(fh);
DeleteFile("__TEST__");
}
else
{
printf("couldn't create file\n");
}
return 0;
}
#include <proto/dos.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char c;
if (argc < 2)
{
fprintf(stderr, "usage: %s <varname>\n", argv[0]);
return 20;
}
if (GetVar(argv[1], &c, 1, GVF_BINARY_VAR) == 0)
{
LONG len = IoErr();
char *buf = malloc(len + 1);
if (!buf)
{
PrintFault(ERROR_NO_FREE_STORE, argv[0]);
return 20;
}
printf("IoErr() says the len of the value of the var '%s' is: %ld\n", argv[1], len);
len = GetVar(argv[1], buf, len+1, GVF_BINARY_VAR);
printf("GetVar() says the len of the value of the var '%s' is: %ld - its value is '%s'\n",
argv[1], len, buf);
free(buf);
return 0;
}
PrintFault(IoErr(), argv[1]);
return 20;
}
Names
[edit | edit source]The FileInfoBlock look like this:
struct FileInfoBlock {
LONG fib_DiskKey;
LONG fib_DirEntryType;
char fib_FileName[108];
LONG fib_Protection;
LONG fib_EntryType;
LONG fib_Size;
LONG fib_NumBlocks;
struct DateStamp fib_Date;
char fib_Comment[80];
char fib_Reserved[36];
};
*fib_DiskKey: Key number for the disk. Usually of no interest for us. *fib_DirEntryType: If the number is smaller than zero it is a file. On the other hand, if the number is larger than zero it is a directory. *fib_FileName: Null terminated string containing the filename. (Do not use longer filenames than 30 characters.) FileInfoBlock fib_Comment and fib_FileName should be BCPL strings would make porting of ftp-handler easier and Staf wrote that BCPL strings will become BCPL strings on all platforms. *fib_Protection: Field containing the protection flags: FIBF_DELETE : the file/directory can not be deleted. FIBF_EXECUTE : the file can not be executed. FIBF_WRITE : you can not write to the file. FIBF_READ : you can not read the file. FIBF_ARCHIVE : Archive bit. FIBF_PURE : Pure bit. FIBF_SCRIPT : Script bit. (Note! All of the flags are for the moment not working!) *fib_EntryType: File/Directory entry type number. Usually of no interest for us. *fib_Size: Size of the file (in bytes). *fib_NumBlocks: Number of blocks in the file. *fib_Date: Structure containing the date when the file was latest updated/created. *fib_Comment: Null terminated string containing the file comment. (Max 80 characters including the NULL sign.) The Amiga Dos/Examine() docs say that the buffers are ASCIIZ when Dos/Examine() returns. The only confusion was what the *handler* fills in the FIB on an ACTION_EXAMINE_OBJECT. On m68k it was always BCPL-style. There was some idea that on non-m68k handlers should use ASCIIZ, to save the BCPL2ASCIIZ step in Dos/Examine(). The *handler* always fills in as BCPL style, and Dos/Examine() always runs a BCPL2ASCIIZ conversion on fib_Comment and fib_FileName *regardless* of architecture. Especially since a length-terminated string is quicker to convert to a null-terminated string than the other way around. Only handler writers need to be aware of these details. *fib_Reserved: This field is for the moment reserved, and may therefore not be used.
#include <dos/dos.h>
#include <proto/dos.h>
#include <stdio.h>
int main(void)
{
BPTR fh = Open("__TEST__", MODE_NEWFILE);
if (fh != NULL)
{
struct FileInfoBlock *fib = AllocDosObject(DOS_FIB, NULL);
if (fib != NULL)
{
if (ExamineFH(fh, fib))
{
printf("got fib.filename = %s\n", fib->fib_FileName);
}
else
{
printf("examinefh failed, ioerr = %ld\n", IoErr());
}
FreeDosObject(DOS_FIB, fib);
}
else
{
printf("couldn't allocate fileinfoblock\n");
}
Close(fh);
DeleteFile("__TEST__");
}
else
{
printf("couldn't create file\n");
}
return 0;
}
At the moment can get the name of the 'double-clicked' file with some code
if (argc == 0)
{
struct WBStartup *startup = (struct WBStartup *) argv;
if (startup->sm_NumArgs > 1)
{
BPTR parentlock = startup->sm_ArgList[1].wa_Lock;
char *filename = startup->sm_ArgList[1].wa_Name;
}
}
But cannot get the directory from which my file is 'double-clicked' using "GetCurrentDirName". If set CLI as ToolType, GetCurrentDirName works, but then get bogus data as filename. As your program is launched by Workbench, it doesn't have a CLI structure indeed, hence why GetCurrentDirName() doesn't work without CLI tooltype. But you should be able to use NameFromLock() and find what you need from there.
BPTR GetCurrentDir (void) { BPTR dir; dir = CurrentDir(0); CurrentDir(dir); return dir; }
GetCurrentDirName() gets the current path string from the program's CLI structure and returns an error if there is no CLI structure (IoErr() == ERROR_OBJECT_WRONG_TYPE).
#include <proto/exec.h>
#include <proto/dos.h>
#include <dos/dostags.h>
/*
BOOL myGetCurrentDirName (STRPTR buffer,LONG length)
{
BPTR lock;
BOOL success = FALSE;
if (lock = Lock ("",SHARED_LOCK))
{
success = NameFromLock (lock,buffer,length);
UnLock (lock);
}
return (success);
}
*/
{
struct Process *pr = (struct Process *) FindTask (NULL);
struct Message *msg;
BPTR win;
WaitPort (&pr->pr_MsgPort);
msg = GetMsg (&pr->pr_MsgPort);
win = Open ("con:////DirName/CLOSE/WAIT",MODE_NEWFILE);
if (win)
{
char buffer[256];
if (GetCurrentDirName (buffer,256))
{
FPrintf (win,"Dir Name = <%s>n",buffer);
}
else
{
Fault (IoErr(),NULL,buffer,256);
FPrintf (win,"Error: %sn",buffer);
}
Close (win);
}
Forbid();
ReplyMsg (msg);
}
int main (void)
{
struct Message *msg = AllocVec (sizeof(struct Message),MEMF_CLEAR|MEMF_PUBLIC);
if (msg)
{
struct MsgPort *port = CreateMsgPort();
if (port)
{
struct Process *pr = CreateNewProcTags (NP_Entry,proc,TAG_END);
if (pr)
{
msg->mn_ReplyPort = port;
PutMsg (&pr->pr_MsgPort,msg);
WaitPort (port);
GetMsg (port);
}
DeleteMsgPort (port);
}
FreeVec (msg);
}
return (0);
}
Scanning
[edit | edit source]MatchFirst() and passed to MatchNext() and MatchEnd()
ParsePattern() function creates a case sensitive wildcard string, whereas the ParsePatternNoCase() functions creates a case insensitive wildcard string. case sensitive MatchPattern() or the case insensitive MatchPatternNoCase()
Command line parsing through ReadArgs() using a RDArgs structure
CompareDates()
AmigaDos wild card are ? # % | * [] ` ~
Examples
[edit | edit source]#include <stdlib.h>
/* Write some numbers to a file */
void WriteNumbers(void) {
BPTR fh;
int n;
UBYTE numstr[5];
printf("Writing numbers to numbers.dat\n\n");
/* Open file for writing */
fh = Open("numbers.dat", MODE_NEWFILE);
if (fh) {
for (n=1; n<=20; n++) {
/* Convert number to a string with new line
& write string to file */
sprintf(numstr, "%d\n", n);
FPuts(fh, numstr);
}
Close(fh);
}
}
/* Read numbers from a file */
void ReadNumbers(void) {
BPTR fh;
int i;
UBYTE numstr[5];
UBYTE *buffer;
printf("Reading file numbers.dat.\n");
/* Open existing file for reading */
fh = Open("numbers.dat", MODE_OLDFILE);
if (fh) {
/* Read a string and check for End of File */
while (buffer = FGets(fh, numstr, 5L)) {
/* Convert string to number and print it */
i = atoi(numstr);
printf("Number %d\n", i);
}
Close(fh);
}
printf("EOF found\n");
}
int main(void)
{
WriteNumbers();
ReadNumbers();
return 0;
}
#include <stdlib.h>
#include <dos/dos.h>
#include <defines/dos.h>
extern struct SYSBase *SysBase;
extern struct DOSBase *DOSBase;
int main(int argc, char *argv[])
{
int ioerr, fsize, res;
BPTR fhandle;
if (argc != 2) {
printf("Wrong number of arguments\n");
return 1;
}
fhandle = Open(argv[1], MODE_OLDFILE);
if (!fhandle) {
ioerr = IoErr();
printf("Cannot open '%s'\n", argv[1]);
PrintFault(ioerr, "IoErr() says: ");
return 1;
}
res = Seek(fhandle, 0, OFFSET_END);
if (res == -1) {
ioerr = IoErr();
printf("Cannot seek to the end of file\n");
PrintFault(ioerr, "IoErr() says: ");
Close(fhandle);
return 1;
}
fsize = Seek(fhandle, 0, OFFSET_BEGINNING);
printf("File size = %d\n", fsize);
Close(fhandle);
return 0;
}
Filesystems
[edit | edit source]A device, handler and filesystem must be re-entrant.
The difference between a handler and a filesystem is that a filesystem is required to expose a directory structure and files. Implicitly this means a good 60% of dos packets.
The general rule of thumb was to implement 'virtual' devices (no underlying hardware) as a handler rather than a full-blown file system as in the case of ram-handler. Obviously that is not the written in stone but it is the established pattern for the OS.
Emulating a foreign filesystem falls into the class of a full blown fileystem, however given that there is no underlying hardware for a virtual device, the need for a device is probably a waste of effort and better implemented as fat-handler.
After "Assign DISMOUNT"? I don't know, but AROS's Assign command didn't call ACTION_END, which would be necessary to shut down the handler fully?
Filesystems can be locked with LDF_READ|LDF_DEVICES, but unlocked with LDF_READ|LDF_VOLUMES.
A device entry should not be removed from dl_DosList or the code unloaded until all associated locks either direct or indirect have been released. Specifically, if a device has medium mounted (aka volume), any attempt to remove the device, be it a hardware device or a virtual device MUST be denied. The best way to detect if a device is mounted without triggering a requester that says "Please insert volume BLAH:" is to use LockDosList \ AttemptLockDosList and then FindDOSEntry.
struct Process * this_process;
APTR old_window_ptr;
BPTR lock;
this_process = (struct Process *)FindTask(NULL);
old_window_ptr = this_process->pr_WindowPtr;
this_process->pr_WindowPtr = (APTR)-1;
UnLock(lock = Lock("device_name:",SHARED_LOCK));
this_process->pr_WindowPtr = old_window_ptr;
If the lock obtained is not (BPTR)NULL then the device/volume/assignment you tried is available, otherwise it's not. Note that this method does not specifically check for devices, you'd have to scan the DOSList for that.
Given that a dl_Volume and a dl_Device are 'paired', any attempt to remove the device should validate the device's volume and its associated locks and return a fail with the appropriate error code.
It is the filesystem which determines the max size, and >4GB is possible so long as you correctly speak to the filesystem.
They are only restricted by -:
- software being written before the 64-bit DOS extensions,
- software not using those extensions since there is no actual "API" call replacements, and they require direct communication to the filesystem.
First thing was to add 64-bit support, so that it can handle partitions larger than 4GB. Added new code in the cache to probe the underlying device to see if it supports 64-bit extensions, and then later if a request comes in for data that is over the 4GB boundary, use a 64-bit read or write operation rather than the standard one (or error, if the probe didn't find any 64-bit extensions). There's three commonly-used 64-bit extensions in the Amiga world - TD64 ie TrackDisk64, New-style TD64, and DirectSCSI. The first two are supported, but DirectSCSI shouldn't be hard to add.
Implementing write-back caching where the handler asks the cache to write some data, the cache reports success immediately but just marks the data as "to be written". Then at regular intervals (eg five seconds) it writes all of these "dirty" blocks out to disk in one go. This makes things feel faster for the user, and has the potential to reduce disk activity (== less wear and lower power consumption), at the risk of losing data in the event of a power failure or loss of the device (like pulling the disk out). Typically removable media uses write-through caching (ie write immediately), while fixed disks use write-back.
Since this requires a separate task that sits and waits and flushes the dirty blocks when called, it means the cache needs locking. Locking will also be needed in the future if a filesystem wanted to be multi-threaded (and the cache is actually in a cache.library, available to all). So far there is locking around cache operations, but not block operations.
To promote a read lock to a write lock. Usually you have to drop the original lock before taking the write lock, which means there's a moment where you're not holding any lock and someone can come and steal it out from under you. Workaround for POSIX threads requires condition variables which we don't currently have for AROS semaphores.
References
[edit | edit source]Packets
[edit | edit source]Packets (little messages wrapped within a Dos call) are sent to a file system or handler can be divided into categories as follows
* Basic Input/Output (handlers and file systems) - Open, Read (ACTION_READ), Write (ACTION_WRITE), Seek, Close, LockRecord, UnLockRecord, SetFileSize, * File/Directory Manipulation/Information (handlers) - Lock, DupLock, UnLock, Examine, ExNext, ExAll, CreateDir, Rename, DeleteFile, * Volume Manipulation/Information - Info (ACTION_DISK_INFO), Relabel (ACTION_RENAME_DISK), Format (ACTION_FORMAT), * Handler Maintenance and Control - AddBuffers (ACTION_MORE_CACHE), Inhibit (ACTION_INHIBIT), * Handler Internal - ACTION_NIL, ACTION_READ_RETURN, ACTION_WRITE_RETURN, ACTION_TIMER * Console Only Packets (handler only - ignored by file system) - WaitForChar (ACTION_WAIT_CHAR),
struct DosPacket { struct Message *dp_Link; /* EXEC message */ struct MsgPort *dp_Port; /* Reply port for the packet */ /* Must be filled in each send. */ LONG dp_Type; /* See ACTION_... 'R' means Read, 'W' means Write to the file system */ LONG dp_Res1; /* result that would have been returned by the function, e.g. Write ('W') returns actual * length written */ LONG dp_Res2; /* For file system calls - returned by IoErr() */ /* Device packets common equivalents */ #define dp_Action dp_Type #define dp_Status dp_Res1 #define dp_Status2 dp_Res2 #define dp_BufAddr dp_Arg1 LONG dp_Arg1; LONG dp_Arg2; LONG dp_Arg3; LONG dp_Arg4; LONG dp_Arg5; LONG dp_Arg6; LONG dp_Arg7; };
struct StandardPacket { struct Message sp_Msg; struct DosPacket sp_Pkt; }; /* StandardPacket */ /* Packet types */ #define ACTION_NIL 0 #define ACTION_STARTUP 0 #define ACTION_GET_BLOCK 2 /* OBSOLETE */ #define ACTION_SET_MAP 4 #define ACTION_DIE 5 #define ACTION_EVENT 6 #define ACTION_CURRENT_VOLUME 7 #define ACTION_LOCATE_OBJECT 8 #define ACTION_RENAME_DISK 9 #define ACTION_WRITE 'W' #define ACTION_READ 'R' #define ACTION_FREE_LOCK 15 #define ACTION_DELETE_OBJECT 16 #define ACTION_RENAME_OBJECT 17 #define ACTION_MORE_CACHE 18 #define ACTION_COPY_DIR 19 #define ACTION_WAIT_CHAR 20 #define ACTION_SET_PROTECT 21 #define ACTION_CREATE_DIR 22 #define ACTION_EXAMINE_OBJECT 23 #define ACTION_EXAMINE_NEXT 24 #define ACTION_DISK_INFO 25 #define ACTION_INFO 26 #define ACTION_FLUSH 27 #define ACTION_SET_COMMENT 28 #define ACTION_PARENT 29 #define ACTION_TIMER 30 #define ACTION_INHIBIT 31 #define ACTION_DISK_TYPE 32 #define ACTION_DISK_CHANGE 33 #define ACTION_SET_DATE 34 #define ACTION_SCREEN_MODE 994 #define ACTION_READ_RETURN 1001 #define ACTION_WRITE_RETURN 1002 #define ACTION_SEEK 1008 #define ACTION_FINDUPDATE 1004 #define ACTION_FINDINPUT 1005 #define ACTION_FINDOUTPUT 1006 #define ACTION_END 1007 #define ACTION_SET_FILE_SIZE 1022 /* fast file system only in 1.3 */ #define ACTION_WRITE_PROTECT 1023 /* fast file system only in 1.3 */ /* new 2.0 packets */ #define ACTION_SAME_LOCK 40 #define ACTION_CHANGE_SIGNAL 995 #define ACTION_FORMAT 1020 #define ACTION_MAKE_LINK 1021 /**/ /**/ #define ACTION_READ_LINK 1024 #define ACTION_FH_FROM_LOCK 1026 #define ACTION_IS_FILESYSTEM 1027 #define ACTION_CHANGE_MODE 1028 /**/ #define ACTION_COPY_DIR_FH 1030 #define ACTION_PARENT_FH 1031 #define ACTION_EXAMINE_ALL 1033 #define ACTION_EXAMINE_FH 1034 #define ACTION_LOCK_RECORD 2008 #define ACTION_FREE_RECORD 2009 #define ACTION_ADD_NOTIFY 4097 #define ACTION_REMOVE_NOTIFY 4098 #define ACTION_SERIALIZE_DISK 4200
struct ErrorString { LONG *estr_Nums; UBYTE *estr_Strings; };
AFS handler for example isn't fully "dos packetized" yet. ("real" dos packets and FSA stuff can't work together) They can. We discussed this with Rob Norris. The basic idea was to check what is pointed to by file_handle or lock's Port (or Process) member. It's either NT_PROCESS, or NT_DEVICE type node. This way you can distinguish what are you talking to. Additionally, your con.handler should be able to work nicely with packet.handler. If it doesn't, packet.handler needs to be fixed.
Some issues, all related to AROS's hardcoded 32-bit values, etc. (fib_Size,for starters). What is the best thing to do about this? MorphOS uses 64-bit versions of the DOS functions and structures, though I am not sure about OS4.
OS4 uses the following 64-bit-packet types because I was asked to support them in the WinUAE directory filesystem emulation years ago:
#define ACTION_CHANGE_FILE_POSITION64 8001 #define ACTION_GET_FILE_POSITION64 8002 #define ACTION_CHANGE_FILE_SIZE64 8003 #define ACTION_GET_FILE_SIZE64 8004
Old packets return file size = 2GB-1 if real size is >=2G. guess we need to alter DoPkt etc to return a QUAD? OK from what I can see DoPkt and the internal dopacket both need to be changed to return a QUAD and DosPacket->dp_Res1 needs to be a QUAD (which will break compatibility afaict). I think this is more desirable than trying to change all of the dos structures to use 64-bit values though?
Is it enough to say code accessing blocknr/size/etc should check if they equal 0x7FFFFFFF, and use 64bit packet calls directly if so?
MorphOS, phase5, Ralph Babel and some CBM engineers defined TD64 standard. MorphOS dosextens.h that describes their 64-bit packet extensions.
TD64 has nothing to do with 64-bit DOS file access. And yes, we have long supported TD64 (along with NSD). Without it, AROS couldn't access more than the first 4GB of any hard drive.
Process
[edit | edit source]Plain tasks have limitations in as much they must not call a function of dos.library or a function that could call a function of dos.library. Processes do not have this limitation.
A process is an expanded task. Opposed to a task, it can use functions of dos.library, because a process structure contains some special fields, concerning files and directories.
struct Process { struct pr_Task; struct pr_MsgPort; WORD pr_pad; BPTR pr_SegList; LONG pr_stackSize; APTR pr_GlobVec; LONG pr_TaskNum; BPTR pr_StackBase; LONG pr_Result2; BPTR pr_CurrentDir; BPTR pr_CIS; BPTR pr_COS; APTR pr_ConsoleTask; APTR pr_FileSystemTask; BPTR pr_CLI; APTR pr_ReturnAddr; APTR pr_PktWait; APTR pr_WindowPtr; BPTR pr_HomeDir; LONG pr_Flags; APTR pr_ExitCode; LONG pr_ExitData; APTR pr_Arguments; struct pr_LocalVrs; APTR pr_ShellPrivate; BPTR pr_CES; };
pr_Flags Flags
prb_FreeSegList prb_FreeCurrDir prb_FreeCli prb_CloseInput prb_CloseOutput prb_FreeArgs
It's cleaner than shoving everything in globals. Also, let's say you wish to run the same thread code on slightly different data (not much point unless you have multiple CPU cores, but anyway), NP_UserData allows you to use the same code over and over, just passing it different start data. You could, of course set up a message port and do it that way, but for simple tasks find passing a direct pointer to the data and using semaphores, easier. You can share just about any library pointer between processes.
Every process using bsdsocket.library must open that library on its own. Sharing bsdsocketbase is not permitted.
Generally speaking tasks should share the same address space, while processes not, and at least for that reason libraries should be reopened personally by each process.
Because people do not respect such rules, it is not possible to introduce memory-protection mechanisms to the system.
The easiest way is to call CreateNewProc() with no tags for the NP_Input, NP_Output,NP_Error, these will then open a default "NIL:" stream for the child.
int ProcEntry () {
struct Process *proc;
struct Message *msg;
proc = (struct Process *)FindTask(NULL);
WaitPort(&proc->pr_MsgPort);
msg = GetMsg(&proc->pr_MsgPort);
/* ... */
}
Properly (Semaphore) protect any resources such as IO handles etc. that you intend to share between threads and all should be OK.
CreateNewProc() should not create BCPL stack frame and register setup (A5=BCPL_jsr and other related weirdness), only RunCommand() needs to do it. Can see the reason for confusion because CreateNewProc() needs to call BCPL_init() which actually only does part of BCPL setup (stupid segarray and some globvec stuff) but it should not setup BCPL stack frames (which replaces A6 with BCPL_rts when all process entries expect SysBase = crash)
CreateNewProc()->CallEntry() should be as it was:
argPtr = A0, argSize = D0, SysBase = A6, no stack swap
And RunProcess() should be the wild & crazy BCPL stuff.
Why was this change made? Don't see how it helped other architectures, and it certainly did not help m68k. But it should still set pr_ReturnAddr, right? No, because pr_ReturnAddr is only used by exits from RunProcess()/RunCommand().
dos/Exit() does work with normal process started with Create(New)Proc(). It is not BCPL specific. (but perhaps it works differently in "BCPL mode", I haven't tested this yet) AROS/DOS/Exit() should be a no-op. The only callers of Exit() were BCPL routines, and there is a BCPL specific implementation in arch/m68k-amiga/dos/bcpl.S to support those old programs.
Dos/Exit() was a mistake in AmigaOS, and we should not be making it a first-class function. To implement a C-style exit, it should be in the AROS C library, implementing memory free and library closing, etc.
Read more here
The DateStamp structure look like this:
struct DateStamp { LONG ds_Days; /* days since 01-Jan-1978 */ LONG ds_Minute; /* minutes past midnight */ LONG ds_Tick; /* ticks past the last minute */ };
DosBase
[edit | edit source]The four public structures, DosLibrary (dl_), RootNode (rn_), DosInfo (di_), and DevInfo (dvi_) are involved. revisited DOSBase and removed almost all AROS-specific fields from it
dl_DevInfo - replaced with di_DevInfo in struct DosInfo
dl_SegList - simply not used, dos.library is ROM resident and doesn't have own seglist.
dl_ResList - removed, now it's DosInfo->di_ResList
The only thing that is left over is dl_Flags. DirectoryOpus uses this on AROS. Can it be replaced correct rn_Flags right now, or this should be done in ABI v1. Other private fields are still there, they are really private.
struct RootNode { BPTR rn_TaskArray; BPTR rn_ConsoleSegment; struct DateStamp rn_time; LONG rn_RestartSeg; BPTR rn_Info; BPTR rn_FileHandlerSegment; };
struct DosInfo { BPTR di_McName; BPTR di_DevInfo; BPTR di_Devices; BPTR di_Handlers; BPTR di_NextEntry; LONG di_UseCount; BPTR di_SegPtr; BPTR di_SegName; };
struct DevInfo { BPTR dvi_Next; LONG dvi_Type; APTR dvi_Task; BPTR dvi_Lock; BPTR dvi_Handler; LONG dvi_StackSize; LONG dvi_Priority; LONG dvi_Startup; BPTR dvi_SegList; BPTR dvi_GlobVec; BPTR dvi_Name; };
After the FGetC-s come Seek-s and then a Read. all for that same file_handle. I haven't checked what the arguments are and what happens exactly, but that's similar to the pattern for precise memory allocation: With 'getc' you read the file buffer in steps of one and only count the characters or bytes, until you get to the end of the word or line or file or whatever you want to read. Then you 'seek' to back up to where you started, you 'alloc' the counted size of memory, and then you 'read' the whole thing in one go. It's a way to avoid memory-related problems, but it may be rather heavy on the i/o-side.
FGetC like C (f)getc is a function meant to be called a lot of time. It is often used as a function pointer passed to parsing functions. Programs using fscanf etc. will use a lot of FGetC. As it is buffered I doubt it is a performance bottleneck (unless you patch it of course and print out a line on a console with each call).
#include <proto/dos.h>
#include <proto/utility.h>
UBYTE get_char()
{
UBYTE buffer;
BPTR in = Input();
SetMode (in,1); /* set to RAW mode */
Read (in,&buffer,1);
SetMode (in,0); /* set to CON mode */
if (buffer == '\r')
buffer = '\n';
return (ToUpper (buffer));
}
int main (void)
{
UBYTE key;
Printf ("Please press a key: ");
Flush (Output());
key = get_char();
Printf ("\nThe key you pressed is %lc\n",key);
return (0);
}
BPTR Open(CONST_STRPTR name, LONG accessMode) (D1,D2) BOOL Close(BPTR file) (D1) LONG Read(BPTR file, APTR buffer, LONG length) (D1, D2, D3) LONG Write(BPTR file, CONST_APTR buffer, LONG length) (D1, D2, D3) BPTR Input() () BPTR Output() () LONG Seek(BPTR file, LONG position, LONG mode) (D1,D2,D3) BOOL DeleteFile(CONST_STRPTR name) (D1) LONG Rename(CONST_STRPTR oldName, CONST_STRPTR newName) (D1,D2) BPTR Lock(CONST_STRPTR name, LONG accessMode) (D1,D2) BOOL UnLock(BPTR lock) (D1) BPTR DupLock(BPTR lock) (D1) LONG Examine(BPTR lock, struct FileInfoBlock* fib) (D1,D2) LONG ExNext(BPTR lock, struct FileInfoBlock* fileInfoBlock) (D1,D2) LONG Info(BPTR lock, struct InfoData* parameterBlock) (D1,D2) BPTR CreateDir(CONST_STRPTR name) (D1) BPTR CurrentDir(BPTR lock) (D1) SIPTR IoErr() () struct MsgPort* CreateProc(CONST_STRPTR name, LONG pri, BPTR segList, LONG stackSize) (D1,D2,D3,D4) void Exit(LONG returnCode) (D1) BPTR LoadSeg(CONST_STRPTR name) (D1) BOOL UnLoadSeg(BPTR seglist) (D1) struct MsgPort* RunHandler(struct DeviceNode *devnode, const char *path) (A0,A1) struct Device *DeviceProc(CONST_STRPTR name) (D1) LONG SetComment(CONST_STRPTR name, CONST_STRPTR comment) (D1,D2) LONG SetProtection(CONST_STRPTR name, ULONG protect) (D1,D2) struct DateStamp *DateStamp(struct DateStamp *date) (D1) void Delay(ULONG timeout) (D1) LONG WaitForChar(BPTR file, LONG timeout) (D1,D2) BPTR ParentDir(BPTR lock) (D1) LONG IsInteractive(BPTR file) (D1) LONG Execute(CONST_STRPTR string, BPTR input, BPTR output) (D1,D2,D3) APTR AllocDosObject(ULONG type, struct TagItem *tags) (D1,D2) void FreeDosObject(ULONG type, APTR ptr) (D1,D2) LONG DoPkt(struct MsgPort* port, LONG action, SIPTR arg1, SIPTR arg2, SIPTR arg3, SIPTR arg4, SIPTR arg5) void SendPkt(struct DosPacket *dp, struct MsgPort *port, struct MsgPort *replyport) (D1,D2,D3) struct DosPacket *WaitPkt() () void ReplyPkt(struct DosPacket *dp, LONG res1, LONG res2) (D1,D2,D3) void AbortPkt(struct MsgPort *port, struct DosPacket *pkt) (D1,D2) BOOL LockRecord(BPTR fh, ULONG offset, ULONG length, ULONG mode, ULONG timeout) (D1,D2,D3,D4,D5) BOOL LockRecords(struct RecordLock *recArray, ULONG timeout) (D1,D2) BOOL UnLockRecord(BPTR fh, ULONG offset, ULONG length) (D1,D2,D3) BOOL UnLockRecords(struct RecordLock * recArray) (D1) BPTR SelectInput(BPTR fh) (D1) BPTR SelectOutput(BPTR fh) (D1) LONG FGetC(BPTR file) (D1) LONG FPutC(BPTR file, LONG character) (D1,D2) LONG UnGetC(BPTR file, LONG character) (D1,D2) LONG FRead(BPTR fh, APTR block, ULONG blocklen, ULONG number) (D1,D2,D3,D4) LONG FWrite(BPTR fh, CONST_APTR block, ULONG blocklen, ULONG numblocks) (D1,D2,D3,D4) STRPTR FGets(BPTR fh, STRPTR buf, ULONG buflen) (D1,D2,D3) LONG FPuts(BPTR file, CONST_STRPTR string) (D1,D2) LONG VFWritef(BPTR fh, CONST_STRPTR fmt, const IPTR *argarray) (D1,D2,D3) LONG VFPrintf(BPTR file, CONST_STRPTR format, const IPTR *argarray) (D1,D2,D3) LONG Flush(BPTR file) (D1) LONG SetVBuf(BPTR file, STRPTR buff, LONG type, LONG size) (D1,D2,D3,D4) BPTR DupLockFromFH(BPTR lock) (D1) BPTR OpenFromLock(BPTR lock) (D1) BPTR ParentOfFH(BPTR fh) (D1) BOOL ExamineFH(BPTR fh, struct FileInfoBlock* fib) (D1,D2) BOOL SetFileDate(CONST_STRPTR name, const struct DateStamp *date) (D1,D2) BOOL NameFromLock(BPTR lock, STRPTR buffer, LONG length) (D1,D2,D3) BOOL NameFromFH(BPTR fh, STRPTR buffer, LONG length) (D1,D2,D3) LONG SplitName(CONST_STRPTR name, ULONG separator, STRPTR buf, LONG oldpos, LONG size) (D1,D2,D3,D4,D5) LONG SameLock(BPTR lock1, BPTR lock2) (D1,D2) LONG SetMode(BPTR fh, LONG mode) (D1,D2) BOOL ExAll(BPTR lock, struct ExAllData *buffer, LONG size, LONG data, struct ExAllControl *control) (D1,D2,D3,D4,D5) LONG ReadLink(struct MsgPort *port, BPTR lock, CONST_STRPTR path, STRPTR buffer, ULONG size) (D1,D2,D3,D4,D5) LONG MakeLink(CONST_STRPTR name, APTR dest, LONG soft) (D1,D2,D3) BOOL ChangeMode(ULONG type, BPTR object, ULONG newmode) (D1,D2,D3) LONG SetFileSize(BPTR file, LONG offset, LONG mode) (D1,D2,D3) SIPTR SetIoErr(SIPTR result) (D1) BOOL Fault(LONG code, CONST_STRPTR header, STRPTR buffer, LONG len) (D1,D2,D3,D4) BOOL PrintFault(LONG code, CONST_STRPTR header) (D1,D2) BOOL ErrorReport(LONG code, LONG type, IPTR arg1, struct MsgPort *device) (D1,D2,D3,D4) LONG DisplayError(CONST_STRPTR formatStr, ULONG flags, APTR args) (A0,D0,A1) struct CommandLineInterface *Cli() () struct Process *CreateNewProc(const struct TagItem *tags) (D1) NP_Seglist, NP_FreeSeglist, NP_Entry, NP_Input, NP_Output, NP_Error, NP_CloseInput, NP_CloseOutput, NP_CloseError NP_CurrentDir, NP_StackSize, NP_Name, NP_Priority, NP_CopyVars, NP_Path, NP_Arguments, NP_ConsoleTask NP_WindowPtr, NP_HomeDir, NP_Cli, NP_CommandName, NP_NotifyOnDeath, NP_Synchronous, NP_ExitCode, NP_ExitData NP_UserData TAG_DONE LONG RunCommand(BPTR segList, ULONG stacksize, STRPTR argptr, ULONG argsize) (D1,D2,D3,D4) struct MsgPort *GetConsoleTask() () struct MsgPort *SetConsoleTask(struct MsgPort *handler) (D1) struct MsgPort *GetFileSysTask() () struct MsgPort *SetFileSysTask(struct MsgPort *task) (D1) STRPTR GetArgStr() () STRPTR SetArgStr(CONST_STRPTR string) (D1) struct Process *FindCliProc(ULONG num) (D1) ULONG MaxCli() () BOOL SetCurrentDirName(CONST_STRPTR name) (D1) BOOL GetCurrentDirName(STRPTR buf, LONG len) (D1,D2) BOOL SetProgramName(CONST_STRPTR name) (D1) BOOL GetProgramName(STRPTR buf, LONG len) (D1,D2) BOOL SetPrompt(CONST_STRPTR name) (D1) BOOL GetPrompt(STRPTR buf, LONG len) (D1,D2) BPTR SetProgramDir(BPTR lock) (D1) BPTR GetProgramDir() () LONG SystemTagList(CONST_STRPTR command, const struct TagItem *tags) (D1,D2) LONG AssignLock(CONST_STRPTR name, BPTR lock) (D1,D2) BOOL AssignLate(CONST_STRPTR name, CONST_STRPTR path) (D1,D2) BOOL AssignPath(CONST_STRPTR name, CONST_STRPTR path) (D1,D2) BOOL AssignAdd(CONST_STRPTR name, BPTR lock) (D1,D2) LONG RemAssignList(CONST_STRPTR name, BPTR lock) (D1,D2) struct DevProc *GetDeviceProc(CONST_STRPTR name, struct DevProc *dp) (D1,D2) void FreeDeviceProc(struct DevProc *dp) (D1) struct DosList *LockDosList(ULONG flags) (D1) void UnLockDosList(ULONG flags) (D1) struct DosList *AttemptLockDosList(ULONG flags) (D1) LONG RemDosEntry(struct DosList *dlist) (D1) LONG AddDosEntry(struct DosList *dlist) (D1) struct DosList *FindDosEntry(struct DosList *dlist, CONST_STRPTR name, ULONG flags) (D1,D2,D3) struct DosList *NextDosEntry(struct DosList *dlist, ULONG flags) (D1,D2) struct DosList * MakeDosEntry(CONST_STRPTR name, LONG type) (D1,D2) void FreeDosEntry(struct DosList *dlist) (D1) BOOL IsFileSystem(CONST_STRPTR devicename) (D1) BOOL Format(CONST_STRPTR devicename, CONST_STRPTR volumename, ULONG dostype) (D1,D2,D3) LONG Relabel(CONST_STRPTR drive, CONST_STRPTR newname) (D1,D2) LONG Inhibit(CONST_STRPTR name, LONG onoff) (D1,D2) BOOL AddBuffers(CONST_STRPTR devicename, LONG numbuffers) (D1,D2) LONG CompareDates(const struct DateStamp *date1, const struct DateStamp *date2) (D1,D2) BOOL DateToStr(struct DateTime *datetime) (D1) BOOL StrToDate(struct DateTime *datetime) (D1) BPTR InternalLoadSeg(BPTR fh, BPTR table, LONG_FUNC *functionarray, LONG *stack) (D0,A0,A1,A2) BOOL InternalUnLoadSeg(BPTR seglist, VOID_FUNC freefunc) (D1,A1) BPTR NewLoadSeg(CONST_STRPTR file, const struct TagItem *tags) (D1,D2) BOOL AddSegment(CONST_STRPTR name, BPTR seg, LONG type) (D1,D2,D3) struct Segment *FindSegment(CONST_STRPTR name, struct Segment *seg, BOOL system) (D1,D2,D3) LONG RemSegment(struct Segment *seg) (D1) LONG CheckSignal(LONG mask) (D1) struct RDArgs *ReadArgs(CONST_STRPTR template, IPTR *array, struct RDArgs *rdargs) (D1,D2,D3) LONG FindArg(CONST_STRPTR template, CONST_STRPTR keyword) (D1,D2) LONG ReadItem(STRPTR buffer, LONG maxchars, struct CSource *input) (D1,D2,D3) LONG StrToLong(CONST_STRPTR string, LONG *value) (D1,D2) LONG MatchFirst(CONST_STRPTR pat, struct AnchorPath *AP) (D1,D2) LONG MatchNext(struct AnchorPath *AP) (D1) void MatchEnd(struct AnchorPath *AP) (D1) LONG ParsePattern(CONST_STRPTR Source, STRPTR Dest, LONG DestLength) (D1,D2,D3) BOOL MatchPattern(CONST_STRPTR pat, CONST_STRPTR str) (D1,D2) BPTR Error() () void FreeArgs(struct RDArgs *args) (D1) BPTR SelectError(BPTR fh) (D1) STRPTR FilePart(CONST_STRPTR path) (D1) STRPTR PathPart(CONST_STRPTR path) (D1) BOOL AddPart(STRPTR dirname, CONST_STRPTR filename, ULONG size) (D1,D2,D3) BOOL StartNotify(struct NotifyRequest *notify) (D1) void EndNotify(struct NotifyRequest *notify) (D1) BOOL SetVar(CONST_STRPTR name, CONST_STRPTR buffer, LONG size, LONG flags) (D1,D2,D3,D4) LONG GetVar(CONST_STRPTR name, STRPTR buffer, LONG size, LONG flags) (D1,D2,D3,D4) LONG DeleteVar(CONST_STRPTR name, ULONG flags) (D1,D2) struct LocalVar *FindVar(CONST_STRPTR name, ULONG type) (D1,D2) STRPTR DosGetLocalizedString(LONG stringNum) (D1) IPTR CliInitNewcli(struct DosPacket *dp) (A0) IPTR CliInitRun(struct DosPacket *dp) (A0) LONG WriteChars(CONST_STRPTR buf, ULONG buflen) (D1,D2) LONG PutStr(CONST_STRPTR string) (D1) LONG VPrintf(CONST_STRPTR format, IPTR *argarray) (D1,D2) LONG Pipe(CONST_STRPTR name, BPTR *reader, BPTR *writer) (D1,D2,D3) LONG ParsePatternNoCase(CONST_STRPTR Source, STRPTR Dest, LONG DestLength) (D1,D2,D3) BOOL MatchPatternNoCase(CONST_STRPTR pat, CONST_STRPTR str) (D1,D2) STRPTR DosGetString(LONG stringNum) (D0) BOOL SameDevice(BPTR lock1, BPTR lock2) (D1,D2) void ExAllEnd(BPTR lock, struct ExAllData *buffer, LONG size, LONG data, struct ExAllControl *control) BOOL SetOwner(CONST_STRPTR name, ULONG owner_info) (D1,D2) LONG ScanVars(struct Hook * hook, ULONG flags, APTR userdata) (D1,D2,D3)