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