1440bf90a4980e244d05e882d3aab2ef377f51d3
[obnox/wireshark/wip.git] / epan / to_str.c
1 /* to_str.c
2  * Routines for utilities to convert various other types to strings.
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>         /* needed for <netinet/in.h> */
34 #endif
35
36 #ifdef NEED_SNPRINTF_H
37 # include "snprintf.h"
38 #endif
39
40 #ifdef HAVE_NETINET_IN_H
41 # include <netinet/in.h>        /* needed for <arpa/inet.h> on some platforms */
42 #endif
43
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>
46 #endif
47
48 #ifdef HAVE_SYS_SOCKET_H
49 #include <sys/socket.h>         /* needed to define AF_ values on UNIX */
50 #endif
51
52 #ifdef HAVE_WINSOCK2_H
53 #include <winsock2.h>           /* needed to define AF_ values on Windows */
54 #endif
55
56 #ifdef NEED_INET_V6DEFS_H
57 # include "inet_v6defs.h"
58 #endif
59
60 #include "to_str.h"
61 #include "value_string.h"
62 #include "addr_resolv.h"
63 #include "pint.h"
64 #include "atalk-utils.h"
65 #include "sna-utils.h"
66 #include "osi-utils.h"
67 #include <epan/dissectors/packet-mtp3.h>
68 #include <stdio.h>
69 #include <time.h>
70
71 #define MAX_BYTESTRING_LEN      6
72
73 /* Routine to convert a sequence of bytes to a hex string, one byte/two hex
74  * digits at at a time, with a specified punctuation character between
75  * the bytes.  The sequence of bytes must be no longer than
76  * MAX_BYTESTRING_LEN.
77  *
78  * If punct is '\0', no punctuation is applied (and thus
79  * the resulting string is (len-1) bytes shorter)
80  */
81 static gchar *
82 bytestring_to_str(const guint8 *ad, guint32 len, char punct) {
83   static gchar  str[3][MAX_BYTESTRING_LEN*3];
84   static gchar *cur;
85   gchar        *p;
86   int          i;
87   guint32      octet;
88   /* At least one version of Apple's C compiler/linker is buggy, causing
89      a complaint from the linker about the "literal C string section"
90      not ending with '\0' if we initialize a 16-element "char" array with
91      a 16-character string, the fact that initializing such an array with
92      such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
93      '\0' byte in the string nonwithstanding. */
94   static const gchar hex_digits[16] =
95       { '0', '1', '2', '3', '4', '5', '6', '7',
96         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
97
98   g_assert(len > 0 && len <= MAX_BYTESTRING_LEN);
99   len--;
100
101   if (cur == &str[0][0]) {
102     cur = &str[1][0];
103   } else if (cur == &str[1][0]) {
104     cur = &str[2][0];
105   } else {
106     cur = &str[0][0];
107   }
108   p = &cur[18];
109   *--p = '\0';
110   i = len;
111   for (;;) {
112     octet = ad[i];
113     *--p = hex_digits[octet&0xF];
114     octet >>= 4;
115     *--p = hex_digits[octet&0xF];
116     if (i == 0)
117       break;
118     if (punct)
119       *--p = punct;
120     i--;
121   }
122   return p;
123 }
124
125 /* Wrapper for the most common case of asking
126  * for a string using a colon as the hex-digit separator.
127  */
128 /* XXX FIXME   
129 remove this one later when every call has been converted to address_to_str() 
130 */
131 gchar *
132 ether_to_str(const guint8 *ad)
133 {
134         return bytestring_to_str(ad, 6, ':');
135 }
136
137 /* 
138  This function is very fast and this function is called a lot.
139  XXX update the address_to_str stuff to use this function.
140 */
141 gchar *
142 ip_to_str(const guint8 *ad) {
143   static gchar  str[4][16];
144   static int   cur_idx=0;
145   gchar *cur;
146
147   cur_idx++;
148   if(cur_idx>3){ 
149      cur_idx=0;
150   }
151   cur=&str[cur_idx][0];
152
153   ip_to_str_buf(ad, cur);
154   return cur;
155 }
156
157 /* 
158  This function is very fast and this function is called a lot.
159  XXX update the address_to_str stuff to use this function.
160 */
161 static const char * const fast_strings[] = {
162 "0", "1", "2", "3", "4", "5", "6", "7", 
163 "8", "9", "10", "11", "12", "13", "14", "15", 
164 "16", "17", "18", "19", "20", "21", "22", "23", 
165 "24", "25", "26", "27", "28", "29", "30", "31", 
166 "32", "33", "34", "35", "36", "37", "38", "39", 
167 "40", "41", "42", "43", "44", "45", "46", "47", 
168 "48", "49", "50", "51", "52", "53", "54", "55", 
169 "56", "57", "58", "59", "60", "61", "62", "63", 
170 "64", "65", "66", "67", "68", "69", "70", "71", 
171 "72", "73", "74", "75", "76", "77", "78", "79", 
172 "80", "81", "82", "83", "84", "85", "86", "87", 
173 "88", "89", "90", "91", "92", "93", "94", "95", 
174 "96", "97", "98", "99", "100", "101", "102", "103", 
175 "104", "105", "106", "107", "108", "109", "110", "111", 
176 "112", "113", "114", "115", "116", "117", "118", "119", 
177 "120", "121", "122", "123", "124", "125", "126", "127", 
178 "128", "129", "130", "131", "132", "133", "134", "135", 
179 "136", "137", "138", "139", "140", "141", "142", "143", 
180 "144", "145", "146", "147", "148", "149", "150", "151", 
181 "152", "153", "154", "155", "156", "157", "158", "159", 
182 "160", "161", "162", "163", "164", "165", "166", "167", 
183 "168", "169", "170", "171", "172", "173", "174", "175", 
184 "176", "177", "178", "179", "180", "181", "182", "183", 
185 "184", "185", "186", "187", "188", "189", "190", "191", 
186 "192", "193", "194", "195", "196", "197", "198", "199", 
187 "200", "201", "202", "203", "204", "205", "206", "207", 
188 "208", "209", "210", "211", "212", "213", "214", "215", 
189 "216", "217", "218", "219", "220", "221", "222", "223", 
190 "224", "225", "226", "227", "228", "229", "230", "231", 
191 "232", "233", "234", "235", "236", "237", "238", "239", 
192 "240", "241", "242", "243", "244", "245", "246", "247", 
193 "248", "249", "250", "251", "252", "253", "254", "255"
194 };
195 void
196 ip_to_str_buf(const guint8 *ad, gchar *buf)
197 {
198         register gchar const *p;
199         register gchar *b=buf;
200         register gchar c;
201
202         p=fast_strings[*ad++];
203         while((c=*p)){
204                 *b++=c;
205                 p++;
206         }
207         *b++='.';
208
209         p=fast_strings[*ad++];
210         while((c=*p)){
211                 *b++=c;
212                 p++;
213         }
214         *b++='.';
215
216         p=fast_strings[*ad++];
217         while((c=*p)){
218                 *b++=c;
219                 p++;
220         }
221         *b++='.';
222
223         p=fast_strings[*ad++];
224         while((c=*p)){
225                 *b++=c;
226                 p++;
227         }
228         *b=0;
229 }
230
231
232 /* XXX FIXME   
233 remove this one later when every call has been converted to address_to_str() 
234 */
235 gchar *
236 ip6_to_str(const struct e_in6_addr *ad) {
237 #ifndef INET6_ADDRSTRLEN
238 #define INET6_ADDRSTRLEN 46
239 #endif
240   static int i=0;
241   static gchar *strp, str[4][INET6_ADDRSTRLEN];
242
243   i++;
244   if(i>=4){
245     i=0;
246   }
247   strp=str[i];
248
249   ip6_to_str_buf(ad, strp);
250   return strp;
251 }
252
253 void
254 ip6_to_str_buf(const struct e_in6_addr *ad, gchar *buf)
255 {
256   inet_ntop(AF_INET6, (const guchar*)ad, buf, INET6_ADDRSTRLEN);
257 }
258
259 gchar*
260 ipx_addr_to_str(guint32 net, const guint8 *ad)
261 {
262         static gchar    str[3][8+1+MAXNAMELEN+1]; /* 8 digits, 1 period, NAME, 1 null */
263         static gchar    *cur;
264         char            *name;
265
266         if (cur == &str[0][0]) {
267                 cur = &str[1][0];
268         } else if (cur == &str[1][0]) {
269                 cur = &str[2][0];
270         } else {
271                 cur = &str[0][0];
272         }
273
274         name = get_ether_name_if_known(ad);
275
276         if (name) {
277                 sprintf(cur, "%s.%s", get_ipxnet_name(net), name);
278         }
279         else {
280                 sprintf(cur, "%s.%s", get_ipxnet_name(net),
281                     bytestring_to_str(ad, 6, '\0'));
282         }
283         return cur;
284 }
285
286 gchar*
287 ipxnet_to_string(const guint8 *ad)
288 {
289         guint32 addr = pntohl(ad);
290         return ipxnet_to_str_punct(addr, ' ');
291 }
292
293 gchar *
294 ipxnet_to_str_punct(const guint32 ad, char punct)
295 {
296   static gchar  str[3][12];
297   static gchar *cur;
298   gchar        *p;
299   int          i;
300   guint32      octet;
301   /* At least one version of Apple's C compiler/linker is buggy, causing
302      a complaint from the linker about the "literal C string section"
303      not ending with '\0' if we initialize a 16-element "char" array with
304      a 16-character string, the fact that initializing such an array with
305      such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
306      '\0' byte in the string nonwithstanding. */
307   static const gchar hex_digits[16] =
308       { '0', '1', '2', '3', '4', '5', '6', '7',
309         '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
310   static const guint32  octet_mask[4] =
311           { 0xff000000 , 0x00ff0000, 0x0000ff00, 0x000000ff };
312
313   if (cur == &str[0][0]) {
314     cur = &str[1][0];
315   } else if (cur == &str[1][0]) {
316     cur = &str[2][0];
317   } else {
318     cur = &str[0][0];
319   }
320   p = &cur[12];
321   *--p = '\0';
322   i = 3;
323   for (;;) {
324     octet = (ad & octet_mask[i]) >> ((3 - i) * 8);
325     *--p = hex_digits[octet&0xF];
326     octet >>= 4;
327     *--p = hex_digits[octet&0xF];
328     if (i == 0)
329       break;
330     if (punct)
331       *--p = punct;
332     i--;
333   }
334   return p;
335 }
336
337 gchar *
338 vines_addr_to_str(const guint8 *addrp)
339 {
340   static gchar  str[3][214];
341   static gchar  *cur;
342
343   if (cur == &str[0][0]) {
344     cur = &str[1][0];
345   } else if (cur == &str[1][0]) {
346     cur = &str[2][0];
347   } else {
348     cur = &str[0][0];
349   }
350   vines_addr_to_str_buf(addrp, cur);
351   return cur;
352 }
353
354 void
355 vines_addr_to_str_buf(const guint8 *addrp, gchar *buf)
356 {
357   sprintf(buf, "%08x.%04x", pntohl(&addrp[0]), pntohs(&addrp[4]));
358 }
359
360 #define PLURALIZE(n)    (((n) > 1) ? "s" : "")
361 #define COMMA(do_it)    ((do_it) ? ", " : "")
362
363 /*
364  * Maximum length of a string showing days/hours/minutes/seconds.
365  * (Does not include the terminating '\0'.)
366  */
367 #define TIME_SECS_LEN   (8+1+4+2+2+5+2+2+7+2+2+7)
368
369 /*
370  * Convert a value in seconds and fractions of a second to a string,
371  * giving time in days, hours, minutes, and seconds, and put the result
372  * into a buffer.
373  * "is_nsecs" says that "frac" is microseconds if true and milliseconds
374  * if false.
375  */
376 static void
377 time_secs_to_str_buf(guint32 time, guint32 frac, gboolean is_nsecs,
378                            gchar *buf)
379 {
380   static gchar *p;
381   int hours, mins, secs;
382   gboolean do_comma = FALSE;
383
384   secs = time % 60;
385   time /= 60;
386   mins = time % 60;
387   time /= 60;
388   hours = time % 24;
389   time /= 24;
390
391   p = buf;
392   if (time != 0) {
393     sprintf(p, "%u day%s", time, PLURALIZE(time));
394     p += strlen(p);
395     do_comma = TRUE;
396   }
397   if (hours != 0) {
398     sprintf(p, "%s%u hour%s", COMMA(do_comma), hours, PLURALIZE(hours));
399     p += strlen(p);
400     do_comma = TRUE;
401   }
402   if (mins != 0) {
403     sprintf(p, "%s%u minute%s", COMMA(do_comma), mins, PLURALIZE(mins));
404     p += strlen(p);
405     do_comma = TRUE;
406   }
407   if (secs != 0 || frac != 0) {
408     if (frac != 0) {
409       if (is_nsecs)
410         sprintf(p, "%s%u.%09u seconds", COMMA(do_comma), secs, frac);
411       else
412         sprintf(p, "%s%u.%03u seconds", COMMA(do_comma), secs, frac);
413     } else
414       sprintf(p, "%s%u second%s", COMMA(do_comma), secs, PLURALIZE(secs));
415   }
416 }
417
418 gchar *
419 time_secs_to_str(guint32 time)
420 {
421   static gchar  str[3][TIME_SECS_LEN+1];
422   static gchar *cur;
423
424   if (cur == &str[0][0]) {
425     cur = &str[1][0];
426   } else if (cur == &str[1][0]) {
427     cur = &str[2][0];
428   } else {
429     cur = &str[0][0];
430   }
431
432   if (time == 0) {
433     sprintf(cur, "0 time");
434     return cur;
435   }
436
437   time_secs_to_str_buf(time, 0, FALSE, cur);
438   return cur;
439 }
440
441 gchar *
442 time_msecs_to_str(guint32 time)
443 {
444   static gchar  str[3][TIME_SECS_LEN+1+3+1];
445   static gchar *cur;
446   int msecs;
447
448   if (cur == &str[0][0]) {
449     cur = &str[1][0];
450   } else if (cur == &str[1][0]) {
451     cur = &str[2][0];
452   } else {
453     cur = &str[0][0];
454   }
455
456   if (time == 0) {
457     sprintf(cur, "0 time");
458     return cur;
459   }
460
461   msecs = time % 1000;
462   time /= 1000;
463
464   time_secs_to_str_buf(time, msecs, FALSE, cur);
465   return cur;
466 }
467
468 static const char *mon_names[12] = {
469         "Jan",
470         "Feb",
471         "Mar",
472         "Apr",
473         "May",
474         "Jun",
475         "Jul",
476         "Aug",
477         "Sep",
478         "Oct",
479         "Nov",
480         "Dec"
481 };
482
483 gchar *
484 abs_time_to_str(nstime_t *abs_time)
485 {
486         struct tm *tmp;
487         static gchar *cur;
488         static char str[3][3+1+2+2+4+1+2+1+2+1+2+1+9+1];
489
490         if (cur == &str[0][0]) {
491                 cur = &str[1][0];
492         } else if (cur == &str[1][0]) {
493                 cur = &str[2][0];
494         } else {
495                 cur = &str[0][0];
496         }
497
498         tmp = localtime(&abs_time->secs);
499         if (tmp) {
500                 sprintf(cur, "%s %2d, %d %02d:%02d:%02d.%09ld",
501                     mon_names[tmp->tm_mon],
502                     tmp->tm_mday,
503                     tmp->tm_year + 1900,
504                     tmp->tm_hour,
505                     tmp->tm_min,
506                     tmp->tm_sec,
507                     (long)abs_time->nsecs);
508         } else
509                 strncpy(cur, "Not representable", sizeof(str[0]));
510         return cur;
511 }
512
513 gchar *
514 abs_time_secs_to_str(time_t abs_time)
515 {
516         struct tm *tmp;
517         static gchar *cur;
518         static char str[3][3+1+2+2+4+1+2+1+2+1+2+1];
519
520         if (cur == &str[0][0]) {
521                 cur = &str[1][0];
522         } else if (cur == &str[1][0]) {
523                 cur = &str[2][0];
524         } else {
525                 cur = &str[0][0];
526         }
527
528         tmp = localtime(&abs_time);
529         if (tmp) {
530                 sprintf(cur, "%s %2d, %d %02d:%02d:%02d",
531                     mon_names[tmp->tm_mon],
532                     tmp->tm_mday,
533                     tmp->tm_year + 1900,
534                     tmp->tm_hour,
535                     tmp->tm_min,
536                     tmp->tm_sec);
537         } else
538                 strncpy(cur, "Not representable", sizeof(str[0]));
539         return cur;
540 }
541
542 void
543 display_signed_time(gchar *buf, int buflen, gint32 sec, gint32 frac,
544     time_res_t units)
545 {
546         char *sign;
547
548         /* If the fractional part of the time stamp is negative,
549            print its absolute value and, if the seconds part isn't
550            (the seconds part should be zero in that case), stick
551            a "-" in front of the entire time stamp. */
552         sign = "";
553         if (frac < 0) {
554                 frac = -frac;
555                 if (sec >= 0)
556                         sign = "-";
557         }
558         switch (units) {
559
560         case MSECS:
561                 snprintf(buf, buflen, "%s%d.%03d", sign, sec, frac);
562                 break;
563
564         case USECS:
565                 snprintf(buf, buflen, "%s%d.%06d", sign, sec, frac);
566                 break;
567
568         case NSECS:
569                 snprintf(buf, buflen, "%s%d.%09d", sign, sec, frac);
570                 break;
571         }
572 }
573
574 /*
575  * Display a relative time as days/hours/minutes/seconds.
576  */
577 gchar *
578 rel_time_to_str(nstime_t *rel_time)
579 {
580         static gchar *cur;
581         static char str[3][1+TIME_SECS_LEN+1+6+1];
582         char *p;
583         char *sign;
584         guint32 time;
585         gint32 nsec;
586
587         if (cur == &str[0][0]) {
588                 cur = &str[1][0];
589         } else if (cur == &str[1][0]) {
590                 cur = &str[2][0];
591         } else {
592                 cur = &str[0][0];
593         }
594         p = cur;
595
596         /* If the nanoseconds part of the time stamp is negative,
597            print its absolute value and, if the seconds part isn't
598            (the seconds part should be zero in that case), stick
599            a "-" in front of the entire time stamp. */
600         sign = "";
601         time = rel_time->secs;
602         nsec = rel_time->nsecs;
603         if (time == 0 && nsec == 0) {
604                 sprintf(cur, "0.000000000 seconds");
605                 return cur;
606         }
607         if (nsec < 0) {
608                 nsec = -nsec;
609                 *p++ = '-';
610
611                 /*
612                  * We assume here that "rel_time->secs" is negative
613                  * or zero; if it's not, the time stamp is bogus,
614                  * with a positive seconds and negative microseconds.
615                  */
616                 time = -rel_time->secs;
617         }
618
619         time_secs_to_str_buf(time, nsec, TRUE, p);
620         return cur;
621 }
622
623 #define REL_TIME_SECS_LEN       (1+10+1+9+1)
624
625 /*
626  * Display a relative time as seconds.
627  */
628 gchar *
629 rel_time_to_secs_str(nstime_t *rel_time)
630 {
631         static gchar *cur;
632         static char str[3][REL_TIME_SECS_LEN];
633
634         if (cur == &str[0][0]) {
635                 cur = &str[1][0];
636         } else if (cur == &str[1][0]) {
637                 cur = &str[2][0];
638         } else {
639                 cur = &str[0][0];
640         }
641
642         display_signed_time(cur, REL_TIME_SECS_LEN, rel_time->secs,
643             rel_time->nsecs, NSECS);
644         return cur;
645 }
646
647
648 /* XXX FIXME   
649 remove this one later when every call has been converted to address_to_str() 
650 */
651 gchar *
652 fc_to_str(const guint8 *ad)
653 {
654     return bytestring_to_str (ad, 3, '.');
655 }
656
657 /* FC Network Header Network Address Authority Identifiers */
658
659 #define FC_NH_NAA_IEEE          1       /* IEEE 802.1a */
660 #define FC_NH_NAA_IEEE_E        2       /* IEEE Exteneded */
661 #define FC_NH_NAA_LOCAL         3
662 #define FC_NH_NAA_IP            4       /* 32-bit IP address */
663 #define FC_NH_NAA_IEEE_R        5       /* IEEE Registered */
664 #define FC_NH_NAA_IEEE_R_E      6       /* IEEE Registered Exteneded */
665 /* according to FC-PH 3 draft these are now reclaimed and reserved */
666 #define FC_NH_NAA_CCITT_INDV    12      /* CCITT 60 bit individual address */
667 #define FC_NH_NAA_CCITT_GRP     14      /* CCITT 60 bit group address */
668
669 gchar *
670 fcwwn_to_str (const guint8 *ad)
671 {
672     int fmt;
673     guint8 oui[6];
674     static gchar ethstr[512];
675     
676     if (ad == NULL) return NULL;
677     
678     fmt = (ad[0] & 0xF0) >> 4;
679
680     switch (fmt) {
681
682     case FC_NH_NAA_IEEE:
683     case FC_NH_NAA_IEEE_E:
684         memcpy (oui, &ad[2], 6);
685         sprintf (ethstr, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)", ad[0], 
686                  ad[1], ad[2], ad[3], ad[4], ad[5], ad[6], ad[7],
687                  get_manuf_name (oui));
688         break;
689
690     case FC_NH_NAA_IEEE_R:
691         oui[0] = ((ad[0] & 0x0F) << 4) | ((ad[1] & 0xF0) >> 4);
692         oui[1] = ((ad[1] & 0x0F) << 4) | ((ad[2] & 0xF0) >> 4);
693         oui[2] = ((ad[2] & 0x0F) << 4) | ((ad[3] & 0xF0) >> 4);
694         oui[3] = ((ad[3] & 0x0F) << 4) | ((ad[4] & 0xF0) >> 4);
695         oui[4] = ((ad[4] & 0x0F) << 4) | ((ad[5] & 0xF0) >> 4);
696         oui[5] = ((ad[5] & 0x0F) << 4) | ((ad[6] & 0xF0) >> 4);
697
698         sprintf (ethstr, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)", ad[0],
699                  ad[1], ad[2], ad[3], ad[4], ad[5], ad[6], ad[7],
700                  get_manuf_name (oui));
701         break;
702
703     default:
704         sprintf (ethstr, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", ad[0],
705                  ad[1], ad[2], ad[3], ad[4], ad[5], ad[6], ad[7]);
706         break;
707     }
708     return (ethstr);
709 }
710
711 /* Generate, into "buf", a string showing the bits of a bitfield.
712    Return a pointer to the character after that string. */
713 char *
714 other_decode_bitfield_value(char *buf, guint32 val, guint32 mask, int width)
715 {
716   int i;
717   guint32 bit;
718   char *p;
719
720   i = 0;
721   p = buf;
722   bit = 1 << (width - 1);
723   for (;;) {
724     if (mask & bit) {
725       /* This bit is part of the field.  Show its value. */
726       if (val & bit)
727         *p++ = '1';
728       else
729         *p++ = '0';
730     } else {
731       /* This bit is not part of the field. */
732       *p++ = '.';
733     }
734     bit >>= 1;
735     i++;
736     if (i >= width)
737       break;
738     if (i % 4 == 0)
739       *p++ = ' ';
740   }
741   *p = '\0';
742   return p;
743 }
744
745 char *
746 decode_bitfield_value(char *buf, guint32 val, guint32 mask, int width)
747 {
748   char *p;
749
750   p = other_decode_bitfield_value(buf, val, mask, width);
751   strcpy(p, " = ");
752   p += 3;
753   return p;
754 }
755
756 /* Generate a string describing a Boolean bitfield (a one-bit field that
757    says something is either true of false). */
758 const char *
759 decode_boolean_bitfield(guint32 val, guint32 mask, int width,
760     const char *truedesc, const char *falsedesc)
761 {
762   static char buf[1025];
763   char *p;
764
765   p = decode_bitfield_value(buf, val, mask, width);
766   if (val & mask)
767     strcpy(p, truedesc);
768   else
769     strcpy(p, falsedesc);
770   return buf;
771 }
772
773 /* Generate a string describing a numeric bitfield (an N-bit field whose
774    value is just a number). */
775 const char *
776 decode_numeric_bitfield(guint32 val, guint32 mask, int width,
777     const char *fmt)
778 {
779   static char buf[1025];
780   char *p;
781   int shift = 0;
782
783   /* Compute the number of bits we have to shift the bitfield right
784      to extract its value. */
785   while ((mask & (1<<shift)) == 0)
786     shift++;
787
788   p = decode_bitfield_value(buf, val, mask, width);
789   sprintf(p, fmt, (val & mask) >> shift);
790   return buf;
791 }
792
793
794 /*XXX FIXME the code below may be called very very frequently in the future.
795   optimize it for speed and get rid of the slow sprintfs */
796 /* XXX - perhaps we should have individual address types register
797    a table of routines to do operations such as address-to-name translation,
798    address-to-string translation, and the like, and have this call them,
799    and also have an address-to-string-with-a-name routine */
800 /* XXX - use this, and that future address-to-string-with-a-name routine,
801    in "col_set_addr()"; it might also be useful to have address types
802    export the names of the source and destination address fields, so
803    that "col_set_addr()" need know nothing whatsoever about particular
804    address types */
805 /* convert an address struct into a printable string */
806 gchar*  
807 address_to_str(address *addr)
808 {
809 #ifndef INET6_ADDRSTRLEN
810 #define INET6_ADDRSTRLEN 46
811 #endif
812   static int i=0;
813   static gchar *strp, str[16][INET6_ADDRSTRLEN];/* IPv6 is the largest one */
814
815   i++;
816   if(i>=16){
817     i=0;
818   }
819   strp=str[i];
820
821   address_to_str_buf(addr, strp);
822   return strp;
823 }
824
825 void
826 address_to_str_buf(address *addr, gchar *buf)
827 {
828   struct atalk_ddp_addr ddp_addr;
829
830   switch(addr->type){
831   case AT_ETHER:
832     sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", addr->data[0], addr->data[1], addr->data[2], addr->data[3], addr->data[4], addr->data[5]);
833     break;
834   case AT_IPv4:
835     ip_to_str_buf(addr->data, buf);
836     break;
837   case AT_IPv6:
838     inet_ntop(AF_INET6, addr->data, buf, INET6_ADDRSTRLEN);
839     break;
840   case AT_IPX:
841     sprintf(buf, "%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x", addr->data[0], addr->data[1], addr->data[2], addr->data[3], addr->data[4], addr->data[5], addr->data[6], addr->data[7], addr->data[8], addr->data[9]);
842     break;
843   case AT_SNA:
844     sna_fid_to_str_buf(addr, buf);
845     break;
846   case AT_ATALK:
847     memcpy(&ddp_addr, addr->data, sizeof ddp_addr);
848     atalk_addr_to_str_buf(&ddp_addr, buf);
849     break;
850   case AT_VINES:
851     vines_addr_to_str_buf(addr->data, buf);
852     break;
853   case AT_OSI:
854     print_nsap_net_buf(addr->data, addr->len, buf);
855     break;
856   case AT_ARCNET:
857     sprintf(buf, "0x%02X", addr->data[0]);
858     break;
859   case AT_FC:
860     sprintf(buf, "%02x.%02x.%02x", addr->data[0], addr->data[1], addr->data[2]);
861     break;
862   case AT_SS7PC:
863     mtp3_addr_to_str_buf(addr->data, buf);
864     break;
865   case AT_EUI64:
866     sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
867             addr->data[0], addr->data[1], addr->data[2], addr->data[3],
868             addr->data[4], addr->data[5], addr->data[6], addr->data[7]);
869     break;
870   default:
871     g_assert_not_reached();
872   }
873 }