{****************************************************************************

						  FPKPascal Runtime-Library
						  Copyright (c) 1994,97 by
					Florian Klaempfl and Michael Spiegel
						 OS/2 port by Dani‰l Mantione

 ****************************************************************************}

{
  History:
  2.7.1994: Version 0.2
			Datenstrukturen sind deklariert sowie
			50 % der Unterprogramme sind implementiert
  12.8.1994: exec implemented
  14.8.1994: findfirst and findnext implemented
  24.8.1994: Version 0.3
  28.2.1995: Version 0.31
			 some parameter lists with const optimized
   3.7.1996: bug in fsplit removed (dir and ext were not intializised)
   7.7.1996: packtime and unpacktime implemented
  20.9.1996: Version 0.5
			 setftime and getftime implemented
			 some optimizations done (integer -> longint)
			 procedure fsearch from the LINUX version ported
			 msdos call implemented
  26th november 1996:
			 better fexpand
  29th january 1997:
			 bug in getftime and setftime removed
			 setfattr and getfattr added
   2th february 1997: Version 0.9
			 bug of searchrec corrected
   2 june 1997:
			 OS/2 support added.
   12 june 1997:
			 OS/2 port done.
}

unit dos;

{$I os.inc}

  interface

	uses
	   strings;

	const
	   { bit masks for CPU flags}
	   fcarry = $0001;
	   fparity = $0004;
	   fauxiliary = $0010;
	   fzero = $0040;
       fsign = $0080;
       foverflow  = $0800;

       { Bitmasken fuer Dateiattribute }
       readonly = $01;
       hidden = $02;
       sysfile = $04;
       volumeid = $08;
       directory = $10;
       archive = $20;
       anyfile = $3F;
       fmclosed = $D7B0;
       fminput = $D7B1;
       fmoutput = $D7B2;
       fminout = $D7B3;

    type
	   { some string types }
	   {$IFDEF OS2}
	   comstr=string; 				{Filenames can be long in OS/2.}
	   pathstr=string;
	   {$ELSE}
	   comstr = string[127];        { Kommandozeilenstring }
	   pathstr = string[79];        { String fuer einen Pfadnamen }
	   {$ENDIF}
	   dirstr = string[67];         { String fuer kompletten Pfad }
	   namestr = string[8];         { Dateinamenstring }
	   extstr = string[4];          { String fuer Dateinamensuffix }

	   { search record which is used by findfirst and findnext }
{$PACKRECORDS 1}
	   searchrec = record
		  fill : array[1..21] of byte;
		  attr : byte;
		  time : longint;
		  {$IFNDEF OS2}			{ A DJGPP strange thing.}
		  reserved : word; 		{ requires the DOS extender (DJ GNU-C) }
		  {$ENDIF}
		  size : longint;
		  {$IFNDEF OS2}
		  name : string[15]; 	{ the same size as declared by (DJ GNU C) }
		  {$ELSE}
		  name:string;			{Filenames can be very long in OS/2!}
		  {$ENDIF}
	   end;
{$PACKRECORDS 2}

       { file record for untyped files }
       filerec = record
          handle : word;
          mode : word;
		  recsize : word;
          _private : array[1..26] of byte;
          userdata: array[1..16] of byte;
          name: array[0..79] of char;
	   end;

       { file record for text files }
	   textbuf = array[0..127] of char;

       textrec = record
          handle : word;
          mode : word;
          bufSize : word;
          _private : word;
          bufpos : word;
          bufend : word;
          bufptr : ^textbuf;
          openfunc : pointer;
		  inoutfunc : pointer;
          flushfunc : pointer;
          closefunc : pointer;
          userdata : array[1..16] of byte;
		  name : array[0..79] of char;
		  buffer : textbuf;
       end;

       { data structure for the registers needed by msdos and intr }	
       registers = record
		 case i : integer of
			0 : (ax,f1,bx,f2,cx,f3,dx,f4,bp,f5,si,f51,di,f6,ds,f7,es,f8,flags,fs,gs : word);
			1 : (al,ah,f9,f10,bl,bh,f11,f12,cl,ch,f13,f14,dl,dh : byte);
			2 : (eax,  ebx,  ecx,  edx,  ebp,  esi,  edi : longint);
	   end;

	   { record for date and time }
	   datetime = record
		  year,month,day,hour,min,sec : word;
	   end;

	{Flags for the exec procedure:

	 Starting the program:
	 efwait:		Wait until program terminates, otherwise the program
					continues execution.
	 efno_wait:		? Function unknown. Not implemented in EMX.
	 efoverlay:		Terminate this program, then execute the requested
					program. WARNING: Exit-procedures are not called!
	 efdebug:		Debug program. Details are unknown.
	 efsession:		Do not execute as child of this program. Use a seperate
					session instead.
	 efdetach:		Detached. Function unknown. Info wanted!
	 efpm:			Run as presentation manager program.

	 Determining the window state of the program:
	 efdefault:		Run the pm program in it's default situation.
	 efminimize:	Run the pm program minimized.
	 efmaximize:	Run the pm program maximized.
	 effullscreen:	Run the non-pm program fullscreen.
	 efwindowed:	Run the non-pm program in a window.

	 Other options are not implemented defined because lack of
	 knowledge abou what they do.}

	type	execrunflags=(efwait,efno_wait,efoverlay,efdebug,efsession,
						  efdetach,efpm);
			execwinflags=(efdefault,efminimize,efmaximize,effullscreen,
						  efwindowed);
			execset=set of execrunflags;

	var
	   { error variable }
	   doserror : integer;

	procedure getdate(var year,month,day,dayofweek : word);
	procedure gettime(var hour,minute,second,sec100 : word);
	function dosversion : word;
	procedure setdate(year,month,day : word);
	procedure settime(hour,minute,second,sec100 : word);
	procedure getcbreak(var breakvalue : boolean);
	procedure setcbreak(breakvalue : boolean);
	procedure getverify(var verify : boolean);
	procedure setverify(verify : boolean);
	function diskfree(drive : byte) : longint;
	function disksize(drive : byte) : longint;
	procedure findfirst(const path : pathstr;attr : word;var f : searchRec);
	procedure findnext(var f : searchRec);

	{ is a dummy }
	procedure swapvectors;

{   not supported:
	procedure getintvec(intno : byte;var vector : pointer);
	procedure setintvec(intno : byte;vector : pointer);
	procedure keep(exitcode : word);
}
	procedure msdos(var regs : registers);
	procedure intr(intno : byte;var regs : registers);

	procedure getfattr(var f;var attr : word);
	procedure setfattr(var f;attr : word);

	function fsearch(const path : pathstr;dirlist : string) : pathstr;
	procedure getftime(var f;var time : longint);
	procedure setftime(var f;time : longint);
	procedure packtime (var d: datetime; var time: longint);
	procedure unpacktime (time: longint; var d: datetime);
	function fexpand(const path : pathstr) : pathstr;
	procedure fsplit(path : pathstr;var dir : dirstr;var name : namestr;
	  var ext : extstr);
	procedure exec(const path : pathstr;const comline : comstr);
	{$IFDEF OS2}
	function exec(path:pathstr;runflags:execset;winflags:execwinflags;
				  const comline:comstr):longint;
	{$ENDIF}
	function dosexitcode : word;
    function envcount : longint;
	function envstr(index : longint) : string;
	function getenv(const envvar : string): string;

  implementation

  {$ifdef OS2}

  type	OS2FSAllocate=record
			idfilesystem,
			csectorunit,
			cunit,
			cunitavail:longint;
			cbsector:word;
		end;

  function _DosQueryFSInfo(driveno:word;infolevel:word;
					  var info;infolen:word):word;[C];

  {$endif OS2}

	{ this was first written for the LINUX version,    }
	{ by Michael Van Canneyt but it works also         }
	{ for the DOS version (I hope so)                  }
	function fsearch(const path : pathstr;dirlist : string) : pathstr;

	  var
		 newdir : pathstr;
		 p1 : byte;
		 s : searchrec;

	  begin
		 if (pos('?',path)<>0) or (pos('*',path)<>0) then
		   { No wildcards allowed in these things }
		   fsearch:=''
		 else
		   begin
			  repeat
				{ get first path }
				p1:=pos(';',dirlist);
				if p1>0 then
				  begin
					 newdir:=copy(dirlist,1,p1-1);
					 delete(dirlist,1,p1)
				  end
				else
				  begin
					 newdir:=dirlist;
					 dirlist:=''
                  end;
                findfirst(newdir+'\'+path,anyfile,s);
                if doserror=0 then
                  begin
                     newdir:=newdir+'\'+s.name;
                     { this was for LINUX:
                     if pos('.\',newdir)=1 then
                       delete(newdir, 1, 2)
                     { DOS strips off an initial .\ }
					 }
				  end
				else newdir:='';
			  until(dirlist='') or (length(newdir)>0);
			  fsearch:=newdir;
		   end;
	  end;

	procedure getftime(var f;var time : longint);

	  begin
	{$IFNDEF OS2}
		 asm
			{ load handle }
			movl f,%ebx
			movw (%ebx),%bx
			{ get date }
			movw $0x5700,%ax
			int $0x21
			shll $16,%edx
			movw %cx,%dx
			movl time,%ebx
			movl %edx,(%ebx)
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
	{$ELSE}
		 asm
			{ load handle }
			movl f,%ebx
			movw (%ebx),%bx
			{ get date }
			movw $0x5700,%ax
			call ___syscall
			shll $16,%edx
			movw %cx,%dx
			movl time,%ebx
			movl %edx,(%ebx)
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
	{$ENDIF}
	  end;

   procedure setftime(var f;time : longint);

	  begin
	  {$IFNDEF OS2}
		 asm
			{ load handle }
			movl f,%ebx
			movw (%ebx),%bx
			movl time,%ecx
			shldl $16,%ecx,%edx
			{ set date }
			movw $0x5701,%ax
			int $0x21
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
	  {$ELSE}
		 asm
			{ load handle }
			movl f,%ebx
			movw (%ebx),%bx
			movl time,%ecx
			shldl $16,%ecx,%edx
			{ set date }
			movw $0x5701,%ax
			call ___syscall
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
	  {$ENDIF}
	  end;

	procedure msdos(var regs : registers);

	{ Not recommended for EMX. Only works in DOS mode, not in OS/2 mode.}

	  begin
		 intr($21,regs);
	  end;

	procedure intr(intno : byte;var regs : registers);

	{ Not recommended for EMX. Only works in DOS mode, not in OS/2 mode.}

	  begin
		 asm
			.data
	int86:
			.byte        0xcd
	int86_vec:
			.byte        0x03
			jmp        int86_retjmp

			.text
			movl        8(%ebp),%eax
			movb        %al,int86_vec

			movl        10(%ebp),%eax
			; do not use first int
			addl        $2,%eax

			movl        4(%eax),%ebx
			movl        8(%eax),%ecx
			movl        12(%eax),%edx
			movl        16(%eax),%ebp
			movl        20(%eax),%esi
			movl        24(%eax),%edi
			movl        (%eax),%eax

			jmp        int86
	int86_retjmp:
			pushf
			pushl	%ebp
			pushl       %eax
			movl        %esp,%ebp
            ; calc EBP new
            addl        $12,%ebp
			movl        10(%ebp),%eax
            ; do not use first int
			addl        $2,%eax

			popl        (%eax)
            movl        %ebx,4(%eax)
            movl        %ecx,8(%eax)
            movl        %edx,12(%eax)
			; restore EBP
            popl	%edx
			movl	%edx,16(%eax)
			movl        %esi,20(%eax)
			movl        %edi,24(%eax)
			; ignore ES and DS
			popl        %ebx        /* flags */
			movl        %ebx,32(%eax)
			; FS and GS too
		 end;
	  end;

	var
	   lastdosexitcode : word;

	{$IFNDEF OS2}

	procedure exec(const path : pathstr;const comline : comstr);

	  procedure do_system(p : pchar);

		begin
		   asm
			  movl 12(%ebp),%ebx
			  movw $0xff07,%ax
			  int $0x21
			  movw %ax,_LASTDOSEXITCODE
		   end;
		end;

	  var
		 execute : string;
		 b : array[0..255] of char;

	  begin
		 execute:=path+' '+comline;
		 move(execute[1],b,length(execute));
		 b[length(execute)]:=#0;
		 do_system(b);
	  end;
	{$ELSE}

	procedure exec(const path:pathstr;const comline:comstr);

	{Execute a program.}

	begin
		exec(path,[efwait],efdefault,comline);
	end;

	function exec(path:pathstr;runflags:execset;winflags:execwinflags;
				  const comline:comstr):longint;

	{Execute a program. More suitable for OS/2 than the exec above.}

	{512 bytes should be enough to contain the command-line.}

	type	bytearray=array[0..8191] of byte;
			Pbytearray=^bytearray;
         { replaced by pbyte that works on smallset an normal set
			setarray=array[0..31] of byte; by (PM) }
         pbyte = ^byte;

			execstruc=record
				argofs,envofs,nameofs:pointer;
				argseg,envseg,nameseg:word;
				numarg,sizearg,
				numenv,sizeenv:word;
				mode1,mode2:byte;
			end;

	var	args:Pbytearray;
		env:Pbytearray;
		i,j:word;
		es:execstruc;
		esadr:pointer;

	begin
		getmem(args,512);
		getmem(env,8192);
		i:=1;
		j:=0;
		es.numarg:=0;
		while i<=length(comline) do
			begin
				if comline[i]<>' ' then
					begin
						{Commandline argument found. Copy it.}
						inc(es.numarg);
						args^[j]:=$80;
						inc(j);
						while (i<=length(comline)) and (comline[i]<>' ') do
							begin
								args^[j]:=byte(comline[i]);
								inc(j);
								inc(i);
							end;
						args^[j]:=0;
						inc(j);
					end;
				inc(i);
			end;
		args^[j]:=0;
		inc(j);

		{Commandline ready, now build the environment.

		 Oh boy, I always had the opinion that executing a program under Dos
		 was a hard job!}

		asm
			movl env,%edi		{Setup destination pointer.}
			movl _envc,%ecx		{Load number of arguments in edx.}
			movl _environ,%esi	{Load env. strings.}
			xorl %edx,%edx		{Count environment size.}
		exa1:
			lodsl				{Load a Pchar.}
			xchgl %eax,%ebx
		exa2:
			movb (%ebx),%al		{Load a byte.}
			incl %ebx			{Point to next byte.}
			stosb				{Store it.}
			incl %edx			{Increase counter.}
			cmpb $0,%al			{Ready ?.}
			jne exa2
			loop exa1			{Next argument.}
			stosb				{Store an extra 0 to finish. (AL is now 0).}
			incl %edx
			movl %edx,(24)es	{Store environment size.}
		end;

		{Environtment ready, now set-up exec structure.}
		es.argofs:=args;
		es.envofs:=env;
		asm
			leal path,%esi
			lodsb
			movzbl %al,%eax
			incl %eax
			addl %eax,%esi
			movb $0,(%esi)
		end;
		es.nameofs:=pointer(longint(@path)+1);
		asm
			movw %ss,(12)es		{Compiler doesn't like record elems in asm.}
			movw %ss,(14)es
			movw %ss,(16)es
		end;
		es.sizearg:=j;
		es.numenv:=0;
		{Typecasting of sets in FPK is a bit hard.}
      { this way works allways (PM) }
		es.mode1:=pbyte(@runflags)^;
		es.mode2:=byte(winflags);

		{Now exec the program.}
		esadr:=@es;
		asm
			movl esadr,%edx
			mov $0x7f06,%ax
			call ___syscall
			jnc exprg1
			movl %eax,U_DOS_DOSERROR
			xorl %eax,%eax
			decl %eax
		exprg1:
			movl %eax,__RESULT
		end;

		freemem(args,512);
		freemem(env,8192);
		{Phew! That's it. This was the most sophisticated procedure to call
		 a system function I ever wrote!}
	end;

	{$ENDIF}

	function dosexitcode : word;

	  begin
		 dosexitcode:=lastdosexitcode;
	  end;

	function dosversion : word;

	  begin
		{$IFNDEF OS2}
		 asm
			movb $0x30,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			leave
			ret
		 end;
		{$ELSE}
		 {Returns DOS version in DOS and OS/2 version in OS/2}
		 asm
			movb $0x30,%ah
			call ___syscall
			leave
			ret
		 end;
		{$ENDIF}
	  end;

	procedure getdate(var year,month,day,dayofweek : word);

	  begin
		{$IFNDEF OS/2}
		 asm
			movb $0x2a,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			xorb %ah,%ah
			movl 20(%ebp),%edi
			stosw
			movl 16(%ebp),%edi
			movb %dl,%al
			stosw
			movl 12(%ebp),%edi
			movb %dh,%al
			stosw
			movl 8(%ebp),%edi
			movw %cx,%ax
			stosw
		 end;
		{$ELSE}
		 asm
			movb $0x2a,%ah
			call ___syscall
			xorb %ah,%ah
			movl 20(%ebp),%edi
			stosw
			movl 16(%ebp),%edi
			movb %dl,%al
			stosw
			movl 12(%ebp),%edi
			movb %dh,%al
			stosw
			movl 8(%ebp),%edi
			xchgw %ecx,%eax
			stosw
		 end;
		{$ENDIF}
	  end;

	procedure setdate(year,month,day : word);

	  begin
		{$IFNDEF OS2}
		 asm
			movw 8(%ebp),%cx
			movb 10(%ebp),%dh
			movb 12(%ebp),%dl
			movb $0x2b,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
		{$ELSE}
		{DOS only! You cannot change the system date in OS/2!}
		 asm
			movw 8(%ebp),%cx
			movb 10(%ebp),%dh
			movb 12(%ebp),%dl
			movb $0x2b,%ah
			call ___syscall
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
		{$ENDIF}
	  end;

	procedure gettime(var hour,minute,second,sec100 : word);

	  begin
		{$IFNDEF OS2}
		 asm
			movb $0x2c,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			xorb %ah,%ah
			movl 20(%ebp),%edi
			movb %dl,%al
			stosw
			movl 16(%ebp),%edi
			movb %dh,%al
			stosw
			movl 12(%ebp),%edi
			movb %cl,%al
			stosw
			movl 8(%ebp),%edi
			movb %ch,%al
			stosw
		 end;
		{$ELSE}
		 asm
			movb $0x2c,%ah
			call ___syscall
			xorb %ah,%ah
			movl 20(%ebp),%edi
			movb %dl,%al
			stosw
			movl 16(%ebp),%edi
			movb %dh,%al
			stosw
			movl 12(%ebp),%edi
			movb %cl,%al
			stosw
			movl 8(%ebp),%edi
			movb %ch,%al
			stosw
		 end;
		{$ENDIF}
	  end;

	procedure settime(hour,minute,second,sec100 : word);

	  begin
		{$IFNDEF OS2}
		 asm
			movb 8(%ebp),%ch
			movb 10(%ebp),%cl
			movb 12(%ebp),%dh
			movb 14(%ebp),%dl
			movb $0x2d,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
		{$ELSE}
		 asm
			movb 8(%ebp),%ch
			movb 10(%ebp),%cl
			movb 12(%ebp),%dh
			movb 14(%ebp),%dl
			movb $0x2d,%ah
			call ___syscall
			xorb %ah,%ah
			movw %ax,U_DOS_DOSERROR
		 end;
		{$ENDIF}
	  end;

	procedure getcbreak(var breakvalue : boolean);

	  begin
		{$IFNDEF OS2}
		 asm
			movw $0x3300,%ax
			pushl %ebp
			int $0x21
			popl %ebp
			movl 8(%ebp),%eax
			movb %dl,(%eax)
		 end;
		{$ELSE}
		 {! Do not use in OS/2. Also not recommended in DOS. Use
			signal handling instead.}
		 asm
			movw $0x3300,%ax
			call ___syscall
			movl 8(%ebp),%eax
			movb %dl,(%eax)
		 end;
		{$ENDIF}
	  end;

	procedure setcbreak(breakvalue : boolean);

	  begin
		{$IFNDEF OS2}
		 asm
			movb 8(%ebp),%dl
			movl $0x3301,%ax
			pushl %ebp
			int $0x21
			popl %ebp
		 end;
		{$ELSE}
		 {! Do not use in OS/2. Also not recommended in DOS. Use
			signal handling instead.}
		 asm
			movb 8(%ebp),%dl
			movl $0x3301,%ax
			call ___syscall
		 end;
		{$ENDIF}
	  end;

	procedure getverify(var verify : boolean);

	  begin
		{$IFNDEF OS2}
		 asm
			movb $0x54,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			movl 8(%ebp),%edi
			stosb
		 end;
		{$ELSE}
		 {! Do not use in OS/2.}
		 asm
			movb $0x54,%ah
			call ___syscall
			movl 8(%ebp),%edi
			stosb
		 end;
		{$ENDIF}
	  end;

	procedure setverify(verify : boolean);

	  begin
		{$IFNDEF OS2}
		 asm
			movb 8(%ebp),%al
			movl $0x2e,%ah
			pushl %ebp
			int $0x21
			popl %ebp
		 end;
		{$ELSE}
		 {! Do not use in OS/2.}
		 asm
			movb 8(%ebp),%al
			movl $0x2e,%ah
			call ___syscall
		 end;
		{$ENDIF}
	  end;

	function diskfree(drive : byte) : longint;

	var	fi:OS2FSallocate;

	begin
		{$IFNDEF OS2}
		asm
			movb 8(%ebp),%dl
			movb $0x36,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			cmpw $-1,%ax
			je LDISKFREE1
			mulw %cx
			mulw %bx
			shll $16,%edx
			movw %ax,%dx
			movl %edx,%eax
			leave
			ret
		 LDISKFREE1:
			cwde
			leave
			ret
		end;
		{$ELSE}
		if os_mode=osDOS then
			{Function 36 is not supported in OS/2.}
			asm
				movb 8(%ebp),%dl
				movb $0x36,%ah
				call ___syscall
				cmpw $-1,%ax
				je LDISKFREE1
				mulw %cx
				mulw %bx
				shll $16,%edx
				movw %ax,%dx
				xchgl %edx,%eax
				leave
				ret
			 LDISKFREE1:
				cwde
				leave
				ret
			end
		 else
			{In OS/2, we use the filesystem information.}
			begin
				doserror:=_dosqueryFSinfo(drive,1,FI,sizeof(FI));
				if doserror=0 then
					diskfree:=FI.cunitavail*FI.csectorunit*FI.cbsector
				else
					diskfree:=-1;
			end;
		{$ENDIF}
	end;

	function disksize(drive : byte) : longint;

	begin
		{$IFNDEF OS/2}
		asm
			movb 8(%ebp),%dl
			movb $0x36,%ah
			pushl %ebp
			int $0x21
			popl %ebp
			movw %dx,%bx
			cmpw $-1,%ax
			je LDISKSIZE1
			mulw %cx
			mulw %bx
			shll $16,%edx
			movw %ax,%dx
			movl %edx,%eax
			leave
			ret
		 LDISKSIZE1:
			movl $-1,%eax
			leave
			ret
		end;
		{$ELSE}
		if os_mode=osDOS then
			{Function 36 is not supported in OS/2.}
			asm
				movb 8(%ebp),%dl
				movb $0x36,%ah
				call ___syscall
				movw %dx,%bx
				cmpw $-1,%ax
				je LDISKSIZE1
				mulw %cx
				mulw %bx
				shll $16,%edx
				movw %ax,%dx
				xchgl %edx,%eax
				leave
				ret
			 LDISKSIZE1:
				cwde
				leave
				ret
			 end;
		 else
			{In OS/2, we use the filesystem information.}
			begin
				doserror:=dosQFSinfo(drive,1,FI,sizeof(FI));
				if doserror=0 then
					diskfree:=FI.cunit*FI.csectorunit*FI.cbsector
				else
					diskfree:=-1;
			end;
		{$ENDIF}
	  end;

	procedure searchrec2dossearchrec(var f : searchrec);

	  var
		 l,i : longint;

	  {$IFDEF OS2}
	  const	namesize=255;
	  {$ELSE}
	  const	namesize=12;
	  {$ENDIF}

	  begin
		 l:=length(f.name);
		 for i:=1 to namesize do
		   f.name[i-1]:=f.name[i];
		 f.name[l]:=#0;
	  end;

	procedure dossearchrec2searchrec(var f : searchrec);

	  var
		 l,i : longint;

	  {$IFDEF OS2}
	  const	namesize=255;
	  {$ELSE}
	  const namesize=12;
	  {$ENDIF}

	  begin
		 for i:=0 to namesize do
		   if f.name[i]=#0 then
			 begin
				l:=i;
				break;
			 end;
		 for i:=namesize-1 downto 0 do
		   f.name[i+1]:=f.name[i];
		 f.name[0]:=chr(l);
	  end;

	procedure findfirst(const path : pathstr;attr : word;var f : searchRec);

	  procedure _findfirst(path : pchar;attr : word;var f : searchrec);

		begin
		   {$IFNDEF OS2}
		   asm
			  movl 18(%ebp),%edx
			  movb $0x1a,%ah
			  int $0x21
			  movl 12(%esp),%edx
			  movzwl 16(%esp),%ecx
			  movb $0x4e,%ah
			  int $0x21
			  jnc LFF
			  movw %ax,U_DOS_DOSERROR
		   LFF:
		   end;
		   {$ELSE}
		   asm
			  movl 12(%esp),%edx
			  movw 16(%esp),%cx
			  {No need to set DTA in EMX. Just give a pointer in ESI.}
			  movl 18(%ebp),%esi
			  movb $0x4e,%ah
			  call ___syscall
			  jnc LFF
			  movw %ax,U_DOS_DOSERROR
		   LFF:
		   end;
		   {$ENDIF}
		end;

	  var
		 path0 : array[0..80] of char;

	  begin
		 { no error }
		 doserror:=0;
		 strpcopy(path0,path);
		 _findfirst(path0,attr,f);
		 dossearchrec2searchrec(f);
	  end;

	procedure findnext(var f : searchRec);

	  procedure _findnext(var f : searchrec);

		begin
		   {$IFNDEF OS2}
		   asm
			  movl 12(%ebp),%edx
			  movb $0x1a,%ah
			  int $0x21
			  movb $0x4f,%ah
			  int $0x21
			  jnc LFN
			  movw %ax,U_DOS_DOSERROR
		   LFN:
		   end;
		   {$ELSE}
		   asm
			  movl 12(%ebp),%esi
			  movb $0x4f,%ah
			  call ___syscall
			  jnc LFN
			  movw %ax,U_DOS_DOSERROR
		   LFN:
		   end;
		   {$ENDIF}
		end;

	  begin
		 { no error }
		 doserror:=0;
		 searchrec2dossearchrec(f);
		 _findnext(f);
		 dossearchrec2searchrec(f);
	  end;

	procedure swapvectors;

	  begin
		 { tut nichts, DOS-Extender bernimmt das N”tige }
		 { normalerweise selber                          }
		 { nur aus Kompatibilit„tsgrnden implementiert  }
	  end;

	type
	   ppchar = ^pchar;

	function envs : ppchar;

	  begin
		 asm
			movl _environ,%eax
			leave
			ret
		 end ['EAX'];
	  end;

	function envcount : longint;

	  var
		 hp : ppchar;

	  begin
		{$IFNDEF OS2}
		 hp:=envs;
		 envcount:=0;
		 while assigned(hp^) do
		   begin
			  { not the best solution, but quite understandable }
			  inc(envcount);
			  hp:=hp+4;
		   end;
		{$ELSE}
			asm
				movl _envc,%eax
				leave
				ret
			end ['EAX'];
		{$ENDIF}
	  end;

	function envstr(index : longint) : string;

	  var
		 hp : ppchar;

	  begin
		 if (index<=0) or (index>envcount) then
		   begin
              envstr:='';
              exit;
           end;
		 hp:=envs+4*(index-1);
         envstr:=strpas(hp^);
	  end;

	function getenv(const envvar : string) : string;

      var
         hs,_envvar : string;
         eqpos,i : longint;

      begin
         _envvar:=upcase(envvar);
         getenv:='';
         for i:=1 to envcount do
           begin
              hs:=envstr(i);
              eqpos:=pos('=',hs);
			  if copy(hs,1,eqpos-1)=_envvar then
                begin
				   getenv:=copy(hs,eqpos+1,length(hs)-eqpos);
                   exit;
                end;
           end;
      end;

    procedure fsplit(path : pathstr;var dir : dirstr;var name : namestr;
      var ext : extstr);

      var
         p1 : byte;

      begin
         { try to find out a extension }
         p1:=pos('.',path);
         if p1>0 then
		   begin
              ext:=copy(path,p1,4);
              delete(path,p1,length(path)-p1+1);
           end
         else
           ext:='';
		 { get drive name }
         p1:=pos(':',path);
         if p1>0 then
           begin
			  dir:=path[1]+':';
              delete(path,1,p1);
           end
         else
           dir:='';
         { split the path and the name, there are no more path informtions }
         { if path contains no backslashes                                 }
         while true do
           begin
              p1:=pos('\',path);
              if p1=0 then
                break;
              dir:=dir+copy(path,1,p1);
              delete(path,1,p1);
           end;
         name:=path;
      end;

    function fexpand(const path : pathstr) : pathstr;

      function get_current_drive : byte;
      
        var
           r : registers;
           
        begin
           r.ah:=$19;
           msdos(r);
           get_current_drive:=r.al;
        end;           

       var
          s,pa : string[79];

       begin
		  { There are differences between FPKPascal and Turbo Pascal
            e.g. for the string 'D:\DEMO\..\HELLO' which isn't handled }
          getdir(0,s);
          pa:=upcase(path);
          if (ord(pa[0])>1) and (((pa[1]>='A') and (pa[1]<='Z')) and (pa[2]=':')) then
            begin
               if (ord(pa[0])>2) and (pa[3]<>'\') then
                 if pa[1]=s[1] then
                   pa:=s+'\'+copy (pa,3,length(pa))
                 else
				   pa:=pa[1]+':\'+copy (pa,3,length(pa))
            end
          else
            if pa[1]='\' then 
              pa:=s[1]+':'+pa
            else if s[0]=#3 then
              pa:=s+pa
            else
              pa:=s+'\'+pa;
          fexpand:=pa;
       end;

     procedure packtime(var d : datetime;var time : longint);

       var
          zs : longint;

	   begin
          time:=-1980;
          time:=time+d.year and 127;
          time:=time shl 4;
          time:=time+d.month;
          time:=time shl 5;
          time:=time+d.day;
          time:=time shl 16;
          zs:=d.hour;
          zs:=zs shl 6;
          zs:=zs+d.min;
          zs:=zs shl 5;
          zs:=zs+d.sec div 2;
          time:=time+(zs and $ffff);
       end;

     procedure unpacktime (time: longint; var d: datetime);

	   begin
          d.sec:=(time and 31) * 2;
          time:=time shr 5;
          d.min:=time and 63;
          time:=time shr 6;
          d.hour:=time and 31;
          time:=time shr 5;
          d.day:=time and 31;
          time:=time shr 5;
          d.month:=time and 15;
          time:=time shr 4;
          d.year:=time + 1980;
       end;

    procedure getfattr(var f;var attr : word);

      var
         { to avoid problems }
		 n : array[0..255] of char;
		 {$IFNDEF OS2}
		 r : registers;
		 {$ENDIF}

	  begin
		 strpcopy(n,filerec(f).name);
		{$IFNDEF OS2}
		 r.ax:=$4300;
		 r.edx:=longint(@n);
		 msdos(r);
		 attr:=r.cx;
		{$ELSE}
		 {Alas, msdos(r) doesn't work when we are running in OS/2.}
		 asm
			movw $0x4300,%ax
			leal n,%edx
			call ___syscall
			movl attr,%ebx
			movw %cx,(%ebx)
		 end;
		{$ENDIF}
	  end;

	procedure setfattr(var f;attr : word);

	  var
		 { to avoid problems }
		 n : array[0..255] of char;
		 {$IFNDEF OS2}
		 r : registers;
		 {$ENDIF}

	  begin
		 strpcopy(n,filerec(f).name);
		{$IFNDEF OS2}
		 r.ax:=$4301;
		 r.edx:=longint(@n);
		 r.cx:=attr;
		 msdos(r);
		{$ELSE}
		 {Alas, msdos(r) doesn't work when we are running in OS/2.}
		 asm
			movw $0x4301,%ax
			leal n,%edx
			movw attr,%cx
			call ___syscall
		 end;
		{$ENDIF}
	  end;
end.
