xref: /onnv-gate/usr/src/lib/libast/common/tm/tmxfmt.c (revision 4887:feebf9260c2e)
1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
18*4887Schin *                  David Korn <dgk@research.att.com>                   *
19*4887Schin *                   Phong Vo <kpv@research.att.com>                    *
20*4887Schin *                                                                      *
21*4887Schin ***********************************************************************/
22*4887Schin #pragma prototyped
23*4887Schin /*
24*4887Schin  * Glenn Fowler
25*4887Schin  * AT&T Research
26*4887Schin  *
27*4887Schin  * Time_t conversion support
28*4887Schin  */
29*4887Schin 
30*4887Schin #include <tmx.h>
31*4887Schin #include <ctype.h>
32*4887Schin 
33*4887Schin #define warped(t,n)	((t)<((n)-tmxsns(6L*30L*24L*60L*60L,0))||(t)>((n)+tmxsns(24L*60L*60L,0)))
34*4887Schin 
35*4887Schin /*
36*4887Schin  * format n with padding p into s
37*4887Schin  * return end of s
38*4887Schin  *
39*4887Schin  * p:	<0	blank padding
40*4887Schin  *	 0	no padding
41*4887Schin  *	>0	0 padding
42*4887Schin  */
43*4887Schin 
44*4887Schin static char*
45*4887Schin number(register char* s, register char* e, register long n, register int p, int w, int pad)
46*4887Schin {
47*4887Schin 	char*	b;
48*4887Schin 
49*4887Schin 	if (w && w > p)
50*4887Schin 		p = w;
51*4887Schin 	switch (pad)
52*4887Schin 	{
53*4887Schin 	case '-':
54*4887Schin 		p = 0;
55*4887Schin 		break;
56*4887Schin 	case '_':
57*4887Schin 		if (p > 0)
58*4887Schin 			p = -p;
59*4887Schin 		break;
60*4887Schin 	case '0':
61*4887Schin 		if (p < 0)
62*4887Schin 			p = -p;
63*4887Schin 		break;
64*4887Schin 	}
65*4887Schin 	b = s;
66*4887Schin 	if (p > 0)
67*4887Schin 		s += sfsprintf(s, e - s, "%0*lu", p, n);
68*4887Schin 	else if (p < 0)
69*4887Schin 		s += sfsprintf(s, e - s, "%*lu", -p, n);
70*4887Schin 	else
71*4887Schin 		s += sfsprintf(s, e - s, "%lu", n);
72*4887Schin 	if (w && (s - b) > w)
73*4887Schin 		*(s = b + w) = 0;
74*4887Schin 	return s;
75*4887Schin }
76*4887Schin 
77*4887Schin typedef struct Stack_s
78*4887Schin {
79*4887Schin 	char*		format;
80*4887Schin 	int		delimiter;
81*4887Schin } Stack_t;
82*4887Schin 
83*4887Schin /*
84*4887Schin  * format t into buf of length len
85*4887Schin  * end of buf is returned
86*4887Schin  */
87*4887Schin 
88*4887Schin char*
89*4887Schin tmxfmt(char* buf, size_t len, const char* format, Time_t t)
90*4887Schin {
91*4887Schin 	register char*	cp;
92*4887Schin 	register char*	ep;
93*4887Schin 	register char*	p;
94*4887Schin 	register int	n;
95*4887Schin 	int		c;
96*4887Schin 	int		i;
97*4887Schin 	int		flags;
98*4887Schin 	int		pad;
99*4887Schin 	int		delimiter;
100*4887Schin 	int		width;
101*4887Schin 	int		prec;
102*4887Schin 	int		parts;
103*4887Schin 	char*		f;
104*4887Schin 	const char*	oformat;
105*4887Schin 	Tm_t*		tp;
106*4887Schin 	Tm_zone_t*	zp;
107*4887Schin 	Time_t		now;
108*4887Schin 	Stack_t*	sp;
109*4887Schin 	Stack_t		stack[8];
110*4887Schin 	char		fmt[32];
111*4887Schin 
112*4887Schin 	tmlocale();
113*4887Schin 	tp = tmxmake(t);
114*4887Schin 	if (!format || !*format)
115*4887Schin 		format = tm_info.deformat;
116*4887Schin 	oformat = format;
117*4887Schin 	flags = tm_info.flags;
118*4887Schin 	sp = &stack[0];
119*4887Schin 	cp = buf;
120*4887Schin 	ep = buf + len;
121*4887Schin 	delimiter = 0;
122*4887Schin 	for (;;)
123*4887Schin 	{
124*4887Schin 		if ((c = *format++) == delimiter)
125*4887Schin 		{
126*4887Schin 			delimiter = 0;
127*4887Schin 			if (sp <= &stack[0])
128*4887Schin 				break;
129*4887Schin 			sp--;
130*4887Schin 			format = sp->format;
131*4887Schin 			delimiter = sp->delimiter;
132*4887Schin 			continue;
133*4887Schin 		}
134*4887Schin 		if (c != '%')
135*4887Schin 		{
136*4887Schin 			if (cp < ep)
137*4887Schin 				*cp++ = c;
138*4887Schin 			continue;
139*4887Schin 		}
140*4887Schin 		pad = 0;
141*4887Schin 		width = 0;
142*4887Schin 		prec = 0;
143*4887Schin 		parts = 0;
144*4887Schin 		for (;;)
145*4887Schin 		{
146*4887Schin 			switch (c = *format++)
147*4887Schin 			{
148*4887Schin 			case '_':
149*4887Schin 			case '-':
150*4887Schin 				pad = c;
151*4887Schin 				continue;
152*4887Schin 			case 'E':
153*4887Schin 			case 'O':
154*4887Schin 				if (!isalpha(*format))
155*4887Schin 					break;
156*4887Schin 				continue;
157*4887Schin 			case '0':
158*4887Schin 				if (!parts)
159*4887Schin 				{
160*4887Schin 					pad = c;
161*4887Schin 					continue;
162*4887Schin 				}
163*4887Schin 				/*FALLTHROUGH*/
164*4887Schin 			case '1':
165*4887Schin 			case '2':
166*4887Schin 			case '3':
167*4887Schin 			case '4':
168*4887Schin 			case '5':
169*4887Schin 			case '6':
170*4887Schin 			case '7':
171*4887Schin 			case '8':
172*4887Schin 			case '9':
173*4887Schin 				switch (parts)
174*4887Schin 				{
175*4887Schin 				case 0:
176*4887Schin 					parts++;
177*4887Schin 					/*FALLTHROUGH*/
178*4887Schin 				case 1:
179*4887Schin 					width = width * 10 + (c - '0');
180*4887Schin 					break;
181*4887Schin 				case 2:
182*4887Schin 					prec = prec * 10 + (c - '0');
183*4887Schin 					break;
184*4887Schin 				}
185*4887Schin 				continue;
186*4887Schin 			case '.':
187*4887Schin 				if (!parts++)
188*4887Schin 					parts++;
189*4887Schin 				continue;
190*4887Schin 			default:
191*4887Schin 				break;
192*4887Schin 			}
193*4887Schin 			break;
194*4887Schin 		}
195*4887Schin 		switch (c)
196*4887Schin 		{
197*4887Schin 		case 0:
198*4887Schin 			format--;
199*4887Schin 			continue;
200*4887Schin 		case '%':
201*4887Schin 			if (cp < ep)
202*4887Schin 				*cp++ = '%';
203*4887Schin 			continue;
204*4887Schin 		case '?':
205*4887Schin 			if (tm_info.deformat != tm_info.format[TM_DEFAULT])
206*4887Schin 				format = tm_info.deformat;
207*4887Schin 			else if (!*format)
208*4887Schin 				format = tm_info.format[TM_DEFAULT];
209*4887Schin 			continue;
210*4887Schin 		case 'a':	/* abbreviated day of week name */
211*4887Schin 			n = TM_DAY_ABBREV + tp->tm_wday;
212*4887Schin 			goto index;
213*4887Schin 		case 'A':	/* day of week name */
214*4887Schin 			n = TM_DAY + tp->tm_wday;
215*4887Schin 			goto index;
216*4887Schin 		case 'b':	/* abbreviated month name */
217*4887Schin 		case 'h':
218*4887Schin 			n = TM_MONTH_ABBREV + tp->tm_mon;
219*4887Schin 			goto index;
220*4887Schin 		case 'B':	/* month name */
221*4887Schin 			n = TM_MONTH + tp->tm_mon;
222*4887Schin 			goto index;
223*4887Schin 		case 'c':	/* `ctime(3)' date sans newline */
224*4887Schin 			p = tm_info.format[TM_CTIME];
225*4887Schin 			goto push;
226*4887Schin 		case 'C':	/* 2 digit century */
227*4887Schin 			cp = number(cp, ep, (long)(1900 + tp->tm_year) / 100, 2, width, pad);
228*4887Schin 			continue;
229*4887Schin 		case 'd':	/* day of month */
230*4887Schin 			cp = number(cp, ep, (long)tp->tm_mday, 2, width, pad);
231*4887Schin 			continue;
232*4887Schin 		case 'D':	/* date */
233*4887Schin 			p = tm_info.format[TM_DATE];
234*4887Schin 			goto push;
235*4887Schin 		case 'E':       /* OBSOLETE no pad day of month */
236*4887Schin 			cp = number(cp, ep, (long)tp->tm_mday, 0, width, pad);
237*4887Schin 			continue;
238*4887Schin 		case 'e':       /* blank padded day of month */
239*4887Schin 			cp = number(cp, ep, (long)tp->tm_mday, -2, width, pad);
240*4887Schin 			continue;
241*4887Schin 		case 'f':	/* TM_DEFAULT override */
242*4887Schin 		case 'o':	/* OBSOLETE */
243*4887Schin 			p = tm_info.deformat;
244*4887Schin 			goto push;
245*4887Schin 		case 'F':	/* TM_DEFAULT */
246*4887Schin 		case 'O':	/* OBSOLETE */
247*4887Schin 			p = tm_info.format[TM_DEFAULT];
248*4887Schin 			goto push;
249*4887Schin 		case 'g':	/* %V 2 digit year */
250*4887Schin 		case 'G':	/* %V 4 digit year */
251*4887Schin 			n = tp->tm_year + 1900;
252*4887Schin 			if (tp->tm_yday < 7)
253*4887Schin 			{
254*4887Schin 				if (tmweek(tp, 2, -1, -1) >= 52)
255*4887Schin 					n--;
256*4887Schin 			}
257*4887Schin 			else if (tp->tm_yday > 358)
258*4887Schin 			{
259*4887Schin 				if (tmweek(tp, 2, -1, -1) <= 1)
260*4887Schin 					n++;
261*4887Schin 			}
262*4887Schin 			if (c == 'g')
263*4887Schin 			{
264*4887Schin 				n %= 100;
265*4887Schin 				c = 2;
266*4887Schin 			}
267*4887Schin 			else
268*4887Schin 				c = 4;
269*4887Schin 			cp = number(cp, ep, (long)n, c, width, pad);
270*4887Schin 			continue;
271*4887Schin 		case 'H':	/* hour (0 - 23) */
272*4887Schin 			cp = number(cp, ep, (long)tp->tm_hour, 2, width, pad);
273*4887Schin 			continue;
274*4887Schin 		case 'i':	/* international `date(1)' date */
275*4887Schin 			p = tm_info.format[TM_INTERNATIONAL];
276*4887Schin 			goto push;
277*4887Schin 		case 'I':	/* hour (0 - 12) */
278*4887Schin 			if ((n = tp->tm_hour) > 12) n -= 12;
279*4887Schin 			else if (n == 0) n = 12;
280*4887Schin 			cp = number(cp, ep, (long)n, 2, width, pad);
281*4887Schin 			continue;
282*4887Schin 		case 'J':	/* Julian date (0 offset) */
283*4887Schin 			cp = number(cp, ep, (long)tp->tm_yday, 3, width, pad);
284*4887Schin 			continue;
285*4887Schin 		case 'j':	/* Julian date (1 offset) */
286*4887Schin 			cp = number(cp, ep, (long)(tp->tm_yday + 1), 3, width, pad);
287*4887Schin 			continue;
288*4887Schin 		case 'k':	/* `date(1)' date */
289*4887Schin 			p = tm_info.format[TM_DATE_1];
290*4887Schin 			goto push;
291*4887Schin 		case 'K':
292*4887Schin 			p = "%Y-%m-%d+%H:%M:%S";
293*4887Schin 			goto push;
294*4887Schin 		case 'l':	/* `ls -l' date */
295*4887Schin 			if (t)
296*4887Schin 			{
297*4887Schin 				now = tmxgettime();
298*4887Schin 				if (warped(t, now))
299*4887Schin 				{
300*4887Schin 					p = tm_info.format[TM_DISTANT];
301*4887Schin 					goto push;
302*4887Schin 				}
303*4887Schin 			}
304*4887Schin 			p = tm_info.format[TM_RECENT];
305*4887Schin 			goto push;
306*4887Schin 		case 'm':	/* month number */
307*4887Schin 			cp = number(cp, ep, (long)(tp->tm_mon + 1), 2, width, pad);
308*4887Schin 			continue;
309*4887Schin 		case 'M':	/* minutes */
310*4887Schin 			cp = number(cp, ep, (long)tp->tm_min, 2, width, pad);
311*4887Schin 			continue;
312*4887Schin 		case 'n':
313*4887Schin 			if (cp < ep)
314*4887Schin 				*cp++ = '\n';
315*4887Schin 			continue;
316*4887Schin 		case 'N':	/* nanosecond part */
317*4887Schin 			cp = number(cp, ep, (long)tp->tm_nsec, 9, width, pad);
318*4887Schin 			continue;
319*4887Schin 		case 'p':	/* meridian */
320*4887Schin 			n = TM_MERIDIAN + (tp->tm_hour >= 12);
321*4887Schin 			goto index;
322*4887Schin 		case 'q':	/* time zone type (nation code) */
323*4887Schin 			if (!(flags & TM_UTC))
324*4887Schin 			{
325*4887Schin 				if ((zp = tm_info.zone) != tm_info.local)
326*4887Schin 					for (; zp >= tm_data.zone; zp--)
327*4887Schin 						if (p = zp->type)
328*4887Schin 							goto string;
329*4887Schin 				else if (p = zp->type)
330*4887Schin 					goto string;
331*4887Schin 			}
332*4887Schin 			continue;
333*4887Schin 		case 'Q':	/* %Q<alpha> or %Q<delim>recent<delim>distant<delim> */
334*4887Schin 			if (c = *format)
335*4887Schin 			{
336*4887Schin 				format++;
337*4887Schin 				if (isalpha(c))
338*4887Schin 				{
339*4887Schin 					switch (c)
340*4887Schin 					{
341*4887Schin 					case 'd':	/* `ls -l' distant date */
342*4887Schin 						p = tm_info.format[TM_DISTANT];
343*4887Schin 						goto push;
344*4887Schin 					case 'r':	/* `ls -l' recent date */
345*4887Schin 						p = tm_info.format[TM_RECENT];
346*4887Schin 						goto push;
347*4887Schin 					default:
348*4887Schin 						format--;
349*4887Schin 						break;
350*4887Schin 					}
351*4887Schin 				}
352*4887Schin 				else
353*4887Schin 				{
354*4887Schin 					if (t)
355*4887Schin 					{
356*4887Schin 						now = tmxgettime();
357*4887Schin 						p = warped(t, now) ? (char*)0 : (char*)format;
358*4887Schin 					}
359*4887Schin 					else
360*4887Schin 						p = (char*)format;
361*4887Schin 					i = 0;
362*4887Schin 					while (n = *format)
363*4887Schin 					{
364*4887Schin 						format++;
365*4887Schin 						if (n == c)
366*4887Schin 						{
367*4887Schin 							if (!p)
368*4887Schin 								p = (char*)format;
369*4887Schin 							if (++i == 2)
370*4887Schin 								goto push_delimiter;
371*4887Schin 						}
372*4887Schin 					}
373*4887Schin 				}
374*4887Schin 			}
375*4887Schin 			continue;
376*4887Schin 		case 'r':
377*4887Schin 			p = tm_info.format[TM_MERIDIAN_TIME];
378*4887Schin 			goto push;
379*4887Schin 		case 'R':
380*4887Schin 			p = "%H:%M";
381*4887Schin 			goto push;
382*4887Schin 		case 's':	/* seconds[.nanoseconds] since the epoch */
383*4887Schin 		case '#':
384*4887Schin 			if (t)
385*4887Schin 				now = t;
386*4887Schin 			else
387*4887Schin 				now = tmxgettime();
388*4887Schin 			f = fmt;
389*4887Schin 			*f++ = '%';
390*4887Schin 			if (pad == '0')
391*4887Schin 				*f++ = pad;
392*4887Schin 			if (width)
393*4887Schin 				f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "%d", width);
394*4887Schin 			f += sfsprintf(f, &fmt[sizeof(fmt)] - f, "I%du", sizeof(Tmxsec_t));
395*4887Schin 			cp += sfsprintf(cp, ep - cp, fmt, tmxsec(now));
396*4887Schin 			if (parts > 1)
397*4887Schin 			{
398*4887Schin 				n = sfsprintf(cp, ep - cp, ".%09I*u", sizeof(Tmxnsec_t), tmxnsec(now));
399*4887Schin 				if (prec && n >= prec)
400*4887Schin 					n = prec + 1;
401*4887Schin 				cp += n;
402*4887Schin 			}
403*4887Schin 			continue;
404*4887Schin 		case 'S':	/* seconds */
405*4887Schin 			cp = number(cp, ep, (long)tp->tm_sec, 2, width, pad);
406*4887Schin 			if ((flags & TM_SUBSECOND) && (format - 2) != oformat)
407*4887Schin 			{
408*4887Schin 				p = ".%N";
409*4887Schin 				goto push;
410*4887Schin 			}
411*4887Schin 			continue;
412*4887Schin 		case 't':
413*4887Schin 			if (cp < ep)
414*4887Schin 				*cp++ = '\t';
415*4887Schin 			continue;
416*4887Schin 		case 'T':
417*4887Schin 			p = tm_info.format[TM_TIME];
418*4887Schin 			goto push;
419*4887Schin 		case 'u':	/* weekday number [1(Monday)-7] */
420*4887Schin 			if (!(i = tp->tm_wday))
421*4887Schin 				i = 7;
422*4887Schin 			cp = number(cp, ep, (long)i, 0, width, pad);
423*4887Schin 			continue;
424*4887Schin 		case 'U':	/* week number, Sunday as first day */
425*4887Schin 			cp = number(cp, ep, (long)tmweek(tp, 0, -1, -1), 2, width, pad);
426*4887Schin 			continue;
427*4887Schin 		case 'V':	/* ISO week number */
428*4887Schin 			cp = number(cp, ep, (long)tmweek(tp, 2, -1, -1), 2, width, pad);
429*4887Schin 			continue;
430*4887Schin 		case 'W':	/* week number, Monday as first day */
431*4887Schin 			cp = number(cp, ep, (long)tmweek(tp, 1, -1, -1), 2, width, pad);
432*4887Schin 			continue;
433*4887Schin 		case 'w':	/* weekday number [0(Sunday)-6] */
434*4887Schin 			cp = number(cp, ep, (long)tp->tm_wday, 0, width, pad);
435*4887Schin 			continue;
436*4887Schin 		case 'x':
437*4887Schin 			p = tm_info.format[TM_DATE];
438*4887Schin 			goto push;
439*4887Schin 		case 'X':
440*4887Schin 			p = tm_info.format[TM_TIME];
441*4887Schin 			goto push;
442*4887Schin 		case 'y':	/* year in the form yy */
443*4887Schin 			cp = number(cp, ep, (long)(tp->tm_year % 100), 2, width, pad);
444*4887Schin 			continue;
445*4887Schin 		case 'Y':	/* year in the form ccyy */
446*4887Schin 			cp = number(cp, ep, (long)(1900 + tp->tm_year), 4, width, pad);
447*4887Schin 			continue;
448*4887Schin 		case 'z':	/* time zone west offset */
449*4887Schin 			if ((ep - cp) >= 16)
450*4887Schin 				cp = tmpoff(cp, ep - cp, "", (flags & TM_UTC) ? 0 : tm_info.zone->west, 24 * 60);
451*4887Schin 			continue;
452*4887Schin 		case 'Z':	/* time zone */
453*4887Schin 			p = (flags & TM_UTC) ? tm_info.format[TM_UT] : tp->tm_isdst && tm_info.zone->daylight ? tm_info.zone->daylight : tm_info.zone->standard;
454*4887Schin 			goto string;
455*4887Schin 		case '+':	/* old %+flag */
456*4887Schin 		case '!':	/* old %!flag */
457*4887Schin 			format--;
458*4887Schin 			/*FALLTHROUGH*/
459*4887Schin 		case '=':	/* %=[=][+-]flag */
460*4887Schin 			if (i = *format == '=')
461*4887Schin 				format++;
462*4887Schin 			if (*format == '+' || *format == '-' || *format == '!')
463*4887Schin 				c = *format++;
464*4887Schin 			else
465*4887Schin 				c = '+';
466*4887Schin 			switch (*format++)
467*4887Schin 			{
468*4887Schin 			case 0:
469*4887Schin 				format--;
470*4887Schin 				continue;
471*4887Schin 			case 'l':
472*4887Schin 				n = TM_LEAP;
473*4887Schin 				break;
474*4887Schin 			case 'n':
475*4887Schin 			case 's':
476*4887Schin 				n = TM_SUBSECOND;
477*4887Schin 				break;
478*4887Schin 			case 'u':
479*4887Schin 				n = TM_UTC;
480*4887Schin 				break;
481*4887Schin 			}
482*4887Schin 			if (n)
483*4887Schin 			{
484*4887Schin 				/*
485*4887Schin 				 * right, the global state stinks
486*4887Schin 				 * but we respect its locale-like status
487*4887Schin 				 */
488*4887Schin 
489*4887Schin 				if (c == '+')
490*4887Schin 				{
491*4887Schin 					if (!(flags & n))
492*4887Schin 					{
493*4887Schin 						flags |= n;
494*4887Schin 						tm_info.flags |= n;
495*4887Schin 						tp = tmxmake(t);
496*4887Schin 						if (!i)
497*4887Schin 							tm_info.flags &= ~n;
498*4887Schin 					}
499*4887Schin 				}
500*4887Schin 				else if (flags & n)
501*4887Schin 				{
502*4887Schin 					flags &= ~n;
503*4887Schin 					tm_info.flags &= ~n;
504*4887Schin 					tp = tmxmake(t);
505*4887Schin 					if (!i)
506*4887Schin 						tm_info.flags |= n;
507*4887Schin 				}
508*4887Schin 			}
509*4887Schin 			continue;
510*4887Schin 		default:
511*4887Schin 			if (cp < ep)
512*4887Schin 				*cp++ = '%';
513*4887Schin 			if (cp < ep)
514*4887Schin 				*cp++ = c;
515*4887Schin 			continue;
516*4887Schin 		}
517*4887Schin 	index:
518*4887Schin 		p = tm_info.format[n];
519*4887Schin 	string:
520*4887Schin 		while (cp < ep && (*cp = *p++))
521*4887Schin 			cp++;
522*4887Schin 		continue;
523*4887Schin 	push:
524*4887Schin 		c = 0;
525*4887Schin 	push_delimiter:
526*4887Schin 		if (sp < &stack[elementsof(stack)])
527*4887Schin 		{
528*4887Schin 			sp->format = (char*)format;
529*4887Schin 			format = p;
530*4887Schin 			sp->delimiter = delimiter;
531*4887Schin 			delimiter = c;
532*4887Schin 			sp++;
533*4887Schin 		}
534*4887Schin 		continue;
535*4887Schin 	}
536*4887Schin 	tm_info.flags = flags;
537*4887Schin 	if (cp >= ep)
538*4887Schin 		cp = ep - 1;
539*4887Schin 	*cp = 0;
540*4887Schin 	return cp;
541*4887Schin }
542