Aros/Developer/Docs/Libraries/DOS

Navbar for the Aros wikibook
Aros User
Aros User Docs
Aros User FAQs
Aros User Applications
Aros User DOS Shell
Aros/User/AmigaLegacy
Aros Dev Docs
Aros Developer Docs
Porting Software from AmigaOS/SDL
For Zune Beginners
Zune .MUI Classes
For SDL Beginners
Aros Developer BuildSystem
Specific platforms
Aros x86 Complete System HCL
Aros x86 Audio/Video Support
Aros x86 Network Support
Aros Intel AMD x86 Installing
Aros Storage Support IDE SATA etc
Aros Poseidon USB Support
x86-64 Support
Motorola 68k Amiga Support
Linux and FreeBSD Support
Windows Mingw and MacOSX Support
Android Support
Arm Raspberry Pi Support
PPC Power Architecture
misc
Aros Public License

Introduction

edit

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

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

Open Close Files

edit

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

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

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

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

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

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

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
#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

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.

fm2000 source and docs

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 -:

  1. software being written before the 64-bit DOS extensions,
  2. 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

Packets

edit

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

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

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)