]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/i4b/isdndecode/main.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / i4b / isdndecode / main.c
1 /*
2  * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  *---------------------------------------------------------------------------
26  *
27  *      main.c - isdndecode main program file
28  *      -------------------------------------
29  *
30  *      $Id: main.c,v 1.13 2000/02/21 15:17:17 hm Exp $
31  *
32  * $FreeBSD$
33  *
34  *      last edit-date: [Mon Feb 21 16:19:30 2000]
35  *
36  *---------------------------------------------------------------------------*/
37
38 #include "decode.h"
39 #include <unistd.h>
40
41 unsigned char buf[BSIZE];
42 FILE *Fout = NULL;
43 FILE *BP = NULL;
44 int outflag = 1;
45 int header = 1;
46 int print_q921 = 1;
47 int unit = 0;
48 int dchan = 0;
49 int bchan = 0;
50 int traceon = 0;
51 int analyze = 0;
52 int Rx = RxUDEF;
53 int Tx = TxUDEF;
54 int f;
55 int Bopt = 0;
56 int Popt = 0;
57 int bpopt = 0;
58 int info = 0;
59 int xflag = 0;
60
61 int enable_trace = TRACE_D_RX | TRACE_D_TX;
62         
63 static char outfilename[1024];
64 static char BPfilename[1024];
65
66 static void dumpbuf( int n, unsigned char *buf, i4b_trace_hdr_t *hdr);
67 static int switch_driver( int value, int rx, int tx );
68 static void usage( void );
69 static void exit_hdl( void );
70 static void reopenfiles( int );
71
72
73 /*---------------------------------------------------------------------------*
74  *      usage intructions
75  *---------------------------------------------------------------------------*/
76 void
77 usage(void)
78 {
79         fprintf(stderr,"\n");
80         fprintf(stderr,"isdndecode - isdn4bsd package ISDN decoder for passive cards (%d.%d.%d)\n", VERSION, REL, STEP);
81         fprintf(stderr,"usage: isdntrace -a -b -d -f <file> -h -i -l -n <val> -o -p <file> -r -u <unit>\n");
82         fprintf(stderr,"                 -x -B -P -R <unit> -T <unit>\n");
83         fprintf(stderr,"       -a        analyzer mode ................................... (default off)\n");
84         fprintf(stderr,"       -b        switch B channel trace on ....................... (default off)\n");
85         fprintf(stderr,"       -d        switch D channel trace off ....................... (default on)\n");
86         fprintf(stderr,"       -f <file> write output to file filename ........... (default %s0)\n", DECODE_FILE_NAME);
87         fprintf(stderr,"       -h        don't print header for each message ............. (default off)\n");
88         fprintf(stderr,"       -i        print I.430 (layer 1) INFO signals .............. (default off)\n");   
89         fprintf(stderr,"       -l        don't decode low layer Q.921 messages ........... (default off)\n");
90         fprintf(stderr,"       -o        don't write output to a file .................... (default off)\n");
91         fprintf(stderr,"       -p <file> specify filename for -B and -P ........ (default %s0)\n", BIN_FILE_NAME);
92         fprintf(stderr,"       -u <unit> specify controller unit number ............... (default unit 0)\n");
93         fprintf(stderr,"       -x        print packets with unknown protocoldiscriminator  (default off)\n");   
94         fprintf(stderr,"       -B        write binary trace data to file filename ........ (default off)\n");
95         fprintf(stderr,"       -P        playback from binary trace data file ............ (default off)\n");
96         fprintf(stderr,"       -R <unit> analyze Rx controller unit number (for -a) ... (default unit %d)\n", RxUDEF);
97         fprintf(stderr,"       -T <unit> analyze Tx controller unit number (for -a) ... (default unit %d)\n", TxUDEF);
98         fprintf(stderr,"\n");
99         exit(1);
100 }
101
102 /*---------------------------------------------------------------------------*
103  *      main
104  *---------------------------------------------------------------------------*/
105 int
106 main(int argc, char *argv[])
107 {
108         char devicename[80];
109         char headerbuf[256];
110                 
111         int n;
112         int c;
113         char *b;
114
115         char *outfile = DECODE_FILE_NAME;
116         char *binfile = BIN_FILE_NAME;
117         int outfileset = 0;
118         time_t tm;
119         
120         i4b_trace_hdr_t *ithp = NULL;
121         int l;
122
123         b = &buf[sizeof(i4b_trace_hdr_t)];
124         
125         while( (c = getopt(argc, argv, "abdf:hiln:op:u:xBPR:T:")) != -1)
126         {
127                 switch(c)
128                 {
129                         case 'a':
130                                 analyze = 1;
131                                 break;
132                                 
133                         case 'b':
134                                 enable_trace |= (TRACE_B_RX | TRACE_B_TX);
135                                 break;
136
137                         case 'd':
138                                 enable_trace &= (~(TRACE_D_TX | TRACE_D_RX));
139                                 break;
140
141                         case 'o':
142                                 outflag = 0;
143                                 break;
144
145                         case 'f':
146                                 outfile = optarg;
147                                 outfileset = 1;
148                                 break;
149                         
150                         case 'h':
151                                 header = 0;
152                                 break;
153
154                         case 'i':
155                                 enable_trace |= TRACE_I;
156                                 info = 1;
157                                 break;
158
159                         case 'l':
160                                 print_q921 = 0;
161                                 break;
162
163                         case 'p':
164                                 binfile = optarg;
165                                 bpopt = 1;
166                                 break;
167                         
168                         case 'u':
169                                 unit = atoi(optarg);
170                                 if(unit < 0 || unit >= MAX_CONTROLLERS)
171                                         usage();
172                                 break;
173
174                         case 'x':
175                                 xflag = 1;
176                                 break;
177
178                         case 'B':
179                                 Bopt = 1;
180                                 break;
181                         
182                         case 'P':
183                                 Popt = 1;
184                                 break;
185                         
186                         case 'R':
187                                 Rx = atoi(optarg);
188                                 if(Rx < 0 || Rx >= MAX_CONTROLLERS)
189                                         usage();
190                                 break;
191
192                         case 'T':
193                                 Tx = atoi(optarg);
194                                 if(Tx < 0 || Tx >= MAX_CONTROLLERS)
195                                         usage();
196                                 break;
197
198                         case '?':
199                         default:
200                                 usage();
201                                 break;
202                 }
203         }
204
205         if(enable_trace == 0)
206                 usage();
207
208         if(Bopt && Popt)
209                 usage();
210                 
211         atexit(exit_hdl);
212
213         if(Bopt)
214         {
215                 if(bpopt)
216                         sprintf(BPfilename, "%s", binfile);
217                 else
218                         sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
219                         
220                 if((BP = fopen(BPfilename, "r")) != NULL)
221                 {
222                         char buffer[1024];
223                         fclose(BP);
224                         sprintf(buffer, "%s%s", BPfilename, DECODE_FILE_NAME_BAK); 
225                         rename(BPfilename, buffer);
226                 }                       
227                 if((BP = fopen(BPfilename, "w")) == NULL)
228                 {
229                         char buffer[80];
230
231                         sprintf(buffer, "Error opening file [%s]", BPfilename);
232                         perror(buffer);
233                         exit(1);
234                 }
235                 
236                 if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
237                 {
238                         char buffer[80];
239
240                         sprintf(buffer, "Error setting file [%s] to unbuffered", BPfilename);
241                         perror(buffer);
242                         exit(1);
243                 }
244         }               
245
246         if(Popt)
247         {
248                 if(bpopt)
249                         sprintf(BPfilename, "%s", binfile);
250                 else
251                         sprintf(BPfilename, "%s%d", BIN_FILE_NAME, unit);
252                         
253                 if((BP = fopen(BPfilename, "r")) == NULL)
254                 {
255                         char buffer[80];
256
257                         sprintf(buffer, "Error opening file [%s]", BPfilename);
258                         perror(buffer);
259                         exit(1);
260                 }
261         }
262         else
263         {               
264                 sprintf(devicename, "%s%d", I4BTRC_DEVICE, unit);
265         
266                 if((f = open(devicename, O_RDWR)) < 0)
267                 {
268                         char buffer[80];
269         
270                         sprintf(buffer, "Error opening trace device [%s]", devicename);
271                         perror(buffer);
272                         exit(1);
273                 }
274         }
275         
276         if(outflag)
277         {
278                 if(outfileset == 0)
279                         sprintf(outfilename, "%s%d", DECODE_FILE_NAME, unit);
280                 else
281                         strcpy(outfilename, outfile);
282                         
283                         
284                 if((Fout = fopen(outfilename, "r")) != NULL)
285                 {
286                         char buffer[1024];
287                         fclose(Fout);
288                         sprintf(buffer, "%s%s", outfilename, DECODE_FILE_NAME_BAK); 
289                         rename(outfilename, buffer);
290                 }
291                         
292                 if((Fout = fopen(outfilename, "w")) == NULL)
293                 {
294                         char buffer[80];
295
296                         sprintf(buffer, "Error opening file [%s]", outfilename);
297                         perror(buffer);
298                         exit(1);
299                 }
300                 
301                 if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
302                 {
303                         char buffer[80];
304
305                         sprintf(buffer, "Error setting file [%s] to unbuffered", outfile);
306                         perror(buffer);
307                         exit(1);
308                 }
309         }
310
311         if((setvbuf(stdout, (char *)NULL, _IOLBF, 0)) != 0)
312         {
313                 char buffer[80];
314
315                 sprintf(buffer, "Error setting stdout to line-buffered");
316                 perror(buffer);
317                 exit(1);
318         }
319
320         if(!Popt)
321         {
322                 if((switch_driver(enable_trace, Rx, Tx)) == -1)
323                         exit(1);
324                 else
325                         traceon = 1;
326         }
327                 
328         signal(SIGHUP, SIG_IGN);        /* ignore hangup signal */
329         signal(SIGUSR1, reopenfiles);   /* rotate logfile(s)    */      
330
331         time(&tm);
332         
333         if(analyze)
334         {
335                 sprintf(headerbuf, "\n==== isdnanalyze controller rx #%d - tx #%d ==== started %s",
336                                 Rx, Tx, ctime(&tm));
337         }
338         else
339         {
340                 sprintf(headerbuf, "\n=========== isdntrace controller #%d =========== started %s",
341                                 unit, ctime(&tm));
342         }
343         
344         printf("%s", headerbuf);
345         
346         if(outflag)
347                 fprintf(Fout, "%s", headerbuf);
348
349         for (;;)
350         {
351                 if(Popt == 0)
352                 {
353                         n = read(f, buf, BSIZE);
354
355                         if(Bopt)
356                         {
357                                 if((fwrite(buf, 1, n, BP)) != n)
358                                 {
359                                         char buffer[80];
360                                         sprintf(buffer, "Error writing file [%s]", BPfilename);
361                                         perror(buffer);
362                                         exit(1);
363                                 }
364                         }
365
366                         n -= sizeof(i4b_trace_hdr_t);                   
367                 }
368                 else
369                 {                       
370                         if((fread(buf, 1, sizeof(i4b_trace_hdr_t), BP)) != sizeof(i4b_trace_hdr_t))
371                         {
372                                 if(feof(BP))
373                                 {
374                                         printf("\nEnd of playback input file reached.\n");
375                                         exit(0);
376                                 }
377                                 else
378                                 {
379                                         char buffer[80];
380                                         sprintf(buffer, "Error reading hdr from file [%s]", BPfilename);
381                                         perror(buffer);
382                                         exit(1);
383                                 }
384                         }
385
386                         ithp = (i4b_trace_hdr_t *)buf;
387                         l = ithp->length - sizeof(i4b_trace_hdr_t);
388                         
389                         if((n = fread(buf+sizeof(i4b_trace_hdr_t), 1, l , BP)) != l)
390                         {
391                                 char buffer[80];
392                                 sprintf(buffer, "Error reading data from file [%s]", BPfilename);
393                                 perror(buffer);
394                                 exit(1);
395                         }
396
397                 }
398
399                 if(n > 0)
400                 {
401                         dumpbuf(n, b, (i4b_trace_hdr_t *)buf);
402                 }
403         }
404 }
405
406 /*---------------------------------------------------------------------------*
407  *      format header into static buffer, return buffer address
408  *---------------------------------------------------------------------------*/
409 char *
410 fmt_hdr(i4b_trace_hdr_t *hdr, int frm_len)
411 {
412         struct tm *s;
413         static char hbuf[256];
414         int i = 0;
415
416         s = localtime((time_t *)&(hdr->time.tv_sec));
417
418         if(hdr->type == TRC_CH_I)               /* Layer 1 INFO's */
419         {
420                 sprintf(hbuf,"\n-- %s - unit:%d ---------------- time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u ",
421                         ((hdr->dir) ? "NT->TE" : "TE->NT"),
422                         hdr->unit,
423                         s->tm_mday,
424                         s->tm_mon + 1,
425                         s->tm_hour,
426                         s->tm_min,
427                         s->tm_sec,
428                         (u_int32_t)hdr->time.tv_usec);
429         }
430         else
431         {
432                 if(hdr->trunc > 0)
433                 {
434                         sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d (%d) ",
435                                 ((hdr->dir) ? "NT->TE" : "TE->NT"),
436                                 hdr->unit,
437                                 hdr->count,
438                                 s->tm_mday,
439                                 s->tm_mon + 1,
440                                 s->tm_hour,
441                                 s->tm_min,
442                                 s->tm_sec,
443                                 (u_int32_t)hdr->time.tv_usec,
444                                 frm_len,
445                                 hdr->trunc);
446                 }
447                 else
448                 {
449                         sprintf(hbuf,"\n-- %s - unit:%d - frame:%6.6u - time:%2.2d.%2.2d %2.2d:%2.2d:%2.2d.%06u - length:%d ",
450                                 ((hdr->dir) ? "NT->TE" : "TE->NT"),
451                                 hdr->unit,
452                                 hdr->count,
453                                 s->tm_mday,
454                                 s->tm_mon + 1,
455                                 s->tm_hour,
456                                 s->tm_min,
457                                 s->tm_sec,
458                                 (u_int32_t)hdr->time.tv_usec,
459                                 frm_len);
460                 }
461         }
462         
463         for(i=strlen(hbuf); i <= NCOLS;)
464                 hbuf[i++] = '-';
465
466         hbuf[i++] = '\n';
467         hbuf[i] = '\0';
468         
469         return(hbuf);
470 }
471
472 /*---------------------------------------------------------------------------*
473  *      decode protocol and output to file(s)
474  *---------------------------------------------------------------------------*/
475 static void
476 dumpbuf(int n, unsigned char *buf, i4b_trace_hdr_t *hdr)
477 {
478         static char l1buf[128];
479         static unsigned char l2buf[32000];
480         static unsigned char l3buf[32000];
481         int cnt;
482         int nsave = n;
483         char *pbuf;
484         int i, j;
485
486         l1buf[0] = '\0';
487         l2buf[0] = '\0';
488         l3buf[0] = '\0';
489
490         switch(hdr->type)
491         {
492                 case TRC_CH_I:          /* Layer 1 INFO's */
493                         if(enable_trace & TRACE_I)
494                                 layer1(l1buf, buf);
495                         break;
496                         
497                 case TRC_CH_D:          /* D-channel data */
498                         cnt = layer2(l2buf, buf, hdr->dir, print_q921);
499
500                         if(print_q921 == 0)
501                                 l2buf[0] = '\0';
502
503                         n -= cnt;
504                         buf += cnt;
505                 
506                         if(n)
507                         {
508                                 if((*buf != 0x08) && (xflag == 0))
509                                 {
510                                         l2buf[0] = '\0';
511                                         l3buf[0] = '\0';
512                                         break;
513                                 }
514                                 layer3(l3buf, n, cnt, buf);
515                         }
516                         break;
517
518                 default:        /* B-channel data */
519         
520                         pbuf = &l2buf[0];
521         
522                         for (i = 0; i < n; i += 16)
523                         {
524                                 sprintf((pbuf+strlen(pbuf)),"B%d:%.3x  ", hdr->type, i);
525         
526                                 for (j = 0; j < 16; j++)
527                                         if (i + j < n)
528                                                 sprintf((pbuf+strlen(pbuf)),"%02x ", buf[i + j]);
529                                         else
530                                                 sprintf((pbuf+strlen(pbuf)),"   ");
531         
532                                 sprintf((pbuf+strlen(pbuf)),"      ");
533         
534                                 for (j = 0; j < 16 && i + j < n; j++)
535                                         if (isprint(buf[i + j]))
536                                                 sprintf((pbuf+strlen(pbuf)),"%c", buf[i + j]);
537                                         else
538                                                 sprintf((pbuf+strlen(pbuf)),".");
539         
540                                 sprintf((pbuf+strlen(pbuf)),"\n");
541                         }
542                         break;
543         }
544         
545         if(header && ((l1buf[0] != '\0' || l2buf[0] != '\0') || (l3buf[0] != 0)))
546         {
547                 char *p;
548                 p = fmt_hdr(hdr, nsave);
549                 printf("%s", p);
550                 if(outflag)
551                         fprintf(Fout, "%s", p);
552         }
553
554         if(l1buf[0] != '\0')
555         {       
556                 printf("%s", l1buf);
557                 if(outflag)
558                         fprintf(Fout, "%s", l1buf);
559         }
560
561         if(l2buf[0] != '\0')
562         {       
563                 printf("%s", l2buf);
564                 if(outflag)
565                         fprintf(Fout, "%s", l2buf);
566         }
567
568         if(l3buf[0] != '\0')
569         {
570                 printf("%s", l3buf);
571                 if(outflag)
572                         fprintf(Fout, "%s", l3buf);
573         }
574 }
575
576 /*---------------------------------------------------------------------------*
577  *      exit handler function to be called at program exit
578  *---------------------------------------------------------------------------*/
579 void
580 exit_hdl()
581 {
582         if(traceon)
583                 switch_driver(TRACE_OFF, Rx, Tx);
584 }
585
586 /*---------------------------------------------------------------------------*
587  *      switch driver debugging output on/off
588  *---------------------------------------------------------------------------*/
589 static int
590 switch_driver(int value, int rx, int tx)
591 {
592         char buffer[80];
593         int v = value;
594
595         if(analyze == 0)
596         {
597                 if(ioctl(f, I4B_TRC_SET, &v) < 0)
598                 {
599                         sprintf(buffer, "Error ioctl I4B_TRC_SET, val = %d", v);
600                         perror(buffer);
601                         return(-1);
602                 }
603         }
604         else
605         {
606                 if(value == TRACE_OFF)
607                 {
608                         if(ioctl(f, I4B_TRC_RESETA, &v) < 0)
609                         {
610                                 sprintf(buffer, "Error ioctl I4B_TRC_RESETA - ");
611                                 perror(buffer);
612                                 return(-1);
613                         }
614                 }
615                 else
616                 {
617                         i4b_trace_setupa_t tsa;
618                         
619                         tsa.rxunit = rx;
620                         tsa.rxflags = value;
621                         tsa.txunit = tx;
622                         tsa.txflags = value;
623                         
624                         if(ioctl(f, I4B_TRC_SETA, &tsa) < 0)
625                         {
626                                 sprintf(buffer, "Error ioctl I4B_TRC_SETA, val = %d", v);
627                                 perror(buffer);
628                                 return(-1);
629                         }
630                 }
631         }
632         return(0);
633 }
634
635 /*---------------------------------------------------------------------------*
636  *      reopen files to support rotating logfile(s) on SIGUSR1
637  *
638  *      based on an idea from Ripley (ripley@nostromo.in-berlin.de)
639  *
640  *      close file and reopen it for append. this will be a nop
641  *      if the previously opened file hasn't moved but will open
642  *      a new one otherwise, thus enabling a rotation...
643  * 
644  *---------------------------------------------------------------------------*/
645 static void
646 reopenfiles(int dummy)
647 {
648         if(outflag)
649         {
650                 fclose(Fout);
651
652                 if((Fout = fopen(outfilename, "a")) == NULL)
653                 {
654                         char buffer[80];
655
656                         sprintf(buffer, "Error re-opening file [%s]", outfilename);
657                         perror(buffer);
658                         exit(1);
659                 }
660
661                 if((setvbuf(Fout, (char *)NULL, _IONBF, 0)) != 0)
662                 {
663                         char buffer[80];
664
665                         sprintf(buffer, "Error re-setting file [%s] to unbuffered", outfilename);
666                         perror(buffer);
667                         exit(1);
668                 }
669         }
670
671         if(Bopt)
672         {
673                 
674                 fclose(BP);
675
676                 if((BP = fopen(BPfilename, "a")) == NULL)
677                 {
678                         char buffer[80];
679
680                         sprintf(buffer, "Error re-opening file [%s]", BPfilename);
681                         perror(buffer);
682                         exit(1);
683                 }
684
685                 if((setvbuf(BP, (char *)NULL, _IONBF, 0)) != 0)
686                 {
687                         char buffer[80];
688
689                         sprintf(buffer, "Error re-setting file [%s] to unbuffered", BPfilename);
690                         perror(buffer);
691                         exit(1);
692                 }
693         }
694 }
695
696 /*---------------------------------------------------------------------------*
697  *      decode extension bit
698  *---------------------------------------------------------------------------*/
699 void
700 extension(int layer, char *buffer, int cnt, unsigned char value, unsigned char mask)
701 {
702         sprintline(layer, buffer, cnt, value, mask, "Extension Bit = %c (%s)",
703                 (value & mask) ? '1' : '0',
704                 (value & mask) ? "no extension, final octet" : "with extension, octet follows");
705 }
706
707 /*---------------------------------------------------------------------------*
708  *      print bits as 0/1 available for mask
709  *---------------------------------------------------------------------------*/
710 static char *
711 print_bits(unsigned char val, unsigned char mask)
712 {
713         static char buffer[10];
714         int i = 0;
715         int length = 8;
716
717         while(length--)
718         {
719                 if(mask & 0x80)
720                 {
721                         if(val & 0x80)
722                                 buffer[i++] = '1';
723                         else
724                                 buffer[i++] = '0';
725                 }
726                 else
727                 {
728                         buffer[i++] = '-';
729                 }
730                 val = val << 1;
731                 mask = mask << 1;               
732         }
733         buffer[i] = '\0';       
734         return(buffer);
735 }
736
737 /*---------------------------------------------------------------------------*
738  *      print one decoded output line
739  *---------------------------------------------------------------------------*/
740 void
741 sprintline(int layer, char *buffer, int oct_count, int oct_val,
742                 int oct_mask, const char *fmt, ...)
743 {
744         char lbuffer[256];
745         static int lastcount = -1;
746         char *ptr;
747         va_list ap;
748
749         va_start(ap, fmt);
750
751         if(oct_count != lastcount)
752         {
753                 lastcount = oct_count;
754
755                 sprintf(lbuffer, "L%d %2d %02X %s ",
756                         layer,
757                         oct_count,
758                         oct_val,
759                         print_bits(oct_val, oct_mask));
760         }
761         else
762         {
763                 sprintf(lbuffer, "         %s ",
764                         print_bits(oct_val, oct_mask));
765         }
766
767         vsprintf(lbuffer+strlen(lbuffer), fmt, ap);
768
769         va_end(ap);
770
771         sprintf(lbuffer+strlen(lbuffer), "\n");
772
773         if((ptr = rindex(lbuffer, '(')) != NULL)
774         {
775                 char *s = lbuffer;
776                 char *b = buffer;
777                 int len = strlen(lbuffer);
778                 int i;
779                 
780                 for(s = lbuffer; s < ptr; *b++ = *s++)
781                         ;
782                 for(i = 0;(i+len) <= NCOLS; *b++ = ' ', i++)
783                         ;
784                 for(; *s; *b++ = *s++)
785                         ;
786                 *b = '\0';
787         }
788         else
789         {
790                 strcpy(buffer, lbuffer);
791         }
792 }
793
794 /* EOF */