/*
    ####             #    #     # #
    #   #            #    #       #          The FreeWare C library for
    #   #  ##   ###  #  # #     # ###             RISC OS machines
    #   # #  # #     # #  #     # #  #   ___________________________________
    #   # ####  ###  ##   #     # #  #
    #   # #        # # #  #     # #  #    Please refer to the accompanying
    ####   ### ####  #  # ##### # ###    documentation for conditions of use
    ________________________________________________________________________

    File:    Filing.h
    Author:  Copyright  1994, 1995 Sergio Monesi
    Version: 1.04 (27 Aug 1995)
    Purpose: Easy access to files informations, directory scans, filenames
             handling.
    Mods:    06 Jun 1995 - changed some procedure names to a more
                           DeskLib-compliant style
             15 Jul 1995 - added Filing_CanonicalisePath
             27 Aug 1995 - made Filing_ScanDir and Filing_ScanDirByDir
                           reentrant. Added Filing_SingleDirEntry and
                           Filing_SingleDirEntry2
*/

#ifndef __dl_filing_h
#define __dl_filing_h

#ifndef __dl_core_h
#include "Core.h"
#endif

/*M*************************************************************************/

  #ifndef filing_MAXLEAFNAMELEN
    #define filing_MAXLEAFNAMELEN 32
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  MACRO:    Constant: filing_MAXLEAFNAMELEN

  Purpose:  The maximum length of a leafname (ie. the filename without the
            path) that can be stored in a filing_direntry or
            filing_fulldirentry structure.
            A value of 13 is enough for FileCore based filing system
            (like ADFS, IDEFS, SCSI, etc.) and for DOS discs.
            A value of 32 seems to be reasonable for every filing system.
            You can #define it before including this header if you want
            to be sure that it is set to the desired value (ie. even if
            someone changes this header, your value will be still valid).
  SeeAlso:  filing_direntry; filing_fulldirentry;

****************************************************************************/
  #endif


/*T*************************************************************************/

  typedef enum {
    filing_NOTFOUND  = 0,
    filing_FILE      = 1,
    filing_DIRECTORY = 2,
    filing_IMAGEFILE = 3
  } filing_objtype;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Object types.
  Fields:   -
  SeeAlso:  filing_direntry; filing_fulldirentry;

****************************************************************************/


/*T*************************************************************************/

  typedef struct {
    int            loadaddr;
    int            execaddr;
    int            length;
    int            attrib;
    filing_objtype objtype;
    char           name[ filing_MAXLEAFNAMELEN];
  } filing_direntry;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Contains file informations (read from directory)
  Fields:   Auto-explicatives...
  SeeAlso:  filing_fulldirentry;

****************************************************************************/


/*T*************************************************************************/

  typedef struct {
    int            loadaddr;
    int            execaddr;
    int            length;
    int            attrib;
    filing_objtype objtype;
    int            SIN;
    char           date[ 5];
    char           name[ filing_MAXLEAFNAMELEN];
  } filing_fulldirentry;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Contains full file informations (read from directory)
  Fields:   Auto-explicatives...
  SeeAlso:  filing_direntry;

****************************************************************************/


/*T*************************************************************************/

  typedef enum {
    readdirtype_DIRENTRY,
    readdirtype_FULLDIRENTRY,
    readdirtype_NAMEONLY
  } filing_readdirtype;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  These are the way in which the objects in a directory can be
            read using Filing_ReadDir function.
  Fields:   readdirtype_DIRENTRY - reads filing_direntry objects
            readdirtype_FULLDIRENTRY - reads filing_fulldirentry objects
            readdirtype_NAMEONLY - reads just the names of the files
  SeeAlso:  filing_direntry; filing_fulldirentry; Filing_OpenDir;
            Filing_ReadDir;

****************************************************************************/


/*T*************************************************************************/

  typedef struct {
    char               *dirname;
    void               *buf;
    int                size;
    filing_readdirtype type;
    union {
      filing_direntry     *direntry;
      filing_fulldirentry *fulldirentry;
      char                *name;
      void                *act;
    } act;
    int  offset;
    int  read;
    char *match;
  } filing_dirdata;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Contains the data needed by Filing_OpenDir, Filing_ReadDir and
            Filing_CloseDir. It is automatically filled by Filing_OpenDir
            and updated by Filing_ReadDir. It should not be used by the
            user, apart from the 'act' field that should be used to read
            the current object.
  Fields:   The fields should be ignored since they are for internal use.
            The only important field is:
            act - this union contains the current object that will be
                  returned by Filing_ReadDir. Remember that you should
                  only use the type chosen when you called Filing_OpenDir
  SeeAlso:  filing_direntry; filing_fulldirentry; filing_readdirtype;
            Filing_OpenDir; Filing_ReadDir; Filing_CloseDir;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_OpenDir( char *dirname, filing_dirdata *dirdata,
                            int bufsize, filing_readdirtype type);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Prepares a directory to be read with Filing_ReadDir (ie.
            allocates the buffer and set the starting parameters). After
            the directory has been read, Filing_CloseDir should be called
            to free the buffer.
  Inputs:   dirname - the directory pathname
            dirdata - a pointer to a (already allocated!) structure that
                      will be filled in by the function
            bufsize - the size of the buffer that should be allocated. Note
                      that the buffer will be allocated by Filing_OpenDir
                      itself, you just have to give it the size, so that you
                      can save some memory (but remember that, the bigger
                      the buffer, the faster the reading). bufsize should
                      be at least the size of filing_direntry,
                      filing_fulldirentry or leafname maxlength, depending
                      on the type you want to read
            type - the type of data you want to read from the directory
                   using Filing_ReadDir
  Outputs:  -
  Returns:  NULL if no error, otherwise the error generated by RISC OS.
            If an error is returned, the buffer is not allocated
  Errors:   The following custom errors could be returned (apart from the
            RISC OS generated ones):
            &00801C00 - Out of memory
            &00801C01 - Directory is actually a file
            &00801C02 - Directory doesn't exist
  SeeAlso:  filing_direntry; filing_fulldirentry; filing_readdirtype;
            Filing_ReadDir; Filing_CloseDir;

****************************************************************************/


/*F*************************************************************************/

  void *Filing_ReadDir( filing_dirdata *dirdata);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads the directory content, one object each call.
            Filing_OpenDir must be called before this and Filing_CloseDir
            must be called when there are no more entries to read
  Inputs:   dirdata - a pointer to a structure that defines the directory
                      that will be read. This structure should have been
                      already filled by Filing_OpenDir
  Outputs:  -
  Returns:  The next object to read in the directory or NULL if finished.
            The returned value is a pointer to a filing_direntry,
            filing_fulldirentry or char depending from the type set with
            Filing_OpenDir
  Errors:   Generally no errors should be caused by this function, since
            they should be already discovered by Filing_OpenDir. Anyway,
            if something strange happens, a NULL pointer will be
            returned
  SeeAlso:  Filing_OpenDir; Filing_CloseDir; filing_direntry;
            filing_fulldirentry; filing_readdirtype;

****************************************************************************/


/*F*************************************************************************/

  BOOL Filing_CloseDir( filing_dirdata *dirdata);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Closes a directory after it has been opened with Filing_OpenDir
            and read with Filing_ReadDir. This function frees the buffer
            allocated by Filing_OpenDir.
  Inputs:   dirdata - a pointer to a structure that defines the directory
                      that will be closed.
  Outputs:  -
  Returns:  TRUE if there are no problems, FALSE if something goes wrong
            while freeing the buffer
  Errors:   -
  SeeAlso:  Filing_OpenDir; Filing_ReadDir; filing_direntry;
            filing_fulldirentry; filing_readdirtype;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_ReadDirNames( char *dirname, char *buf,
                                 int *number, int *offset,
                                 int size, char *match);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads the directory content (only the file names): this function
            can be called more than once if the content is greater than the
            size of the buffer.
            (veneer for SWI OS_GBPB 9)
  Inputs:   dirname - the directory pathname
            buf - pointer to a buffer where the content will be stored
            number - number of elements to read
            offset - the offset to the first element to read
            size - the length of the buffer
            match - (wildcarded) filename to match
  Outputs:  number - the number of elements read
            offset - the next element to read or -1 if finished
  Returns:  NULL if no error, otherwise the error returned by RISC OS
  Errors:   -
  SeeAlso:  Filing_ReadDirEntry; Filing_ReadFullDirEntry;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_ReadDirEntry( char *dirname, filing_direntry *buf,
                                 int *number, int *offset,
                                 int size, char *match);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads the directory content: this function can be called more
            than once if the content is greater than the size of the buffer.
            (veneer for SWI OS_GBPB 10)
  Inputs:   dirname - the directory pathname
            buf - pointer to a buffer where the content will be stored
            number - number of elements to read
            offset - the offset to the first element to read
            size - the length of the buffer
            match - (wildcarded) filename to match
  Outputs:  number - the number of elements read
            offset - the next element to read or -1 if finished
  Returns:  NULL if no error, otherwise the error returned by RISC OS
  Errors:   -
  SeeAlso:  filing_direntry; Filing_ReadDirNames; Filing_ReadFullDirEntry;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_ReadFullDirEntry( char *dirname, filing_fulldirentry *buf,
                                     int *number, int *offset,
                                     int size, char *match);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads the directory content (full info version): this function
            can be called more than once if the content is greater than the
            size of the buffer.
            (veneer for SWI OS_GBPB 11)
  Inputs:   dirname - the directory pathname
            buf - pointer to a buffer where the content will be stored
            number - number of elements to read
            offset - the offset to the first element to read
            size - the length of the buffer
            match - (wildcarded) filename to match
  Outputs:  number - the number of elements read
            offset - the next element to read or -1 if finished
  Returns:  NULL if no error, otherwise the error
  Errors:   Every possible error returned by RISC OS
  SeeAlso:  filing_fulldirentry; Filing_ReadDirNames; Filing_ReadDirEntry;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_ReadCatalogue( char *filename, filing_objtype *objtype,
                                  int *loadaddr, int *execaddr, int *length,
                                  int *attrib, int *filetype);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads file informations.
            (veneer for SWI OS_File 20)
  Inputs:   filename - the file pathname
  Outputs:  objtype, loadaddr, execaddr, length, attrib, filetype - the
            desired file informations. If the file doesn't exist, objtype=0
            and the other informations are meaningless; no error is
            returned
  Returns:  NULL if no error, otherwise the error returned by RISC OS
  Errors:   -
  SeeAlso:  -

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_SingleDirEntry( char *filename,
                                   filing_direntry *buf, int size);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads a single directory entry. This function uses
            Filing_ReadDirEntry() to read the file informations from its
            directory.
  Inputs:   filename - the file pathname
            buf - pointer to a buffer where the content will be stored
            size - the length of the buffer
  Outputs:  -
  Returns:  NULL if no error, otherwise the error.
            If the file doesn't exist (but its pathname exists!) no error
            will be returned but objtype in the filing_fulldirentry structure
            will be set to 0.
            If the pathname is illegal or some directory in this path doesn't
            exist an error will be returned.
  Errors:   Every possible error returned by RISC OS
  SeeAlso:  filing_direntry; Filing_ReadDirEntry;
            Filing_SingleDirEntry2; Filing_SingleFullDirEntry;

****************************************************************************/


/*F*************************************************************************/

 os_error *Filing_SingleDirEntry2( char *dirname,
                                   filing_direntry *buf,
                                   int size, char *filename);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads a single directory entry (dirname and filename splitted).
            This function uses Filing_ReadDirEntry() to read the file
            informations from its directory.
  Inputs:   dirname - the directory pathname
            buf - pointer to a buffer where the content will be stored
            size - the length of the buffer
            filename - the filename (only the leafname!)
  Outputs:  -
  Returns:  NULL if no error, otherwise the error.
            If the file doesn't exist (but the pathname exists!) no error
            will be returned but objtype in the filing_fulldirentry structure
            will be set to 0.
            If the pathname is illegal or some directory in it doesn't exist
            an error will be returned.
  Errors:   -
  SeeAlso:  filing_direntry; Filing_ReadDirEntry;
            Filing_SingleDirEntry;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_SingleFullDirEntry( char *filename,
                                       filing_fulldirentry *buf, int size);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads a single directory entry (full info version). This
            function uses Filing_ReadFullDirEntry() to read the (full) file
            informations from its directory. This is (for what I know!) the
            only way to read the SIN of a single file.
  Inputs:   filename - the file pathname
            buf - pointer to a buffer where the content will be stored
            size - the length of the buffer
  Outputs:  -
  Returns:  NULL if no error, otherwise the error.
            If the file doesn't exist (but its pathname exists!) no error
            will be returned but objtype in the filing_fulldirentry structure
            will be set to 0.
            If the pathname is illegal or some directory in this path doesn't
            exist an error will be returned.
  Errors:   Every possible error returned by RISC OS
  SeeAlso:  filing_fulldirentry; Filing_ReadFullDirEntry;
            Filing_SingleFullDirEntry2; Filing_SingleDirEntry;

****************************************************************************/


/*F*************************************************************************/

 os_error *Filing_SingleFullDirEntry2( char *dirname,
                                       filing_fulldirentry *buf,
                                       int size, char *filename);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Reads a single directory entry (full info version, dirname and
            filename splitted). This function uses Filing_ReadFullDirEntry()
            to read the (full) file informations from its directory. This is
            (for what I know!) the only way to read the SIN of a single file.
  Inputs:   dirname - the directory pathname
            buf - pointer to a buffer where the content will be stored
            size - the length of the buffer
            filename - the filename (only the leafname!)
  Outputs:  -
  Returns:  NULL if no error, otherwise the error.
            If the file doesn't exist (but the pathname exists!) no error
            will be returned but objtype in the filing_fulldirentry structure
            will be set to 0.
            If the pathname is illegal or some directory in it doesn't exist
            an error will be returned.
  Errors:   -
  SeeAlso:  filing_fulldirentry; Filing_ReadFullDirEntry;
            Filing_SingleFullDirEntry;

****************************************************************************/


/*F*************************************************************************/

  char *Filing_GetPathname( char *filename, char *pathname);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Gets the pathname from a filename (ie. strips out the leafname)
  Inputs:   filename - the filename. If this is a NULL pointer nothing will
                       happen
            pathname - the string that will contain the pathname. If this is
                       a NULL pointer nothing will happen
  Outputs:  pathname - the pathname. If filename is a leafname (ie. no '.'s
                       in it) pathname will be an empty string
  Returns:  pathname
  Errors:   -
  SeeAlso:  Filing_GetLeafname; Filing_FindLeafname;

****************************************************************************/


/*F*************************************************************************/

  char *Filing_GetLeafname( char *filename, char *leafname);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Gets the leafname from a filename (ie. strips out the path)
  Inputs:   filename - the filename. If this is a NULL pointer nothing will
                       happen
            leafname - the string that will contain the pathname. If this is
                       a NULL pointer nothing will happen
  Outputs:  leafname - the leafname. If filename is just a leafname itself
                       (ie. no '.'s in it) leafname will be the same as
                       filename
  Returns:  leafname
  Errors:   -
  SeeAlso:  Filing_FindLeafname; Filing_GetPathname;

****************************************************************************/


/*F*************************************************************************/

  char *Filing_FindLeafname( char *filename);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Finds the leafname inside a full pathname
  Inputs:   filename - the filename. If this is a NULL pointer nothing will
                       happen
  Outputs:  -
  Returns:  a pointer to the leafname (this pointer will be inside filename).
            If filename is just a leafname (ie. no '.'s in it) the returned
            value will be filename itself
  Errors:   -
  SeeAlso:  Filing_GetLeafname; Filing_GetPathname;

****************************************************************************/


/*F*************************************************************************/

  char *Filing_MakePath( char *newpath, char *dirname, char *leafname);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Constructs the full pathname from the directory and leafname
  Inputs:   newpath - the string that will contain the full pathname
            dirname - the directory name
            leafname - the file leafname
  Outputs:  newpath - the desired full pathname (ie. dirname+'.'+leafname)
  Returns:  newpath
  Errors:   -
  SeeAlso:  -

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_CanonicalisePath( char *pathname, char *buffer,
                                     int size, int *spare);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Converts a pathname to a canonicalised path
            (veneer for SWI OS_FSControl 37)
  Inputs:   pathname - the pathname to convert
            buffer - pointer to the buffer where the canonicalised path
                     will be stored
            size - the length of the buffer
  Outputs:  spare - the number of spare bytes in the buffer
  Returns:  NULL if no error, otherwise the error returned by RISC OS
  Errors:   -
  SeeAlso:  -

****************************************************************************/


/*T*************************************************************************/

  typedef os_error *(Filing_ScanDir_StartDir) ( char *dirname,
                                                filing_fulldirentry *dirdata);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  The type of functions that are called by Filing_ScanDir (and
            similar functions) every time a new directory is scanned.
            If this functions returns a non-NULL pointer the scan will be
            aborted and Filing_ScanDir will return this error.
  Fields:   dirname - the directory name
            dirdata - the data of the directory (only if available, see
                      Filing_ScanDir and similar functions)
  SeeAlso:  Filing_ScanDir;

****************************************************************************/


/*T*************************************************************************/

  typedef os_error *(Filing_ScanDir_FoundFile) ( char *dirname,
                                                 filing_fulldirentry *filedata);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  The type of functions that are called by Filing_ScanDir (and
            similar functions) at every file. If this functions returns a
            non-NULL pointer the scan will be aborted and Filing_ScanDir
            will return this error.
  Fields:   dirname - the directory name
            filedata - the data of the current file inside the directory
  SeeAlso:  Filing_ScanDir;

****************************************************************************/


/*T*************************************************************************/

  typedef os_error *(Filing_ScanDir_EndDir) ( char *dirname,
                                              filing_fulldirentry *dirdata);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  The type of functions that are called by Filing_ScanDir (and
            similar functions) every time the scan of the directory is
            finished. If this functions returns a non-NULL pointer the scan
            will be aborted and Filing_ScanDir will return this error.
  Fields:   dirname - the directory name
            dirdata - the data of the directory (only if available, see
                      Filing_ScanDir and similar functions)
  SeeAlso:  Filing_ScanDir;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_ScanDir( char *dirname,
                            Filing_ScanDir_StartDir *startdirproc,
                            Filing_ScanDir_FoundFile *foundfileproc,
                            Filing_ScanDir_EndDir *enddirproc);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Scans a directory tree calling the specified functions.
            This function is reentrant: you can call it from one of the
            functions it calls.
            Subdirectories may be scanned before the parent directory has
            been completely scanned, hence 'startdirproc' may be called more
            than once before the corresponding 'enddirproc' is called.
            If you need to end the scan of a directory before starting the
            scan of a new one you can use Filing_ScanDirByDir.
  Inputs:   dirname - the directory name where the scan will start
            startdirproc - the function called every time a new directory
                           is started, including the one specified by
                           'dirname' (ie. before the files contained in it
                           are scanned). If this is NULL, no function will
                           be called.
            foundfileproc - the function called every time a new file is
                            found (including directories). If this is NULL,
                            no function will be called.
            enddirproc - the function called every time a directory scan
                         ends (ie. after all the files contained in it
                         have been scanned). If this is NULL, no function
                         will be called.
  Outputs:  -
  Returns:  NULL if no error, otherwise the error (generated by RISC OS
            during the scan or returned by a function)
  Errors:   -
  SeeAlso:  Filing_ScanDir_StartDir; Filing_ScanDir_FoundFile;
            Filing_ScanDir_EndDir; Filing_ScanDirByDir;

****************************************************************************/


/*F*************************************************************************/

  os_error *Filing_ScanDirByDir( char *dirname,
                                 Filing_ScanDir_StartDir *startdirproc,
                                 Filing_ScanDir_FoundFile *foundfileproc,
                                 Filing_ScanDir_EndDir *enddirproc);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  Purpose:  Scans a directory tree calling the specified functions.
            This function is reentrant: you can call it from one of the
            functions it calls.
            When a directory is scanned, 'startdirproc' is called, then
            'foundfileproc' is called for every file (including directories)
            contained in it and then 'enddirproc' is called. Finally, if the
            directory contained some subdirectory, they will be scanned.
            If you only need to scan directories and subdirectories in no
            particular order, see Filing_ScanDir since it is faster.
            Unlike Filing_Scandir, it is easy to prevent Filing_ScanDirByDir
            from recursively scan subdirectories: you should just return an
            error (eg (os_error *)-1) from 'enddirproc' so that only the
            files in the first directory will be scanned and then the error
            will be returned.
  Inputs:   dirname - the directory name where the scan will start
            startdirproc - the function called every time a new directory
                           is started, including the one specified by
                           'dirname' (ie. before the files contained in it
                           are scanned). If this is NULL, no function will
                           be called.
            foundfileproc - the function called every time a new file is
                            found (including directories). If this is NULL,
                            no function will be called.
            enddirproc - the function called every time a directory scan
                         ends (ie. after all the files contained in it
                         have been scanned). If this is NULL, no function
                         will be called.
  Outputs:  -
  Returns:  NULL if no error, otherwise the error (generated by RISC OS
            during the scan or returned by a function)
  Errors:   -
  SeeAlso:  Filing_ScanDir_StartDir; Filing_ScanDir_FoundFile;
            Filing_ScanDir_EndDir; Filing_ScanDir;

****************************************************************************/


#endif
