aboutsummaryrefslogtreecommitdiff
path: root/core/common/files.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/common/files.c')
-rw-r--r--core/common/files.c901
1 files changed, 901 insertions, 0 deletions
diff --git a/core/common/files.c b/core/common/files.c
new file mode 100644
index 0000000..8c3be7d
--- /dev/null
+++ b/core/common/files.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright � 2006-2013 SplinterGU (Fenix/Bennugd)
+ * Copyright � 2002-2006 Fenix Team (Fenix)
+ * Copyright � 1999-2002 Jos� Luis Cebri�n Pag�e (Fenix)
+ *
+ * This file is part of Bennu - Game Development
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef TARGET_BEOS
+#include <posix/assert.h>
+#else
+#include <assert.h>
+#endif
+
+#include <stdint.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "files.h"
+
+#define MAX_POSSIBLE_PATHS 128
+
+char * possible_paths[MAX_POSSIBLE_PATHS] = { NULL } ;
+
+int opened_files = 0;
+
+typedef struct
+{
+ char * stubname ;
+ char * name ;
+ int offset ;
+ int size ;
+ FILE * fp ;
+}
+XFILE ;
+
+XFILE * x_file = NULL ;
+int max_x_files = 0;
+
+int x_files_count = 0 ;
+
+/* Add new file to PATH */
+
+void xfile_init( int maxfiles )
+{
+ x_file = ( XFILE * ) calloc( sizeof( XFILE ), maxfiles );
+ max_x_files = maxfiles;
+}
+
+void file_add_xfile( file * fp, char * stubname, long offset, char * name, int size )
+{
+ char * ptr ;
+
+ assert( x_files_count < max_x_files ) ;
+ assert( fp->type == F_FILE ) ;
+
+ x_file[x_files_count].stubname = strdup( stubname );
+ x_file[x_files_count].fp = fp->fp ;
+ x_file[x_files_count].offset = offset ;
+ x_file[x_files_count].size = size ;
+ x_file[x_files_count].name = strdup( name ) ;
+
+ ptr = x_file[x_files_count].name;
+ while ( *ptr )
+ {
+ if ( *ptr == '\\' ) *ptr = '/'; /* Unix style */
+ ptr++;
+ }
+
+ x_files_count++ ;
+}
+
+/* Read a datablock from file */
+
+int file_read( file * fp, void * buffer, int len )
+{
+ assert( len != 0 );
+
+ if ( fp->type == F_XFILE )
+ {
+ XFILE * xf ;
+ int result ;
+
+ xf = &x_file[fp->n] ;
+
+ if (( len + fp->pos ) > ( xf->offset + xf->size ) )
+ {
+ fp->eof = 1 ;
+ len = xf->size + xf->offset - fp->pos ;
+ }
+
+ fseek( fp->fp, fp->pos, SEEK_SET ) ;
+ result = fread( buffer, 1, len, fp->fp ) ;
+
+ fp->pos = ftell( fp->fp ) ;
+ return result ;
+ }
+
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE )
+ {
+ int result = gzread( fp->gz, buffer, len ) ;
+ fp->error = ( result < len );
+ if ( result < 0 ) result = 0;
+ return result ;
+ }
+#endif
+
+ return fread( buffer, 1, len, fp->fp ) ;
+}
+
+/* Save a unquoted string to a file */
+
+int file_qputs( file * fp, char * buffer )
+{
+ char dest[1024], * optr ;
+ const char * ptr ;
+
+ ptr = buffer ;
+ optr = dest ;
+ while ( *ptr )
+ {
+ if ( optr > dest + 1000 )
+ {
+ *optr++ = '\\' ;
+ *optr++ = '\n' ;
+ *optr = 0 ;
+ file_write( fp, dest, optr - dest ) ;
+ optr = dest ;
+ }
+ if ( *ptr == '\n' )
+ {
+ *optr++ = '\\' ;
+ *optr++ = 'n' ;
+ ptr++ ;
+ continue ;
+ }
+ if ( *ptr == '\\' )
+ {
+ *optr++ = '\\' ;
+ *optr++ = *ptr++ ;
+ continue ;
+ }
+ *optr++ = *ptr++ ;
+ }
+ *optr++ = '\n' ;
+ return file_write( fp, dest, optr - dest ) ;
+}
+
+/* Load a string from a file and unquoted it */
+
+int file_qgets( file * fp, char * buffer, int len )
+{
+ char * ptr, * result = NULL ;
+
+ if ( fp->type == F_XFILE )
+ {
+ XFILE * xf ;
+ int l = 0;
+ char * ptr = result = buffer ;
+
+ xf = &x_file[fp->n] ;
+
+ fseek( fp->fp, fp->pos, SEEK_SET ) ;
+ while ( l < len )
+ {
+ if ( fp->pos >= xf->offset + xf->size )
+ {
+ fp->eof = 1 ;
+ break ;
+ }
+ fread( ptr, 1, 1, fp->fp ) ;
+ l++ ;
+ fp->pos++ ;
+ if ( *ptr++ == '\n' ) break ;
+ }
+ *ptr = 0 ;
+ fp->pos = ftell( fp->fp ) ;
+
+ if ( l == 0 ) return 0 ;
+ }
+#ifndef NO_ZLIB
+ else if ( fp->type == F_GZFILE )
+ {
+ result = gzgets( fp->gz, buffer, len ) ;
+ }
+#endif
+ else
+ {
+ result = fgets( buffer, len, fp->fp );
+ }
+
+ if ( result == NULL )
+ {
+ buffer[0] = 0 ; return 0 ;
+ }
+
+ ptr = buffer ;
+ while ( *ptr )
+ {
+ if ( *ptr == '\\' )
+ {
+ if ( ptr[1] == 'n' ) ptr[1] = '\n' ;
+ strcpy( ptr, ptr + 1 ) ;
+ ptr++ ;
+ continue ;
+ }
+ if ( *ptr == '\n' )
+ {
+ *ptr = 0 ;
+ break ;
+ }
+ ptr++ ;
+ }
+ return strlen( buffer ) ;
+}
+
+/* Save a string to file */
+
+int file_puts( file * fp, char * buffer )
+{
+ return file_write( fp, buffer, strlen( buffer ) ) ;
+}
+
+/* Load a string from a file and unquoted it */
+
+int file_gets( file * fp, char * buffer, int len )
+{
+ char * result = NULL ;
+
+ if ( fp->type == F_XFILE )
+ {
+ XFILE * xf ;
+ int l = 0;
+ char * ptr = result = buffer ;
+
+ xf = &x_file[fp->n] ;
+
+ fseek( fp->fp, fp->pos, SEEK_SET ) ;
+ while ( l < len )
+ {
+ if ( fp->pos >= xf->offset + xf->size )
+ {
+ fp->eof = 1 ;
+ break ;
+ }
+ fread( ptr, 1, 1, fp->fp ) ;
+ l++ ;
+ fp->pos++ ;
+ if ( *ptr++ == '\n' ) break ;
+ }
+ *ptr = 0 ;
+ fp->pos = ftell( fp->fp ) ;
+
+ if ( l == 0 ) return 0 ;
+ }
+#ifndef NO_ZLIB
+ else if ( fp->type == F_GZFILE )
+ {
+ result = gzgets( fp->gz, buffer, len ) ;
+ }
+#endif
+ else
+ {
+ result = fgets( buffer, len, fp->fp );
+ }
+
+ if ( result == NULL )
+ {
+ buffer[0] = 0 ;
+ return 0 ;
+ }
+
+ return strlen( buffer ) ;
+}
+
+/* Save an int data to a binary file */
+
+int file_writeSint8( file * fp, int8_t * buffer )
+{
+ return file_write( fp, buffer, sizeof( uint8_t ) );
+}
+
+int file_writeUint8( file * fp, uint8_t * buffer )
+{
+ return file_write( fp, buffer, sizeof( uint8_t ) );
+}
+
+int file_writeSint16( file * fp, int16_t * buffer )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_write( fp, buffer, sizeof( int16_t ) );
+#else
+ file_write( fp, ( uint8_t * )buffer + 1, 1 );
+ return file_write( fp, ( uint8_t * )buffer, 1 );
+#endif
+}
+
+int file_writeUint16( file * fp, uint16_t * buffer )
+{
+ return file_writeSint16( fp, ( int16_t * ) buffer );
+}
+
+int file_writeSint32( file * fp, int32_t * buffer )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_write( fp, buffer, sizeof( int32_t ) );
+#else
+ file_write( fp, ( uint8_t * )buffer + 3, 1 );
+ file_write( fp, ( uint8_t * )buffer + 2, 1 );
+ file_write( fp, ( uint8_t * )buffer + 1, 1 );
+ return file_write( fp, ( uint8_t * )buffer, 1 );
+#endif
+}
+
+int file_writeUint32( file * fp, uint32_t * buffer )
+{
+ return file_writeSint32( fp, ( int32_t * )buffer );
+}
+
+/* Save an array to a binary file */
+
+int file_writeSint8A( file * fp, int8_t * buffer, int n )
+{
+ return file_write( fp, buffer, n );
+}
+
+int file_writeUint8A( file * fp, uint8_t * buffer, int n )
+{
+ return file_write( fp, buffer, n );
+}
+
+int file_writeSint16A( file * fp, int16_t * buffer, int n )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_write( fp, buffer, n << 1 ) >> 1 ;
+#else
+ int i;
+ for ( i = 0; i < n; i++ )
+ if ( !file_writeSint16( fp, ( int16_t * )buffer++ ) ) break;
+ return i;
+#endif
+}
+
+int file_writeUint16A( file * fp, uint16_t * buffer, int n )
+{
+ return file_writeSint16A( fp, ( int16_t * )buffer, n );
+}
+
+int file_writeSint32A( file * fp, int32_t * buffer, int n )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_write( fp, buffer, n << 2 ) >> 2;
+#else
+ int i;
+ for ( i = 0; i < n; i++ )
+ if ( !file_writeSint32( fp, ( int32_t * )buffer++ ) ) break;
+ return i;
+#endif
+}
+
+int file_writeUint32A( file * fp, uint32_t * buffer, int n )
+{
+ return file_writeSint32A( fp, ( int32_t * )buffer, n );
+}
+
+/* Read an int data from a binary file */
+
+int file_readSint8( file * fp, int8_t * buffer )
+{
+ return file_read( fp, buffer, sizeof( int8_t ) );
+}
+
+int file_readUint8( file * fp, uint8_t * buffer )
+{
+ return file_read( fp, buffer, sizeof( int8_t ) );
+}
+
+int file_readSint16( file * fp, int16_t * buffer )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_read( fp, buffer, sizeof( int16_t ) );
+#else
+ file_read( fp, ( uint8_t * )buffer + 1, 1 );
+ return file_read( fp, ( uint8_t * )buffer, 1 );
+#endif
+}
+
+int file_readUint16( file * fp, uint16_t * buffer )
+{
+ return file_readSint16( fp, (int16_t *)buffer );
+}
+
+int file_readSint32( file * fp, int32_t * buffer )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_read( fp, buffer, sizeof( int32_t ) );
+#else
+ file_read( fp, ( uint8_t * )buffer + 3, 1 );
+ file_read( fp, ( uint8_t * )buffer + 2, 1 );
+ file_read( fp, ( uint8_t * )buffer + 1, 1 );
+ return file_read( fp, ( uint8_t * )buffer, 1 );
+#endif
+}
+
+int file_readUint32( file * fp, uint32_t * buffer )
+{
+ return file_readSint32( fp, ( int32_t * )buffer );
+}
+
+/* Read an array from a binary file */
+
+int file_readSint8A( file * fp, int8_t * buffer, int n )
+{
+ return file_read( fp, buffer, n );
+}
+
+int file_readUint8A( file * fp, uint8_t * buffer, int n )
+{
+ return file_read( fp, buffer, n );
+}
+
+int file_readSint16A( file * fp, int16_t * buffer, int n )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_read( fp, buffer, n << 1 ) >> 1;
+#else
+ int i;
+ for ( i = 0; i < n; i++ )
+ if ( !file_readSint16( fp, ( int16_t * )buffer++ ) ) break;
+ return i;
+#endif
+}
+
+int file_readUint16A( file * fp, uint16_t * buffer, int n )
+{
+ return file_readSint16A( fp, ( int16_t * )buffer, n );
+}
+
+int file_readSint32A( file * fp, int32_t * buffer, int n )
+{
+#if __BYTEORDER == __LIL_ENDIAN
+ return file_read( fp, buffer, n << 2 ) >> 2;
+#else
+ int i;
+ for ( i = 0; i < n; i++ )
+ if ( !file_readSint32( fp, ( int32_t * )buffer++ ) ) break;
+ return i;
+#endif
+}
+
+int file_readUint32A( file * fp, uint32_t * buffer, int n )
+{
+ return file_readSint32A( fp, ( int32_t * )buffer, n );
+}
+
+
+/* Write a datablock to a file */
+
+int file_write( file * fp, void * buffer, int len )
+{
+ if ( fp->type == F_XFILE )
+ {
+ XFILE * xf ;
+ int result ;
+
+ xf = &x_file[fp->n] ;
+
+ if (( len + fp->pos ) > ( xf->offset + xf->size ) )
+ {
+ fp->eof = 1 ;
+ len = xf->size + xf->offset - fp->pos ;
+ }
+ fseek( fp->fp, fp->pos, SEEK_SET ) ;
+ result = fwrite( buffer, 1, len, fp->fp ) ;
+ fp->pos = ftell( fp->fp ) ;
+ return result ;
+ }
+
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE )
+ {
+ int result = gzwrite( fp->gz, buffer, len ) ;
+ if (( fp->error = ( result < 0 ) ) != 0 ) result = 0 ;
+ return ( result < len ) ? 0 : len ;
+ }
+#endif
+
+ return fwrite( buffer, 1, len, fp->fp ) ;
+}
+
+/* Return file size */
+
+int file_size( file * fp )
+{
+ long pos, size ;
+
+ if ( fp->type == F_XFILE ) return x_file[fp->n].size ;
+
+ pos = file_pos( fp );
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE )
+ {
+ char buffer[8192];
+ size = pos;
+ while ( !file_eof( fp ) ) size += file_read( fp, buffer, sizeof(buffer) );
+ }
+ else
+#endif
+ {
+ file_seek(fp, 0, SEEK_END ) ;
+ size = file_pos( fp ) ;
+ }
+ file_seek(fp, pos, SEEK_SET ) ;
+
+ return size ;
+}
+
+/* Get current file pointer position */
+
+int file_pos( file * fp )
+{
+ if ( fp->type == F_XFILE ) return fp->pos - x_file[fp->n].offset ;
+
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE ) return gztell( fp->gz ) ;
+#endif
+
+ return ftell( fp->fp ) ;
+}
+
+int file_flush( file * fp )
+{
+ if ( fp->type == F_XFILE ) return 0 ;
+
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE ) return 0 ;
+#endif
+
+ return fflush( fp->fp ) ;
+}
+
+/* Set current file pointer position */
+
+int file_seek( file * fp, int pos, int where )
+{
+ assert( fp );
+ if ( fp->type == F_XFILE )
+ {
+ if ( where == SEEK_END )
+ pos = x_file[fp->n].size - pos + 1 ;
+ else if ( where == SEEK_CUR )
+ pos += ( fp->pos - x_file[fp->n].offset );
+
+ if ( x_file[fp->n].size < pos ) pos = x_file[fp->n].size ;
+
+ if ( pos < 0 ) pos = 0 ;
+
+ fp->pos = pos + x_file[fp->n].offset ;
+ return pos ;
+ }
+
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE )
+ {
+ assert( fp->gz );
+ return gzseek( fp->gz, pos, where ) ;
+ }
+#endif
+
+ assert( fp->fp );
+ return fseek( fp->fp, pos, where ) ;
+}
+
+void file_rewind( file * fp )
+{
+ fp->error = 0;
+
+ switch ( fp->type )
+ {
+ case F_XFILE:
+ fp->pos = x_file[fp->n].offset ;
+ break;
+
+#ifndef NO_ZLIB
+ case F_GZFILE:
+ gzrewind( fp->gz ) ;
+ break;
+#endif
+
+ default:
+ rewind( fp->fp ) ;
+ }
+}
+
+/* Open file */
+
+static int open_raw( file * f, const char * filename, const char * mode )
+{
+ char _mode[5];
+ char *p;
+
+#ifndef NO_ZLIB
+ if ( !strchr( mode, '0' ) )
+ {
+ f->type = F_GZFILE ;
+ f->gz = gzopen( filename, mode ) ;
+ f->eof = 0 ;
+ if ( f->gz ) return 1 ;
+ }
+#endif
+
+ p = _mode;
+ while ( *mode )
+ {
+ if ( *mode != '0' )
+ {
+ *p = *mode;
+ p++;
+ }
+ mode++;
+ }
+ *p = '\0';
+
+ f->eof = 0 ;
+ f->type = F_FILE ;
+ f->fp = fopen( filename, _mode ) ;
+ if ( f->fp ) return 1 ;
+ return 0 ;
+}
+
+file * file_open( const char * filename, char * mode )
+{
+ char work [__MAX_PATH];
+ char here [__MAX_PATH];
+
+ char * name = NULL ;
+ char * p, c ;
+ int i;
+
+ file * f ;
+
+ f = ( file * ) calloc( 1, sizeof( file ) ) ;
+ assert( f ) ;
+
+ p = f->name;
+ while ( *filename )
+ {
+ *p++ = *filename++;
+ if ( p[-1] == '\\' ) p[-1] = '/'; /* Unix style */
+ }
+ p[0] = '\0';
+
+ filename = f->name;
+
+ if ( open_raw( f, filename, mode ) )
+ {
+ opened_files++;
+ return f ;
+ }
+
+
+ /* if real file don't exists in disk */
+ if ( strchr( mode, 'r' ) && strchr( mode, 'b' ) && /* Only read-only files */
+ !strchr( mode, '+' ) && !strchr( mode, 'w' ) )
+ {
+ for ( i = 0 ; i < x_files_count ; i++ )
+ {
+ if ( strcmp( filename, x_file[i].name ) == 0 )
+ {
+ f->eof = 0;
+ f->pos = x_file[i].offset;
+ f->type = F_XFILE;
+ f->n = i;
+ f->fp = fopen( x_file[i].stubname, "rb" );
+
+ opened_files++;
+ return f ;
+ }
+ }
+ }
+
+ p = name = work;
+ while (( c = *filename ) )
+ {
+ if ( c == '\\' || c == '/' )
+ {
+ c = 0;
+ name = p + 1;
+ }
+ *p++ = c;
+ filename++;
+ }
+ *p = '\0';
+
+ /* Use file extension for search in a directory named as it (for example: FPG dir for .FPG files) */
+ if ( strchr( name, '.' ) )
+ {
+ strcpy( here, strrchr( name, '.' ) + 1 ) ;
+ strcat( here, PATH_SEP ) ;
+ strcat( here, name ) ;
+ if ( open_raw( f, here, mode ) )
+ {
+ opened_files++;
+ return f ;
+ }
+ }
+
+ for ( i = 0 ; possible_paths[i] ; i++ )
+ {
+ strcpy( here, possible_paths[i] ) ;
+ strcat( here, name ) ;
+ if ( open_raw( f, here, mode ) )
+ {
+ opened_files++;
+ return f ;
+ }
+ }
+
+ free( f ) ;
+ return 0 ;
+}
+
+/* Close file */
+
+void file_close( file * fp )
+{
+ if ( fp == NULL ) return;
+ if ( fp->type == F_FILE ) fclose( fp->fp ) ;
+ if ( fp->type == F_XFILE ) fclose( fp->fp ) ;
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE ) gzclose( fp->gz ) ;
+#endif
+ opened_files--;
+ free( fp ) ;
+}
+
+/* Add a new dir to PATH */
+
+void file_addp( const char * path )
+{
+ char truepath[__MAX_PATH];
+ int n ;
+
+ if ( !path || !*path ) return;
+
+ strcpy( truepath, path ) ;
+
+ for ( n = 0 ; truepath[n] ; n++ ) if ( truepath[n] == '\\' ) truepath[n] = '/' ;
+ if ( truepath[strlen( truepath )-1] != '/' ) strcat( truepath, "/" ) ;
+
+ for ( n = 0 ; n < MAX_POSSIBLE_PATHS - 1 && possible_paths[n] ; n++ ) ;
+
+ possible_paths[n] = strdup( truepath ) ;
+ possible_paths[n+1] = NULL ;
+}
+
+/* --- */
+
+int file_remove( const char * filename )
+{
+ return ( remove( filename ) );
+}
+
+/* --- */
+
+int file_move( const char * source_file, const char * target_file )
+{
+ return ( rename( source_file, target_file ) );
+}
+
+/* Check for file exists */
+
+int file_exists( const char * filename )
+{
+ file * fp ;
+
+ fp = file_open( filename, "rb" ) ;
+ if ( fp )
+ {
+ file_close( fp ) ;
+ return 1 ;
+ }
+ return 0 ;
+}
+
+/* Check for file end is reached */
+
+int file_eof( file * fp )
+{
+ if ( fp->type == F_XFILE )
+ {
+ return fp->eof ? 1 : 0;
+ }
+
+#ifndef NO_ZLIB
+ if ( fp->type == F_GZFILE )
+ {
+ if ( fp->error ) return 1 ;
+ return gzeof( fp->gz ) ? 1 : 0;
+ }
+#endif
+
+ return feof( fp->fp ) ? 1 : 0;
+}
+
+/* Get the FILE * of the file */
+
+FILE * file_fp( file * f )
+{
+ if ( f->type == F_XFILE )
+ {
+// XFILE * xf = &x_file[f->n] ;
+ fseek( f->fp, f->pos, SEEK_SET ) ;
+ return f->fp ;
+ }
+
+ return f->fp ;
+}
+
+/* ------------------------------------------------------------------------------------ */
+
+char * getfullpath( char *rel_path )
+{
+ char fullpath[ __MAX_PATH ] = "";
+#ifdef _WIN32
+ GetFullPathName( rel_path, sizeof( fullpath ), fullpath, NULL );
+#else
+ realpath( rel_path, fullpath );
+#endif
+ if ( *fullpath ) return strdup( fullpath );
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------------------ */
+
+#ifdef _WIN32
+ #define ENV_PATH_SEP ';'
+#else
+ #define ENV_PATH_SEP ':'
+#endif
+
+char * whereis( char *file )
+{
+ char * path = getenv( "PATH" ), *pact = path, *p;
+ char fullname[ __MAX_PATH ];
+
+ while ( pact && *pact )
+ {
+ struct stat st;
+
+ if ( ( p = strchr( pact, ENV_PATH_SEP ) ) ) *p = '\0';
+ sprintf( fullname, "%s%s%s", pact, ( pact[ strlen( pact ) - 1 ] == ENV_PATH_SEP ) ? "" : PATH_SEP, file );
+
+ if ( !stat( fullname, &st ) && S_ISREG( st.st_mode ) )
+ {
+ pact = strdup( pact );
+ if ( p ) *p = ENV_PATH_SEP;
+ return ( pact );
+ }
+
+ if ( !p ) break;
+
+ *p = ENV_PATH_SEP;
+ pact = p + 1;
+ }
+
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------------------ */