
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: FILE.C
*
* An object representing a file and the lines of text it contains.
*
* Functions:
*
* file_new()
* file_getdiritem()
* file_delete()
* file_getlinelist()
* file_discardlines()
* file_reset()
* file_readlines()
*
* Comments:
*
* A FILEDATA object is initialised with a DIRITEM handle from which it
* can get a filename. It knows how to supply a list of LINE handles for the
* lines of text in the file.
*
* The file is read into memory optionally on creation of the FILEDATA object:
* otherwise, at the first call to file_getlinelist. It can be discarded
* by calling file_discardlines: in this case, it will be re-read next time
* file_getlinelist is called.
*
* Calling file_reset will cause line_reset to be called for all lines
* in the list. This clears any links.
*
* We allocate all memory from a gmem* heap hHeap, assumed to be declared and
* initialised elsewhere.
*
****************************************************************************/

#include <windows.h>
#include <stdlib.h>
#include <string.h>

#include "gutils.h"
#include "windiff.h"
#include "list.h"
#include "line.h"
#include "scandir.h"
#include "file.h"

extern HANDLE hHeap;

struct filedata {

        DIRITEM diritem;        /* handle to file name information */
        LIST lines;             /* NULL if lines not read in */
};


void file_readlines(FILEDATA fd);

/****************************************************************************
 * Function: file_new
 *
 * Purpose:
 * 
 * Creates a new FILEDATA, based on a DIRITEM. the filedata will retain
 * the diritem handle for use in fetching filenames and handles.
 *
 * If the bRead is set, the file will be read into memory. If not, this
 * will be done during the first call to file_getlines.
 *
 ***************************************************************************/
FILEDATA
file_new(DIRITEM fiName, BOOL bRead)
{
        FILEDATA fd;

        fd = (FILEDATA) gmem_get(hHeap, sizeof(struct filedata));
        if (fd == NULL) {
                return(NULL);
        }

        fd->diritem = fiName;
        fd->lines = NULL;

        if (bRead) {
                file_readlines(fd);
        }

        return(fd);
}

/****************************************************************************
 * Function: file_getdiritem
 *
 * Purpose:
 * 
 * Returns a handle to the DIRITEM used to create this FILEDATA
 *
 ***************************************************************************/
DIRITEM
file_getdiritem(FILEDATA fd)
{
        if (fd == NULL) {
                return(NULL);
        }

        return(fd->diritem);
}


/****************************************************************************
 * Function: file_delete
 *
 * Purpose:
 * 
 * Deletes a filedata and its associated list of lines. Note that the diritem
 * is not deleted (this is owned by the DIRLIST, and will be deleted
 * when the DIRLIST is deleted)
 *
 ***************************************************************************/
void
file_delete(FILEDATA fd)
{
        if (fd == NULL) {
                return;
        }

        /* throw away the line list, if there is one */
        file_discardlines(fd);

        gmem_free(hHeap, (LPSTR) fd, sizeof(struct filedata));
}

/****************************************************************************
 * Function: file_getlinelist
 *
 * Purpose:
 * 
 * Returns a handle to a list of lines in this file. The items in the
 * list are LINE handles.
 *
 * The first call to this function will cause the file to be read into
 * memory if bRead was FALSE on the call to file_new, or if file_discardlines
 * has since been called.
 *
 * The list of lines returned should not be deleted except by calls to
 * file_delete or file_discardlines.
 *
 ***************************************************************************/
LIST
file_getlinelist(FILEDATA fd)
{
        if (fd == NULL) {
                return NULL;
        }

        if (fd->lines == NULL) {
                file_readlines(fd);
        }
        return(fd->lines);
}


/****************************************************************************
 * Function: file_discardlines
 *
 * Purpose:
 * 
 * Discards the list of lines associated with a file. This will cause
 * the file to be re-read next time file_getlinelist is called.
 *
 ***************************************************************************/
void
file_discardlines(FILEDATA fd)
{
        LINE line;

        if (fd == NULL) {
                return;
        }

        if (fd->lines != NULL) {

                /* clear each line to free any memory associated
                 * with them, then discard the entire list
                 */
                List_TRAVERSE(fd->lines, line) {
                        line_delete(line);
                }
                List_Destroy(&fd->lines);
        }

        /* this is probably done in List_Destroy, but better do it anyway*/
        fd->lines = NULL;
}


/****************************************************************************
 * Function: file_reset
 *
 * Purpose:
 * 
 * Forces a reset of each line in the list. The function line_reset discards 
 * links between lines, and any hashcode information. This would be used if
 * the compare options or hashcode options have changed.
 *
 ***************************************************************************/
void
file_reset(FILEDATA fd)
{
        LINE line;

        if (fd == NULL) {
                return;
        }

        if (fd->lines != NULL) {

                List_TRAVERSE(fd->lines, line)  {
                        line_reset(line);
                }
        }
}


/****************************************************************************
 * Function: file_readlines
 *
 * Purpose:
 * 
 * Reads the file into a list of lines.
 *
 * Comments:
 *
 * We use the buffered read functions to read a block at a time, and
 * return us a pointer to a line within the block. The line we are
 * pointed to is not null terminated. from this we do a line_new: this
 * will make a copy of the text (since we want to re-use the buffer), and
 * will null-terminate its copy.
 *
 *
 ***************************************************************************/
void
file_readlines(FILEDATA fd)
{
        LPSTR textp;
        int fh;
        FILEBUFFER fbuf;
        int linelen;
        int linenr = 1;
        HCURSOR hcurs;

        hcurs = SetCursor(LoadCursor(NULL, IDC_WAIT));

        /* open the file */
        fh = dir_openfile(fd->diritem);

        if (fh < 0) {
                SetCursor(hcurs);
                return;
        }
        /* initialise the file buffering */
        fbuf = readfile_new(fh);


        /* make an empty list for the files */
        fd->lines = List_Create();

        while ( (textp = readfile_next(fbuf, &linelen)) != NULL) {

                line_new(textp, linelen, linenr++, fd->lines);

        }

        /* close filehandle and free buffer */
        readfile_delete(fbuf);

        dir_closefile(fd->diritem, fh);

        SetCursor(hcurs);
}






