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