/*	-*- mode: c; mode: fold -*-	*/
# include	"config.h"
# include	<stdlib.h>
# include	<string.h>
# include	<time.h>
# include	"pager.h"

/*{{{	typedefs */
typedef struct {
# ifndef	NDEBUG
# define	MAGIC		MKMAGIC ('u', 'c', 'p', '\0')
	long	magic;
# endif		/* NDEBUG */
	void	*sp;		/* the serial connection		*/
	void	*ctab;		/* the conversion table			*/
	void	(*logger) (char, char *, ...);
	char	*uline;		/* received input line			*/
	
	string_t
		*callid;	/* caller id				*/
	Bool	xtend;		/* use extended UCP version		*/
	int	send_tout;	/* timeout during sending		*/
	int	send_retry;	/* # of retries to send a message	*/
	int	rds_tout;	/* timeout for status report		*/
	date_t	delay;		/* delay message			*/
	date_t	expire;		/* expire message			*/
	Bool	rds;		/* request delivery status		*/

	int	cnr;		/* current transaction number		*/
}	ucp;

typedef struct {
	char	*AdC;
	char	*OAdC;
	char	*PID;
	char	*na;
	char	*MT;
	char	*Msg;

	char		*adc;
	char		*oadc;
	int		pid;
	int		mt;
	string_t	*msg;
}	bsimple;

typedef struct {
	char	*AdC;
	char	*OAdC;
	char	*AC;
	char	*NRq;
	char	*NAdC;
	char	*NT;
	char	*NPID;
	char	*na1;
	char	*na2;
	char	*na3;
	char	*DD;
	char	*DDT;
	char	*VP;
	char	*RPID;
	char	*SCTS;
	char	*DSt;
	char	*Rsn;
	char	*DSCTS;
	char	*MT;
	char	*NB;
	char	*Msg;
	char	*MMS;
	char	*na4;
	char	*DCS;
	char	*MCL;
	char	*RPI;
	char	*na5;
	char	*na6;
	char	*res1;
	char	*res2;
	char	*res3;
	char	*res4;
	char	*res5;
	
	char		*adc;
	char		*oadc;
	char		*ac;
	Bool		nrq;
	char		*nadc;
	int		nt;
	int		npid;
	Bool		dd;
	date_t		ddt;
	date_t		vp;
	int		rpid;
	date_t		scts;
	int		dst;
	int		rsn;
	date_t		dscts;
	int		mt;
	int		nb;
	string_t	*msg;
	Bool		mms;
	Bool		dcs;
	int		mcl;
	int		rpi;
}	bextend;

typedef struct {
	char	*Res;
	char	*MVP;
	char	*EC;
	char	*MSG;

	Bool	ack;
	date_t	mvp;
	int	ec;
	char	*adc;
	date_t	scts;
}	banswer;

typedef struct {
	int	trn;
	int	len;
	char	ttyp;
	int	ot;
	char	*data;
	int	cnt;
	union {
		bsimple	s;
		bextend	e;
		banswer	a;
	}	b;
	int	chksum;
}	frame;
/*}}}*/
/*{{{	support routines */
static char_t
hex (int val)
{
	switch (val) {
	default:
	case 0:		return (char_t) '0';
	case 1:		return (char_t) '1';
	case 2:		return (char_t) '2';
	case 3:		return (char_t) '3';
	case 4:		return (char_t) '4';
	case 5:		return (char_t) '5';
	case 6:		return (char_t) '6';
	case 7:		return (char_t) '7';
	case 8:		return (char_t) '8';
	case 9:		return (char_t) '9';
	case 10:	return (char_t) 'A';
	case 11:	return (char_t) 'B';
	case 12:	return (char_t) 'C';
	case 13:	return (char_t) 'D';
	case 14:	return (char_t) 'E';
	case 15:	return (char_t) 'F';
	}
}

static int
unhex (char ch)
{
	switch (ch) {
	default:
	case '0':	return 0;
	case '1':	return 1;
	case '2':	return 2;
	case '3':	return 3;
	case '4':	return 4;
	case '5':	return 5;
	case '6':	return 6;
	case '7':	return 7;
	case '8':	return 8;
	case '9':	return 9;
	case 'A':
	case 'a':	return 10;
	case 'B':
	case 'b':	return 11;
	case 'C':
	case 'c':	return 12;
	case 'D':
	case 'd':	return 13;
	case 'E':
	case 'e':	return 14;
	case 'F':
	case 'f':	return 15;
	}
}
/*}}}*/
/*{{{	frame basics */
static frame *
new_frame (char ttyp, int ot)
{
	frame	*f;
	bsimple	*s;
	bextend	*e;
	banswer	*a;
	
	if (f = (frame *) malloc (sizeof (frame))) {
		f -> trn = 0;
		f -> len = 0;
		f -> ttyp = ttyp;
		f -> ot = ot;
		f -> data = NULL;
		f -> cnt = 0;
		memset (& f -> b, 0, sizeof (f -> b));
		f -> chksum = 0;
		if (f -> ttyp == 'R')
			switch (f -> ot) {
			case 1:
			case 31:
			case 51:
			case 52:
			case 53:
			case 55:
			case 56:
			case 57:
			case 58:
				a = & f -> b.a;
				a -> Res = NULL;
				a -> MVP = NULL;
				a -> EC = NULL;
				a -> MSG = NULL;

				a -> ack = False;
				dat_clear (& a -> mvp);
				a -> ec = -1;
				a -> adc = NULL;
				dat_clear (& a -> scts);
				break;
			}
		else if (f -> ttyp == 'O')
			switch (f -> ot) {
			case 1:
			case 31:
				s = & f -> b.s;
				s -> AdC = NULL;
				s -> OAdC = NULL;
				s -> PID = NULL;
				s -> na = NULL;
				s -> MT = NULL;
				s -> Msg = NULL;
				
				s -> adc = NULL;
				s -> oadc = NULL;
				s -> pid = -1;
				s -> mt = -1;
				s -> msg = NULL;
				break;
			case 51:
			case 52:
			case 53:
			case 55:
			case 56:
			case 57:
			case 58:
				e = & f -> b.e;
				e -> AdC = NULL;
				e -> OAdC = NULL;
				e -> AC = NULL;
				e -> NRq = NULL;
				e -> NAdC = NULL;
				e -> NT = NULL;
				e -> NPID = NULL;
				e -> na1 = NULL;
				e -> na2 = NULL;
				e -> na3 = NULL;
				e -> DD = NULL;
				e -> DDT = NULL;
				e -> VP = NULL;
				e -> RPID = NULL;
				e -> SCTS = NULL;
				e -> DSt = NULL;
				e -> Rsn = NULL;
				e -> DSCTS = NULL;
				e -> MT = NULL;
				e -> NB = NULL;
				e -> Msg = NULL;
				e -> MMS = NULL;
				e -> na4 = NULL;
				e -> DCS = NULL;
				e -> MCL = NULL;
				e -> RPI = NULL;
				e -> na5 = NULL;
				e -> na6 = NULL;
				e -> res1 = NULL;
				e -> res2 = NULL;
				e -> res3 = NULL;
				e -> res4 = NULL;
				e -> res5 = NULL;
	
				e -> adc = NULL;
				e -> oadc = NULL;
				e -> ac = NULL;
				e -> nrq = False;
				e -> nadc = NULL;
				e -> nt = -1;
				e -> npid = -1;
				e -> dd = False;
				dat_clear (& e -> ddt);
				dat_clear (& e -> vp);
				e -> rpid = -1;
				dat_clear (& e -> scts);
				e -> dst = -1;
				e -> rsn = -1;
				dat_clear (& e -> dscts);
				e -> mt = -1;
				e -> nb = -1;
				e -> msg = NULL;
				e -> mms = False;
				e -> dcs = False;
				e -> mcl = -1;
				e -> rpi = -1;
				break;
			}
	}
	return f;
}

static void
free_body (frame *f)
{
# define	dfr(xxx)	if (xxx) free (xxx)	
	bsimple	*s;
	bextend	*e;
	banswer	*a;
	
	s = NULL;
	e = NULL;
	a = NULL;
	if (f -> ttyp == 'O')
		switch (f -> ot) {
		case 1:
		case 31:
			s = & f -> b.s;
			break;
		case 51:
		case 52:
		case 53:
		case 55:
		case 56:
		case 57:
		case 58:
			e = & f -> b.e;
			break;
		}
	else if (f -> ttyp == 'R')
		switch (f -> ot) {
		case 1:
		case 31:
		case 51:
		case 52:
		case 53:
		case 55:
		case 56:
		case 57:
		case 58:
			a = & f -> b.a;
			break;
		}
	if (s) {
		dfr (s -> AdC);
		dfr (s -> OAdC);
		dfr (s -> PID);
		dfr (s -> na);
		dfr (s -> MT);
		dfr (s -> Msg);
		dfr (s -> adc);
		dfr (s -> oadc);
		sfree (s -> msg);
	} else if (e) {
		dfr (e -> AdC);
		dfr (e -> OAdC);
		dfr (e -> AC);
		dfr (e -> NRq);
		dfr (e -> NAdC);
		dfr (e -> NT);
		dfr (e -> NPID);
		dfr (e -> na1);
		dfr (e -> na2);
		dfr (e -> na3);
		dfr (e -> DD);
		dfr (e -> DDT);
		dfr (e -> VP);
		dfr (e -> RPID);
		dfr (e -> SCTS);
		dfr (e -> DSt);
		dfr (e -> Rsn);
		dfr (e -> DSCTS);
		dfr (e -> MT);
		dfr (e -> NB);
		dfr (e -> Msg);
		dfr (e -> MMS);
		dfr (e -> na4);
		dfr (e -> DCS);
		dfr (e -> MCL);
		dfr (e -> RPI);
		dfr (e -> na5);
		dfr (e -> na6);
		dfr (e -> res1);
		dfr (e -> res2);
		dfr (e -> res3);
		dfr (e -> res4);
		dfr (e -> res5);

		dfr (e -> adc);
		dfr (e -> oadc);
		dfr (e -> ac);
		dfr (e -> nadc);
		sfree (e -> msg);
	} else if (a) {
		dfr (a -> Res);
		dfr (a -> MVP);
		dfr (a -> EC);
		dfr (a -> MSG);
		dfr (a -> adc);
	}
# undef		dfr	
}
	
static void
free_frame (frame *f)
{
	if (f) {
		if (f -> data)
			free (f -> data);
		free_body (f);
		free (f);
	}
}
/*}}}*/
/*{{{	assemble */
static char *
encode (ucp *u, string_t *s, Bool docv)
{
	char	*ret;
	char_t	ch;
	int	c;
	int	len;
	int	n, m;

	len = s ? (s -> len * 2) : 0;
	if (ret = malloc (len + 2)) {
		for (n = 0, m = 0; n < len; ++m) {
			if (docv) {
				if ((c = cv_conv (u -> ctab, s -> str[m])) < 0)
					continue;
				ch = (char_t) c;
			} else
				ch = s -> str[m];
			ret[n++] = hex ((ch >> 4) & 0xf);
			ret[n++] = hex (ch & 0xf);
		}
		ret[n] = '\0';
	}
	return ret;
}

static inline void
gbool (char **ptr, Bool what, char *t, char *f)
{
	if (*ptr)
		free (*ptr);
	if (what)
		*ptr = t ? strdup (t) : NULL;
	else
		*ptr = f ? strdup (f) : NULL;
}

static inline void
gdate (char **ptr, date_t *d, int len)
{
	if (*ptr)
		free (*ptr);
	if (d -> day > 0) {
		if (*ptr = malloc (14)) {
			sprintf (*ptr, "%02d%02d%02d%02d%02d%02d",
				 d -> day, d -> mon, d -> year % 100,
				 d -> hour, d -> min, d -> sec);
			if (len < 12)
				(*ptr)[len] = '\0';
		}
	} else
		*ptr = NULL;
}

static inline void
gint (char **ptr, int val, char *fmt, int def)
{
	if (*ptr)
		free (*ptr);
	if (val == -1)
		val = def;
	if (val != -1) {
		if (*ptr = malloc (32))
			sprintf (*ptr, (fmt ? fmt : "%d"), val);
	} else
		*ptr = NULL;
}

static inline void
gstr (char **ptr, char *str)
{
	if (*ptr)
		free (*ptr);
	*ptr = str ? strdup (str) : NULL;
}

static string_t *
assemble_frame (ucp *u, frame *f)
{
	bsimple		*s;
	bextend		*e;
	banswer		*a;
	string_t	*ret;
	Bool		fail;
	char		buf[64], *ptr;
	int		len, n;
	unsigned long	chk;
	
	if (! (ret = snew (NULL, 128)))
		return NULL;
	if (! (scopyc (ret, "\x02") && scatc (ret, "00/00000/")))
		return sfree (ret);
	sprintf (buf, "%c/%02d/", f -> ttyp, f -> ot);
	if (! scatc (ret, buf))
		return sfree (ret);
	fail = False;
	if (f -> ttyp == 'R') {
		a = & f -> b.a;
		switch (f -> ot) {
		default:
			fail = True;
			break;
		case 1:
		case 31:
		case 51:
		case 52:
		case 53:
		case 55:
		case 56:
		case 57:
		case 58:
			gbool (& a -> Res, a -> ack, "A", "N");
			if (a -> MSG) {
				free (a -> MSG);
				a -> MSG = NULL;
			}
			if (a -> ack) {
				gdate (& a -> MVP, & a -> mvp, 10);
				ptr = NULL;
				gdate (& ptr, & a -> scts, 12);
				if (ptr) {
					if (a -> adc) {
						if (a -> MSG = malloc (strlen (ptr) + strlen (a -> adc) + 4))
							sprintf (a -> MSG, "%s:%s", a -> adc, ptr);
						free (ptr);
					} else
						a -> MSG = ptr;
				} else if (a -> adc)
					a -> MSG = strdup (a -> adc);
			} else
				gint (& a -> EC, a -> ec, NULL, 3);
			if (((f -> ot == 1) || (f -> ot == 31)) && a -> ack) {
				if (! (scatc (ret, a -> Res) && scatc (ret, "/") &&
				       scatc (ret, a -> MSG) && scatc (ret, "/")))
					fail = True;
			} else if (! (scatc (ret, a -> Res) && scatc (ret, "/") &&
				      scatc (ret, (a -> ack ? a -> MVP : a -> EC)) && scatc (ret, "/") &&
				      scatc (ret, a -> MSG) && scatc (ret, "/")))
				fail = True;
			break;
		}
	} else if (f -> ttyp == 'O') {
		s = & f -> b.s;
		e = & f -> b.e;
		switch (f -> ot) {
		default:
			fail = True;
			break;
		case 1:
			gstr (& s -> AdC, s -> adc);
			gstr (& s -> OAdC, s -> oadc);
			gstr (& s -> na, NULL);
			gint (& s -> MT, s -> mt, NULL, 3);
			if (s -> Msg)
				free (s -> Msg);
			s -> Msg = encode (u, s -> msg, True);
			if (! (scatc (ret, s -> AdC) && scatc (ret, "/") &&
			       scatc (ret, s -> OAdC) && scatc (ret, "/") &&
			       scatc (ret, s -> na) && scatc (ret, "/") &&
			       scatc (ret, s -> MT) && scatc (ret, "/") &&
			       scatc (ret, s -> Msg) && scatc (ret, "/")))
				fail = True;
			break;
		case 31:
			gstr (& s -> AdC, s -> adc);
			gint (& s -> PID, s -> pid, "%04d", -1);
			if (! (scatc (ret, s -> AdC) && scatc (ret, "/") &&
			       scatc (ret, s -> PID) && scatc (ret, "/")))
				fail = True;
			break;
		case 51:
		case 52:
		case 53:
		case 55:
		case 56:
		case 57:
		case 58:
			gstr (& e -> AdC, e -> adc);
			gstr (& e -> OAdC, e -> oadc);
			gstr (& e -> AC, e -> ac);
			gbool (& e -> NRq, e -> nrq, "1", NULL);
			gstr (& e -> NAdC, (e -> nrq ? e -> nadc : NULL));
			gint (& e -> NT, (e -> nrq ? e -> nt : -1), NULL, -1);
			gint (& e -> NPID, (e -> nrq ? e -> npid : -1), "%04d", -1);
			gstr (& e -> na1, NULL);
			gstr (& e -> na2, NULL);
			gstr (& e -> na3, NULL);
			gbool (& e -> DD, e -> dd, "1", NULL);
			if (e -> dd)
				gdate (& e -> DDT, & e -> ddt, 10);
			else
				gstr (& e -> DDT, NULL);
			gdate (& e -> VP, & e -> vp, 10);
			gint (& e -> RPID, e -> rpid, "%04d", -1);
			gdate (& e -> SCTS, & e -> scts, 12);
			gint (& e -> DSt, e -> dst, NULL, -1);
			gint (& e -> Rsn, e -> rsn, "%03d", -1);
			gdate (& e -> DSCTS, & e -> dscts, 12);
			gint (& e -> MT, e -> mt, NULL, 3);
			if (e -> mt == 4)
				if (e -> nb != -1)
					len = e -> nb;
				else
					len = e -> msg ? e -> msg -> len * 8 : 0;
			else
				len = -1;
			gint (& e -> NB, len, NULL, -1);
			if (e -> Msg)
				free (e -> Msg);
			e -> Msg = encode (u, e -> msg, (e -> mt == 4 ? False : True));
			gbool (& e -> MMS, e -> mms, "1", NULL);
			gstr (& e -> na4, NULL);
			gbool (& e -> DCS, e -> dcs, "1", NULL);
			gint (& e -> MCL, e -> mcl, NULL, -1);
			gint (& e -> RPI, e -> rpi, NULL, -1);
			gstr (& e -> na5, NULL);
			gstr (& e -> na6, NULL);
			gstr (& e -> res1, NULL);
			gstr (& e -> res2, NULL);
			gstr (& e -> res3, NULL);
			gstr (& e -> res4, NULL);
			gstr (& e -> res5, NULL);
			if (! (scatc (ret, e -> AdC) && scatc (ret, "/") &&
			       scatc (ret, e -> OAdC) && scatc (ret, "/") &&
			       scatc (ret, e -> AC) && scatc (ret, "/") &&
			       scatc (ret, e -> NRq) && scatc (ret, "/") &&
			       scatc (ret, e -> NAdC) && scatc (ret, "/") &&
			       scatc (ret, e -> NT) && scatc (ret, "/") &&
			       scatc (ret, e -> NPID) && scatc (ret, "/") &&
			       scatc (ret, e -> na1) && scatc (ret, "/") &&
			       scatc (ret, e -> na2) && scatc (ret, "/") &&
			       scatc (ret, e -> na3) && scatc (ret, "/") &&
			       scatc (ret, e -> DD) && scatc (ret, "/") &&
			       scatc (ret, e -> DDT) && scatc (ret, "/") &&
			       scatc (ret, e -> VP) && scatc (ret, "/") &&
			       scatc (ret, e -> RPID) && scatc (ret, "/") &&
			       scatc (ret, e -> SCTS) && scatc (ret, "/") &&
			       scatc (ret, e -> DSt) && scatc (ret, "/") &&
			       scatc (ret, e -> Rsn) && scatc (ret, "/") &&
			       scatc (ret, e -> DSCTS) && scatc (ret, "/") &&
			       scatc (ret, e -> MT) && scatc (ret, "/") &&
			       scatc (ret, e -> NB) && scatc (ret, "/") &&
			       scatc (ret, e -> Msg) && scatc (ret, "/") &&
			       scatc (ret, e -> MMS) && scatc (ret, "/") &&
			       scatc (ret, e -> na4) && scatc (ret, "/") &&
			       scatc (ret, e -> DCS) && scatc (ret, "/") &&
			       scatc (ret, e -> MCL) && scatc (ret, "/") &&
			       scatc (ret, e -> RPI) && scatc (ret, "/") &&
			       scatc (ret, e -> na5) && scatc (ret, "/") &&
			       scatc (ret, e -> na6) && scatc (ret, "/") &&
			       scatc (ret, e -> res1) && scatc (ret, "/") &&
			       scatc (ret, e -> res2) && scatc (ret, "/") &&
			       scatc (ret, e -> res3) && scatc (ret, "/") &&
			       scatc (ret, e -> res4) && scatc (ret, "/") &&
			       scatc (ret, e -> res5) && scatc (ret, "/")))
				fail = True;
			break;
		}
	} else
		fail = True;
	if (! fail) {
		len = ret -> len - 1 + 2;
		sprintf (buf, "%02d/%05d", f -> trn, len);
		if (sputc (ret, buf, 1, -1)) {
			for (n = 1, chk = 0; n < ret -> len; ++n)
				chk += ret -> str[n] & 0xff;
			if (sexpand (ret, ret -> len + 4)) {
				ret -> str[ret -> len++] = hex ((chk >> 4) & 0xf);
				ret -> str[ret -> len++] = hex (chk & 0xf);
				ret -> str[ret -> len++] = '\x03';
			}
		} else
			fail = True;
	}
	if (fail)
		ret = sfree (ret);
	return ret;
}
/*}}}*/
/*{{{	parse */
static date_t
parse_date (char *str)
{
	date_t		ret;
	int		n;
	int		val;
	time_t		tim;
	struct tm	*tt;

	ret.day = 0;
	ret.mon = 0;
	ret.year = 0;
	ret.hour = 0;
	ret.min = 0;
	ret.sec = 0;
	if (str)
		for (n = 0; (n < 6) && *str && *(str + 1); ++n) {
			val = unhex (*str) * 10 + unhex (*(str + 1));
			str += 2;
			switch (n) {
			case 0:	ret.day = val;	break;
			case 1:	ret.mon = val;	break;
			case 2:	ret.year = val;	break;
			case 3:	ret.hour = val;	break;
			case 4:	ret.min = val;	break;
			case 5:	ret.sec = val;	break;
			}
		}
	time (& tim);
	if (tt = localtime (& tim))
		ret.year += ((tt -> tm_year + 1900) / 100) * 100;
	return ret;
}

static string_t *
parse_message (ucp *u, int mt, char *msg)
{
	string_t	*ret;
	int		len;
	int		n;
	char_t		ch;
	void		*ct;
	
	if ((mt != 4) && (u -> ctab))
		ct = cv_reverse (u -> ctab);
	else
		ct = NULL;
	len = strlen (msg);
	if (ret = snew (NULL, len / 2 + 2))
		for (n = 0; n + 1 < len; n += 2) {
			ch = (unhex (msg[n]) << 4) | unhex (msg[n + 1]);
			ret -> str[ret -> len++] = cv_conv (ct, ch);
		}
	if (ct)
		cv_free (ct);
	return ret;
}

static Bool
parse_body (ucp *u, frame *f)
{
	Bool	ret;
	char	*dat, *ptr, *sav;
	char	**rev;
	Bool	done, fail;
	int	n;
	
	if (! (dat = strdup (f -> data)))
		return False;
	ret = False;
	done = False;
	fail = False;
	for (ptr = dat, n = 0; (! done) && (! fail) && ptr; ++n) {
		sav = ptr;
		if (ptr = strchr (ptr, '/'))
			*ptr++ = '\0';
		rev = NULL;
		if (f -> ttyp == 'O') {
			switch (f -> ot) {
			case 1:
				switch (n) {
				case 0:		rev = & f -> b.s.AdC;	break;
				case 1:
					rev = & f -> b.s.OAdC;
					f -> b.s.adc = f -> b.s.AdC ? strdup (f -> b.s.AdC) : NULL;
					break;
				case 2:	
					rev = & f -> b.s.na;
					f -> b.s.oadc = f -> b.s.OAdC ? strdup (f -> b.s.OAdC) : NULL;
					break;
				case 3:		rev = & f -> b.s.MT;	break;
				case 4:
					rev = & f -> b.s.Msg;
					f -> b.s.mt = f -> b.s.MT ? atoi (f -> b.s.MT) : -1;
					done = True;
					break;
				}
				break;
			case 31:
				switch (n) {
				case 0:		rev = & f -> b.s.AdC;	break;
				case 1:	
					rev = & f -> b.s.PID;
					f -> b.s.adc = f -> b.s.AdC ? strdup (f -> b.s.AdC) : NULL;
					done = True;
					break;
				}
				break;
			case 51:
			case 52:
			case 53:
			case 55:
			case 56:
			case 57:
			case 58:
				switch (n) {
				case 0:		rev = & f -> b.e.AdC;	break;
				case 1:
					rev = & f -> b.e.OAdC;
					f -> b.e.adc = f -> b.e.AdC ? strdup (f -> b.e.AdC) : NULL;
					break;
				case 2:
					rev = & f -> b.e.AC;
					f -> b.e.oadc = f -> b.e.OAdC ? strdup (f -> b.e.OAdC) : NULL;
					break;
				case 3:	
					rev = & f -> b.e.NRq;
					f -> b.e.ac = f -> b.e.AC ? strdup (f -> b.e.AC) : NULL;
					break;
				case 4:
					rev = & f -> b.e.NAdC;
					if (f -> b.e.NRq)
						f -> b.e.nrq = (atoi (f -> b.e.NRq) == 1 ? True : False);
					else
						f -> b.e.nrq = False;
					break;
				case 5:	
					rev = & f -> b.e.NT;
					f -> b.e.nadc = f -> b.e.NAdC ? strdup (f -> b.e.NAdC) : NULL;
					break;
				case 6:
					rev = & f -> b.e.NPID;
					f -> b.e.nt = f -> b.e.NT ? atoi (f -> b.e.NT) : 0;
					break;
				case 7:
					rev = & f -> b.e.na1;
					f -> b.e.npid = f -> b.e.NPID ? atoi (f -> b.e.NPID) : -1;
					break;
				case 8:		rev = & f -> b.e.na2;	break;
				case 9:		rev = & f -> b.e.na3;	break;
				case 10:	rev = & f -> b.e.DD;	break;
				case 11:
					rev = & f -> b.e.DDT;
					if (f -> b.e.DD)
						f -> b.e.dd = (atoi (f -> b.e.DD) == 1 ? True : False);
					else
						f -> b.e.dd = False;
					break;
				case 12:
					rev = & f -> b.e.VP;
					f -> b.e.ddt = parse_date (f -> b.e.DDT);
					break;
				case 13:
					rev = & f -> b.e.RPID;
					f -> b.e.vp = parse_date (f -> b.e.VP);
					break;
				case 14:
					rev = & f -> b.e.SCTS;
					f -> b.e.rpid = f -> b.e.RPID ? atoi (f -> b.e.RPID) : -1;
					break;
				case 15:
					rev = & f -> b.e.DSt;
					f -> b.e.scts = parse_date (f -> b.e.SCTS);
					break;
				case 16:
					rev = & f -> b.e.Rsn;
					f -> b.e.dst = f -> b.e.DSt ? atoi (f -> b.e.DSt) : -1;
					break;
				case 17:
					rev = & f -> b.e.DSCTS;
					f -> b.e.rsn = f -> b.e.Rsn ? atoi (f -> b.e.Rsn) : 0;
					break;
				case 18:
					rev = & f -> b.e.MT;
					f -> b.e.dscts = parse_date (f -> b.e.DSCTS);
					break;
				case 19:
					rev = & f -> b.e.NB;
					f -> b.e.mt = f -> b.e.MT ? atoi (f -> b.e.MT) : -1;
					break;
				case 20:
					rev = & f -> b.e.Msg;
					f -> b.e.nb = f -> b.e.NB ? atoi (f -> b.e.NB) : 0;
					break;
				case 21:
					rev = & f -> b.e.MMS;
					if (f -> b.e.Msg)
						if (! (f -> b.e.msg = parse_message (u, f -> b.e.mt, f -> b.e.Msg)))
							fail = True;
					break;
				case 22:
					rev = & f -> b.e.na4;
					if (f -> b.e.MMS)
						f -> b.e.mms = (atoi (f -> b.e.MMS) == 1 ? True : False);
					else
						f -> b.e.mms = False;
					break;
				case 23:	rev = & f -> b.e.DCS;	break;
				case 24:
					rev = & f -> b.e.MCL;
					if (f -> b.e.DCS)
						f -> b.e.dcs = (atoi (f -> b.e.DCS) == 1 ? True : False);
					else
						f -> b.e.dcs = False;
					break;
				case 25:
					rev = & f -> b.e.RPI;
					f -> b.e.mcl = f -> b.e.MCL ? atoi (f -> b.e.MCL) : -1;
					break;
				case 26:
					rev = & f -> b.e.na5;
					f -> b.e.rpi = f -> b.e.RPI ? atoi (f -> b.e.RPI) : -1;
					break;
				case 27:	rev = & f -> b.e.na6;	break;
				case 28:	rev = & f -> b.e.res1;	break;
				case 29:	rev = & f -> b.e.res2;	break;
				case 30:	rev = & f -> b.e.res3;	break;
				case 31:	rev = & f -> b.e.res4;	break;
				case 32:
					rev = & f -> b.e.res5;
					done = True;
					break;
				}
				break;
			}
		} else if (f -> ttyp == 'R') {
			switch (f -> ot) {
			case 1:
			case 31:
				switch (n) {
				case 0:		rev = & f -> b.a.Res;	break;
				case 1:
					if (f -> b.a.Res) {
						if (f -> b.a.Res[0] == 'A') {
							f -> b.a.ack = True;
							rev = & f -> b.a.MSG;
							done = True;
						} else if (f -> b.a.Res[0] == 'N') {
							f -> b.a.ack = False;
							rev = & f -> b.a.EC;
						} else
							fail = True;
					}
					break;
				case 2:
					if (! f -> b.a.ack) {
						f -> b.a.ec = f -> b.a.EC ? atoi (f -> b.a.EC) : -1;
						rev = & f -> b.a.MSG;
						done = True;
					}
					break;
				}
				break;
			case 51:
			case 52:
			case 53:
			case 55:
			case 56:
			case 57:
			case 58:
				switch (n) {
				case 0:		rev = & f -> b.a.Res;	break;
				case 1:
					if (f -> b.a.Res) {
						if (f -> b.a.Res[0] == 'A') {
							f -> b.a.ack = True;
							rev = & f -> b.a.MVP;
						} else if (f -> b.a.Res[0] == 'N') {
							f -> b.a.ack = False;
							rev = & f -> b.a.EC;
						} else
							fail = True;
					} else
						fail = True;
					break;
				case 2:
					if (f -> b.a.ack)
						f -> b.a.mvp = parse_date (f -> b.a.MVP);
					else
						f -> b.a.ec = f -> b.a.EC ? atoi (f -> b.a.EC) : -1;
					rev = & f -> b.a.MSG;
					done = True;
					break;
				}
				break;
			}
		} else
			fail = True;
		if (! fail) {
			if (! rev)
				fail = True;
			else {
				if (*sav) {
					if (! (*rev = strdup (sav)))
						fail = True;
				} else
					*rev = NULL;
			}
			if ((! ptr) && (! done))
				fail = True;
		}
	}
	if (done && (! fail) && (! ptr)) {
		if (f -> ttyp == 'O') {
			switch (f -> ot) {
			case 1:
			case 31:
				if (f -> b.s.Msg)
					if (! (f -> b.s.msg = parse_message (u, f -> b.s.mt, f -> b.s.Msg)))
						fail = True;
				break;
			}
		} else if (f -> ttyp == 'R') {
			switch (f -> ot) {
			case 1:
			case 31:
			case 51:
			case 52:
			case 53:
			case 55:
			case 56:
			case 57:
			case 58:
				if (f -> b.a.MSG) {
					if (! (f -> b.a.adc = strdup (f -> b.a.MSG)))
						fail = True;
					else {
						if (ptr = strchr (f -> b.a.adc, ':'))
							*ptr++ = '\0';
						f -> b.a.scts = parse_date (ptr);
					}
				}
				break;
			}
		} else
			fail = True;
		if (! fail)
			ret = True;
	}
	free (dat);
	return ret;
}

static frame *
parse_frame (ucp *u, char *str)
{
	frame		*f;
	char		*start, *end;
	char		*ptr, *sav;
	int		n, len;
	unsigned int	chksum;
	
	f = NULL;
	if (str = strdup (str)) {
		start = strchr (str, '\x02');
		end = strchr (str, '\x03');
		ptr = strrchr (str, '/');
		if (start && end && (end > start) &&
		    ptr && (start + 1 < ptr) && (ptr + 3 == end) &&
		    (f = new_frame ('\0', 0))) {
			memset (f, 0, sizeof (frame));
			len = (int) ((unsigned long) end - (unsigned long) start) - 1;
			for (chksum = 0, n = 1; n < len - 1; ++n)
				chksum += (unsigned char) start[n];
			chksum &= 0xff;
			ptr = start + 1;
			for (n = 0; n < 4; ++n) {
				if (! (sav = ptr))
					break;
				if (ptr = strchr (ptr, '/'))
					*ptr++ = '\0';
				switch (n) {
				case 0:
					f -> trn = atoi (sav);
					break;
				case 1:
					f -> len = atoi (sav);
					break;
				case 2:
					f -> ttyp = *sav;
					break;
				case 3:
					f -> ot = atoi (sav);
					break;
				}
			}
			sav = ptr;
			if ((n < 4) || (f -> len != len) || (! sav) || (! (ptr = strrchr (sav, '/')))) {
				free (f);
				f = NULL;
			} else {
				f -> cnt = (int) ((unsigned long) ptr - (unsigned long) sav);
				++ptr;
				if (*ptr && *(ptr + 1))
					f -> chksum = (unhex (*ptr) << 4) | unhex (*(ptr + 1));
				else
					f -> chksum = -1;
				if ((chksum == f -> chksum) && (f -> data = malloc (f -> cnt + 1))) {
					memcpy (f -> data, sav, f -> cnt);
					f -> data[f -> cnt] = '\0';
					if (! parse_body (u, f)) {
						free_frame (f);
						f = NULL;
					}
				} else {
					free (f);
					f = NULL;
				}
			}
		}
		free (str);
	}
	return f;
}
/*}}}*/
/*{{{	convert */
static string_t *
convert_ucp (ucp *u, Bool last, string_t *pagerid, string_t *msg)
{
	string_t	*ret;
	frame		*f;
	int		ot;
	int		n;
	int		ch;
	char		*adc, *oadc;
	
	ret = NULL;
	if (! u -> xtend)
		ot = 1;
	else
		ot = 51;
	if (f = new_frame ('O', ot)) {
		f -> trn = u -> cnr++;
		adc = sextract (pagerid);
		oadc = u -> callid ? sextract (u -> callid) : NULL;
		if (msg)
			msg = snew (msg -> str, msg -> len);
		if (u -> xtend) {
			f -> b.e.adc = adc;
			f -> b.e.oadc = oadc;
			if (u -> rds && (u -> delay.day <= 0)) {
				f -> b.e.nrq = True;
				f -> b.e.nt = 7;
			} else
				f -> b.e.nrq = False;
			if (u -> delay.day > 0) {
					f -> b.e.dd = True;
				f -> b.e.ddt = u -> delay;
			} else
				f -> b.e.dd = False;
			if (u -> expire.day > 0)
				f -> b.e.vp = u -> expire;
			for (n = 0; n < msg -> len; ++n)
				if ((ch = cv_conv (u -> ctab, msg -> str[n])) != -1)
					if (ch & 0x80)
						break;
			if (n < msg -> len) {
				f -> b.e.mt = 4;
				f -> b.e.nb = msg -> len * 8;
			} else
				f -> b.e.mt = 3;
			f -> b.e.msg = msg;
		} else {
			f -> b.s.adc = adc;
			f -> b.s.oadc = oadc;
			f -> b.s.mt = 3;
			f -> b.s.msg = msg;
		}
		ret = assemble_frame (u, f);
		free_frame (f);
	}
	return ret;
}
/*}}}*/
/*{{{	callback interface */
static void
grabline (void *sp, string_t *s, char_t sep, void *data)
{
	ucp	*u = (ucp *) data;
	char	*str;

	MCHK (u);
	if (u) {
		if (u -> uline) {
			free (u -> uline);
			u -> uline = NULL;
		}
		if (str = sextract (s)) {
			if (u -> uline = malloc (strlen (str) + 4))
				sprintf (u -> uline, "%s%c", str, (char) sep);
			free (str);
		}
	}
}
/*}}}*/
/*{{{	interpret originate message */
static void
interpret_originate (ucp *u, frame *f)
{
	string_t	*msg;
	
	msg = NULL;
	switch (f -> ot) {
	case 1:
	case 31:
		msg = f -> b.s.msg;
		break;
	case 51:
	case 52:
	case 53:
	case 55:
	case 56:
	case 57:
	case 58:
		msg = f -> b.e.msg;
		break;
	}
	if (msg)
		V (1, ("Got message type %d: `%s'\n", f -> ot, schar (msg)));
}
/*}}}*/
/*{{{	send a message */
static Bool
send_msg (ucp *u, string_t *msg, Bool wds, int *err)
{
	Bool		ret;
	Bool		dosend;
	int		n;
	int		ep;
	frame		*f, *ans;
	banswer		*an;
	string_t	*astr;
	char		*ptr;

	ret = False;
	dosend = True;
	for (n = 0; n < 2; ++n) {
		if ((! n) && dosend) {
			dosend = False;
			if (tty_send (u -> sp, msg -> str, msg -> len) != msg -> len) {
				V (1, ("Unable to send message\n"));
				*err = ERR_FAIL;
				break;
			}
		}
		if (n && (! wds))
			continue;
		ep = tty_expect (u -> sp, (n ? u -> rds_tout : u -> send_tout), "\x03", 1, NULL);
		if ((ep != 1) || (! u -> uline)) {
			if (ep < 0)
				*err = ERR_FAIL;
			break;
		}
		if (f = parse_frame (u, u -> uline)) {
			switch (f -> ttyp) {
			case 'R':
				if (f -> b.a.ack) {
					V (1, ("Message spooled\n"));
					ret = True;
					break;
				} else {
					switch (f -> b.a.ec) {
					default:
						ptr = "unknown error";
						break;
					case 1:
						ptr = "checksum error";
						break;
					case 2:
						ptr = "syntax error";
						break;
					case 3:
						ptr = "operation not supported by system";
						break;
					case 4:
						ptr = "operation not allowed (at this point)";
						break;
					case 5:
						ptr = "call barring active";
						break;
					case 6:
						ptr = "AdC invalid";
						break;
					case 7:
						ptr = "authentication failure";
						break;
					case 8:
						ptr = "legitimisation code for all calls, failure";
						break;
					case 9:
						ptr = "GA not valid";
						break;
					case 10:
						ptr = "repetition not allowed";
						break;
					case 11:
						ptr = "legitimisation code for repetition, failure";
						break;
					case 12:
						ptr = "priority call not allowed";
						break;
					case 13:
						ptr = "legitimisation code for priority, failure";
						break;
					case 14:
						ptr = "urgent message not allowed";
						break;
					case 15:
						ptr = "legitimisation code for urgent message, failure";
						break;
					case 16:
						ptr = "reveerse charging not allowed";
						break;
					case 17:
						ptr = "legitimisation code for reverse charging, failure";
						break;
					case 18:
						ptr = "deferred delivery not allowed";
						break;
					case 19:
						ptr = "new AC not valid";
						break;
					case 20:
						ptr = "new legitimisation code not valid";
						break;
					case 21:
						ptr = "standard text not valid";
						break;
					case 22:
						ptr = "time period not valid";
						break;
					case 23:
						ptr = "message type not supported by system";
						break;
					case 24:
						ptr = "message too long";
						break;
					case 25:
						ptr = "requested standard text not valid";
						break;
					case 26:
						ptr = "message type not valid for pager type";
						break;
					case 27:
						ptr = "message not found in SMSC";
						break;
					case 30:
						ptr = "subscriber hang-up";
						break;
					case 31:
						ptr = "fax group not supported";
						break;
					case 32:
						ptr = "fax message type not supported";
						break;
					case 33:
						ptr = "address already in list (60 series)";
						break;
					case 34:
						ptr = "address not in list (60 series)";
						break;
					case 35:
						ptr = "list full (60 series)";
						break;
					}
					V (1, ("Invalid message (error %d) %s\n", f -> b.a.ec, (ptr ? ptr : "")));
					switch (f -> b.a.ec) {
					case -1:
					case 1:		case 2:		case 3:
					case 6:		case 7:		case 8:
					case 9:		case 10:	case 11:
					case 12:	case 13:	case 14:
					case 15:	case 16:	case 17:
					case 18:	case 19:	case 20:
					case 21:	case 22:	case 23:
					case 24:	case 25:	case 26:
					case 27:	case 31:	case 32:
					case 33:	case 34:	case 35:
						*err = ERR_FAIL;
						break;
					default:
						*err = ERR_FATAL;
						break;
					}
				}
				break;
			case 'O':
				if (n && wds && (f -> ot == 53)) {
					if (u -> logger) {
						switch (f -> b.e.dst) {
						default:	ptr = NULL;	break;
						case 0:	ptr = "delivered";	break;
						case 1:	ptr = "buffered";	break;
						}
						if (ptr)
							(*u -> logger) (LG_PROTO, "UCP: message %s", ptr);
					}
					printf ("Message ");
					switch (f -> b.e.dst) {
					case 0:	printf ("delivered");		break;
					case 1:	printf ("buffered");		break;
					case 2:	printf ("not delivered");	break;
					default:
						printf ("failed");
						break;
					}
					printf (" (%d)", f -> b.e.rsn);
					switch (f -> b.e.rsn) {
					default:
						ptr = "unknown error";
						break;
					case 0x01:	
						ptr = "successful delivered";
						break;
					case 0x02: case 0x03: case 0x04:
					case 0x05: case 0x06: case 0x07:
						ptr = "temporary no service";
						break;
					case 0x09:
						ptr = "unknown service";
						break;
					case 0x0a:
						ptr = "network timeout";
						break;
					case 0x32:
						ptr = "storing time expired";
						break;
					case 0x64:
						ptr = "service not supported";
						break;
					case 0x65:
						ptr = "receiver unknown";
						break;
					case 0x66:
						ptr = "service not available";
						break;
					case 0x67:
						ptr = "call locked";
						break;
					case 0x68:
						ptr = "operation locked";
						break;
					case 0x69:
						ptr = "service center overrun";
						break;
					case 0x6a:
						ptr = "service not supported";
						break;
					case 0x6b:
						ptr = "receiver temporary not reachable";
						break;
					case 0x6c:
						ptr = "delivering error";
						break;
					case 0x6d:
						ptr = "receiver run out of memory";
						break;
					case 0x6e:
						ptr = "protocol error";
						break;
					case 0x6f:
						ptr = "receiver does not support service";
						break;
					case 0x70:
						ptr = "unknown serice center";
						break;
					case 0x71:
						ptr = "service center overrun";
						break;
					case 0x72:
						ptr = "illegal receiving device";
						break;
					case 0x73:
						ptr = "receiver no customer";
						break;
					case 0x74:
						ptr = "error in receiving device";
						break;
					case 0x75:
						ptr = "lower protocol not available";
						break;
					case 0x76:
						ptr = "system error";
						break;
					case 0x77:
						ptr = "PLMN system error";
						break;
					case 0x78:
						ptr = "HLR system error";
						break;
					case 0x79:
						ptr = "VLR system error";
						break;
					case 0x7a:
						ptr = "previous VLR system error";
						break;
					case 0x7b:
						ptr = "error on delivering (check receiver ID)";
						break;
					case 0x7c:
						ptr = "VMSC system error";
						break;
					case 0x7d:
						ptr = "EIR system error";
						break;
					case 0x7e:
						ptr = "system error";
						break;
					case 0x7f:
						ptr = "unexpected data";
						break;
					case 0xc8:
						ptr = "addressing error for service center";
						break;
					case 0xc9:
						ptr = "invalid absolute storing time";
						break;
					case 0xca:
						ptr = "message too large";
						break;
					case 0xcb:
						ptr = "GDM message cannot be extracted";
						break;
					case 0xcc:
						ptr = "translation into IA5 not possible";
						break;
					case 0xcd:
						ptr = "invalid format of storing time";
						break;
					case 0xce:
						ptr = "invalid receiver address";
						break;
					case 0xcf:
						ptr = "message sent twice";
						break;
					case 0xd0:
						ptr = "invalid message type";
						break;
					}
					if (ptr)
						printf (" reason is %s", ptr);
					if (f -> b.e.msg)
						printf (": %s", schar (f -> b.e.msg));
					printf ("\n");
				} else {
					if (! n)
						--n;
					interpret_originate (u, f);
				}
				if (ans = new_frame ('R', f -> ot)) {
					ans -> trn = f -> trn;
					an = & ans -> b.a;
					an -> adc = u -> callid ? sextract (u -> callid) : NULL;
					dat_localtime (& an -> scts);
					switch (f -> ot) {
					case 1:
						an -> ack = False;
						an -> ec = 23;
						break;
					case 31:
						an -> ack = True;
						dosend = True;
						break;
					case 51:
						an -> ack = False;
						an -> ec = 23;
						break;
					case 52:
						an -> ack = True;
						break;
					case 53:
						an -> ack = True;
						break;
					case 55:
						an -> ack = False;
						an -> ec = 23;
						break;
					case 56:
						an -> ack = False;
						an -> ec = 23;
						break;
					case 57:
						an -> ack = True;
						break;
					case 58:
						an -> ack = True;
						break;
					}
					if (astr = assemble_frame (u, ans)) {
						if (tty_send (u -> sp, astr -> str, astr -> len) != astr -> len) {
							V (1, ("Unable to send answer\n"));
							*err = ERR_FAIL;
						}
						sfree (astr);
					}
					free_frame (ans);
				}
				break;
			}
			free_frame (f);
			if (*err != NO_ERR)
				break;
		}
	}
	return ret;
}
/*}}}*/
/*{{{	login/logout/transmit */
int
ucp_login (void *up, string_t *callid)
{
	ucp	*u = (ucp *) up;

	MCHK (u);
	if ((! u) || (! u -> sp))
		return ERR_ABORT;
	u -> cnr = 0;
	u -> callid = sfree (u -> callid);
	if (callid && (! (u -> callid = snew (callid -> str, callid -> len))))
		return ERR_ABORT;
	return NO_ERR;
}

int
ucp_logout (void *up)
{
	MCHK ((ucp *) up);
	return NO_ERR;
}

int
ucp_transmit (void *up, string_t *pagerid, string_t *msg, Bool last)
{
	ucp		*u = (ucp *) up;
	int		n, err;
	Bool		done;
	string_t	*smsg;
	
	MCHK (u);
	if ((! u) || (! u -> sp))
		return ERR_FATAL;
	err = NO_ERR;
	tty_set_line_callback (u -> sp, grabline, "\x03", (void *) u);
	for (n = 0; n < u -> send_retry; ++n)
		if (smsg = convert_ucp (u, last, pagerid, msg)) {
			done = send_msg (u, smsg, (u -> delay.day > 0 ? False : u -> rds), & err);
			sfree (smsg);
			if (done || (err != NO_ERR))
				break;
		}
	if ((n == u -> send_retry) || (err != NO_ERR)) {
		if (err == NO_ERR)
			err = ERR_FAIL;
		V (1, ("Unable to send message\n"));
	}
	tty_set_line_callback (u -> sp, NULL, NULL, NULL);
	if (u -> uline) {
		free (u -> uline);
		u -> uline = NULL;
	}
	return err;
}
/*}}}*/
/*{{{	configuration */
void
ucp_config (void *up, void (*logger) (char, char *, ...),
	    Bool xtend, int stout, int retry, int rtout,
	    date_t *delay, date_t *expire, Bool rds)
{
	ucp	*u = (ucp *) up;
	
	MCHK (u);
	if (u) {
		u -> logger = logger;
		u -> xtend = xtend;
		if (u -> xtend)
			u -> rds = rds;
		else {
			u -> rds = False;
			dat_clear (& u -> delay);
			dat_clear (& u -> expire);
		}
		if (stout >= 0)
			u -> send_tout = stout;
		if (retry >= 0)
			u -> send_retry = retry;
		if (rtout >= 0)
			u -> rds_tout = rtout;
		if (u -> xtend) {
			if (delay)
				u -> delay = *delay;
			if (expire)
				u -> expire = *expire;
		}
	}
}

void
ucp_set_convtable (void *up, void *ctab)
{
	ucp	*u = (ucp *) up;
	
	MCHK (u);
	if (u) {
		if (u -> ctab)
			cv_free (u -> ctab);
		u -> ctab = ctab;
	}
}

void
ucp_add_convtable (void *up, void *ctab)
{
	ucp	*u = (ucp *) up;
	
	MCHK (u);
	if (u) {
		if (! u -> ctab)
			u -> ctab = cv_new ();
		if (u -> ctab)
			cv_merge (u -> ctab, ctab, True);
	}
}
/*}}}*/
/*{{{	new/free/etc */
void *
ucp_new (void *sp)
{
	ucp	*u;
	
	if (u = (ucp *) malloc (sizeof (ucp))) {
# ifndef	NDEBUG
		u -> magic = MAGIC;
# endif		/* NDEBUG */
		u -> sp = sp;
		u -> ctab = NULL;
		u -> logger = NULL;
		u -> uline = NULL;
		u -> callid = NULL;
		u -> xtend = False;
		u -> send_tout = 60;
		u -> send_retry = 3;
		u -> rds_tout = 40;
		dat_clear (& u -> delay);
		dat_clear (& u -> expire);
		u -> rds = False;
		u -> cnr = 0;
	}
	return (void *) u;
}

void *
ucp_free (void *up)
{
	ucp	*u = (ucp *) up;
	
	MCHK (u);
	if (u) {
		if (u -> ctab)
			cv_free (u -> ctab);
		if (u -> uline)
			free (u -> uline);
		if (u -> callid)
			sfree (u -> callid);
		free (u);
	}
	return NULL;
}

int
ucp_preinit (void)
{
	return 0;
}

void
ucp_postdeinit (void)
{
}
/*}}}*/

