/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (c) 1995-1999.  The Regents of the University of California.  All     */
/*   rights reserved.                                                      */
/*                                                                         */
/*   This work was produced at the University of California, Lawrence      */
/*   Livermore National Laboratory (UC LLNL) under contract no.            */
/*   W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy     */
/*   (DOE) and The Regents of the University of California (University)    */
/*   for the operation of UC LLNL.  Copyright is reserved to the           */
/*   University for purposes of controlled dissemination,                  */
/*   commercialization through formal licensing, or other disposition      */
/*   under terms of Contract 48; DOE policies, regulations and orders;     */
/*   and U.S. statutes.  The rights of the Federal Government are          */
/*   reserved under Contract 48 subject to the restrictions agreed upon    */
/*   by the DOE and University.                                            */
/*                                                                         */
/*                                                                         */
/*                              DISCLAIMER                                 */
/*                                                                         */
/*   This software was prepared as an account of work sponsored by an      */
/*   agency of the United States Government.  Neither the United States    */
/*   Government nor the University of California nor any of their          */
/*   employees, makes any warranty, express or implied, or assumes any     */
/*   liability or responsibility for the accuracy, completeness, or        */
/*   usefulness of any information, apparatus, product, or process         */
/*   disclosed, or represents that its specific commercial products,       */
/*   process, or service by trade name, trademark, manufacturer, or        */
/*   otherwise, does not necessarily constitute or imply its               */
/*   endorsement, recommendation, or favoring by the United States         */
/*   Government or the University of California. The views and opinions    */
/*   of the authors expressed herein do not necessarily state or reflect   */
/*   those of the United States Government or the University of            */
/*   California, and shall not be used for advertising or product          */
/*   endorsement purposes.                                                 */
/*                                                                         */
/*   Permission to use, copy, modify and distribute this software and its  */
/*   documentation for any non-commercial purpose, without fee, is         */
/*   hereby granted, provided that the above copyright notice and this     */
/*   permission notice appear in all copies of the software and            */
/*   supporting documentation, and that all UC LLNL identification in      */
/*   the user interface remain unchanged.  The title to copyright LLNL     */
/*   XDIR shall at all times remain with The Regents of the University     */
/*   of California and users agree to preserve same. Users seeking the     */
/*   right to make derivative works with LLNL XDIR for commercial          */
/*   purposes may obtain a license from the Lawrence Livermore National    */
/*   Laboratory's Technology Transfer Office, P.O. Box 808, L-795,         */
/*   Livermore, CA 94550.                                                  */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <Xm/Xm.h>
#include "xdir.h"
#include "list.h"
#include "history.h"
#include "str.h"

#define CURRENT_HISTORY_VERSION  3
#define FIRST_HISTORY_FILE       ".xdircache"
#define CURRENT_HISTORY_FILE     ".xdirhistory"
#define SAVED_HISTORY_FILE       ".xdirhistory-old"
#define MAXHLINE                 200

struct history_st *history = NULL;

static char *reformat_msg = "Your LLNL XDIR history file, $HOME/%s,\nhas been reformatted.  If you wish to run your\nold version of LLNL XDIR, first change the name\nof $HOME/%s to $HOME/%s.";

static int history_version = CURRENT_HISTORY_VERSION;
static char *current_history_file;
static char *first_history_file;
static char *saved_history_file;
static char *history_file;

static char *history_label[] = {
	"Dir:",
	"Wildcard:",
	"User:",
	"Quote:"
};

extern int max_history_hosts;
extern int max_history[];

void add_to_history();


/*
 * construct_history_filename - Form all possible history file names.
 */
void
construct_history_filename()
{
    char *homedir = getenv("HOME");

	/* Construct current file name */
    current_history_file = XtMalloc(strlen(homedir)+strlen(CURRENT_HISTORY_FILE)
		+2);
    strcpy(current_history_file, homedir);
    strcat(current_history_file, "/");
    strcat(current_history_file, CURRENT_HISTORY_FILE);

	/* Construct obsolete file name */
    first_history_file = XtMalloc(strlen(homedir)+strlen(FIRST_HISTORY_FILE)+2);
    strcpy(first_history_file, homedir);
    strcat(first_history_file, "/");
    strcat(first_history_file, FIRST_HISTORY_FILE);

	/* Construct saved file name */
    saved_history_file = XtMalloc(strlen(homedir)+strlen(SAVED_HISTORY_FILE)+2);
    strcpy(saved_history_file, homedir);
    strcat(saved_history_file, "/");
    strcat(saved_history_file, SAVED_HISTORY_FILE);
}


/*
 * save_history - Write current history entries to history file.  Returns 0 
 *                if successful, else -1.  w_parent is the widget to center
 *                error dialogs over.
 */
save_history(w_parent)
Widget w_parent;
{
	static FILE *fp;
	struct host_link *hptr;
	struct item_link *cptr;
	int i;
	int saved_obsolete_file = False;
	char *msg;

	/* If obsolete history file is in use, save a copy of it */
	if (history_version < CURRENT_HISTORY_VERSION) {
		if (!history_file)   /* Sanity check */
			fatal_error("Bug in save_history()");
		if (rename(history_file, saved_history_file) == 0)
			saved_obsolete_file = True;
	}

	/* Create the new history file */
	if ((fp = fopen(current_history_file, "w")) == NULL)
		return -1;

	/* First line of history file contains version number */
	fprintf(fp, "History_Version: %d\n", CURRENT_HISTORY_VERSION);

	/* Save history */
	hptr = history->host_tail;
	while (hptr) {
		fprintf(fp, "Host: %s\n", hptr->host_name);
		for (i=0; i<NUMKINDS; i++) {
			cptr = hptr->kind[i].tail;
			while (cptr) {
				fprintf(fp, "   %s %s\n", history_label[i],  cptr->value);
				cptr = cptr->prev;
			}
		}
		hptr = hptr->prev;
	}

	fclose(fp);

	/* If an obsolete history file is in use, tell user it was saved */
	if (saved_obsolete_file) {
		msg = XtMalloc(strlen(reformat_msg)+60);
		if (history_version < 3)
			sprintf(msg, reformat_msg, FIRST_HISTORY_FILE, SAVED_HISTORY_FILE,
				FIRST_HISTORY_FILE);
		else
			sprintf(msg, reformat_msg, CURRENT_HISTORY_FILE,
				SAVED_HISTORY_FILE, CURRENT_HISTORY_FILE);
		info_dialog(msg, w_parent);
		XtFree(msg);
	}

	return 0;
}


/*
 * read_history_from_file - Initialize history entries with info from
 *                          history file.
 */
void
read_history_from_file()
{
	FILE *fp;
	char history_line[MAXHLINE+1];
	int len;
	char *id;
	char *value;
	char *host_name = NULL;
	int i;

	/* Verify that history is empty (Sanity check) */
	if (history)
		fatal_error("Bug in read_history_from_file()");

	/* Initialize history */
	history = XtNew(struct history_st);
	history->host_head = NULL;
	history->host_tail = NULL;
	history->host_count = 0;

	/* Try to open history file */
	if ((fp = fopen(current_history_file, "r")))
		history_file = current_history_file;
	else if ((fp = fopen(first_history_file, "r")))
		history_file = first_history_file;
	else {
		history_file = NULL;
		return;
	}

	/* Process history info */
	history_version = 1;
	while (fgets(history_line, MAXHLINE+1, fp) != NULL) {
		len = strlen(history_line);
		if (history_line[len-1] != '\n')
			continue;
		if ((id = strtok(history_line, "\t\n ")) == NULL)
			continue;
		if ((value = strtok(NULL, "\n")) == NULL)
			continue;
		if (strcmp(id, "Cache_Version:") == 0)
			sscanf(value, "%d", &history_version);
		if (strcmp(id, "History_Version:") == 0)
			sscanf(value, "%d", &history_version);
		if (strcmp(id, "Host:") == 0) {
			XtFree(host_name);
	        host_name = XtNewString(value);
		} else 
			for (i=0; i<NUMKINDS; i++)
				if (strcmp(id, history_label[i]) == 0) {
					/* Fix switched "Quote:"/"User:" in early history files */
					if (history_version == 1) {
						if (i == USER)
							i = QUOTE;
						else if (i == QUOTE)
							i = USER;
					}
					if (host_name)
						add_to_history(i, host_name, value);
					break;
				}
	}

	fclose(fp);
	XtFree(host_name);
}


/*
 * add_to_history - Add the specified host and value to the history
 *                  specified with "kind_id" (DIRECTORY, WILDCARD,
 *                  QUOTE or USER).
 */
void
add_to_history(kind_id, host_name, value)
int kind_id;
char *host_name;
char *value;
{
	struct host_link *hptr;
	struct item_link *cptr;
	struct item_link *next;
	int i;
	int found;

	/* Search for host name */
	found = False;
	if (history->host_head) {
		hptr = history->host_head;
		while (hptr) {
			if (strcmp(hptr->host_name, host_name) == 0) {
				found = True;
				break;
			}
			hptr = hptr->next;
		}
	}

	/* If host name found, move to head of list, else create entry for host */
	if (found) {
		if (hptr != history->host_head) {
			hptr->prev->next = hptr->next;
			if (hptr == history->host_tail)
				history->host_tail = hptr->prev;
			else
				hptr->next->prev = hptr->prev;
			hptr->prev = NULL;
			hptr->next = history->host_head;
			history->host_head->prev = hptr;
			history->host_head = hptr;
		}
	} else {
		hptr = XtNew(struct host_link);
		for (i=0; i<NUMKINDS; i++) {
			hptr->kind[i].head = NULL;
			hptr->kind[i].tail = NULL;
			hptr->kind[i].count = 0;
		}
		hptr->host_name = XtNewString(host_name);
		hptr->next = history->host_head;
		hptr->prev = NULL;
		if (history->host_head)
			history->host_head->prev = hptr;
		history->host_head = hptr;
		if (history->host_tail == NULL)
			history->host_tail = hptr;
		history->host_count++;
	}

	/* If necessary, reduce number of host entries to maximum allowable */
	while (history->host_count > max_history_hosts) {
		hptr = history->host_tail;
		history->host_tail = hptr->prev;
		XtFree(hptr->host_name);
		for (i=0; i<NUMKINDS; i++) {
			cptr = hptr->kind[i].head;
			while (cptr) {
				XtFree(cptr->value);
				next = cptr->next;
				XtFree((char *)cptr);
				cptr = next;
			}
		}
		XtFree((char *)hptr);
		history->host_tail->next = NULL;
		history->host_count--;
	}

	/* Search the history for the value */
	found = False;
	if (history->host_head->kind[kind_id].head) {
		cptr = history->host_head->kind[kind_id].head;
		while (cptr) {
			if (strcmp(cptr->value, value) == 0) {
				found = True;
				break;
			}
			cptr = cptr->next;
		}
	}

	/* If value found, move to head of list, else create entry for value */
	if (found) {
		if (cptr != history->host_head->kind[kind_id].head) {
			cptr->prev->next = cptr->next;
			if (cptr == history->host_head->kind[kind_id].tail)
				history->host_head->kind[kind_id].tail = cptr->prev;
			else
				cptr->next->prev = cptr->prev;
			cptr->prev = NULL;
			cptr->next = history->host_head->kind[kind_id].head;
			history->host_head->kind[kind_id].head->prev = cptr;
			history->host_head->kind[kind_id].head = cptr;
		}
	} else {
		cptr = XtNew(struct item_link);
		cptr->value = XtNewString(value);
		cptr->next = history->host_head->kind[kind_id].head;
		cptr->prev = NULL;
		if (history->host_head->kind[kind_id].head)
			history->host_head->kind[kind_id].head->prev = cptr;
		history->host_head->kind[kind_id].head = cptr;
		if (history->host_head->kind[kind_id].tail == NULL)
			history->host_head->kind[kind_id].tail = cptr;
		history->host_head->kind[kind_id].count++;
	}

	/* If necessary, reduce number of values in history to maximum allowable */
	while (history->host_head->kind[kind_id].count > max_history[kind_id]) {
		cptr = history->host_head->kind[kind_id].tail;
		history->host_head->kind[kind_id].tail = cptr->prev;
		XtFree(cptr->value);
		XtFree((char *)cptr);
		history->host_head->kind[kind_id].tail->next = NULL;
		history->host_head->kind[kind_id].count--;
	}
}


/*
 * retrieve_history - Sets "list" to point to a struct containing the
 *                    history values for "hostname" ("kind_id" can
 *                    have the values DIRECTORY, WILDCARD, QUOTE or USER).
 *                    Memory pointed to by "list" should be released by
 *                    calling release_array_list().  If the "sort"
 *                    flag is set, then the list is returned sorted,
 *                    otherwise it is returned in order of last use.
 */
void
retrieve_history(kind_id, hostname, list, sort)
int kind_id;
char *hostname;
struct sl_struct **list;
int sort;
{
	char **values;
	int indx;
	struct host_link *hptr;
	struct item_link *cptr;

    /* Search for host name */
    hptr = history->host_head;
    while (hptr) {
        if (strcmp(hptr->host_name, hostname) == 0)
            break;
		hptr = hptr->next;
	}

	/* If host name not found, return empty list */
	if (hptr == NULL) {
		*list = XtNew(struct sl_struct);
		(*list)->nentries = 0;
		(*list)->entries = (char **)XtMalloc(0);
		return;
	}

	/* Host name found.  Convert linked list of values into array */
	values = (char **)XtMalloc(sizeof(char *)*hptr->kind[kind_id].count);
	cptr = hptr->kind[kind_id].head;
	indx = 0;
	while (cptr) {
		values[indx] = XtNewString(cptr->value);
		indx++;
		cptr = cptr->next;
	}

	/* Sanity check */
	if (indx != hptr->kind[kind_id].count)
		fatal_error("Bug in retrieve_history()");

	/* Sort entries */
	if (sort)
		quicksort(values, hptr->kind[kind_id].count, strcmp);

	/* Return struct */
	*list = XtNew(struct sl_struct);
	(*list)->nentries = hptr->kind[kind_id].count;
	(*list)->entries = values;
}


/*
 * retrieve_hostuser_history - Sets "list" to point to a struct containing the
 *                             expressions in the hostname/username history.
 *                             Memory pointed to by "list" should be released
 *                             by calling release_array_list().  If the "sort"
 *                             flag is set, then the list is returned sorted,
 *                             otherwise it is returned in order of last use.
 */
void
retrieve_hostuser_history(list, sort)
struct sl_struct **list;
int sort;
{
	char **hostusers;
	int hostuser_count;
	struct host_link *hptr;
	struct item_link *cptr;
	int indx;

	/* Count number of host user pairs */
	hostuser_count = 0;
	hptr = history->host_head;
	while (hptr) {
		cptr = hptr->kind[USER].head;
		while (cptr) {
			hostuser_count++;
			cptr = cptr->next;
		}
		hptr = hptr->next;
	}

	/* Convert linked list of hostname/username pairs into array */
	hostusers = (char **)XtMalloc(sizeof(char *)*hostuser_count);
	indx = 0;
	hptr = history->host_head;
	while (hptr) {
		cptr = hptr->kind[USER].head;
		while (cptr) {
			hostusers[indx] = XtMalloc(strlen(hptr->host_name)+
				strlen(cptr->value)+6);
			sprintf(hostusers[indx], "%s   (%s)", hptr->host_name,cptr->value);
			indx++;
			cptr = cptr->next;
		}
		hptr = hptr->next;
	}

	/* Sanity check */
	if (indx != hostuser_count)
		fatal_error("Bug in retrieve_hostuser_history()");

	/* Sort entries */
	if (sort)
		quicksort(hostusers, hostuser_count, strcmp);

	/* Return struct */
	*list = XtNew(struct sl_struct);
	(*list)->nentries = hostuser_count;
	(*list)->entries = hostusers;
}



syntax highlighted by Code2HTML, v. 0.9.1