/*
 * console.c  -  user interface I/O routines
 *
 * Copyright (C) 1998-2003 Gero Kuhlmann <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: console.c,v 1.4 2003/01/25 23:29:42 gkminix Exp $
 */

#define NEED_DIR 1
#include <common.h>
#include <nblib.h>
#include "console.h"



/*
 * Variables local to this module
 */
static int curline = 0;			/* current screen line */



/*
 * Directory entry name list
 */
struct direntry {
	struct direntry *next;
	char            *fname;
};



/*
 * Compare a description table entry for qsort routine
 */
static int descmp(entry1, entry2)
const voidstar entry1;
const voidstar entry2;
{
  struct direntry *dp1 = *((struct direntry **)entry1);
  struct direntry *dp2 = *((struct direntry **)entry2);

  assert(dp1 != NULL && dp2 != NULL);
  if (dp1->fname == NULL && dp2->fname != NULL)
	return(-1);
  else if (dp1->fname != NULL && dp2->fname == NULL)
	return(1);
  else if (dp1->fname == NULL && dp2->fname == NULL)
	return(0);

  return(strcmp(dp1->fname, dp2->fname));
}



/*
 * Compare a directory entry name against one pattern.
 */
static int singledircmp(name, pat)
char *name;
char *pat;
{
  while (*pat && *name) {
	if (*pat == '*') {
		pat++;
		if (!*pat)
			return(TRUE);
		while (*name && !singledircmp(name, pat))
			name++;
		return(*name);
	} else if (*pat == '?' || *pat == *name) {
		pat++;
		name++;
	} else
		break;
  }
  return(!*pat && !*name);
}



/*
 * Compare a directory entry name against a list of patterns.
 */
static int dircmp(name, pattern)
char *name;
char *pattern;
{
  char *cp1, *cp2;
  char *pat = NULL;

  if (pattern == NULL)
	return(TRUE);

  copystr(&pat, pattern);
  cp1 = pat;
  while (cp1 != NULL) {
	if ((cp2 = strchr(cp1, ':')) != NULL)
		*(cp2++) = '\0';
	if (*cp1 && singledircmp(name, cp1)) {
		free(pat);
		return(TRUE);
	}
	cp1 = cp2;
  }
  free(pat);
  return(FALSE);
}



/*
 * Get a list of directory entries with name patterns.
 */
static struct direntry **getdirlist(dirname, pattern)
char *dirname;
char *pattern;
{
  DIR *dir;
  struct dirent *ent;
  struct direntry *dp;
  struct direntry **dirlist = NULL;
  struct direntry *firstdir = NULL;
  int count, i;

  /* Open the directory */
  if ((dir = opendir(dirname)) == NULL)
	return(NULL);

  /* Step through all directory entries */
  count = 0;
  while ((ent = readdir(dir)) != NULL) {
	if (dircmp(ent->d_name, pattern)) {
		dp = (struct direntry *)nbmalloc(sizeof(struct direntry));
		copystr(&(dp->fname), ent->d_name);
		dp->next = firstdir;
		firstdir = dp;
		count++;
	}
  }
  closedir(dir);

  /* Generate sorted array of directory entries */
  if (count > 0) {
	dirlist = (struct direntry **)nbmalloc((count + 1) *
						sizeof(struct direntry *));
	i = 0;
	dp = firstdir;
	while (dp != NULL && i < count) {
		dirlist[i] = dp;
		dp = dp->next;
		dirlist[i]->next = NULL;
		i++;
	}
	assert(i == count);
	dirlist[i] = NULL;
	qsort(dirlist, count, sizeof(struct direntry *), &descmp);
  }
  return(dirlist);
}



/*
 * Delete directory list
 */
static void deldirlist(dirlist)
struct direntry **dirlist;
{
  struct direntry **dpp;

  if (dirlist == NULL)
	return;

  dpp = dirlist;
  while (*dpp != NULL) {
	free((*dpp)->fname);
	free(*dpp);
	dpp++;
  }
  free(dirlist);
}



/*
 * Print a new line, and increase the number of displayed lines
 */
static void printnl()
{
  putchar('\n');
  curline++;
  if (curline > LISTLINES) {
	printf("\nPress <ENTER> to continue listing ");
	(void)getstring(NULL);
	putchar('\n');
	curline = 0;
  }
}



/*
 * Print list of directory entries matching a pattern
 */
static void printdir(dirname, pattern)
char *dirname;
char *pattern;
{
  struct direntry **dirlist;
  char *dirbuf = NULL;
  char *cp1, *cp2, *cp3;
  int i, j, k;

  /* Generate local copy of directory name list */
  copystr(&dirbuf, dirname);
  cp1 = dirbuf;
  while (cp1 != NULL) {
	/* Find end of subdirectory name */
	if ((cp2 = strchr(cp1, ':')) != NULL)
		*(cp2++) = '\0';

	/* Get list of files in directory */
	if ((dirlist = getdirlist(cp1, pattern)) == NULL) {
		cp1 = cp2;
		continue;
	}

	/* Print listing header */
	printf("Contents of %s:", cp1);
	printnl();

	/* Print all files in directory */
	i = 0;
	while (dirlist[i] != NULL) {
		printf("  ");
		for (j = 0; j < 3 && dirlist[i] != NULL; i++, j++) {
			cp3 = dirlist[i]->fname;
			for (k = 0; k < 20 && *cp3; k++, cp3++)
				putchar(*cp3);
			for ( ; k < 24; k++)
				putchar(' ');
		}
		printnl();
	}

	/* Prepare for next directory */
	deldirlist(dirlist);
	if ((cp1 = cp2) != NULL) {
		printnl();
		printnl();
	}
  }
  free(dirbuf);
}



/*
 * Get a string response from the user
 */
char *getstring(prompt)
char *prompt;
{
  static char buf[1024];
  char *cp;

  if (prompt != NULL)
	printf("%s: ", prompt);
  fflush(stdout);
  fgets(buf, sizeof(buf) - 1, stdin);
  for (cp = buf; *cp && *cp != '\n'; cp++) ;
  *cp = '\0';
  return(buf);
}



/*
 * Get a numeric response from the user
 */
long getnum(prompt, min, max, hex)
char *prompt;
long min;
long max;
int hex;
{
  char *cp;
  long j;
  int i, indent = 0;

  if (prompt != NULL)
	indent = strspn(prompt, " ");

  while (TRUE) {
	if (prompt != NULL)
		printf("%s (%s): ", prompt, hex ? "hex" : "decimal");
	cp = getstring(NULL);
	if (*cp) {
		if ((hex ? sscanf(cp, "%lx", &j) : sscanf(cp, "%ld", &j)) != 1)
			j = min - 1;
		if (j < min || j > max) {
			for (i = 0; i < indent; i++)
				putchar(' ');
			if (hex)
				printf("  Allowed values are [%lX..%lX]\n",
								min, max);
			else
				printf("  Allowed values are [%ld..%ld]\n",
								min, max);
		} else
			break;
	}
  }
  return(j);
}



/*
 * Get a list selection from the user
 */
int getsel(prompt, minsel, maxsel, defsel)
char *prompt;
int minsel;
int maxsel;
int defsel;
{
  char *cp;
  int sel = minsel - 1;
  int i;

  while (TRUE) {
	if (defsel >= minsel && defsel <= maxsel) {
		i = 1;
		if (prompt != NULL)
			printf("%s [%d]: ", prompt, defsel);
		cp = getstring(NULL);
		if (!*cp)
			sel = defsel;
		else
			i = sscanf(cp, "%d", &sel);
	} else {
		i = 0;
		if (prompt != NULL)
			printf("%s: ", prompt);
		cp = getstring(NULL);
		if (*cp)
			i = sscanf(cp, "%d", &sel);
	}
	if (i == 1 && sel >= minsel && sel <= maxsel)
		break;
  }
  return(sel);
}



/*
 * Get a yes/no response from the user
 */
int getyn(prompt, def)
char *prompt;
int def;
{
  char *cp;

  while (TRUE) {
	if (prompt != NULL)
		printf("%s (y/n) [%s] ? ", prompt, def ? "yes" : "no" );
	cp = getstring(NULL);
	if (!*cp)
		return(def);
	else if (toupper(*cp) == 'Y')
		return(TRUE);
	else if (toupper(*cp) == 'N')
		return(FALSE);
  }
}



/*
 * Ask user about a file name
 */
char *getfilename(prompt, searchdir, pattern)
char *prompt;
char *searchdir;
char *pattern;
{
  char *filename, *cp, *dir;

  filename = NULL;
  while (filename == NULL) {
	if (prompt != NULL) {
		printf(prompt);
		dir = getstring(" ('*' for directory listing)");
	} else {
		dir = getstring(NULL);
	}
	dir += strspn(dir, " \t");
	cp = dir + strcspn(dir, " \t:");
	*cp = '\0';
	if ((cp = strchr(dir, '*')) != NULL) {
		if ((cp > dir && cp[-1] != '/') || cp[1] != '\0')
			printf("Invalid directory specification\n");
		else if (dir == cp)
			printdir(searchdir, pattern);
		else {
			cp[-1] = '\0';
			if (!*dir)
				printdir("/", pattern);
			else
				printdir(dir, pattern);
		}
	} else if (*dir) {
		copystr(&filename, dir);
		checkaccess(&filename, searchdir);
		if (filename == NULL)
			printf("Unable to find and/or access file %s\n", dir);
	}
  }
  return(filename);
}



syntax highlighted by Code2HTML, v. 0.9.1