/* fileindex.c -- Copyright 1989, 1990, 1993, 1994 Liam R. Quin.
 * All Rights Reserved.
 * This code is NOT in the public domain.
 * See the file COPYRIGHT for full details.
 *
 * A simple program to give information about one or more files about
 * which information is stored in the lq-text database.
 */

static char *Revision = "@(#) $Id: lqfile.c,v 1.16 2001/05/31 03:50:13 liam Exp $";

#include "globals.h" /* defines and declarations for database filenames */
#include "error.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h> /* for O_RDONLY etc */
#endif

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#else
# include <malloc.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "emalloc.h"
#include "fileinfo.h"
#include "lqutil.h"
#include "liblqtext.h"

char *progname;

/** System calls and library functions used in this file: **/

/** Unix System calls: **/
/** System Library Functions: **/

/** Functions defined within this file: **/

PRIVATE void
AddInfo(
#ifdef HAVE_PROTO
    t_LQTEXT_Database *db,
    char *FileName
#endif
);

PRIVATE void AllInfo(
#ifdef HAVE_PROTO
    t_LQTEXT_Database *db
#endif
);

PRIVATE void DisplayFileInfo(
#ifdef HAVE_PROTO
    t_LQTEXT_Database *db,
    t_FileInfo *FileInfo
#endif
);

PRIVATE void PrintFIDInfo(
#ifdef HAVE_PROTO
    t_LQTEXT_Database *db,
    t_FID FID
#endif
);

PRIVATE void PrintInfo(
#ifdef HAVE_PROTO
    t_LQTEXT_Database *db,
    char *Name
#endif
);

int AllFiles = 0;
int ListMode = 0;
int AddFiles = 0;
int UseMatchFormatForList = 0;

int
main(argc, argv)
    int argc;
    char *argv[];
{
    extern int optind, getopt();
    /** extern char *optarg; (unused at the moment) **/
    int ch;
    int ErrorFlag = 0;
    int FIDMode = 0;
    t_lqdbOptions *Options;
    t_LQTEXT_Database *db;

    progname = argv[0];

    Options = LQT_InitFromArgv(argc, argv);

    /* All programs take Zz:Vv */
    while ((ch = getopt(argc, argv, "Zz:VvAaFlMx")) != EOF) {
	switch (ch) {
	case 'z':
	case 'Z':
	    break; /* done by LQT_InitFromArgv(); */
	case 'V':
	    fprintf(stderr, "%s version %s\n", progname, Revision);
	    break;
	case 'F':
	    FIDMode = 1;
	    break;
	case 'A':
	    AddFiles = 1;
	    break;
	case 'a':
	    AllFiles = 1;
	    break;
	case 'l':
	    ListMode = 1;
	    break;
	case 'M':
	    ListMode = 1;
	    UseMatchFormatForList = 1;
	    break;
	case 'x':
	    ErrorFlag = (-1);
	    break;
	case '?':
	    ErrorFlag = 1;
	    break;
	}
    }

    /* Normally put call to lrqError here to give a helpful message,
     * but not yet ready to ship the error handling package, sorry
     */
    if (ErrorFlag) {
	fprintf(stderr, "%s: usage: %s [options] [files]\n",progname,progname);
	fprintf(stderr, "%s: options are:\n", progname);
	LQT_PrintDefaultUsage(Options);
	fputs("\
	-F      -- treat file names as numeric FIDs instead\n\
	-l	-- list mode: no header output or lines drawn\n\
	-M	-- list mode output is in match format (implies -l)\n\
	-t N	-- set trace level to N [default: 0]\n\
	-x	-- print this explanation\n\
\n\
In addition, if no files are given, the following are understood:\n\
	-A	-- add the named files to the list of known files\n\
	-a	-- list information about all files\n", stderr);
	exit((ErrorFlag > 0) ? 1 : 0);
    }

    db = LQT_OpenDatabase(Options, O_RDONLY, 0);
    if (!db || LQT_ObtainReadOnlyAccess(db) < 0) {
	Error(E_FATAL,
	    "couldn't open lq-text database in directory \"%s\"",
	    db->DatabaseDirectory
	);
    }

    if (AllFiles && AddFiles) {
	fprintf(stderr, "%s: do not use both -a and -A options\n", progname);
	fprintf(stderr, "\tuse %s -x for further explanation.\n", progname);
	exit(1);
    }

    if (optind >= argc && !AllFiles && !AddFiles) {
	Error(E_USAGE|E_FATAL|E_XHINT,
	    "You must either give the -a option or specify files to list"
	);
    }

    if (!AddFiles && !ListMode) {
	printf("%-7.7s | FileType | %-20.20s | %s\n",
		"FID", "Date Last indexed", "Current Location");
	puts(
"========|==========|======================|==================================="
	);
    }
    if (AllFiles) {
	AllInfo(db);
    } else {
	if (AddFiles) {
	    LQT_ObtainWriteAccess(db);
	}

	while (optind < argc) {
	    if (AddFiles) {
		AddInfo(db, argv[optind++]);
	    } else {
		if (FIDMode) {
		    t_FID FID;

		    if ((FID = atol(argv[optind])) == (t_FID) 0) {
			Error(E_WARN,
			    "Invalid FID %s; FIDs must be numeric, > 0L",
			    argv[optind]
			);
		    } else {
			PrintFIDInfo(db, FID);
		    }
		} else {
		    PrintInfo(db, argv[optind]);
		}
		++optind;
	    }
	}
    }
    LQT_SyncAndCloseAllKeyValueDatabases(db);
    exit(0);
    /*NOTREACHED*/
    return 1; /* for lint and gcc... */
}

PRIVATE void
PrintInfo(db, Name)
    t_LQTEXT_Database *db;
    char *Name;
{
    t_FID FID;

    if ((FID = LQT_NameToFID(db, Name)) == (t_FID) 0) {
	Error(E_WARN,
	    "No FID available for filename: %s",
	    Name
	);
    } else {
	PrintFIDInfo(db, FID);
    }
}

PRIVATE void
PrintFIDInfo(db, FID)
    t_LQTEXT_Database *db;
    t_FID FID;
{
    t_FileInfo *FileInfo;

    /* get info from the list */
    if ((FileInfo = LQT_FIDToFileInfo(db, FID)) == (t_FileInfo *) 0) {
	Error(E_WARN,
	    "No Index information for FID: %ld",
	    FID
	);
    } else {
	DisplayFileInfo(db, FileInfo);
    }
}

PRIVATE void
DisplayFileInfo(db, theFileInfo)
    t_LQTEXT_Database *db;
    t_FileInfo *theFileInfo;
{
    extern char *ctime();
    char *DateString;
    register char *p;

    DateString = ctime(&(theFileInfo->Date));

    /* The string returned by ctime usually ends in a newline,
     * which we don't want.  So we'll remove it:
     */
    for (p = DateString; *p; p++) {
	if (*p == '\n' || *p == '\r') {
	    *p = '\0';
	    break;
	}
    }

    if (ListMode) {
	if (UseMatchFormatForList) {
	    /* NumberOfWords, BlockInFile, WordInBlock, FID, DocName */
	    printf("0 0 0 %lu %s\n",
		theFileInfo->FID,
		theFileInfo->Name
	    );
	} else {
	    printf("%lu\t%s\t%s\t%s\n",
		theFileInfo->FID,
		LQT_GetFilterName(db, theFileInfo),
		&DateString[4],
		theFileInfo->Name
	    );
	}
    } else {
	printf("%5lu | %10.10s | %-20.20s | %s\n",
	    theFileInfo->FID,
	    LQT_GetFilterName(db, theFileInfo),
	    &DateString[4],
	    theFileInfo->Name
	);
    }
}

/**
Mon Sep 25 23:58:53 BST 1989
FID     | FileType | Date Last indexed    | Current Location
========|==========|======================|====================================
      1 | plain    | Sep 25 20:31:26 1989 | /usr2/liam/Bible/NT/John/john01.kjv
      2 | netnews  | Sep 25 20:31:28 1989 | alt/lifestyle/barefoot/1064
      3 | SGML     | Sep 25 20:31:30 1989 | /usr2/liam/Bible/NT/John/john03.kjv
**/

PRIVATE void
AllInfo(db)
    t_LQTEXT_Database *db;
{
    t_FileInfo *FileInfo;
    long FID;
    long MaxFid = LQT_GetMaxFID(db);

    for (FID = 0L; FID <= MaxFid; FID++) {
	if ((FileInfo = LQT_FIDToFileInfo(db, FID)) != (t_FileInfo *) 0) {
	    DisplayFileInfo(db, FileInfo);
	    efree((char *) FileInfo); /* NOTDONE use LQT_DestroyFileInfo */
	}
    }
    if (!ListMode) {
	printf("Max File Identifier is %lu\n", MaxFid);
    }
}

PRIVATE void
AddInfo(db, FileName)
    t_LQTEXT_Database *db;
    char *FileName;
{
    extern time_t time();
    t_FileInfo FileInfo;
    struct stat statBuf;

    FileInfo.Name = FileName;
    (void) time(&(FileInfo.Date));
    FileInfo.FID = LQT_GetMaxOrAllocateFID(db, 1); /* 1 = write current */
    FileInfo.Stream = 0;

    if (stat(FileName, &statBuf) < 0) {
	Error(E_WARN|E_SYS,
	    "Couldn't find file \"%s\" to add it's name to the index",
	    FileName
	);
	return;
    }

    /* determine filter type */
    FileInfo.FilterType = LQT_GetFilterType(db, &FileInfo, &statBuf);

    printf("%ld %s (type %d, %s) -- %s\n",
	    FileInfo.FID,
	    FileInfo.Name,
	    FileInfo.FilterType,
	    LQT_GetFilterName(db, &FileInfo),
	    LQT_SaveFileInfo(db, &FileInfo) == 0 ?
			    "Marked as having been indexed." :
			    "Failed to mark file as having been indexed."
    );
}