#pragma extend

#include <rmx.h>

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#define PMAX    30
#define LMAX    30

#define CHAR    1
#define BOL     2
#define EOL     3
#define ANY     4
#define STAR    5
#define ENDPAT  6

/* work variables */

static char     lbuf[LMAX], pbuf[PMAX], dirpath[256], wildcard[20],
                fnext_res[256];
static RMXOBJ   file;

/****************************************************************************/
/* pcompile - compiles a pattern into pbuf                                  */
/****************************************************************************/

static void pcompile( char *source )
    {
    char *s, *p;
    int  c;

    s = source;
    p = pbuf;

    *p++ = BOL;

    while ( c = *s++ )

        switch( c )
            {
            case '?':

                *p++ = ANY;
                break;
            
            case '*':

                *p++ = STAR;
                *p++ = ANY;
                *p++ = ENDPAT;
                break;

            default:

                *p++ = CHAR;
                *p++ = tolower( c );
            }

    *p++ = EOL;
    *p++ = ENDPAT;
    *p++ = 0;
    }

/****************************************************************************/
/* xpmatch - matches a pattern against some data                            */
/****************************************************************************/

static char *xpmatch( char *line, char *pattern)
    {
    char *l, *p, c, *e, *are;
    int  op, n;

    l = line;
    p = pattern;

    while ( ( op = *p++ ) != ENDPAT )

        switch( op )
            {
            case CHAR:

                if ( tolower( *l++ ) != *p++ )
                    return( NULL );
                    
                break;

            case BOL:

                if ( l != lbuf )
                    return( NULL );
                    
                break;

            case EOL:

                if ( *l != '\0' )
                    return( NULL );
                    
                break;

            case ANY:

                if ( *l++ == '\0' )
                    return( NULL );
                    
                break;

            case STAR:

                are = l;

                /* Get longest match */
                 
                while ( *l && ( ( e = xpmatch( l, p ) ) != NULL ) )
                    l = e;
                    
                /* Skip over pattern */
                
                while ( *p++ != ENDPAT );
                
                /* Try to match rest, backing off through longest match */
                
                while ( l >= are )
                    {
                    if ( e = xpmatch( l, p ) )
                        return( e );
                        
                    --l;
                    }
                
                /* backed off to beginning without matching rest, so... */
                
                return( NULL );
            }

    return( l );
    }

/****************************************************************************/
/* pmatch - matches pattern in pbuf against data in lbuf                    */
/****************************************************************************/

static int pmatch()
    {
    char *l;
    
    for ( l = lbuf; *l; l++ )
        if ( xpmatch( l, pbuf ) != NULL)
            return( 1 );
        
    return( 0 );
    }

/****************************************************************************/
/* fnext - find next file in directory which matches ambiguous filename     */
/* passed to ffirst.                                                        */
/****************************************************************************/

char *fnext()
    {
    RMXRET status;
    int    count;
    char   *rsp;
    DIR_ENTRY_IN_FILE entry;

    rsp = NULL;

    for (;;)
        {
        count = RQSREADMOVE( file, &entry, 16, &status );

        if ( ( status != E_OK ) || ( count != 16 ) )
            {
            RQSDELETECONNECTION( file, &status );
            break;
            }

        if ( entry.fnode != 0 )
            {
            strncpy( lbuf, entry.name, 14 );
            lbuf[14] = 0;
        
            if ( pmatch() )
                {
                if ( strncmp( dirpath, "\003:$:", 4 ) == 0 )
                    rsp = lbuf;
                else
                    {
                    cstr( fnext_res, dirpath );
                    strcat( fnext_res, lbuf );
                    rsp = fnext_res;
                    }
                
                break;
                }
            }
        }

    return( rsp );
    }

/****************************************************************************/
/* ffirst - find first file in directory which matches ambiguous filename   */
/****************************************************************************/

char *ffirst( char *pathname )
    {
    char *p, *rsp;
    RMXRET  status;
    
    EXCP_HANDLER_ATTRIBS excp_info;
    
    excp_info.mode = NO_EXCEPTIONS;
    RQSETEXCEPTIONHANDLER( &excp_info, &status );
    
    if ( ( p = strrchr( pathname, '/' ) ) != NULL )
        {
        strcpy( wildcard, &p[1] );
        p[1] = '\0';
        udistr( dirpath, pathname );
        }
    else
        if ( ( p = strrchr( pathname, ':' ) ) != NULL )
            {
            strcpy( wildcard, &p[1] );
            p[1] = '\0';
            udistr( dirpath, pathname );
            }
        else
            {
            strcpy( wildcard, pathname );
            udistr( dirpath, ":$:" );
            }
     
    pcompile( wildcard );
    
    file = RQSATTACHFILE( dirpath, &status );
    
    if ( status != E_OK )
        rsp = NULL;
    else
        {
        RQSOPEN( file, OPEN_READ_SHARE_RW, 2, &status );
        
        if ( status == E_OK )
            rsp = fnext();
        else
            rsp = NULL;
        }
    
    return( rsp );
    }
