238 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			238 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
| /* open for MMIXware.
 | |
| 
 | |
|    Copyright (C) 2001, 2002 Hans-Peter Nilsson
 | |
| 
 | |
|    Permission to use, copy, modify, and distribute this software is
 | |
|    freely granted, provided that the above copyright notice, this notice
 | |
|    and the following disclaimer are preserved with no changes.
 | |
| 
 | |
|    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 | |
|    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 | |
|    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|    PURPOSE.  */
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <fcntl.h>
 | |
| #include <_ansi.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include "sys/syscall.h"
 | |
| #include <errno.h>
 | |
| 
 | |
| /* Let's keep the filehandle array here, since this is a primary
 | |
|    initializer of it.  */
 | |
| unsigned char _MMIX_allocated_filehandle[32] =
 | |
|  {
 | |
|    1,
 | |
|    1,
 | |
|    1,
 | |
|    0, 0, 0, 0, 0,
 | |
|    0, 0, 0, 0, 0, 0, 0, 0,
 | |
|    0, 0, 0, 0, 0, 0, 0, 0,
 | |
|    0, 0, 0, 0, 0, 0, 0, 0
 | |
|  };
 | |
| 
 | |
| int
 | |
| _open (const char *path,
 | |
|        int flags, ...)
 | |
| {
 | |
|   long fileno;
 | |
|   unsigned char mode;
 | |
|   long append_contents = 0;
 | |
|   unsigned long prev_contents_size = 0;
 | |
|   char *prev_contents = NULL;
 | |
|   long ret;
 | |
| 
 | |
|   for (fileno = 0;
 | |
|        fileno < (sizeof (_MMIX_allocated_filehandle) /
 | |
| 		 sizeof (_MMIX_allocated_filehandle[0]));
 | |
|        fileno++)
 | |
|     if (_MMIX_allocated_filehandle[fileno] == 0)
 | |
|       break;
 | |
| 
 | |
|   if (fileno == (sizeof (_MMIX_allocated_filehandle) /
 | |
| 		 sizeof (_MMIX_allocated_filehandle[0])))
 | |
|     {
 | |
|       errno = EMFILE;
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* We map this to a fopen call.  The flags parameter is stymied because
 | |
|      we don't support other than these flags.  */
 | |
|   if (flags & ~(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_APPEND | O_TRUNC))
 | |
|     {
 | |
|       UNIMPLEMENTED (("path: %s, flags: %d", path, flags));
 | |
|       errno = ENOSYS;
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if ((flags & O_ACCMODE) == O_RDONLY)
 | |
|     mode = BinaryRead;
 | |
|   else if ((flags & (O_WRONLY | O_APPEND)) == (O_WRONLY | O_APPEND))
 | |
|     {
 | |
|       mode = BinaryReadWrite;
 | |
|       append_contents = 1;
 | |
|     }
 | |
|   else if ((flags & (O_RDWR | O_APPEND)) == (O_RDWR | O_APPEND))
 | |
|     {
 | |
|       mode = BinaryReadWrite;
 | |
|       append_contents = 1;
 | |
|     }
 | |
|   else if ((flags & (O_WRONLY | O_CREAT)) == (O_WRONLY | O_CREAT)
 | |
| 	   || (flags & (O_WRONLY | O_TRUNC)) == (O_WRONLY | O_TRUNC))
 | |
|     mode = BinaryWrite;
 | |
|   else if ((flags & (O_RDWR | O_CREAT)) == (O_RDWR | O_CREAT))
 | |
|     mode = BinaryReadWrite;
 | |
|   else if (flags & O_RDWR)
 | |
|     mode = BinaryReadWrite;
 | |
|   else
 | |
|     {
 | |
|       errno = EINVAL;
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (append_contents)
 | |
|     {
 | |
|       /* BinaryReadWrite is equal to "w+", so it truncates the file rather
 | |
| 	 than keeping the contents, as can be imagined if you're looking
 | |
| 	 for append functionality.  The only way we can keep the contents
 | |
| 	 so we can append to it, is by first reading in and saving the
 | |
| 	 contents, then re-opening the file as BinaryReadWrite and write
 | |
| 	 the previous contents.  This seems to work for the needs of
 | |
| 	 simple test-programs.  */
 | |
|       long openexist = TRAP3f (SYS_Fopen, fileno, path, BinaryRead);
 | |
|       if (openexist == 0)
 | |
| 	{
 | |
| 	  /* Yes, this file exists, now opened, so let's read it and keep
 | |
|              the contents.  Better have the memory around for this to
 | |
|              work.  */
 | |
| 	  long seekval = TRAP2f (SYS_Fseek, fileno, -1);
 | |
| 
 | |
| 	  if (seekval == 0)
 | |
| 	    {
 | |
| 	      prev_contents_size = TRAP1f (SYS_Ftell, fileno);
 | |
| 
 | |
| 	      /* If the file has non-zero size, we have something to
 | |
| 		 append to.  */
 | |
| 	      if (prev_contents_size != 0)
 | |
| 		{
 | |
| 		  /* Start reading from the beginning.  Ignore the return
 | |
| 		     value from this call: we'll notice if we can't read
 | |
| 		     as much as we want.  */
 | |
| 		  TRAP2f (SYS_Fseek, fileno, 0);
 | |
| 
 | |
| 		  prev_contents = malloc (prev_contents_size);
 | |
| 		  if (prev_contents != 0)
 | |
| 		    {
 | |
| 		      /* I don't like the thought of trying to read the
 | |
| 			 whole file all at once, disregarding the size,
 | |
| 			 because the host system might not support that
 | |
| 			 and we'd get funky errors.  Read in 32k at a
 | |
| 			 time.  */
 | |
| 		      char *ptr = prev_contents;
 | |
| 		      unsigned long read_more = prev_contents_size;
 | |
| 		      unsigned long chunk_size = 1 << 15;
 | |
| 
 | |
| 		      while (read_more >= chunk_size)
 | |
| 			{
 | |
| 			  long readval
 | |
| 			    = TRAP3f (SYS_Fread, fileno, ptr, chunk_size);
 | |
| 
 | |
| 			  if (readval != 0)
 | |
| 			    {
 | |
| 			      free (prev_contents);
 | |
| 			      TRAP1f (SYS_Fclose, fileno);
 | |
| 			      errno = EIO;
 | |
| 			      return -1;
 | |
| 			    }
 | |
| 			  read_more -= chunk_size;
 | |
| 			  ptr += chunk_size;
 | |
| 			}
 | |
| 
 | |
| 		      if (read_more != 0)
 | |
| 			{
 | |
| 			  long readval
 | |
| 			    = TRAP3f (SYS_Fread, fileno, ptr, read_more);
 | |
| 			  if (readval != 0)
 | |
| 			    {
 | |
| 			      free (prev_contents);
 | |
| 			      TRAP1f (SYS_Fclose, fileno);
 | |
| 			      errno = EIO;
 | |
| 			      return -1;
 | |
| 			    }
 | |
| 			}
 | |
| 		    }
 | |
| 		  else
 | |
| 		    {
 | |
| 		      /* Malloc of area to copy to failed.  The glibc
 | |
| 			 manpage says its open can return ENOMEM due to
 | |
| 			 kernel memory failures, so let's do that too
 | |
| 			 here.  */
 | |
| 		      errno = ENOMEM;
 | |
| 		      return -1;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	  else
 | |
| 	    {
 | |
| 	      /* Seek failed.  Gotta be some I/O error.  */
 | |
| 	      errno = EIO;
 | |
| 	      return -1;
 | |
| 	    }
 | |
| 
 | |
| 	  TRAP1f (SYS_Fclose, fileno);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   ret = TRAP3f (SYS_Fopen, fileno, path, mode);
 | |
|   if (ret < 0)
 | |
|     {
 | |
|       /* It's totally unknown what the error was.  We'll just take our
 | |
| 	 chances and assume ENOENT.  */
 | |
|       errno = ENOENT;
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (prev_contents_size != 0)
 | |
|     {
 | |
|       /* Write out the previous contents, a chunk at a time.  Leave the
 | |
| 	 file pointer at the end of the file.  */
 | |
|       unsigned long write_more = prev_contents_size;
 | |
|       unsigned long chunk_size = 1 << 15;
 | |
|       char *ptr = prev_contents;
 | |
| 
 | |
|       while (write_more >= chunk_size)
 | |
| 	{
 | |
| 	  long writeval
 | |
| 	    = TRAP3f (SYS_Fwrite, fileno, ptr, chunk_size);
 | |
| 	  if (writeval != 0)
 | |
| 	    {
 | |
| 	      free (prev_contents);
 | |
| 	      TRAP1f (SYS_Fclose, fileno);
 | |
| 	      errno = EIO;
 | |
| 	      return -1;
 | |
| 	    }
 | |
| 	  write_more -= chunk_size;
 | |
| 	  ptr += chunk_size;
 | |
| 	}
 | |
|       if (write_more != 0)
 | |
| 	{
 | |
| 	  long writeval
 | |
| 	    = TRAP3f (SYS_Fwrite, fileno, ptr, write_more);
 | |
| 	  if (writeval != 0)
 | |
| 	    {
 | |
| 	      free (prev_contents);
 | |
| 	      TRAP1f (SYS_Fclose, fileno);
 | |
| 	      errno = EIO;
 | |
| 	      return -1;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       free (prev_contents);
 | |
|     }
 | |
| 
 | |
|   _MMIX_allocated_filehandle[fileno] = 1;
 | |
| 
 | |
|   return fileno;
 | |
| }
 |