Subject: lib/4257: editline: history() has no generic error handling and isn't reentrant
To: None <gnats-bugs@gnats.netbsd.org>
From: Jaromir Dolecek <dolecek@ics.muni.cz>
List: netbsd-bugs
Date: 10/10/1997 17:22:01
>Number:         4257
>Category:       lib
>Synopsis:       editline: history() has no generic error handling and isn't reentrant
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    lib-bug-people (Library Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Fri Oct 10 08:35:01 1997
>Last-Modified:
>Originator:     Jaromir Dolecek
>Organization:
	ICS MU Brno, Czech Republic
>Release:        1.2G
>Environment:
	
System: NetBSD beleg.ics.muni.cz 1.2G NetBSD 1.2G
Architecture: i386

>Description:
	history() returns pointer to HistEvent, which is not null
	if the operation fails for any reason. However it's not possible 
	to find out the reason of error.
	More importantly, the history() returns pointer leading to
	static struct, which is (or can be) modified by next
	history() call - this means, history is not reentrant. This
	is the only point, where the implementation prevents it 
	from beeing reentrant, so it's wise to change it.
	There are still non-reentrant termlib functions, but this
	can change once there would be put some efford to make
	thread-safe term library.

	I've also noticed editline was not able to compile out of box;
	it complains about not existent "help.h". make help.h
	or adding help.h to AHDR in Makefile was enough to solve it.
	However, following patch dosn't solve this.
>How-To-Repeat:
	
>Fix:
	history() parameters were changed to:
		int history(History *, HistEvent *, int, ...)
	and it returns 0 if everything went okay, or -1 in case
	of error.  Structure ev is updated to contain error message
	and (internal) error code in the latter case.

	I haven't moved error code constants to histedit.h; I'm not
	decided if it would be good thing or not, so I let Christos
	to decide this.

	The only problem is, apps using editline need to be updated;
	I'm sending patches for ftp(1), sh(1) and fsdb(8) in separate
	PRs.

	Text added to manpage needs to be checked by some native
	speaker; my english isn't very good, sorry ;-b

diff -c -r ../libedit_old/editline.3 ./editline.3
*** ../libedit_old/editline.3	Tue Oct  7 10:24:09 1997
--- ./editline.3	Thu Oct  9 18:24:17 1997
***************
*** 87,94 ****
  .Fn history_init
  .Ft void
  .Fn history_end "History *h"
! .Ft HistEvent *
! .Fn history "History h" "int op" "..."
  .Sh DESCRIPTION
  The
  .Nm
--- 87,94 ----
  .Fn history_init
  .Ft void
  .Fn history_end "History *h"
! .Ft int
! .Fn history "History h" "HistEvent *ev" "int op" "..."
  .Sh DESCRIPTION
  The
  .Nm
***************
*** 434,447 ****
  .Fa op
  on the history list, with optional arguments as needed by the
  operation.
  The following values for
  .Fa op
  are supported, along with the required argument list:
  .Bl -tag -width 4n
! .It Dv H_EVENT , Fa "int size"
  Set size of history to
  .Fa size
  elements.
  .It Dv H_END
  Cleans up and finishes with
  .Fa h ,
--- 434,451 ----
  .Fa op
  on the history list, with optional arguments as needed by the
  operation.
+ .Fa ev
+ is changed accordingly to operation.
  The following values for
  .Fa op
  are supported, along with the required argument list:
  .Bl -tag -width 4n
! .It Dv H_SETMAXSIZE , Fa "int size"
  Set size of history to
  .Fa size
  elements.
+ .It Dv H_GETSIZE
+ Get number of events currently in history.
  .It Dv H_END
  Cleans up and finishes with
  .Fa h ,
***************
*** 506,511 ****
--- 510,522 ----
  .El
  .\"XXX.Sh EXAMPLES
  .\"XXX: provide some examples
+ .Sh RETURN VALUES
+ .Ft history
+ function returns 0 if the operation
+ .Ft op
+ succeeds, otherwise -1 is returned and
+ .Ft ev
+ is updated to contain more detailes about error.
  .Sh SEE ALSO
  .Xr editrc 5 ,
  .Xr sh 1 ,
diff -c -r ../libedit_old/hist.c ./hist.c
*** ../libedit_old/hist.c	Tue Oct  7 10:24:08 1997
--- ./hist.c	Thu Oct  9 18:17:17 1997
***************
*** 172,177 ****
      if (el->el_history.ref == NULL)
  	return -1;
      for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el))
! 	(void) fprintf(el->el_outfile, "%d %s", el->el_history.ev->num, str);
      return 0;
  }
--- 172,177 ----
      if (el->el_history.ref == NULL)
  	return -1;
      for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el))
! 	(void) fprintf(el->el_outfile, "%d %s", el->el_history.ev.num, str);
      return 0;
  }
diff -c -r ../libedit_old/hist.h ./hist.h
*** ../libedit_old/hist.h	Tue Oct  7 10:24:08 1997
--- ./hist.h	Thu Oct  9 18:17:03 1997
***************
*** 46,52 ****
  
  #include "histedit.h"
  
! typedef const HistEvent *	(*hist_fun_t) __P((ptr_t, int, ...));
  
  typedef struct el_history_t {
      char *buf;				/* The history buffer		*/
--- 46,52 ----
  
  #include "histedit.h"
  
! typedef int (*hist_fun_t) __P((ptr_t, HistEvent *, int, ...));
  
  typedef struct el_history_t {
      char *buf;				/* The history buffer		*/
***************
*** 54,66 ****
      int eventno;			/* Event we are looking for	*/
      ptr_t ref;				/* Argument for history fcns	*/
      hist_fun_t fun;			/* Event access			*/
!     const HistEvent *ev;		/* Event cookie			*/
  } el_history_t;
  
  #define HIST_FUN(el, fn, arg)	\
!     ((((el)->el_history.ev = \
!        (*(el)->el_history.fun)((el)->el_history.ref, fn, arg)) == NULL) ? \
!      NULL : (el)->el_history.ev->str)
  
  #define	HIST_NEXT(el)		HIST_FUN(el, H_NEXT, NULL)
  #define	HIST_FIRST(el)		HIST_FUN(el, H_FIRST, NULL)
--- 54,65 ----
      int eventno;			/* Event we are looking for	*/
      ptr_t ref;				/* Argument for history fcns	*/
      hist_fun_t fun;			/* Event access			*/
!     HistEvent ev;			/* Event cookie			*/
  } el_history_t;
  
  #define HIST_FUN(el, fn, arg)	\
!     ((( (*(el)->el_history.fun) ( (el)->el_history.ref, &(el)->el_history.ev,\
! 	fn, arg)) == -1) ? NULL : (el)->el_history.ev.str)
  
  #define	HIST_NEXT(el)		HIST_FUN(el, H_NEXT, NULL)
  #define	HIST_FIRST(el)		HIST_FUN(el, H_FIRST, NULL)
diff -c -r ../libedit_old/histedit.h ./histedit.h
*** ../libedit_old/histedit.h	Tue Oct  7 10:24:08 1997
--- ./histedit.h	Thu Oct  9 17:15:40 1997
***************
*** 145,178 ****
  typedef struct history History;
  
  typedef struct HistEvent {
!     int 	  num;
!     const char *str;
  } HistEvent;
  
  /*
   * History access functions.
   */
! History *		history_init	__P((void));
! void 			history_end	__P((History *));
  
! const HistEvent *	history		__P((History *, int, ...));
  
  #define H_FUNC		 0	/* , UTSL		*/
! #define H_EVENT		 1	/* , const int);	*/
! #define H_FIRST		 2	/* , void);		*/
! #define H_LAST		 3	/* , void);		*/
! #define H_PREV		 4	/* , void);		*/
! #define H_NEXT		 5	/* , void);		*/
! #define H_CURR		 6	/* , void);		*/
! #define H_ADD		 7	/* , const char*);	*/
! #define H_ENTER		 8	/* , const char*);	*/
! #define H_END		 9	/* , void);		*/
! #define H_NEXT_STR	10	/* , const char*);	*/
! #define H_PREV_STR	11	/* , const char*);	*/
! #define H_NEXT_EVENT	12	/* , const int);	*/
! #define H_PREV_EVENT	13	/* , const int);	*/
! #define H_LOAD		14	/* , const char *);	*/
! #define H_SAVE		15	/* , const char *);	*/
! #define H_CLEAR		16	/* , void);		*/
  
  #endif /* _h_editline */
--- 145,179 ----
  typedef struct history History;
  
  typedef struct HistEvent {
!     int 	 num;
!     const char	*str;
  } HistEvent;
  
  /*
   * History access functions.
   */
! History *	history_init	__P((void));
! void 		history_end	__P((History *));
  
! int 		history		__P((History *, HistEvent *, int, ...));
  
  #define H_FUNC		 0	/* , UTSL		*/
! #define H_SETMAXSIZE	 1	/* , const int);	*/
! #define H_GETSIZE	 2	/* , void);		*/
! #define H_FIRST		 3	/* , void);		*/
! #define H_LAST		 4	/* , void);		*/
! #define H_PREV		 5	/* , void);		*/
! #define H_NEXT		 6	/* , void);		*/
! #define H_CURR		 7	/* , void);		*/
! #define H_ADD		 8	/* , const char*);	*/
! #define H_ENTER		 9	/* , const char*);	*/
! #define H_END		10	/* , void);		*/
! #define H_NEXT_STR	11	/* , const char*);	*/
! #define H_PREV_STR	12	/* , const char*);	*/
! #define H_NEXT_EVENT	13	/* , const int);	*/
! #define H_PREV_EVENT	14	/* , const int);	*/
! #define H_LOAD		15	/* , const char *);	*/
! #define H_SAVE		16	/* , const char *);	*/
! #define H_CLEAR		17	/* , void);		*/
  
  #endif /* _h_editline */
diff -c -r ../libedit_old/history.c ./history.c
*** ../libedit_old/history.c	Tue Oct  7 10:24:08 1997
--- ./history.c	Thu Oct  9 18:43:44 1997
***************
*** 62,70 ****
  
  #include "histedit.h"
  
! typedef const HistEvent *	(*history_gfun_t) __P((ptr_t));
! typedef const HistEvent *	(*history_efun_t) __P((ptr_t, const char *));
! typedef void 			(*history_vfun_t) __P((ptr_t));
  
  struct history {
      ptr_t	   h_ref;		/* Argument for history fcns	*/
--- 62,70 ----
  
  #include "histedit.h"
  
! typedef int	(*history_gfun_t) __P((ptr_t, HistEvent *));
! typedef int	(*history_efun_t) __P((ptr_t, HistEvent *, const char *));
! typedef void 	(*history_vfun_t) __P((ptr_t, HistEvent *));
  
  struct history {
      ptr_t	   h_ref;		/* Argument for history fcns	*/
***************
*** 78,104 ****
      history_efun_t h_add;		/* Append to an element		*/
  };
  
! #define	HNEXT(h)  	(*(h)->h_next)((h)->h_ref)
! #define	HFIRST(h) 	(*(h)->h_first)((h)->h_ref)
! #define	HPREV(h)  	(*(h)->h_prev)((h)->h_ref)
! #define	HLAST(h) 	(*(h)->h_last)((h)->h_ref)
! #define	HCURR(h) 	(*(h)->h_curr)((h)->h_ref)
! #define	HCLEAR(h) 	(*(h)->h_clear)((h)->h_ref)
! #define	HENTER(h, str)	(*(h)->h_enter)((h)->h_ref, str)
! #define	HADD(h, str)	(*(h)->h_add)((h)->h_ref, str)
  
  #define h_malloc(a)	malloc(a)
  #define h_free(a)	free(a)
  
  
! private int		 history_set_num	__P((History *, int));
! private int		 history_set_fun	__P((History *, History *));
! private int 		 history_load		__P((History *, const char *));
! private int 		 history_save		__P((History *, const char *));
! private const HistEvent *history_prev_event	__P((History *, int));
! private const HistEvent *history_next_event	__P((History *, int));
! private const HistEvent *history_next_string	__P((History *, const char *));
! private const HistEvent *history_prev_string	__P((History *, const char *));
  
  
  /***********************************************************************/
--- 78,105 ----
      history_efun_t h_add;		/* Append to an element		*/
  };
  
! #define	HNEXT(h, ev)  	(*(h)->h_next)((h)->h_ref, ev)
! #define	HFIRST(h, ev) 	(*(h)->h_first)((h)->h_ref, ev)
! #define	HPREV(h, ev)  	(*(h)->h_prev)((h)->h_ref, ev)
! #define	HLAST(h, ev) 	(*(h)->h_last)((h)->h_ref, ev)
! #define	HCURR(h, ev) 	(*(h)->h_curr)((h)->h_ref, ev)
! #define	HCLEAR(h, ev) 	(*(h)->h_clear)((h)->h_ref, ev)
! #define	HENTER(h, ev, str)	(*(h)->h_enter)((h)->h_ref, ev, str)
! #define	HADD(h, ev, str)	(*(h)->h_add)((h)->h_ref, ev, str)
  
  #define h_malloc(a)	malloc(a)
  #define h_free(a)	free(a)
  
  
! private int	history_set_num		__P((History *, HistEvent *, int));
! private int	history_get_size	__P((History *, HistEvent *));
! private int	history_set_fun		__P((History *, History *));
! private int 	history_load		__P((History *, const char *));
! private int 	history_save		__P((History *, const char *));
! private int	history_prev_event	__P((History *, HistEvent *, int));
! private int	history_next_event	__P((History *, HistEvent *, int));
! private int	history_next_string	__P((History *, HistEvent *, const char *));
! private int	history_prev_string	__P((History *, HistEvent *, const char *));
  
  
  /***********************************************************************/
***************
*** 117,233 ****
      hentry_t *cursor;		/* Current element in the list	*/
      int	max;			/* Maximum number of events	*/
      int cur;			/* Current number of events	*/
!     int	eventno;		/* Current event number		*/
  } history_t;
  
! private const HistEvent *history_def_first  __P((ptr_t));
! private const HistEvent *history_def_last   __P((ptr_t));
! private const HistEvent *history_def_next   __P((ptr_t));
! private const HistEvent *history_def_prev   __P((ptr_t));
! private const HistEvent *history_def_curr   __P((ptr_t));
! private const HistEvent *history_def_enter  __P((ptr_t, const char *));
! private const HistEvent *history_def_add    __P((ptr_t, const char *));
! private void             history_def_init   __P((ptr_t *, int));
! private void             history_def_clear  __P((ptr_t));
! private const HistEvent *history_def_insert __P((history_t *, const char *));
! private void             history_def_delete __P((history_t *, hentry_t *));
  
  #define history_def_set(p, num)	(void) (((history_t *) p)->max = (num))
  
  
  /* history_def_first():
   *	Default function to return the first event in the history.
   */
! private const HistEvent * 
! history_def_first(p)
      ptr_t p;
  {
      history_t *h = (history_t *) p;
      h->cursor = h->list.next;
      if (h->cursor != &h->list)
! 	return &h->cursor->ev;
!     else
! 	return NULL;
  }
  
  /* history_def_last():
   *	Default function to return the last event in the history.
   */
! private const HistEvent * 
! history_def_last(p)
      ptr_t p;
  {
      history_t *h = (history_t *) p;
      h->cursor = h->list.prev;
      if (h->cursor != &h->list)
! 	return &h->cursor->ev;
!     else
! 	return NULL;
  }
  
  /* history_def_next():
   *	Default function to return the next event in the history.
   */
! private const HistEvent * 
! history_def_next(p)
      ptr_t p;
  {
      history_t *h = (history_t *) p;
  
      if (h->cursor != &h->list)
  	h->cursor = h->cursor->next;
!     else
! 	return NULL;
  
      if (h->cursor != &h->list)
! 	return &h->cursor->ev;
!     else
! 	return NULL;
  }
  
  
  /* history_def_prev():
   *	Default function to return the previous event in the history.
   */
! private const HistEvent * 
! history_def_prev(p)
      ptr_t p;
  {
      history_t *h = (history_t *) p;
  
      if (h->cursor != &h->list)
  	h->cursor = h->cursor->prev;
!     else
! 	return NULL;
  
      if (h->cursor != &h->list)
! 	return &h->cursor->ev;
!     else
! 	return NULL;
  }
  
  
  /* history_def_curr():
   *	Default function to return the current event in the history.
   */
! private const HistEvent * 
! history_def_curr(p)
      ptr_t p;
  {
      history_t *h = (history_t *) p;
  
      if (h->cursor != &h->list)
! 	return &h->cursor->ev;
!     else
! 	return NULL;
  }
  
  /* history_def_add():
   *	Append string to element
   */
! private const HistEvent *
! history_def_add(p, str)
      ptr_t p;
      const char *str;
  {
      history_t *h = (history_t *) p;
--- 118,322 ----
      hentry_t *cursor;		/* Current element in the list	*/
      int	max;			/* Maximum number of events	*/
      int cur;			/* Current number of events	*/
!     int	eventid;		/* For generation of unique event id	*/ 
  } history_t;
  
! private int	history_def_first  __P((ptr_t, HistEvent *));
! private int	history_def_last   __P((ptr_t, HistEvent *));
! private int	history_def_next   __P((ptr_t, HistEvent *));
! private int	history_def_prev   __P((ptr_t, HistEvent *));
! private int	history_def_curr   __P((ptr_t, HistEvent *));
! private int	history_def_enter  __P((ptr_t, HistEvent *, const char *));
! private int	history_def_add    __P((ptr_t, HistEvent *, const char *));
! private void	history_def_init   __P((ptr_t *, HistEvent *, int));
! private void	history_def_clear  __P((ptr_t, HistEvent *));
! private int	history_def_insert __P((history_t *, HistEvent *,const char *));
! private void	history_def_delete __P((history_t *, HistEvent *, hentry_t *));
! private int	history_def_size   __P((history_t *, HistEvent *));
  
  #define history_def_set(p, num)	(void) (((history_t *) p)->max = (num))
+ #define history_def_getsize(p)  (((history_t *) p)->cur)
  
+ #define he_strerror(code)	err_msg[code]
+ #define he_seterrev(evp, code)	{\
+ 				    evp->num = code;\
+ 				    evp->str = he_strerror(code);\
+ 				}
+ 					
+ /* error messages */
+ static const char *err_msg[] = {
+ 	"OK",
+ 	"malloc() failed",
+ 	"first event not found",
+ 	"last event not found",
+ 	"empty list",
+ 	"no next event",
+ 	"no previous event",
+ 	"current event is invalid",
+ 	"event not found",
+ 	"can't read history from file",
+ 	"can't write history",
+ 	"required parameter(s) not supplied",
+ 	"history size negative",
+ 	"function not allowed with other history-functions-set tha default",
+ 	"bad parameters",
+ };
+ 
+ /* error codes */
+ #define _HE_OK                   0
+ #define _HE_UNKNOWN		 1
+ #define _HE_MALLOC_FAILED        2
+ #define _HE_FIRST_NOTFOUND       3
+ #define _HE_LAST_NOTFOUND        4
+ #define _HE_EMPTY_LIST           5
+ #define _HE_END_REACHED          6
+ #define _HE_START_REACHED	 7
+ #define _HE_CURR_INVALID	 8
+ #define _HE_NOT_FOUND		 9
+ #define _HE_HIST_READ		10
+ #define _HE_HIST_WRITE		11
+ #define _HE_PARAM_MISSING	12
+ #define _HE_SIZE_NEGATIVE	13
+ #define _HE_NOT_ALLOWED		14
+ #define _HE_BAD_PARAM		15
+ 
+ /* he_cpy():
+  *	copy contents of one HistEvent to another
+  */
+ private int
+ he_cpy(tev, sev)
+     HistEvent *tev;
+     const HistEvent *sev;
+ {
+     tev->num = sev->num;
+     tev->str = sev->str;
+ }
  
  /* history_def_first():
   *	Default function to return the first event in the history.
   */
! private int
! history_def_first(p, ev)
      ptr_t p;
+     HistEvent *ev;
  {
      history_t *h = (history_t *) p;
+ 
      h->cursor = h->list.next;
      if (h->cursor != &h->list)
! 	he_cpy(ev, &h->cursor->ev);
!     else {
! 	he_seterrev(ev, _HE_FIRST_NOTFOUND);
! 	return -1;
!     }
! 
!     return 0;
  }
  
  /* history_def_last():
   *	Default function to return the last event in the history.
   */
! private int
! history_def_last(p, ev)
      ptr_t p;
+     HistEvent *ev;
  {
      history_t *h = (history_t *) p;
+ 
      h->cursor = h->list.prev;
      if (h->cursor != &h->list)
! 	he_cpy(ev, &h->cursor->ev);
!     else {
! 	he_seterrev(ev, _HE_LAST_NOTFOUND);
! 	return -1;
!     }
! 
!     return 0;
  }
  
  /* history_def_next():
   *	Default function to return the next event in the history.
   */
! private int
! history_def_next(p, ev)
      ptr_t p;
+     HistEvent *ev;
  {
      history_t *h = (history_t *) p;
  
      if (h->cursor != &h->list)
  	h->cursor = h->cursor->next;
!     else {
! 	he_seterrev(ev, _HE_EMPTY_LIST);
! 	return -1;
!     }
  
      if (h->cursor != &h->list)
! 	he_cpy(ev, &h->cursor->ev);
!     else {
! 	he_seterrev(ev, _HE_END_REACHED);
! 	return -1;
!     }
! 
!     return 0;
  }
  
  
  /* history_def_prev():
   *	Default function to return the previous event in the history.
   */
! private int
! history_def_prev(p, ev)
      ptr_t p;
+    HistEvent *ev;
  {
      history_t *h = (history_t *) p;
  
      if (h->cursor != &h->list)
  	h->cursor = h->cursor->prev;
!     else {
! 	he_seterrev(ev, (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
! 	return -1;
!    }
  
      if (h->cursor != &h->list)
! 	he_cpy(ev, &h->cursor->ev);
!     else {
! 	he_seterrev(ev, _HE_START_REACHED);
! 	return -1;
!     }
! 
!     return 0;
  }
  
  
  /* history_def_curr():
   *	Default function to return the current event in the history.
   */
! private int
! history_def_curr(p, ev)
      ptr_t p;
+    HistEvent *ev;
  {
      history_t *h = (history_t *) p;
  
      if (h->cursor != &h->list)
! 	he_cpy(ev, &h->cursor->ev);
!     else {
! 	he_seterrev(ev, (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
! 	return -1;
!    }
! 
!     return 0;
  }
  
  /* history_def_add():
   *	Append string to element
   */
! private int
! history_def_add(p, ev, str)
      ptr_t p;
+     HistEvent *ev;
      const char *str;
  {
      history_t *h = (history_t *) p;
***************
*** 235,248 ****
      char *s;
  
      if (h->cursor == &h->list)
! 	return (history_def_enter(p, str));
      len = strlen(h->cursor->ev.str) + strlen(str) + 1;
      s = (char *) h_malloc(len);
!     (void)strcpy(s, h->cursor->ev.str);	/* XXX strcpy is safe */
      (void)strcat(s, str);			/* XXX strcat is safe */
      h_free((ptr_t) h->cursor->ev.str);
      h->cursor->ev.str = s;
!     return &h->cursor->ev;
  }
  
  
--- 324,342 ----
      char *s;
  
      if (h->cursor == &h->list)
! 	return (history_def_enter(p, ev, str));
      len = strlen(h->cursor->ev.str) + strlen(str) + 1;
      s = (char *) h_malloc(len);
!     if (!s) {
! 	he_seterrev(ev, _HE_MALLOC_FAILED);
! 	return -1;
!     }
!     (void)strcpy(s, h->cursor->ev.str);		/* XXX strcpy is safe */
      (void)strcat(s, str);			/* XXX strcat is safe */
      h_free((ptr_t) h->cursor->ev.str);
      h->cursor->ev.str = s;
!     he_cpy(ev, &h->cursor->ev);
!     return 0;
  }
  
  
***************
*** 250,257 ****
   *	Delete element hp of the h list
   */
  private void
! history_def_delete(h, hp)
      history_t *h;
      hentry_t *hp;
  {
      if (hp == &h->list)
--- 344,352 ----
   *	Delete element hp of the h list
   */
  private void
! history_def_delete(h, ev, hp)
      history_t *h;
+     HistEvent *ev;
      hentry_t *hp;
  {
      if (hp == &h->list)
***************
*** 267,311 ****
  /* history_def_insert():
   *	Insert element with string str in the h list
   */
! private const HistEvent *
! history_def_insert(h, str)
      history_t *h;
      const char *str;
  {
      h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
!     h->cursor->ev.str = strdup(str);
      h->cursor->next = h->list.next;
      h->cursor->prev = &h->list;
      h->list.next->prev = h->cursor;
      h->list.next = h->cursor;
      h->cur++;
  
!     return &h->cursor->ev;
  }
  
  
  /* history_def_enter():
   *	Default function to enter an item in the history
   */
! private const HistEvent *
! history_def_enter(p, str)
      ptr_t p;
      const char *str;
  {
      history_t *h = (history_t *) p;
-     const HistEvent *ev;
- 
  
!     ev = history_def_insert(h, str);
!     ((HistEvent*) ev)->num = ++h->eventno;
  
      /*
       * Always keep at least one entry.
       * This way we don't have to check for the empty list.
       */
!     while (h->cur > h->max + 1) 
! 	history_def_delete(h, h->list.prev);
!     return ev;
  }
  
  
--- 362,414 ----
  /* history_def_insert():
   *	Insert element with string str in the h list
   */
! private int
! history_def_insert(h, ev, str)
      history_t *h;
+     HistEvent *ev;
      const char *str;
  {
      h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
!     if (h->cursor)
!     	h->cursor->ev.str = strdup(str);
!     if (!h->cursor || !h->cursor->ev.str) {
! 	he_seterrev(ev, _HE_MALLOC_FAILED);
! 	return -1;
!     }
!     h->cursor->ev.num = ++h->eventid;
      h->cursor->next = h->list.next;
      h->cursor->prev = &h->list;
      h->list.next->prev = h->cursor;
      h->list.next = h->cursor;
      h->cur++;
  
!     he_cpy(ev, &h->cursor->ev);
!     return 0;
  }
  
  
  /* history_def_enter():
   *	Default function to enter an item in the history
   */
! private int
! history_def_enter(p, ev, str)
      ptr_t p;
+     HistEvent *ev;
      const char *str;
  {
      history_t *h = (history_t *) p;
  
!     if (history_def_insert(h, ev, str) == -1)
! 	return -1; /* error, keep error message */
  
      /*
       * Always keep at least one entry.
       * This way we don't have to check for the empty list.
       */
!     while (h->cur - 1 > h->max) 
! 	history_def_delete(h, ev, h->list.prev);
! 
!     return 0;
  }
  
  
***************
*** 313,326 ****
   *	Default history initialization function
   */
  private void
! history_def_init(p, n)
      ptr_t *p;
      int n;
  {
      history_t *h = (history_t *) h_malloc(sizeof(history_t));
      if (n <= 0)
  	n = 0;
!     h->eventno = 0;
      h->cur = 0;
      h->max = n;
      h->list.next = h->list.prev = &h->list;
--- 416,430 ----
   *	Default history initialization function
   */
  private void
! history_def_init(p, ev, n)
      ptr_t *p;
+     HistEvent *ev;
      int n;
  {
      history_t *h = (history_t *) h_malloc(sizeof(history_t));
      if (n <= 0)
  	n = 0;
!     h->eventid = 0;
      h->cur = 0;
      h->max = n;
      h->list.next = h->list.prev = &h->list;
***************
*** 335,348 ****
   *	Default history cleanup function
   */
  private void
! history_def_clear(p)
      ptr_t p;
  {
      history_t *h = (history_t *) p;
  
      while (h->list.prev != &h->list)
! 	history_def_delete(h, h->list.prev);
!     h->eventno = 0;
      h->cur = 0;
  }
  
--- 439,453 ----
   *	Default history cleanup function
   */
  private void
! history_def_clear(p, ev)
      ptr_t p;
+     HistEvent *ev;
  {
      history_t *h = (history_t *) p;
  
      while (h->list.prev != &h->list)
! 	history_def_delete(h, ev, h->list.prev);
!     h->eventid = 0;
      h->cur = 0;
  }
  
***************
*** 358,365 ****
  history_init()
  {
      History *h = (History *) h_malloc(sizeof(History));
  
!     history_def_init(&h->h_ref, 0);
  
      h->h_next  = history_def_next;
      h->h_first = history_def_first;
--- 463,471 ----
  history_init()
  {
      History *h = (History *) h_malloc(sizeof(History));
+     HistEvent ev;
  
!     history_def_init(&h->h_ref, &ev, 0);
  
      h->h_next  = history_def_next;
      h->h_first = history_def_first;
***************
*** 381,388 ****
  history_end(h)
      History *h;
  {
      if (h->h_next == history_def_next)
! 	history_def_clear(h->h_ref);
  }
  
  
--- 487,495 ----
  history_end(h)
      History *h;
  {
+     HistEvent ev;
      if (h->h_next == history_def_next)
! 	history_def_clear(h->h_ref, &ev);
  }
  
  
***************
*** 391,420 ****
   *	Set history number of events
   */
  private int
! history_set_num(h, num)
      History *h;
      int num;
  {
!     if (h->h_next != history_def_next || num < 0)
  	return -1;
      history_def_set(h->h_ref, num);
      return 0;
  }
  
  
  /* history_set_fun():
   *	Set history functions
   */
  private int
  history_set_fun(h, nh)
!     History *h, *nh;
  {
      if (nh->h_first == NULL || nh->h_next == NULL ||
          nh->h_last == NULL  || nh->h_prev == NULL || nh->h_curr == NULL ||
  	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
  	nh->h_ref == NULL) {
  	if (h->h_next != history_def_next) {
! 	    history_def_init(&h->h_ref, 0);
  	    h->h_first = history_def_first;
  	    h->h_next  = history_def_next;
  	    h->h_last  = history_def_last;
--- 498,562 ----
   *	Set history number of events
   */
  private int
! history_set_num(h, ev, num)
      History *h;
+     HistEvent *ev;
      int num;
  {
!     if (h->h_next != history_def_next) {
! 	he_seterrev(ev, _HE_NOT_ALLOWED);
! 	return -1;
!     }
! 
!     if (num < 0) {
! 	he_seterrev(ev, _HE_BAD_PARAM);
  	return -1;
+     }
+ 
      history_def_set(h->h_ref, num);
      return 0;
  }
  
+ /* history_get_size():
+  *      Get number of events currently in history
+  */
+ private int
+ history_get_size(h, ev)
+     History *h;
+     HistEvent *ev;
+ {
+     int retval=0;
+  
+     if (h->h_next != history_def_next) {
+ 	he_seterrev(ev, _HE_NOT_ALLOWED);
+ 	return -1;
+     }
+     retval = history_def_getsize(h->h_ref);
+     if (retval < -1) {
+ 	he_seterrev(ev, _HE_SIZE_NEGATIVE);
+ 	return -1;
+     }
+ 
+     ev->num = retval;
+     return 0;
+ }
  
  /* history_set_fun():
   *	Set history functions
   */
  private int
  history_set_fun(h, nh)
!     History *h;
!     History *nh;
  {
+     HistEvent ev;
+ 
      if (nh->h_first == NULL || nh->h_next == NULL ||
          nh->h_last == NULL  || nh->h_prev == NULL || nh->h_curr == NULL ||
  	nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
  	nh->h_ref == NULL) {
  	if (h->h_next != history_def_next) {
! 	    history_def_init(&h->h_ref, &ev, 0);
  	    h->h_first = history_def_first;
  	    h->h_next  = history_def_next;
  	    h->h_last  = history_def_last;
***************
*** 428,434 ****
      }
  
      if (h->h_next == history_def_next)
! 	history_def_clear(h->h_ref);
  
      h->h_first = nh->h_first;
      h->h_next  = nh->h_next;
--- 570,576 ----
      }
  
      if (h->h_next == history_def_next)
! 	history_def_clear(h->h_ref, &ev);
  
      h->h_first = nh->h_first;
      h->h_next  = nh->h_next;
***************
*** 455,460 ****
--- 597,603 ----
      char *line;
      size_t sz;
      int i = -1;
+     HistEvent ev;
  
      if ((fp = fopen(fname, "r")) == NULL)
  	return i;
***************
*** 468,474 ****
      for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
  	char c = line[sz];
  	line[sz] = '\0';
! 	HENTER(h, line);
  	line[sz] = c;
      }
  
--- 611,617 ----
      for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
  	char c = line[sz];
  	line[sz] = '\0';
! 	HENTER(h, &ev, line);
  	line[sz] = c;
      }
  
***************
*** 487,501 ****
      const char *fname;
  {
      FILE *fp;
!     const HistEvent *ev;
!     int i = 0;
  
      if ((fp = fopen(fname, "w")) == NULL)
  	return -1;
  
      (void) fputs(hist_cookie, fp);
!     for (ev = HLAST(h); ev != NULL; ev = HPREV(h), i++)
! 	(void) fprintf(fp, "%s", ev->str);
      (void) fclose(fp);
      return i;
  }
--- 630,644 ----
      const char *fname;
  {
      FILE *fp;
!     HistEvent ev;
!     int i = 0, retval;
  
      if ((fp = fopen(fname, "w")) == NULL)
  	return -1;
  
      (void) fputs(hist_cookie, fp);
!     for (retval = HLAST(h, &ev); retval != -1; retval = HPREV(h, &ev), i++)
! 	(void) fprintf(fp, "%s", ev.str);
      (void) fclose(fp);
      return i;
  }
***************
*** 504,534 ****
  /* history_prev_event():
   *	Find the previous event, with number given
   */
! private const HistEvent *
! history_prev_event(h, num)
      History *h;
      int num;
  {
!     const HistEvent *ev;
!     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
  	if (ev->num == num)
! 	    return ev;
!     return NULL;
  }
  
  
  /* history_next_event():
   *	Find the next event, with number given
   */
! private const HistEvent *
! history_next_event(h, num)
      History *h;
      int num;
  {
!     const HistEvent *ev;
!     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
  	if (ev->num == num)
! 	    return ev;
      return NULL;
  }
  
--- 647,683 ----
  /* history_prev_event():
   *	Find the previous event, with number given
   */
! private int
! history_prev_event(h, ev, num)
      History *h;
+     HistEvent *ev;
      int num;
  {
!     int retval;
!     for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
  	if (ev->num == num)
! 	    return 0;
! 
!     he_seterrev(ev, _HE_NOT_FOUND);
!     return -1;
  }
  
  
  /* history_next_event():
   *	Find the next event, with number given
   */
! private int
! history_next_event(h, ev, num)
      History *h;
+     HistEvent *ev;
      int num;
  {
!     int retval;
!     for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
  	if (ev->num == num)
! 	    return 0;
! 
!     he_seterrev(ev, _HE_NOT_FOUND);
      return NULL;
  }
  
***************
*** 536,553 ****
  /* history_prev_string():
   *	Find the previous event beginning with string
   */
! private const HistEvent *
! history_prev_string(h, str)
      History *h;
      const char* str;
  {
-     const HistEvent *ev;
      size_t len = strlen(str);
  
!     for (ev = HCURR(h); ev != NULL; ev = HNEXT(h))
  	if (strncmp(str, ev->str, len) == 0)
! 	    return ev;
!     return NULL;
  }
  
  
--- 685,705 ----
  /* history_prev_string():
   *	Find the previous event beginning with string
   */
! private int
! history_prev_string(h, ev, str)
      History *h;
+     HistEvent *ev;
      const char* str;
  {
      size_t len = strlen(str);
+     int retval;
  
!     for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
  	if (strncmp(str, ev->str, len) == 0)
! 	    return 0;
! 
!     he_seterrev(ev, _HE_NOT_FOUND);
!     return -1;
  }
  
  
***************
*** 556,668 ****
  /* history_next_string():
   *	Find the next event beginning with string
   */
! private const HistEvent *
! history_next_string(h, str)
      History *h;
      const char* str;
  {
-     const HistEvent *ev;
      size_t len = strlen(str);
  
!     for (ev = HCURR(h); ev != NULL; ev = HPREV(h))
  	if (strncmp(str, ev->str, len) == 0)
! 	    return ev;
!     return NULL;
  }
  
  
  /* history():
   *	User interface to history functions.
   */
! const HistEvent *
  #ifdef __STDC__
! history(History *h, int fun, ...)
  #else
  history(va_alist)
      va_dcl
  #endif
  {
      va_list va;
-     const HistEvent *ev = NULL;
      const char *str;
!     static HistEvent sev = { 0, "" };
  
  #ifdef __STDC__
      va_start(va, fun);
  #else
      History *h; 
      int fun;
      va_start(va);
      h = va_arg(va, History *);
      fun = va_arg(va, int);
  #endif
  
      switch (fun) {
      case H_ADD:
  	str = va_arg(va, const char *);
! 	ev = HADD(h, str);
  	break;
  
      case H_ENTER:
  	str = va_arg(va, const char *);
! 	ev = HENTER(h, str);
  	break;
  
      case H_FIRST:
! 	ev = HFIRST(h);
  	break;
  
      case H_NEXT:
! 	ev = HNEXT(h);
  	break;
  
      case H_LAST:
! 	ev = HLAST(h);
  	break;
  
      case H_PREV:
! 	ev = HPREV(h);
  	break;
  
      case H_CURR:
! 	ev = HCURR(h);
  	break;
  
      case H_CLEAR:
! 	HCLEAR(h);
  	break;
  
      case H_LOAD:
! 	sev.num = history_load(h, va_arg(va, const char *));
! 	ev = &sev;
  	break;
  
      case H_SAVE:
! 	sev.num = history_save(h, va_arg(va, const char *));
! 	ev = &sev;
  	break;
  
      case H_PREV_EVENT:
! 	ev = history_prev_event(h, va_arg(va, int));
  	break;
  
      case H_NEXT_EVENT:
! 	ev = history_next_event(h, va_arg(va, int));
  	break;
  
      case H_PREV_STR:
! 	ev = history_prev_string(h, va_arg(va, const char*));
  	break;
  
      case H_NEXT_STR:
! 	ev = history_next_string(h, va_arg(va, const char*));
  	break;
  
!     case H_EVENT:
! 	if (history_set_num(h, va_arg(va, int)) == 0) {
! 	    sev.num = -1;
! 	    ev = &sev;
! 	}
  	break;
  
      case H_FUNC:
--- 708,826 ----
  /* history_next_string():
   *	Find the next event beginning with string
   */
! private int
! history_next_string(h, ev, str)
      History *h;
+     HistEvent *ev;
      const char* str;
  {
      size_t len = strlen(str);
+     int retval;
  
!     for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
  	if (strncmp(str, ev->str, len) == 0)
! 	    return 0;
! 
!     he_seterrev(ev, _HE_NOT_FOUND);
!     return -1;
  }
  
  
  /* history():
   *	User interface to history functions.
   */
! int
  #ifdef __STDC__
! history(History *h, HistEvent *ev, int fun, ...)
  #else
  history(va_alist)
      va_dcl
  #endif
  {
      va_list va;
      const char *str;
!     int retval;
  
  #ifdef __STDC__
      va_start(va, fun);
  #else
      History *h; 
+     HistEvent *ev;
      int fun;
      va_start(va);
      h = va_arg(va, History *);
+     ev = va_arg(va, HistEvent *);
      fun = va_arg(va, int);
  #endif
  
+     he_seterrev(ev, _HE_OK);
+ 
      switch (fun) {
      case H_ADD:
  	str = va_arg(va, const char *);
! 	retval = HADD(h, ev, str);
  	break;
  
      case H_ENTER:
  	str = va_arg(va, const char *);
! 	retval = HENTER(h, ev, str);
  	break;
  
      case H_FIRST:
! 	retval = HFIRST(h, ev);
  	break;
  
      case H_NEXT:
! 	retval = HNEXT(h, ev);
  	break;
  
      case H_LAST:
! 	retval = HLAST(h, ev);
  	break;
  
      case H_PREV:
! 	retval = HPREV(h, ev);
  	break;
  
      case H_CURR:
! 	retval = HCURR(h, ev);
  	break;
  
      case H_CLEAR:
! 	HCLEAR(h, ev);
! 	retval = 0;
  	break;
  
      case H_LOAD:
! 	retval = history_load(h, va_arg(va, const char *));
! 	if (retval == -1)
! 		he_seterrev(ev, _HE_HIST_READ);
  	break;
  
      case H_SAVE:
! 	retval = history_save(h, va_arg(va, const char *));
! 	if (retval == -1)
! 		he_seterrev(ev, _HE_HIST_WRITE);
  	break;
  
      case H_PREV_EVENT:
! 	retval = history_prev_event(h, ev, va_arg(va, int));
  	break;
  
      case H_NEXT_EVENT:
! 	retval = history_next_event(h, ev, va_arg(va, int));
  	break;
  
      case H_PREV_STR:
! 	retval = history_prev_string(h, ev, va_arg(va, const char*));
  	break;
  
      case H_NEXT_STR:
! 	retval = history_next_string(h, ev, va_arg(va, const char*));
  	break;
  
!     case H_SETMAXSIZE:
! 	retval = history_set_num(h, ev, va_arg(va, int));
  	break;
  
      case H_FUNC:
***************
*** 678,697 ****
  	    hf.h_enter = va_arg(va, history_efun_t);
  	    hf.h_add   = va_arg(va, history_efun_t);
  
! 	    if (history_set_fun(h, &hf) == 0) {
! 		sev.num = -1;
! 		ev = &sev;
  	    }
  	}
  	break;
  
      case H_END:
  	history_end(h);
  	break;
  
      default:
  	break;
      }
      va_end(va);
!     return ev;
  }
--- 836,862 ----
  	    hf.h_enter = va_arg(va, history_efun_t);
  	    hf.h_add   = va_arg(va, history_efun_t);
  
! 	    if (history_set_fun(h, &hf) == -1) {
! 		he_seterrev(ev, _HE_PARAM_MISSING);
! 		retval = -1;
  	    }
  	}
  	break;
  
      case H_END:
  	history_end(h);
+ 	retval = 0;
+ 	break;
+ 
+     case H_GETSIZE:
+ 	retval = history_get_size(h, ev);
  	break;
  
      default:
+ 	retval = -1;
+ 	he_seterrev(ev, _HE_UNKNOWN);
  	break;
      }
      va_end(va);
!     return retval;
  }
>Audit-Trail:
>Unformatted: