/* GetFile. Public Domain by Magnus Holmgren in 1992.
 *
 * Compile with DICE:
 * Dcc -pr -2.0 -mS -proto -mRR -o GetFile GetFile.c dlib:amigas20.lib
 *
 * Note that in order to compile with other compilers, changes are (probably)
 * neccessary.
 */

#include <stdlib.h>
#include <string.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/rdargs.h>
#include <dos/dos.h>
#include <libraries/asl.h>
#include <clib/alib_stdio_protos.h>
#include <clib/asl_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>

/* DICE will init these for us */
extern struct DOSBase *DOSBase;
extern struct ExecBase *SysBase;

struct Flags
{
  STRPTR Dir;
  STRPTR File;
  STRPTR Title;
  STRPTR VarName;
  STRPTR Pattern;
  STRPTR OKText;
  STRPTR CancelText;
  ULONG *Left;
  ULONG *Top;
  ULONG *Width;
  ULONG *Height;
  LONG   Global;
  LONG   NoFiles;
  LONG   Save;
  LONG   Multiselect;
};

const TEXT Template_Msg[] = "DIR=DRAWER,FILE,TITLE,VAR=VARIABLENAME,"
                            "PAT=PATTERN,OK=OKTEXT,CANCEL=CANCELTEXT,"
                            "LEFT/N,TOP/N,WIDTH/N,HEIGHT/N,"
                            "GLOBAL/S,NOFILES/S,SAVE/S,MULTI=MULTISELECT/S";

#define FILEBUF 128
#define DIRBUF 256
#define PATHBUF 512

TEXT File[ FILEBUF + 1 ], Dir[ DIRBUF + 1 ], Fullpath[ PATHBUF + 1 ];

const TEXT VersTag[] = "$VER: GetFile 2.0 (25.11.92)";
const TEXT OS2Required_Msg[] = "GetFile: You need OS 2.04+\n";
const TEXT BufferOverflow_Msg[] = "buffer overflow building filename";

struct TagItem PosItems[] =
{
  ASL_LeftEdge, 0,
  ASL_TopEdge, 0,
  ASL_Width, 0,
  ASL_Height, 0,
  TAG_DONE, 0
};

/* We happen to know that DICE will zero all undeclared variables.. :) */
struct RDArgs *RDArgs;
struct FileRequester *FileReq;
STRPTR Buf;
LONG ExtFlags, FuncFlags;
struct Window *ProcessWindow;
struct AslBase *AslBase;
struct Flags Flags;

VOID QuoteName( STRPTR );
VOID CleanExit( STRPTR, ULONG );

/* Simply clean up and exit gracefully */
VOID CleanExit( STRPTR msg, ULONG code )
{
  /* Print any message */
  if( msg )
    Printf( "GetFile: %s\n", msg );

  FreeAslRequest( FileReq );
  FreeVec( Buf );

  if( RDArgs )
    FreeArgs( RDArgs );

  CloseLibrary( AslBase );
  _exit( code );
}

/* Expands string 'name' to be quoted, if it contains spaces.
 * Assumes that the string will still fit in the buffer after
 * expansion.
 */
VOID QuoteName( STRPTR name )
{
  /* Any space? */
  if( strchr( name, ' ' ) )
  {
    LONG i = strlen( name );

    /* Place last '"', and make sure string is terminated */
    *( name + i + 3 ) = '\0';
    *( name + i + 2 ) = '\"';

    /* Yep, shift string to make room for first '"' */
    for( ; i-- >= 0; )
      *( name + i + 1 ) = *( name + i );

    *name = '\"';
  }
}

/* We use _main iso main to keep code size down. */
_main()
{
  STRPTR pattern = "#?";

  /* Make sure we have OS 2.04 */
  if( SysBase->LibNode.lib_Version < 37 )
  {
    BPTR out;

    if( out = ( BPTR ) Output() )
      Write( out,
             OS2Required_Msg,
             sizeof( OS2Required_Msg ) - 1 );
      _exit( RETURN_FAIL );
  }

  if( !( AslBase = OpenLibrary( "asl.library", 0 ) ) )
    CleanExit( "couldn't open asl.library", RETURN_FAIL );

  ProcessWindow = ( struct Window * )
    ( ( struct Process * ) SysBase->ThisTask )->pr_WindowPtr;
  Flags.Title = "Select a file";
  Flags.VarName = "GetFileResult";

  /* Try to parse the arguments */
  if( !( RDArgs = ReadArgs( Template_Msg, ( ULONG * ) &Flags, NULL ) ) )
  {
    PrintFault( IoErr(), "GetFile" );
    CleanExit( NULL, RETURN_ERROR );
  }

  if( Flags.Dir )
    strncpy( Dir, Flags.Dir, DIRBUF - 1 );

  if( Flags.File )
    strncpy( File, Flags.File, FILEBUF - 1 );

  if( Flags.Save )
    FuncFlags = FILF_SAVE;
  else
    if( Flags.Multiselect )
      FuncFlags = FILF_MULTISELECT;

  if( Flags.Pattern )
  {
    pattern = Flags.Pattern;
    FuncFlags |= FILF_PATGAD;
  }

  if( Flags.NoFiles )
    ExtFlags = FIL1F_NOFILES;

  /* Set "location" tags properly */
  {
    ULONG i, **numptr = &Flags.Left;
    struct TagItem *tags = PosItems;

    for( i = 0; i < 4; i++, tags++, numptr++ )
      if( !( *numptr ) )
        tags->ti_Tag = TAG_IGNORE;
      else
        tags->ti_Data = **numptr;
  }

  if( !( FileReq =
       AllocAslRequestTags( ASL_FileRequest,
                            ASL_Hail, Flags.Title,
                            ASL_File, File,
                            ASL_Dir, Dir,
                            ASL_FuncFlags, FuncFlags,
                            ASL_ExtFlags1, ExtFlags,
                            ASL_Pattern, pattern,
                            ASL_Window, ProcessWindow,
                            Flags.OKText ? ASL_OKText : TAG_IGNORE,
                              Flags.OKText,
                            Flags.CancelText ? ASL_CancelText : TAG_IGNORE,
                              Flags.CancelText,
                            TAG_MORE, PosItems ) ) )
  {
    PrintFault( ERROR_NO_FREE_STORE, "GetFile" );
    CleanExit( NULL, RETURN_FAIL );
  }

  /* No file selected, or maybe the requester failed.. */
  if( !RequestFile( FileReq ) )
    CleanExit( NULL, RETURN_WARN );

  if( FuncFlags & FILF_MULTISELECT )
  {
    /* Handle multiple selection */
    ULONG len = 1, dirlen = 0, i;
    struct WBArg *argptr;

    dirlen = strlen( FileReq->rf_Dir );

    /* Find out required size for result string */
    for( argptr = FileReq->rf_ArgList, i = 0;
         i < FileReq->rf_NumArgs;
         i++, argptr++ )
      len += strlen( argptr->wa_Name ) + 3 + dirlen;

    if( !( Buf = AllocVec( len, MEMF_CLEAR | MEMF_PUBLIC ) ) )
    {
      PrintFault( ERROR_NO_FREE_STORE, "GetFile" );
      CleanExit( NULL, RETURN_FAIL );
    }

    /* Build result string with all names */
    for( argptr = FileReq->rf_ArgList, i = 0;
         i < FileReq->rf_NumArgs;
         i++, argptr++ )
    {
      strncpy( Fullpath, FileReq->rf_Dir, PATHBUF );

      if( !AddPart( Fullpath, argptr->wa_Name, PATHBUF ) )
        CleanExit( BufferOverflow_Msg, RETURN_FAIL );

      QuoteName( Fullpath );
      strcat( Buf, Fullpath );
      strcat( Buf, " " );
    }
  }
  else
  {
    strncpy( Fullpath, FileReq->rf_Dir, 511 );

    if( !AddPart( Fullpath, FileReq->rf_File, 511 ) )
      CleanExit( BufferOverflow_Msg, RETURN_FAIL );

    QuoteName( Fullpath );
  }

  {
    STRPTR result = Buf ? Buf : Fullpath;

    if( *( Flags.VarName ) )
    {
      if( !SetVar( Flags.VarName,
                   result,
                   -1,
                   Flags.Global ? GVF_GLOBAL_ONLY : GVF_LOCAL_ONLY ) )
        CleanExit( "couldn't set result variable", RETURN_FAIL );
    }
    else
      Printf( "%s\n", result );
  }

  CleanExit( NULL, RETURN_OK );
}
