1/*
2 * lib/utils.c		Utility Functions
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup core
14 * @defgroup utils Utilities
15 * @{
16 */
17
18#include <netlink-local.h>
19#include <netlink/netlink.h>
20#include <netlink/utils.h>
21#include <linux/socket.h>
22
23/**
24 * Debug level
25 */
26int nl_debug = 0;
27
28struct nl_dump_params nl_debug_dp = {
29	.dp_type = NL_DUMP_DETAILS,
30};
31
32static void __init nl_debug_init(void)
33{
34	char *nldbg, *end;
35
36	if ((nldbg = getenv("NLDBG"))) {
37		long level = strtol(nldbg, &end, 0);
38		if (nldbg != end)
39			nl_debug = level;
40	}
41
42	nl_debug_dp.dp_fd = stderr;
43}
44
45int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *))
46{
47	FILE *fd;
48	char buf[128];
49
50	fd = fopen(path, "r");
51	if (fd == NULL)
52		return -nl_syserr2nlerr(errno);
53
54	while (fgets(buf, sizeof(buf), fd)) {
55		int goodlen, err;
56		long num;
57		char *end;
58
59		if (*buf == '#' || *buf == '\n' || *buf == '\r')
60			continue;
61
62		num = strtol(buf, &end, 0);
63		if (end == buf)
64			return -NLE_INVAL;
65
66		if (num == LONG_MIN || num == LONG_MAX)
67			return -NLE_RANGE;
68
69		while (*end == ' ' || *end == '\t')
70			end++;
71
72		goodlen = strcspn(end, "#\r\n\t ");
73		if (goodlen == 0)
74			return -NLE_INVAL;
75
76		end[goodlen] = '\0';
77
78		err = cb(num, end);
79		if (err < 0)
80			return err;
81	}
82
83	fclose(fd);
84
85	return 0;
86}
87
88/**
89 * @name Unit Pretty-Printing
90 * @{
91 */
92
93/**
94 * Cancel down a byte counter
95 * @arg	l		byte counter
96 * @arg	unit		destination unit pointer
97 *
98 * Cancels down a byte counter until it reaches a reasonable
99 * unit. The chosen unit is assigned to \a unit.
100 *
101 * @return The cancelled down byte counter in the new unit.
102 */
103double nl_cancel_down_bytes(unsigned long long l, char **unit)
104{
105	if (l >= 1099511627776LL) {
106		*unit = "TiB";
107		return ((double) l) / 1099511627776LL;
108	} else if (l >= 1073741824) {
109		*unit = "GiB";
110		return ((double) l) / 1073741824;
111	} else if (l >= 1048576) {
112		*unit = "MiB";
113		return ((double) l) / 1048576;
114	} else if (l >= 1024) {
115		*unit = "KiB";
116		return ((double) l) / 1024;
117	} else {
118		*unit = "B";
119		return (double) l;
120	}
121}
122
123/**
124 * Cancel down a bit counter
125 * @arg	l		bit counter
126 * @arg unit		destination unit pointer
127 *
128 * Cancels downa bit counter until it reaches a reasonable
129 * unit. The chosen unit is assigned to \a unit.
130 *
131 * @return The cancelled down bit counter in the new unit.
132 */
133double nl_cancel_down_bits(unsigned long long l, char **unit)
134{
135	if (l >= 1099511627776ULL) {
136		*unit = "Tbit";
137		return ((double) l) / 1099511627776ULL;
138	} else if (l >= 1073741824) {
139		*unit = "Gbit";
140		return ((double) l) / 1073741824;
141	} else if (l >= 1048576) {
142		*unit = "Mbit";
143		return ((double) l) / 1048576;
144	} else if (l >= 1024) {
145		*unit = "Kbit";
146		return ((double) l) / 1024;
147	} else {
148		*unit = "bit";
149		return (double) l;
150	}
151
152}
153
154/**
155 * Cancel down a micro second value
156 * @arg	l		micro seconds
157 * @arg unit		destination unit pointer
158 *
159 * Cancels down a microsecond counter until it reaches a
160 * reasonable unit. The chosen unit is assigned to \a unit.
161 *
162 * @return The cancelled down microsecond in the new unit
163 */
164double nl_cancel_down_us(uint32_t l, char **unit)
165{
166	if (l >= 1000000) {
167		*unit = "s";
168		return ((double) l) / 1000000;
169	} else if (l >= 1000) {
170		*unit = "ms";
171		return ((double) l) / 1000;
172	} else {
173		*unit = "us";
174		return (double) l;
175	}
176}
177
178/** @} */
179
180/**
181 * @name Generic Unit Translations
182 * @{
183 */
184
185/**
186 * Convert a character string to a size
187 * @arg str		size encoded as character string
188 *
189 * Converts the specified size as character to the corresponding
190 * number of bytes.
191 *
192 * Supported formats are:
193 *  - b,kb/k,m/mb,gb/g for bytes
194 *  - bit,kbit/mbit/gbit
195 *
196 * @return The number of bytes or -1 if the string is unparseable
197 */
198long nl_size2int(const char *str)
199{
200	char *p;
201	long l = strtol(str, &p, 0);
202	if (p == str)
203		return -NLE_INVAL;
204
205	if (*p) {
206		if (!strcasecmp(p, "kb") || !strcasecmp(p, "k"))
207			l *= 1024;
208		else if (!strcasecmp(p, "gb") || !strcasecmp(p, "g"))
209			l *= 1024*1024*1024;
210		else if (!strcasecmp(p, "gbit"))
211			l *= 1024*1024*1024/8;
212		else if (!strcasecmp(p, "mb") || !strcasecmp(p, "m"))
213			l *= 1024*1024;
214		else if (!strcasecmp(p, "mbit"))
215			l *= 1024*1024/8;
216		else if (!strcasecmp(p, "kbit"))
217			l *= 1024/8;
218		else if (!strcasecmp(p, "bit"))
219			l /= 8;
220		else if (strcasecmp(p, "b") != 0)
221			return -NLE_INVAL;
222	}
223
224	return l;
225}
226
227/**
228 * Convert a character string to a probability
229 * @arg str		probability encoded as character string
230 *
231 * Converts the specified probability as character to the
232 * corresponding probability number.
233 *
234 * Supported formats are:
235 *  - 0.0-1.0
236 *  - 0%-100%
237 *
238 * @return The probability relative to NL_PROB_MIN and NL_PROB_MAX
239 */
240long nl_prob2int(const char *str)
241{
242	char *p;
243	double d = strtod(str, &p);
244
245	if (p == str)
246		return -NLE_INVAL;
247
248	if (d > 1.0)
249		d /= 100.0f;
250
251	if (d > 1.0f || d < 0.0f)
252		return -NLE_RANGE;
253
254	if (*p && strcmp(p, "%") != 0)
255		return -NLE_INVAL;
256
257	return rint(d * NL_PROB_MAX);
258}
259
260/** @} */
261
262/**
263 * @name Time Translations
264 * @{
265 */
266
267#ifdef USER_HZ
268static uint32_t user_hz = USER_HZ;
269#else
270static uint32_t user_hz = 100;
271#endif
272
273static double ticks_per_usec = 1.0f;
274
275/* Retrieves the configured HZ and ticks/us value in the kernel.
276 * The value is cached. Supported ways of getting it:
277 *
278 * 1) environment variable
279 * 2) /proc/net/psched and sysconf
280 *
281 * Supports the environment variables:
282 *   PROC_NET_PSCHED  - may point to psched file in /proc
283 *   PROC_ROOT        - may point to /proc fs */
284static void __init get_psched_settings(void)
285{
286	char name[FILENAME_MAX];
287	FILE *fd;
288	int got_hz = 0;
289
290	if (getenv("HZ")) {
291		long hz = strtol(getenv("HZ"), NULL, 0);
292
293		if (LONG_MIN != hz && LONG_MAX != hz) {
294			user_hz = hz;
295			got_hz = 1;
296		}
297	}
298
299	if (!got_hz)
300		user_hz = sysconf(_SC_CLK_TCK);
301
302	if (getenv("TICKS_PER_USEC")) {
303		double t = strtod(getenv("TICKS_PER_USEC"), NULL);
304		ticks_per_usec = t;
305	}
306	else {
307		if (getenv("PROC_NET_PSCHED"))
308			snprintf(name, sizeof(name), "%s", getenv("PROC_NET_PSCHED"));
309		else if (getenv("PROC_ROOT"))
310			snprintf(name, sizeof(name), "%s/net/psched",
311				 getenv("PROC_ROOT"));
312		else
313			strncpy(name, "/proc/net/psched", sizeof(name) - 1);
314
315		if ((fd = fopen(name, "r"))) {
316			uint32_t tick, us;
317			/* the file contains 4 hexadecimals, but we just use
318			   the first two of them */
319			fscanf(fd, "%08x %08x", &tick, &us);
320			ticks_per_usec = (double)tick/(double)us;
321			fclose(fd);
322		}
323	}
324}
325
326
327/**
328 * Return the value of HZ
329 */
330int nl_get_hz(void)
331{
332	return user_hz;
333}
334
335
336/**
337 * Convert micro seconds to ticks
338 * @arg us		micro seconds
339 * @return number of ticks
340 */
341uint32_t nl_us2ticks(uint32_t us)
342{
343	return us * ticks_per_usec;
344}
345
346
347/**
348 * Convert ticks to micro seconds
349 * @arg ticks		number of ticks
350 * @return microseconds
351 */
352uint32_t nl_ticks2us(uint32_t ticks)
353{
354	return ticks / ticks_per_usec;
355}
356
357int nl_str2msec(const char *str, uint64_t *result)
358{
359	uint64_t total = 0, l;
360	int plen;
361	char *p;
362
363	do {
364		l = strtoul(str, &p, 0);
365		if (p == str)
366			return -NLE_INVAL;
367		else if (*p) {
368			plen = strcspn(p, " \t");
369
370			if (!plen)
371				total += l;
372			else if (!strncasecmp(p, "sec", plen))
373				total += (l * 1000);
374			else if (!strncasecmp(p, "min", plen))
375				total += (l * 1000*60);
376			else if (!strncasecmp(p, "hour", plen))
377				total += (l * 1000*60*60);
378			else if (!strncasecmp(p, "day", plen))
379				total += (l * 1000*60*60*24);
380			else
381				return -NLE_INVAL;
382
383			str = p + plen;
384		} else
385			total += l;
386	} while (*str && *p);
387
388	*result = total;
389
390	return 0;
391}
392
393/**
394 * Convert milliseconds to a character string
395 * @arg msec		number of milliseconds
396 * @arg buf		destination buffer
397 * @arg len		buffer length
398 *
399 * Converts milliseconds to a character string split up in days, hours,
400 * minutes, seconds, and milliseconds and stores it in the specified
401 * destination buffer.
402 *
403 * @return The destination buffer.
404 */
405char * nl_msec2str(uint64_t msec, char *buf, size_t len)
406{
407	int i, split[5];
408	char *units[] = {"d", "h", "m", "s", "msec"};
409
410#define _SPLIT(idx, unit) if ((split[idx] = msec / unit) > 0) msec %= unit
411	_SPLIT(0, 86400000);	/* days */
412	_SPLIT(1, 3600000);	/* hours */
413	_SPLIT(2, 60000);	/* minutes */
414	_SPLIT(3, 1000);	/* seconds */
415#undef  _SPLIT
416	split[4] = msec;
417
418	memset(buf, 0, len);
419
420	for (i = 0; i < ARRAY_SIZE(split); i++) {
421		if (split[i] > 0) {
422			char t[64];
423			snprintf(t, sizeof(t), "%s%d%s",
424				 strlen(buf) ? " " : "", split[i], units[i]);
425			strncat(buf, t, len - strlen(buf) - 1);
426		}
427	}
428
429	return buf;
430}
431
432/** @} */
433
434/**
435 * @name Netlink Family Translations
436 * @{
437 */
438
439static struct trans_tbl nlfamilies[] = {
440	__ADD(NETLINK_ROUTE,route)
441	__ADD(NETLINK_USERSOCK,usersock)
442	__ADD(NETLINK_FIREWALL,firewall)
443	__ADD(NETLINK_INET_DIAG,inetdiag)
444	__ADD(NETLINK_NFLOG,nflog)
445	__ADD(NETLINK_XFRM,xfrm)
446	__ADD(NETLINK_SELINUX,selinux)
447	__ADD(NETLINK_ISCSI,iscsi)
448	__ADD(NETLINK_AUDIT,audit)
449	__ADD(NETLINK_FIB_LOOKUP,fib_lookup)
450	__ADD(NETLINK_CONNECTOR,connector)
451	__ADD(NETLINK_NETFILTER,netfilter)
452	__ADD(NETLINK_IP6_FW,ip6_fw)
453	__ADD(NETLINK_DNRTMSG,dnrtmsg)
454	__ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent)
455	__ADD(NETLINK_GENERIC,generic)
456	__ADD(NETLINK_SCSITRANSPORT,scsitransport)
457	__ADD(NETLINK_ECRYPTFS,ecryptfs)
458};
459
460char * nl_nlfamily2str(int family, char *buf, size_t size)
461{
462	return __type2str(family, buf, size, nlfamilies,
463			  ARRAY_SIZE(nlfamilies));
464}
465
466int nl_str2nlfamily(const char *name)
467{
468	return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies));
469}
470
471/**
472 * @}
473 */
474
475/**
476 * @name Link Layer Protocol Translations
477 * @{
478 */
479
480static struct trans_tbl llprotos[] = {
481	{0, "generic"},
482	__ADD(ARPHRD_ETHER,ether)
483	__ADD(ARPHRD_EETHER,eether)
484	__ADD(ARPHRD_AX25,ax25)
485	__ADD(ARPHRD_PRONET,pronet)
486	__ADD(ARPHRD_CHAOS,chaos)
487	__ADD(ARPHRD_IEEE802,ieee802)
488	__ADD(ARPHRD_ARCNET,arcnet)
489	__ADD(ARPHRD_APPLETLK,atalk)
490	__ADD(ARPHRD_DLCI,dlci)
491	__ADD(ARPHRD_ATM,atm)
492	__ADD(ARPHRD_METRICOM,metricom)
493	__ADD(ARPHRD_IEEE1394,ieee1394)
494#ifdef ARPHRD_EUI64
495	__ADD(ARPHRD_EUI64,eui64)
496#endif
497	__ADD(ARPHRD_INFINIBAND,infiniband)
498	__ADD(ARPHRD_SLIP,slip)
499	__ADD(ARPHRD_CSLIP,cslip)
500	__ADD(ARPHRD_SLIP6,slip6)
501	__ADD(ARPHRD_CSLIP6,cslip6)
502	__ADD(ARPHRD_RSRVD,rsrvd)
503	__ADD(ARPHRD_ADAPT,adapt)
504	__ADD(ARPHRD_ROSE,rose)
505	__ADD(ARPHRD_X25,x25)
506#ifdef ARPHRD_HWX25
507	__ADD(ARPHRD_HWX25,hwx25)
508#endif
509	__ADD(ARPHRD_PPP,ppp)
510	__ADD(ARPHRD_HDLC,hdlc)
511	__ADD(ARPHRD_LAPB,lapb)
512	__ADD(ARPHRD_DDCMP,ddcmp)
513	__ADD(ARPHRD_RAWHDLC,rawhdlc)
514	__ADD(ARPHRD_TUNNEL,ipip)
515	__ADD(ARPHRD_TUNNEL6,tunnel6)
516	__ADD(ARPHRD_FRAD,frad)
517	__ADD(ARPHRD_SKIP,skip)
518	__ADD(ARPHRD_LOOPBACK,loopback)
519	__ADD(ARPHRD_LOCALTLK,localtlk)
520	__ADD(ARPHRD_FDDI,fddi)
521	__ADD(ARPHRD_BIF,bif)
522	__ADD(ARPHRD_SIT,sit)
523	__ADD(ARPHRD_IPDDP,ip/ddp)
524	__ADD(ARPHRD_IPGRE,gre)
525	__ADD(ARPHRD_PIMREG,pimreg)
526	__ADD(ARPHRD_HIPPI,hippi)
527	__ADD(ARPHRD_ASH,ash)
528	__ADD(ARPHRD_ECONET,econet)
529	__ADD(ARPHRD_IRDA,irda)
530	__ADD(ARPHRD_FCPP,fcpp)
531	__ADD(ARPHRD_FCAL,fcal)
532	__ADD(ARPHRD_FCPL,fcpl)
533	__ADD(ARPHRD_FCFABRIC,fcfb_0)
534	__ADD(ARPHRD_FCFABRIC+1,fcfb_1)
535	__ADD(ARPHRD_FCFABRIC+2,fcfb_2)
536	__ADD(ARPHRD_FCFABRIC+3,fcfb_3)
537	__ADD(ARPHRD_FCFABRIC+4,fcfb_4)
538	__ADD(ARPHRD_FCFABRIC+5,fcfb_5)
539	__ADD(ARPHRD_FCFABRIC+6,fcfb_6)
540	__ADD(ARPHRD_FCFABRIC+7,fcfb_7)
541	__ADD(ARPHRD_FCFABRIC+8,fcfb_8)
542	__ADD(ARPHRD_FCFABRIC+9,fcfb_9)
543	__ADD(ARPHRD_FCFABRIC+10,fcfb_10)
544	__ADD(ARPHRD_FCFABRIC+11,fcfb_11)
545	__ADD(ARPHRD_FCFABRIC+12,fcfb_12)
546	__ADD(ARPHRD_IEEE802_TR,tr)
547	__ADD(ARPHRD_IEEE80211,ieee802.11)
548#ifdef ARPHRD_IEEE80211_PRISM
549	__ADD(ARPHRD_IEEE80211_PRISM, ieee802.11_prism)
550#endif
551#ifdef ARPHRD_VOID
552	__ADD(ARPHRD_VOID,void)
553#endif
554};
555
556char * nl_llproto2str(int llproto, char *buf, size_t len)
557{
558	return __type2str(llproto, buf, len, llprotos, ARRAY_SIZE(llprotos));
559}
560
561int nl_str2llproto(const char *name)
562{
563	return __str2type(name, llprotos, ARRAY_SIZE(llprotos));
564}
565
566/** @} */
567
568
569/**
570 * @name Ethernet Protocol Translations
571 * @{
572 */
573
574static struct trans_tbl ether_protos[] = {
575	__ADD(ETH_P_LOOP,loop)
576	__ADD(ETH_P_PUP,pup)
577	__ADD(ETH_P_PUPAT,pupat)
578	__ADD(ETH_P_IP,ip)
579	__ADD(ETH_P_X25,x25)
580	__ADD(ETH_P_ARP,arp)
581	__ADD(ETH_P_BPQ,bpq)
582	__ADD(ETH_P_IEEEPUP,ieeepup)
583	__ADD(ETH_P_IEEEPUPAT,ieeepupat)
584	__ADD(ETH_P_DEC,dec)
585	__ADD(ETH_P_DNA_DL,dna_dl)
586	__ADD(ETH_P_DNA_RC,dna_rc)
587	__ADD(ETH_P_DNA_RT,dna_rt)
588	__ADD(ETH_P_LAT,lat)
589	__ADD(ETH_P_DIAG,diag)
590	__ADD(ETH_P_CUST,cust)
591	__ADD(ETH_P_SCA,sca)
592	__ADD(ETH_P_RARP,rarp)
593	__ADD(ETH_P_ATALK,atalk)
594	__ADD(ETH_P_AARP,aarp)
595#ifdef ETH_P_8021Q
596	__ADD(ETH_P_8021Q,802.1q)
597#endif
598	__ADD(ETH_P_IPX,ipx)
599	__ADD(ETH_P_IPV6,ipv6)
600#ifdef ETH_P_WCCP
601	__ADD(ETH_P_WCCP,wccp)
602#endif
603	__ADD(ETH_P_PPP_DISC,ppp_disc)
604	__ADD(ETH_P_PPP_SES,ppp_ses)
605	__ADD(ETH_P_MPLS_UC,mpls_uc)
606	__ADD(ETH_P_MPLS_MC,mpls_mc)
607	__ADD(ETH_P_ATMMPOA,atmmpoa)
608	__ADD(ETH_P_ATMFATE,atmfate)
609	__ADD(ETH_P_EDP2,edp2)
610	__ADD(ETH_P_802_3,802.3)
611	__ADD(ETH_P_AX25,ax25)
612	__ADD(ETH_P_ALL,all)
613	__ADD(ETH_P_802_2,802.2)
614	__ADD(ETH_P_SNAP,snap)
615	__ADD(ETH_P_DDCMP,ddcmp)
616	__ADD(ETH_P_WAN_PPP,wan_ppp)
617	__ADD(ETH_P_PPP_MP,ppp_mp)
618	__ADD(ETH_P_LOCALTALK,localtalk)
619	__ADD(ETH_P_PPPTALK,ppptalk)
620	__ADD(ETH_P_TR_802_2,tr_802.2)
621	__ADD(ETH_P_MOBITEX,mobitex)
622	__ADD(ETH_P_CONTROL,control)
623	__ADD(ETH_P_IRDA,irda)
624	__ADD(ETH_P_ECONET,econet)
625	__ADD(ETH_P_HDLC,hdlc)
626};
627
628char *nl_ether_proto2str(int eproto, char *buf, size_t len)
629{
630	return __type2str(eproto, buf, len, ether_protos,
631			    ARRAY_SIZE(ether_protos));
632}
633
634int nl_str2ether_proto(const char *name)
635{
636	return __str2type(name, ether_protos, ARRAY_SIZE(ether_protos));
637}
638
639/** @} */
640
641/**
642 * @name IP Protocol Translations
643 * @{
644 */
645
646char *nl_ip_proto2str(int proto, char *buf, size_t len)
647{
648	struct protoent *p = getprotobynumber(proto);
649
650	if (p) {
651		snprintf(buf, len, "%s", p->p_name);
652		return buf;
653	}
654
655	snprintf(buf, len, "0x%x", proto);
656	return buf;
657}
658
659int nl_str2ip_proto(const char *name)
660{
661	struct protoent *p = getprotobyname(name);
662	unsigned long l;
663	char *end;
664
665	if (p)
666		return p->p_proto;
667
668	l = strtoul(name, &end, 0);
669	if (l == ULONG_MAX || *end != '\0')
670		return -NLE_OBJ_NOTFOUND;
671
672	return (int) l;
673}
674
675/** @} */
676
677/**
678 * @name Dumping Helpers
679 * @{
680 */
681
682/**
683 * Handle a new line while dumping
684 * @arg params		Dumping parameters
685 *
686 * This function must be called before dumping any onto a
687 * new line. It will ensure proper prefixing as specified
688 * by the dumping parameters.
689 *
690 * @note This function will NOT dump any newlines itself
691 */
692void nl_new_line(struct nl_dump_params *params)
693{
694	params->dp_line++;
695
696	if (params->dp_prefix) {
697		int i;
698		for (i = 0; i < params->dp_prefix; i++) {
699			if (params->dp_fd)
700				fprintf(params->dp_fd, " ");
701			else if (params->dp_buf)
702				strncat(params->dp_buf, " ",
703					params->dp_buflen -
704					sizeof(params->dp_buf) - 1);
705		}
706	}
707
708	if (params->dp_nl_cb)
709		params->dp_nl_cb(params, params->dp_line);
710}
711
712static void dump_one(struct nl_dump_params *parms, const char *fmt,
713		     va_list args)
714{
715	if (parms->dp_fd)
716		vfprintf(parms->dp_fd, fmt, args);
717	else if (parms->dp_buf || parms->dp_cb) {
718		char *buf = NULL;
719		vasprintf(&buf, fmt, args);
720		if (parms->dp_cb)
721			parms->dp_cb(parms, buf);
722		else
723			strncat(parms->dp_buf, buf,
724			        parms->dp_buflen - strlen(parms->dp_buf) - 1);
725		free(buf);
726	}
727}
728
729
730/**
731 * Dump a formatted character string
732 * @arg params		Dumping parameters
733 * @arg fmt		printf style formatting string
734 * @arg ...		Arguments to formatting string
735 *
736 * Dumps a printf style formatting string to the output device
737 * as specified by the dumping parameters.
738 */
739void nl_dump(struct nl_dump_params *params, const char *fmt, ...)
740{
741	va_list args;
742
743	va_start(args, fmt);
744	dump_one(params, fmt, args);
745	va_end(args);
746}
747
748void nl_dump_line(struct nl_dump_params *parms, const char *fmt, ...)
749{
750	va_list args;
751
752	nl_new_line(parms);
753
754	va_start(args, fmt);
755	dump_one(parms, fmt, args);
756	va_end(args);
757}
758
759
760/** @} */
761
762/** @cond SKIP */
763
764int __trans_list_add(int i, const char *a, struct nl_list_head *head)
765{
766	struct trans_list *tl;
767
768	tl = calloc(1, sizeof(*tl));
769	if (!tl)
770		return -NLE_NOMEM;
771
772	tl->i = i;
773	tl->a = strdup(a);
774
775	nl_list_add_tail(&tl->list, head);
776
777	return 0;
778}
779
780void __trans_list_clear(struct nl_list_head *head)
781{
782	struct trans_list *tl, *next;
783
784	nl_list_for_each_entry_safe(tl, next, head, list) {
785		free(tl->a);
786		free(tl);
787	}
788}
789
790char *__type2str(int type, char *buf, size_t len, struct trans_tbl *tbl,
791		 size_t tbl_len)
792{
793	int i;
794	for (i = 0; i < tbl_len; i++) {
795		if (tbl[i].i == type) {
796			snprintf(buf, len, "%s", tbl[i].a);
797			return buf;
798		}
799	}
800
801	snprintf(buf, len, "0x%x", type);
802	return buf;
803}
804
805char *__list_type2str(int type, char *buf, size_t len,
806		      struct nl_list_head *head)
807{
808	struct trans_list *tl;
809
810	nl_list_for_each_entry(tl, head, list) {
811		if (tl->i == type) {
812			snprintf(buf, len, "%s", tl->a);
813			return buf;
814		}
815	}
816
817	snprintf(buf, len, "0x%x", type);
818	return buf;
819}
820
821char *__flags2str(int flags, char *buf, size_t len,
822		  struct trans_tbl *tbl, size_t tbl_len)
823{
824	int i;
825	int tmp = flags;
826
827	memset(buf, 0, len);
828
829	for (i = 0; i < tbl_len; i++) {
830		if (tbl[i].i & tmp) {
831			tmp &= ~tbl[i].i;
832			strncat(buf, tbl[i].a, len - strlen(buf) - 1);
833			if ((tmp & flags))
834				strncat(buf, ",", len - strlen(buf) - 1);
835		}
836	}
837
838	return buf;
839}
840
841int __str2type(const char *buf, struct trans_tbl *tbl, size_t tbl_len)
842{
843	unsigned long l;
844	char *end;
845	int i;
846
847	if (*buf == '\0')
848		return -NLE_INVAL;
849
850	for (i = 0; i < tbl_len; i++)
851		if (!strcasecmp(tbl[i].a, buf))
852			return tbl[i].i;
853
854	l = strtoul(buf, &end, 0);
855	if (l == ULONG_MAX || *end != '\0')
856		return -NLE_OBJ_NOTFOUND;
857
858	return (int) l;
859}
860
861int __list_str2type(const char *buf, struct nl_list_head *head)
862{
863	struct trans_list *tl;
864	unsigned long l;
865	char *end;
866
867	if (*buf == '\0')
868		return -NLE_INVAL;
869
870	nl_list_for_each_entry(tl, head, list) {
871		if (!strcasecmp(tl->a, buf))
872			return tl->i;
873	}
874
875	l = strtoul(buf, &end, 0);
876	if (l == ULONG_MAX || *end != '\0')
877		return -NLE_OBJ_NOTFOUND;
878
879	return (int) l;
880}
881
882int __str2flags(const char *buf, struct trans_tbl *tbl, size_t tbl_len)
883{
884	int i, flags = 0, len;
885	char *p = (char *) buf, *t;
886
887	for (;;) {
888		if (*p == ' ')
889			p++;
890
891		t = strchr(p, ',');
892		len = t ? t - p : strlen(p);
893		for (i = 0; i < tbl_len; i++)
894			if (!strncasecmp(tbl[i].a, p, len))
895				flags |= tbl[i].i;
896
897		if (!t)
898			return flags;
899
900		p = ++t;
901	}
902
903	return 0;
904}
905
906void dump_from_ops(struct nl_object *obj, struct nl_dump_params *params)
907{
908	int type = params->dp_type;
909
910	if (type < 0 || type > NL_DUMP_MAX)
911		BUG();
912
913	params->dp_line = 0;
914
915	if (params->dp_dump_msgtype) {
916#if 0
917		/* XXX */
918		char buf[64];
919
920		dp_dump_line(params, 0, "%s ",
921			     nl_cache_mngt_type2name(obj->ce_ops,
922			     			     obj->ce_ops->co_protocol,
923						     obj->ce_msgtype,
924						     buf, sizeof(buf)));
925#endif
926		params->dp_pre_dump = 1;
927	}
928
929	if (obj->ce_ops->oo_dump[type])
930		obj->ce_ops->oo_dump[type](obj, params);
931}
932
933/** @endcond */
934
935/** @} */
936