config root man

Current Path : /usr/src/gnu/lib/libodialog/

FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64
Upload File :
Current File : //usr/src/gnu/lib/libodialog/tree.c

/*
 * tree.c -- implements the 'tree' interface element for libdialog
 *
 * Author: Anatoly A. Orehovsky (tolik@mpeks.tomsk.su)
 *
 * Copyright (c) 1997, Anatoly A. Orehovsky
 * 09/28/98 - patched by Anatoly A. Orehovsky (smart_tree())
 *
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD: release/9.1.0/gnu/lib/libodialog/tree.c 114603 2003-05-03 21:31:08Z obrien $");

#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <dialog.h>
#include "dialog.priv.h"
#include <ncurses.h>

/* static utils for make tree */
struct leaf {
	unsigned char *name;		/* name of leaf */
	unsigned char *branches;	/* branches that going by leaf */
	unsigned char slip;		/* slip of leaf*/
	int shift;			/* shift relative root of tree */
};

static int	mk_slip(struct leaf array[], int arr_size, 
			int number, int shift);

/* make tree from file
 *
 * filename	- name of file with like find(1) output
 * p_names	- pointer to array of strings
 * p_size	- pointer to size of array
 * FS		- fields separator
 * p_array	- pointer to array of leafs
 *
 * return values:
 * 0		- ok and names by p_names, size by p_size, array by p_array set
 * -1		- memory allocation error (errno set)
 */  

static int	mk_ftree(char *filename, 
		unsigned char ***p_names, int *p_size, unsigned char FS, 
			struct leaf **p_array);
		
/* make tree from array
 *
 * names	- array of strings
 * size		- size of array
 * FS		- fields separator
 * p_array	- pointer to array of leafs
 *
 * return values:
 * 0		- ok and array by p_array set
 * -1		- memory allocation error (errno set)
 */  
 
static int	mk_tree(unsigned char **names, int size, unsigned char FS, 
			struct leaf **p_array);

/* free memory from tree (leafs)
 *
 * return values:
 * nothing
 */

static void	free_leafs(struct leaf *array, int size);

/* free memory from source data for tree (names)
 *
 * return values:
 * if 0 <= choice <= size - pointer to name from names, 
 *	and memory for name not released (must be freed later)
 * else - NULL (recomended choice -1 for it)
 */

static unsigned char	*free_names(unsigned char **names, 
					int size, int choice);

/* end of static utils for make tree */

/* static utils for ftree */

/* control struct for queue */
struct queue {
	int size;			/* size of queue */
	struct m_queue *first;		/* begin of queue */
	struct m_queue *last;		/* end of queue */
};

/* queue member */
struct m_queue {
	void *pointer;			/* queue member */
	struct m_queue *next;		/* next queue member */
};

/* init struct queue by zeros */
static void	init_queue(struct queue *queue);

/* add pointer to queue */
/* return - pointer or NULL if error */
static void	*p2_queue(struct queue *queue, void *pointer);

/* get first from queue */
/* return - pointer or NULL if queue is empty */
static void	*first_queue(struct queue *queue);

/* make zero terminated array from queue */
/* return - pointer to array or NULL if error */
static void	**q2arr(struct queue *queue, int depth);

/* smart_tree (for like find(1) with -d flag output compliance) */
/* return - not NULL or NULL if malloc error */
static unsigned char	*smart_tree(struct queue *queue, unsigned char FS,
					unsigned char *current,
					unsigned char *prev);

/* end of static utils for ftree */

/* static utils for saved_tree */

/* saved values for unique tree */
struct saved_tree {
	unsigned char **names;	/* names + */ 
	int size;		/* size + */
	unsigned char FS;	/* FS + */
	int height;		/* height + */
	int width;		/* width + */
	int menu_height;	/* menu_height - unique for treebox ? */
	int ch;			/* saved ch - choice */
	int sc;			/* saved sc - scroll */
};

/* search saved tree within queue */
/* return - struct saved_tree * or NULL if not found */
static struct saved_tree *search_saved_tree(struct queue *queue, 
					unsigned char **names,
					int size,
					unsigned char FS,
					int height,
					int width,
					int menu_height);

/* end of static utils for saved_tree */

static void print_item(WINDOW *win, struct leaf item, int choice, int selected);

static void print_position(WINDOW *win, int x, int y,
				int cur_pos, int size);

static int menu_width, item_x;

static int dialog_treemenu(unsigned char *title, unsigned char *prompt, 
			int height, int width, int menu_height, 
				int item_no, struct leaf items[], 
					int *result, 
						int *ch, int *sc);

/*
 * Display a menu for choosing among a number of options
 */
static
int dialog_treemenu(unsigned char *title, unsigned char *prompt, 
			int height, int width, int menu_height, 
				int item_no, struct leaf items[], 
					int *result, 
						int *ch, int *sc)
{
  int i, j, x, y, cur_x, cur_y, box_x, box_y, key = 0, button = 0, choice = 0,
      l, scroll = 0, max_choice, redraw_menu = FALSE;
  WINDOW *dialog, *menu;

  if (ch)  /* restore menu item info */
      choice = *ch;
  if (sc)
      scroll = *sc;

  max_choice = MIN(menu_height, item_no);

  item_x = 0;
  /* Find length of longest item in order to center menu */
  for (i = 0; i < item_no; i++) {
    l = strlen(items[i].name) + strlen(items[i].branches) * 4 + 4;
    item_x = MAX(item_x, l);
  }
  
  if (height < 0)
	height = strheight(prompt)+menu_height+4+2;
  if (width < 0) {
	i = strwidth(prompt);
	j = ((title != NULL) ? strwidth(title) : 0);
	width = MAX(i,j);
	width = MAX(width,item_x+4)+4;
  }
  width = MAX(width,24);

  if (width > COLS)
	width = COLS;
  if (height > LINES)
	height = LINES;
  /* center dialog box on screen */
  x = (COLS - width)/2;
  y = (LINES - height)/2;

#ifdef HAVE_NCURSES
  if (use_shadow)
    draw_shadow(stdscr, y, x, height, width);
#endif
  dialog = newwin(height, width, y, x);
  if (dialog == NULL) {
    endwin();
    fprintf(stderr, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height,width,y,x);
    exit(1);
  }
  keypad(dialog, TRUE);

  draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
  wattrset(dialog, border_attr);
  wmove(dialog, height-3, 0);
  waddch(dialog, ACS_LTEE);
  for (i = 0; i < width-2; i++)
    waddch(dialog, ACS_HLINE);
  wattrset(dialog, dialog_attr);
  waddch(dialog, ACS_RTEE);
  wmove(dialog, height-2, 1);
  for (i = 0; i < width-2; i++)
    waddch(dialog, ' ');

  if (title != NULL) {
    wattrset(dialog, title_attr);
    wmove(dialog, 0, (width - strlen(title))/2 - 1);
    waddch(dialog, ' ');
    waddstr(dialog, title);
    waddch(dialog, ' ');
  }
  wattrset(dialog, dialog_attr);
  wmove(dialog, 1, 2);
  print_autowrap(dialog, prompt, height-1, width-2, width, 1, 2, TRUE, FALSE);

  menu_width = width-6;
  getyx(dialog, cur_y, cur_x);
  box_y = cur_y + 1;
  box_x = (width - menu_width)/2 - 1;

  /* create new window for the menu */
  menu = subwin(dialog, menu_height, menu_width, y + box_y + 1, x + box_x + 1);
  if (menu == NULL) {
    endwin();
    fprintf(stderr, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", menu_height,menu_width,y+box_y+1,x+box_x+1);
    exit(1);
  }
  keypad(menu, TRUE);

  /* draw a box around the menu items */
  draw_box(dialog, box_y, box_x, menu_height+2, menu_width+2, menubox_border_attr, menubox_attr);

  item_x = 1;

  /* Print the menu */
  for (i = 0; i < max_choice; i++)
    print_item(menu, items[(scroll+i)], i, i == choice);
  wnoutrefresh(menu);
  print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
  print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);

  display_helpline(dialog, height-1, width);

  x = width/2-11;
  y = height-2;
  print_button(dialog, "Cancel", y, x+14, FALSE);
  print_button(dialog, "  OK  ", y, x, TRUE);

  wrefresh(dialog);

  while (key != ESC) {
    key = wgetch(dialog);
    /* Check if key pressed matches first character of any item tag in menu */

    if (key == KEY_UP || key == KEY_DOWN || key == '-' || key == '+') {
     if (key == KEY_UP || key == '-') {
        if (!choice) {
          if (scroll) {
#ifdef BROKEN_WSCRL
    /* wscrl() in ncurses 1.8.1 seems to be broken, causing a segmentation
       violation when scrolling windows of height = 4, so scrolling is not
       used for now */
            scroll--;
            getyx(dialog, cur_y, cur_x);    /* Save cursor position */
            /* Reprint menu to scroll down */
            for (i = 0; i < max_choice; i++)
              print_item(menu, items[(scroll+i)], i, i == choice);

#else

            /* Scroll menu down */
            getyx(dialog, cur_y, cur_x);    /* Save cursor position */
            if (menu_height > 1) {
              /* De-highlight current first item before scrolling down */
              print_item(menu, items[scroll], 0, FALSE);
              scrollok(menu, TRUE);
              wscrl(menu, -1);
              scrollok(menu, FALSE);
            }
            scroll--;
            print_item(menu, items[scroll], 0, TRUE);
#endif
            wnoutrefresh(menu);
	    print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
  	    print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);	    
  	    wmove(dialog, cur_y, cur_x);  /* Restore cursor to previous position */        
            wrefresh(dialog);
          }
          continue;    /* wait for another key press */
        }
        else
          i = choice - 1;
      }
      else if (key == KEY_DOWN || key == '+') {
        if (choice == max_choice - 1) {
          if (scroll+choice < item_no-1) {
#ifdef BROKEN_WSCRL
    /* wscrl() in ncurses 1.8.1 seems to be broken, causing a segmentation
       violation when scrolling windows of height = 4, so scrolling is not
       used for now */
            scroll++;
            getyx(dialog, cur_y, cur_x);    /* Save cursor position */
            /* Reprint menu to scroll up */
            for (i = 0; i < max_choice; i++)
              print_item(menu, items[(scroll+i)], i, i == choice);

#else

            /* Scroll menu up */
            getyx(dialog, cur_y, cur_x);    /* Save cursor position */
            if (menu_height > 1) {
              /* De-highlight current last item before scrolling up */
              print_item(menu, items[(scroll+max_choice-1)], max_choice-1, FALSE);
              scrollok(menu, TRUE);
              scroll(menu);
              scrollok(menu, FALSE);
            }
            scroll++;
              print_item(menu, items[(scroll+max_choice-1)], max_choice-1, TRUE);
#endif
            wnoutrefresh(menu);
	    print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
  	    print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);	    
  	    wmove(dialog, cur_y, cur_x);  /* Restore cursor to previous position */        
            wrefresh(dialog);
          }
          continue;    /* wait for another key press */
        }
        else
          i = choice + 1;
      }

      if (i != choice) {
        /* De-highlight current item */
        getyx(dialog, cur_y, cur_x);    /* Save cursor position */
        print_item(menu, items[(scroll+choice)], choice, FALSE);

        /* Highlight new item */
        choice = i;
        print_item(menu, items[(scroll+choice)], choice, TRUE);
        wnoutrefresh(menu);
        print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);
        wmove(dialog, cur_y, cur_x);  /* Restore cursor to previous position */        
        wrefresh(dialog);
      }
      continue;    /* wait for another key press */
    }

    /* save info about menu item position */
    if (ch)
	*ch = choice;
    if (sc)
	*sc = scroll;

    switch (key) {
    case KEY_PPAGE:
    case 'B' :
    case 'b' :
	if (scroll > menu_height) {	/* can we go up? */
	    scroll -= (menu_height);
	} else {
	    scroll = 0;
	}
	redraw_menu = TRUE;
	break;
    case KEY_NPAGE:
    case 'F' :
    case 'f' :
	if (scroll + menu_height >= item_no-1 - menu_height) { /* can we go down a full page? */
	    scroll = item_no - menu_height;
	    if (scroll < 0) scroll = 0;
	} else {
	    scroll += menu_height;
	}
	redraw_menu = TRUE;
	break;
    case KEY_HOME:
    case 'g' :
	scroll = 0;
	choice = 0;
	redraw_menu = TRUE;
	break;
    case KEY_END:
    case 'G' :
	scroll = item_no - menu_height;
	if (scroll < 0) scroll = 0;
	choice = max_choice - 1;
	redraw_menu = TRUE;
	break;
    case 'O':
    case 'o':
        delwin(dialog);
	*result = scroll+choice;
        return 0;
    case 'C':
    case 'c':
        delwin(dialog);
        return 1;
    case KEY_BTAB:
    case TAB:
    case KEY_LEFT:
    case KEY_RIGHT:
        if (!button) {
          button = 1;    /* Indicates "Cancel" button is selected */
          print_button(dialog, "  OK  ", y, x, FALSE);
          print_button(dialog, "Cancel", y, x+14, TRUE);
        }
        else {
          button = 0;    /* Indicates "OK" button is selected */
          print_button(dialog, "Cancel", y, x+14, FALSE);
          print_button(dialog, "  OK  ", y, x, TRUE);
        }
        wrefresh(dialog);
        break;
    case ' ':
    case '\r':
    case '\n':
        delwin(dialog);
        if (!button)
	  *result = scroll+choice;
        return button;
    case ESC:
        break;
    case KEY_F(1):
    case '?':
	display_helpfile();
	break;
    }
    if (redraw_menu) {
	for (i = 0; i < max_choice; i++) {
	    print_item(menu, items[(scroll+i)],
		       i, i == choice);
	}
	wnoutrefresh(menu);
        getyx(dialog, cur_y, cur_x);    /* Save cursor position */	
	print_arrows(dialog, scroll, menu_height, item_no, box_x, box_y, item_x, cur_x, cur_y);
  	print_position(dialog, box_x+menu_width, box_y+menu_height, scroll+choice, item_no);	
  	wmove(dialog, cur_y, cur_x);  /* Restore cursor to previous position */        
	wrefresh(dialog);
	redraw_menu = FALSE;
    }
  }

  delwin(dialog);
  return -1;    /* ESC pressed */
}
/* End of dialog_treemenu() */


/*
 * Print menu item
 */
static void print_item(WINDOW *win, struct leaf item, int choice, int selected)
{
  int i, j = menu_width - 2;
  char *branches = item.branches;

  /* Clear 'residue' of last item */
  wattrset(win, menubox_attr);
  wmove(win, choice, 0);
  for (i = 0; i < menu_width; i++)
    waddch(win, ' ');
  wmove(win, choice, item_x);

  while(*branches && j)
  {
  	switch (*branches++) {
  	case ' ' : waddch(win, ' ');
  		break;
  	case '|' : waddch(win, ACS_VLINE);
  	}
  	
  	j--;
  	i = 3;
  	while(i-- && j)
  	{
	  	waddch(win, ' ');
	  	j--;
	}
  }	
  
  if (j)
  {
	  switch (item.slip) {
	  case '+' : waddch(win, ACS_LTEE);
  		break;
	  case '`' : waddch(win, ACS_LLCORNER);
	  }
	  j--;
  }

  i = 3;
  while(i-- && j)
  {
  	waddch(win, ACS_HLINE);
  	j--;
  }
  
  wattrset(win, selected ? item_selected_attr : item_attr);
  if (j)
	  waddnstr(win, item.name, j);
}
/* End of print_item() */

/*
 * Print current position
 */
static void print_position(WINDOW *win, int x, int y, 
					int cur_pos, int size)
{
  int percent;

  wattrset(win, position_indicator_attr);
  percent = cur_pos == size - 1 ? 100 : (cur_pos * 100)/(size - 1);
  wmove(win, y + 1, x - 6);
  wprintw(win, "(%3d%%)", percent);
}
/* End of print_position() */

/*
 * Display a tree menu from file
 *
 * filename	- file with like find(1) output
 * FS		- fields separator
 * title	- title of dialog box
 * prompt	- prompt text into dialog box
 * height	- height of dialog box
 * width	- width of dialog box
 * menu_height	- height of menu box
 * result	- pointer to char array
 *
 * return values:
 * -1		- ESC pressed
 * 0		- Ok, result set (must be freed later)
 * 1		- Cancel
 */

int dialog_ftree(unsigned char *filename, unsigned char FS,
		unsigned char *title, unsigned char *prompt, 
			int height, int width, int menu_height, 
					unsigned char **result)
{
	int retcode, choice, size;
	struct leaf *items;
	unsigned char **names;
	
	if (mk_ftree(filename, &names, &size, FS, &items))
	{
		perror("dialog_ftree");
		end_dialog();
		exit(-1);
	}
	
	if (!size)
	{
		fprintf(stderr, "\ndialog_ftree: file %s is empty\n", filename);
		end_dialog();
		exit(-1);
	}
	
	retcode = dialog_treemenu(title, prompt, height, width, menu_height,
					size, items, &choice, NULL, NULL);
					
	free_leafs(items, size);
	
	if (!retcode)
		*result = free_names(names, size, choice);
	else
		(void)free_names(names, size, -1);	
					
	return retcode;
}
/* End of dialog_ftree() */

/*
 * Display a tree menu from array
 *
 * names	- array with like find(1) output
 * size		- size of array
 * FS		- fields separator
 * title	- title of dialog box
 * prompt	- prompt text into dialog box
 * height	- height of dialog box
 * width	- width of dialog box
 * menu_height	- height of menu box
 * result	- pointer to char array
 *
 * return values:
 * -1		- ESC pressed
 * 0		- Ok, result set
 * 1		- Cancel
 */

int dialog_tree(unsigned char **names, int size, unsigned char FS,
		unsigned char *title, unsigned char *prompt, 
			int height, int width, int menu_height, 
					unsigned char **result)
{
	int retcode, choice;
	struct leaf *items;
	struct saved_tree *st;
	static struct queue *q_saved_tree = NULL;
	
	if (!size)
	{
		fprintf(stderr, "\ndialog_tree: source array is empty\n");
		end_dialog();
		exit(-1);
	}	
	
	if (mk_tree(names, size, FS, &items))
	{
		perror("dialog_tree");
		end_dialog();
		exit(-1);
	}

/* is tree saved ? */
	if (!(st = search_saved_tree(q_saved_tree, names, 
					size, FS,
					height, width, menu_height))) {
		if (!q_saved_tree) {
			if (!(q_saved_tree = 
				calloc(sizeof (struct queue), 1))) {
				perror("dialog_tree");
				end_dialog();
				exit(-1);
			}
		}

		if (!(st = calloc(sizeof (struct saved_tree), 1))) {
			perror("dialog_tree");
			end_dialog();
			exit(-1);
		}
		
		st->names = names;
		st->size = size;
		st->FS = FS;
		st->height = height;
		st->width = width;
		st->menu_height = menu_height;
		
		if (!p2_queue(q_saved_tree, st)) {
			perror("dialog_tree");
			end_dialog();
			exit(-1);
		}
	}
	
	retcode = dialog_treemenu(title, prompt, height, width, menu_height,
					size, items, &choice, 
					&(st->ch), &(st->sc));
		
	free_leafs(items, size);
				
	if (!retcode)
		*result = names[choice];
		
	return retcode;
}
/* End of dialog_tree() */

/* utils for ftree */

/* init struct queue by zeros */
static void
init_queue(struct queue *queue)
{
	bzero((void *)queue, sizeof(struct queue));
}

/* add pointer to queue */
/* return - pointer or NULL if error */
static void	*
p2_queue(struct queue *queue, void *pointer)
{
	if (!queue)
		return NULL;

	if (!queue->first)
	{
		if (!(queue->first = queue->last = 
			calloc(1, sizeof(struct m_queue))))
				return NULL;

	}
	else 
	{
	if (!(queue->last->next = 
			calloc(1, sizeof(struct m_queue))))
				return NULL;	
	
	queue->last = queue->last->next;
	}
		
	queue->size++;
	return queue->last->pointer = pointer;		
}

/* get first from queue */
/* return - pointer or NULL if queue is empty */
static void	*
first_queue(struct queue *queue)
{
	void *retval;
	struct m_queue *new_first;
	
	if (!queue ||
		!queue->first ||
			!queue->size)
				return NULL;
	
	retval = queue->first->pointer;
	new_first = queue->first->next;
	free(queue->first);
	queue->first = new_first;
	queue->size--;
	
	return retval;
		
}

/* make zero terminated array from queue */
/* return - pointer to array or NULL if error */
static void	**
q2arr(struct queue *queue, int depth)
{
	void **mono, **end;

	if (!queue ||
		!queue->first ||
			!queue->size)
			return NULL;

	/* memory allocation for array */
	if (!(mono = end = malloc(depth * sizeof(void *) + 1)))
		return NULL;
	
	while(depth--)
	{
		if (!(*end++ = first_queue(queue)))
			break;
	}
	
	*end = NULL;
	
	return mono;
	
}

/*
 * smart_tree (for like find(1) with -d flag output compliance)
 *
 * return values:
 * NULL - malloc error
 * not NULL - ok
 *
 */
static
unsigned char *
smart_tree(struct queue *queue,
		unsigned char FS, 
		unsigned char *current, 
		unsigned char *prev) {
	unsigned char *pcurrent = current, *pprev = prev, *toqueue;
	register char break_flag = 0;
	
	while(*pcurrent && *pprev) {
		if (*pcurrent == *pprev) {
			pcurrent++;
			pprev++;
		}
		else {
			break_flag = 1;
			break;
		}
	}

	if (!*pprev || break_flag) {
		if (*pcurrent == FS) {
			pcurrent++;
			
			if ((!*prev) && (*pcurrent)) {
				unsigned char tchar = *pcurrent;
			
				*pcurrent = '\0';
				if (!(toqueue = strdup(current))) {
					*pcurrent = tchar;
					return NULL;
				}
				if (!p2_queue(queue, toqueue)) {
					*pcurrent = tchar;
					return NULL;
				}
				*pcurrent = tchar;			
			}
		}

		while(*pcurrent) {
			if (*pcurrent == FS) {
				*pcurrent = '\0';
				if (!(toqueue = strdup(current))) {
					*pcurrent = FS;
					return NULL;
				}
				if (!p2_queue(queue, toqueue)) {
					*pcurrent = FS;
					return NULL;
				}
				*pcurrent = FS;
			}
			pcurrent++;
		}
		if (!p2_queue(queue, current))
			return NULL;	
	} 
	return current;
}

/* end of utils for ftree */

/* utils for make tree */

/* if error - return -1 */
static
int
mk_slip(struct leaf array[], int arr_size, int number, int shift)
{
	int t_number;
	int t_shift;
	
	if (number > arr_size - 1)
		return number - 1;
	
	t_shift = shift;
	
	if (!(array[number].branches = calloc(1, t_shift + 1)))
			return -1;
			
	(void)memset(array[number].branches, ' ', t_shift);
	
	t_number = number;
	
	while (array[number].shift < array[t_number + 1].shift)
	{
		t_number = mk_slip(array, arr_size, t_number + 1, t_shift + 1);
		if (t_number < 0) 
				return -1;
		if (t_number == arr_size - 1) 
				break;
	}
	
	if (array[number].shift == array[t_number + 1].shift)
		array[number].slip = '+';
	
	if ((array[number].shift > array[t_number + 1].shift) || 
				t_number == arr_size - 1)
		array[number].slip = '`';
	
	return t_number;

} /* mk_slip() */

/* make tree from file
 *
 * filename	- name of file with like find(1) output
 * p_names	- pointer to array of strings
 * p_size	- pointer to size of array
 * FS		- fields separator
 * p_array	- pointer to array of leafs
 *
 * return values:
 * 0		- ok and names by p_names, size by p_size, array by p_array set
 * -1		- memory allocation error (errno set)
 */  

static
int
mk_ftree(char *filename, 
	unsigned char ***p_names, int *p_size, unsigned char FS, 
		struct leaf **p_array)
{
	int NR;	/* number of input records */	
	struct queue queue;
	unsigned char *string, *sstring = "";
	unsigned char **names;
	
	FILE *input_file;
	
	if (!(input_file = fopen(filename, "r")))
				return -1;

	init_queue(&queue);	
	
	if (!(string = malloc(BUFSIZ)))
			return -1;
	
	/* read input file into queue */
	while(fgets(string, BUFSIZ, input_file))
	{
		if (strchr(string, '\n'))
			*strchr(string, '\n') = '\0';
	
		if (!(string = realloc(string, strlen(string) + 1)))
				return -1;
				
		if (!smart_tree(&queue, FS, string, sstring))
				return -1;
		sstring = string;
				
		if (!(string = malloc(BUFSIZ)))
				return -1;
	} /* read input file into queue */	
	
	if (fclose(input_file) == EOF)
			return -1;
	
	if (!(NR = queue.size))
	{
		*p_size = 0;
		return 0;
	}	

	/* make array from queue */
	if (!(names = (unsigned char **)q2arr(&queue, NR)))
			return -1;
			
	*p_names = names;
	*p_size = NR;
	
	/* make tree from array */
	return mk_tree(names, NR, FS, p_array);
	
} /* mk_ftree */

/* make tree from array
 *
 * names	- array of strings
 * size		- size of array
 * FS		- fields separator
 * p_array	- pointer to array of leafs
 *
 * return values:
 * 0		- ok and array by p_array set
 * -1		- memory allocation error (errno set)
 */  
 
static
int
mk_tree(unsigned char **names, int size, unsigned char FS, 
		struct leaf **p_array)
{
	int i;
	struct leaf *array;

	/* make array of leafs */
	if (!(array = calloc(size, sizeof(struct leaf))))
			return -1;
	
	/* init leafs */
	for (i = 0; i < size; i++)
	{
		unsigned char *in_string, *name;
		int shift = 0;
	
		in_string = name = names[i];
		while(*in_string)
		{
			if (*in_string == FS) {
				if (!i && !*(in_string + 1))
					name = in_string;
				else
				{
					shift++;
					name = in_string + 1;
				}
			}
			in_string++;
		}
		array[i].name = name;
		array[i].shift = shift;
		array[i].slip = '\0';
		array[i].branches = NULL;
	} /* init leafs */
	
	/* make slips */
	for (i = 0;i < size; i++)
	{
		i = mk_slip(array, size, i, 0);
		if (i < 0) 
			return -1;
	} /* make slips */

	/* make branches */
	for (i = 1;i < size; i++)
	{
		unsigned char *src = array[i - 1].branches;
		unsigned char *dst = array[i].branches;
	
		while(*src && *dst)
			*dst++ = *src++;
		
		if (*dst)
			switch (array[i - 1].slip) {
			case '+' : *dst = '|'; 
				break;
			case '`' : *dst = ' ';
			}
	} /* make branches */
	
	*p_array = array;
	return 0;

} /* mk_tree() */

/* free memory from tree (leafs)
 *
 * return values:
 * nothing
 */

static
void
free_leafs(struct leaf *array, int size)
{
	struct leaf *p_array = array;
	
	while (size--)
		free(array++->branches);

	free(p_array);
} /* free_leafs() */

/* free memory from source data for tree (names)
 *
 * return values:
 * if 0 <= choice <= size - pointer to name from names, 
 *	and memory for name not released (must be freed later)
 * else - NULL (recomended choice -1 for it)
 */

static
unsigned char *
free_names(unsigned char **names, int size, int choice)
{
	unsigned char *retval = NULL;
	unsigned char **p_names = names;
	
	while (size--)
	{
		if (!choice--)
			retval = *names++;
		else	
			free(*names++);
	}
	free(p_names);
	return retval;
} /* free_names() */

/* end of utils for make tree */

/* static utils for saved_tree */

/* search saved tree within queue */
/* return - struct *saved_tree or NULL if not found */
static 
struct saved_tree *
search_saved_tree(struct queue *queue, unsigned char **names, int size,
					unsigned char FS,
					int height, int width, 
					int menu_height) 
{
	struct m_queue *member;
	struct saved_tree *retval;
	
	if (!queue || !names || !FS || 
		!height || !width || !menu_height)
		return NULL;
	
	if (!(member = queue->first))
		return NULL;

	while (member->next) {
		retval = member->pointer;
		if ((names == retval->names) &&
			(size == retval->size) &&
			(FS == retval->FS) &&
			(height == retval->height) &&
			(width == retval->width) &&
			(menu_height == retval->menu_height))
			return retval;
		member = member->next;
	}
	retval = member->pointer;
	if ((names == retval->names) &&
		(size == retval->size) &&
		(FS == retval->FS) &&
		(height == retval->height) &&
		(width == retval->width) &&
		(menu_height == retval->menu_height))
		return retval;
	return NULL;	
}

/* end of static utils for saved_tree */

Man Man