@node File System Extensions, file system @subheading Description The File System Extensions are a part of the lowest level of I/O operations in the C runtime library of DJGPP. These extensions are provided to allow for cases where Unix uses a file descriptor to access such items as serial ports, memory, and the network, but DOS does not. It allows a set of functions (called an @i{extension}) to gain control when one of these low-level functions is called on a file descriptor set up by the extension. Each extension must provide one or two handler functions. All handler functions take the same arguments: @example int function(__FSEXT_Fnumber func_number, int *rv, va_list args); @end example The @var{func_number} identifies which function is to be emulated. The file @code{} defines the function numbers as follows: @table @code @item __FSEXT_nop A no-op. This is currently unused by the library functions. @item __FSEXT_open An open handler. This is called just before the library is about to issue the DOS OpenFile call on behalf of your program. @item __FSEXT_creat A create handler. Called when a file needs to be created. Note that the handler should both create the ``file'' and open it. @item __FSEXT_read A read handler. Called when data should be read from a ``file''. @item __FSEXT_write A write handler. Called to write data to a ``file''. On ``text'' files it receives the ORIGINAL (unconverted) buffer. @item __FSEXT_ready A ready handler. It is called by @code{select} library function (@pxref{select}) when it needs to know whether a handle used to reference the ``file'' is ready for reading or writing, or has an error condition set. The handler should return an OR'ed bit mask of the following bits (defined on @code{}): @table @code @item __FSEXT_ready_read The ``file'' is ready for reading. @item __FSEXT_ready_write The ``file'' is ready for writing. @item __FSEXT_ready_error The ``file'' has an error condition set. @end table @item __FSEXT_close A close handler. Called when the ``file'' should be closed. @item __FSEXT_fcntl A file fcntl handler. @item __FSEXT_ioctl A file ioctl handler. @item __FSEXT_lseek A file lseek handler (@pxref{lseek}). @item __FSEXT_link A file link handler (@pxref{link}). This is most relevant to file system extensions that emulate a directory structure. @item __FSEXT_unlink A file unlink handler (@pxref{unlink}). This is most relevant to file system extensions that emulate a directory structure. @item __FSEXT_dup A file dup handler (@pxref{dup}). This is called when a new descriptor is needed to refer to an existing descriptor. @item __FSEXT_dup2 A file dup2 handler (@pxref{dup2}). This is called when two different file descriptors are used to refer to the same open file. @item __FSEXT_fstat A file fstat handler (@pxref{fstat}). The extension should fill in various status information about the emulated file. @end table @var{rv} points to a temporary return value pointer. If the function is emulated by the handler, the return value should be stored here, and the handler should return a nonzero value. If the handler returns zero, it is assumed to have not emulated the call, and the regular DOS I/O function will happen. The @var{args} represent the arguments passed to the original function; these point to the actual arguments on the stack, so the emulation may choose to modify them and return zero to the regular function, which will then act on the modified arguments. A normal extension would provide these parts: @itemize @bullet @item Some function to create a connection to the extension. This may be a custom function (such as @code{socket} for networking) or an extension to open (such as @code{/dev/ttyS0} to access the serial port). @item Initialization code that adds the open handler, if any. @item Overrides for the basic I/O functions, such as @code{read} and @code{write}. This is a single function in the extension that uses the function number parameter to select an extension function. @item The core functionality of the extension, if any. @end itemize Please note that the special Unix filenames @file{/dev/null} and @file{/dev/tty} are already mapped to the appropriate DOS names @file{NUL} and @file{CON}, respectively, so you don't need to write extensions for these. @c ---------------------------------------------------------------------- @node __FSEXT_alloc_fd, file system @subheading Syntax @example #include int __FSEXT_alloc_fd(__FSEXT_Function *_function); @end example @subheading Description This function is part of the @ref{File System Extensions}. It is used by extensions that fully emulate the I/O functions, and thus don't have a corresponding DOS file handle. Upon the first call, this function opens DOS's @samp{NUL} device, so as to allocate a handle that DOS won't then reuse. Upon subsequent calls, that handle is duplicated by calling the DOS @code{dup} function; this makes all of the handles use a single entry in the System File Table, and thus be independent of what the @samp{FILES=} parameter of @file{CONFIG.SYS} says. @code{__FSEXT_alloc_fd} also assigns the handler function for the handle it returns. The module is responsible for calling @code{_close} on the descriptor after setting the handler function to zero in the extended close handler. @subheading Return Value If successful, a new file descriptor is returned. On error, a negative number is returned and @var{errno} is set to indicate the error. @subheading Portability @portability !ansi, !posix @subheading Example @example int socket() @{ int fd = __FSEXT_alloc_fd(socket_handler); init_socket(fd); return fd; @} @end example @c ---------------------------------------------------------------------- @node __FSEXT_set_function, file system @subheading Syntax @example #include int __FSEXT_set_function(int _fd, __FSEXT_Function *_function); @end example @subheading Description This function is part of the @ref{File System Extensions}. It is used to set the handler function for those extensions that use DOS files for I/O. One situation where you might need this is when you must catch output to the terminal and play some tricks with it, like colorize it or redirect it to another device. @subheading Return Value Zero in case of success, non-zero in case of failure (like if @var{_fd} is negative). @subheading Portability @portability !ansi, !posix @subheading Example @example #include #include /* A simple example of a write handler which converts DOS I/O to the screen into direct writes to video RAM. */ static int my_screen_write (__FSEXT_Fnumber func, int *retval, va_list rest_args) @{ char *buf, *mybuf; size_t buflen; int fd = va_arg (rest_args, int); if (func != __FSEXT_write || !isatty (fd)) return 0; /* and the usual DOS call will be issued */ buf = va_arg (rest_args, char *); buflen = va_arg (rest_args, size_t); mybuf = alloca (buflen + 1); memcpy (mybuf, buf, buflen); mybuf[buflen] = '\0'; cputs (mybuf); *retval = buflen; return 1; /* meaning that we handled the call */ @} /* Install our handler. The `attribute constructor' causes this function to be called by the startup code. */ static void __attribute__((constructor)) install_screen_write_handler (void) @{ __FSEXT_set_function (fileno (stdout), my_screen_write); @} @end example @c ---------------------------------------------------------------------- @node __FSEXT_get_function, file system @subheading Syntax @example #include __FSEXT_Function *__FSEXT_get_function(int _fd); @end example This function is part of the @ref{File System Extensions}. It is used internal to libc.a to redirect I/O requests to the appropriate extensions. @subheading Portability @portability !ansi, !posix @subheading Example @example _read(int fd, void *buf, int len) @{ __FSEXT_Function *func = __FSEXT_get_function(fd); if (func) @{ int rv; if (func(__FSEXT_read, &rv, &fd)) return rv; @} /* rest of read() */ @} @end example @c ---------------------------------------------------------------------- @node __FSEXT_set_data, file system @subheading Syntax @example #include void * __FSEXT_set_data(int _fd, void *_data); @end example @subheading Description This function is part of the @ref{File System Extensions}. It is used to store a descriptor-specific pointer that can later be retrieved by @code{__FSEXT_get_data} (@pxref{__FSEXT_get_data}). The pointer is not otherwise used. This is useful when writing an extension that may be handling several open pseudo-files. @code{__FSEXT_set_data} can be used when creating or opening the file to store a pointer to data about the specific file. Later, when specific operation needs to be done (e.g. read, write, etc.) a pointer to pseudo-file associated with the file descriptor can be fetched with @code{__FSEXT_get_data}. @subheading Return Value Returns the pointer you passed it, or NULL if there was an error. @subheading Portability @portability !ansi, !posix @subheading Example @example typedef struct @{ void* Ptr; off_t Current_Ofs; size_t Size; @} _mem_file_t; int my_fsext(__FSEXT_Fnumber Op, int* RV, va_list Args) @{ const char* Path; void* Buffer; size_t Size; int fd; _mem_file_t* MPtr; switch (Op) @{ case __FSEXT_creat: /* Create a new memory file */ Path = va_list(Args, const char*); /* Check to see if we should create a new file */ if (strnicmp("/tmp/", Path, 5) != 0) return 0; /* Allocate some memory to keep info on our fake file */ MPtr = malloc(sizeof(_mem_file_t)); if (!MPtr) return 0; memset(MPtr, 0, sizeof(_mem_file_t)); /* Get a file descriptor we can use */ fd = __FSEXT_alloc_fd(my_fsext); if (fd < 0) @{ free(MPtr); return 0; @} /* Now store our note about this file descriptor so we can lookup it up quickly later. */ __FSEXT_set_data(fd, MPtr); /* Return the file descriptor *RV = fd; return 1; case __FSEXT_read: /* Read from our memory file. */ fd = va_list(Args, int); Buffer = va_list(Args, void*); Size = va_list(Args, size_t); /* Look up the information about this file */ MPtr = __FSEXT_get_data(fd); if (!MPtr) @{ *RV = -1; return 1; @} if (MPtr->Current_Ofs >= MPtr->Size) @{ *RV = 0; return 1; @} if (Size > (MPtr->Size - MPtr->Current_Ofs)) Size = MPtr->Size - MPtr->Current_Ofs; memcpy(Buffer, (char*) MPtr->Ptr+MPtr->Current_Ofs, Size); MPtr->Current_Ofs += Size; *RV = Size; return 1; ... @} @} @end example @c ---------------------------------------------------------------------- @node __FSEXT_get_data, file system @subheading Syntax @example #include void *__FSEXT_get_data(int _fd); @end example @subheading Description This function is part of the @ref{File System Extensions}. It is used to retrieve a descriptor-specific pointer that was previously stored by @code{__FSEXT_set_data} (@pxref{__FSEXT_set_data}). The pointer is not otherwise used. @xref{__FSEXT_set_data}, for an example of how this may be used. @subheading Return Value Returns the stored pointer, or NULL if there was an error (or no pointer had been stored). @subheading Portability @portability !ansi, !posix