2010-04-23 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / tools / ccidmon.c
1 /* ccidmon.c - CCID monitor for use with the Linux usbmon facility.
2  *      Copyright (C) 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 /* This utility takes the output of usbmon, filters out the bulk data
22    and prints the CCID messages in a human friendly way.
23
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stddef.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <stdarg.h>
36 #include <assert.h>
37 #include <unistd.h>
38 #include <signal.h>
39
40
41 #ifndef PACKAGE_VERSION
42 # define PACKAGE_VERSION "[build on " __DATE__ " " __TIME__ "]"
43 #endif
44 #ifndef PACKAGE_BUGREPORT
45 # define PACKAGE_BUGREPORT "devnull@example.org"
46 #endif
47 #define PGM "ccidmon"
48
49 /* Option flags. */
50 static int verbose;
51 static int debug;
52 static int skip_escape;
53 static int usb_bus, usb_dev;
54 static int sniffusb;
55  
56
57 /* Error counter.  */
58 static int any_error;
59
60 /* Data storage.  */
61 struct
62 {
63   int is_bi;
64   char address[50];
65   int count;
66   char data[2000];
67 } databuffer;
68
69
70 enum {
71   RDR_to_PC_NotifySlotChange= 0x50,
72   RDR_to_PC_HardwareError   = 0x51,
73
74   PC_to_RDR_SetParameters   = 0x61,
75   PC_to_RDR_IccPowerOn      = 0x62,
76   PC_to_RDR_IccPowerOff     = 0x63,
77   PC_to_RDR_GetSlotStatus   = 0x65,
78   PC_to_RDR_Secure          = 0x69,
79   PC_to_RDR_T0APDU          = 0x6a,
80   PC_to_RDR_Escape          = 0x6b,
81   PC_to_RDR_GetParameters   = 0x6c,
82   PC_to_RDR_ResetParameters = 0x6d,
83   PC_to_RDR_IccClock        = 0x6e,
84   PC_to_RDR_XfrBlock        = 0x6f,
85   PC_to_RDR_Mechanical      = 0x71,
86   PC_to_RDR_Abort           = 0x72,
87   PC_to_RDR_SetDataRate     = 0x73,
88
89   RDR_to_PC_DataBlock       = 0x80,
90   RDR_to_PC_SlotStatus      = 0x81,
91   RDR_to_PC_Parameters      = 0x82,
92   RDR_to_PC_Escape          = 0x83,
93   RDR_to_PC_DataRate        = 0x84
94 };
95
96
97 #define digitp(p)   ((p) >= '0' && (p) <= '9')
98 #define hexdigitp(a) (digitp (a)                     \
99                       || ((a) >= 'A' && (a) <= 'F')  \
100                       || ((a) >= 'a' && (a) <= 'f'))
101 #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
102 #define xtoi_1(p)   ((p) <= '9'? ((p)- '0'): \
103                      (p) <= 'F'? ((p)-'A'+10):((p)-'a'+10))
104
105
106
107 /* Print diagnostic message and exit with failure. */
108 static void
109 die (const char *format, ...)
110 {
111   va_list arg_ptr;
112
113   fflush (stdout);
114   fprintf (stderr, "%s: ", PGM);
115
116   va_start (arg_ptr, format);
117   vfprintf (stderr, format, arg_ptr);
118   va_end (arg_ptr);
119   putc ('\n', stderr);
120
121   exit (1);
122 }
123
124
125 /* Print diagnostic message. */
126 static void
127 err (const char *format, ...)
128 {
129   va_list arg_ptr;
130
131   any_error = 1;
132
133   fflush (stdout);
134   fprintf (stderr, "%s: ", PGM);
135
136   va_start (arg_ptr, format);
137   vfprintf (stderr, format, arg_ptr);
138   va_end (arg_ptr);
139   putc ('\n', stderr);
140 }
141
142
143 /* Convert a little endian stored 4 byte value into an unsigned
144    integer. */
145 static unsigned int 
146 convert_le_u32 (const unsigned char *buf)
147 {
148   return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); 
149 }
150
151
152 /* Convert a little endian stored 2 byte value into an unsigned
153    integer. */
154 static unsigned int 
155 convert_le_u16 (const unsigned char *buf)
156 {
157   return buf[0] | (buf[1] << 8); 
158 }
159
160
161
162
163 static void
164 print_pr_data (const unsigned char *data, size_t datalen, size_t off)
165 {
166   int needlf = 0;
167   int first = 1;
168
169   for (; off < datalen; off++)
170     {
171       if (!(off % 16) || first)
172         {
173           if (needlf)
174             putchar ('\n');
175           printf ("  [%04d] ", off);
176         }
177       printf (" %02X", data[off]);
178       needlf = 1;
179       first = 0;
180     }
181   if (needlf)
182     putchar ('\n');
183 }
184
185  
186 static void
187 print_p2r_header (const char *name, const unsigned char *msg, size_t msglen)
188 {
189   printf ("%s:\n", name);
190   if (msglen < 7)
191     return;
192   printf ("  dwLength ..........: %u\n", convert_le_u32 (msg+1));
193   printf ("  bSlot .............: %u\n", msg[5]);
194   printf ("  bSeq ..............: %u\n", msg[6]);
195 }
196
197
198 static void
199 print_p2r_iccpoweron (const unsigned char *msg, size_t msglen)
200 {
201   print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen);
202   if (msglen < 10)
203     return;
204   printf ("  bPowerSelect ......: 0x%02x (%s)\n", msg[7],
205           msg[7] == 0? "auto":
206           msg[7] == 1? "5.0 V":
207           msg[7] == 2? "3.0 V":
208           msg[7] == 3? "1.8 V":"");
209   print_pr_data (msg, msglen, 8);
210 }
211
212
213 static void
214 print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen)
215 {
216   print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen);
217   print_pr_data (msg, msglen, 7);
218 }
219
220
221 static void
222 print_p2r_getslotstatus (const unsigned char *msg, size_t msglen)
223 {
224   print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen);
225   print_pr_data (msg, msglen, 7);
226 }
227
228
229 static void
230 print_p2r_xfrblock (const unsigned char *msg, size_t msglen)
231 {
232   unsigned int val;
233
234   print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen);
235   if (msglen < 10)
236     return;
237   printf ("  bBWI ..............: 0x%02x\n", msg[7]);
238   val = convert_le_u16 (msg+8);
239   printf ("  wLevelParameter ...: 0x%04x%s\n", val,
240           val == 1? " (continued)":
241           val == 2? " (continues+ends)":
242           val == 3? " (continues+continued)":
243           val == 16? " (DataBlock-expected)":"");
244   print_pr_data (msg, msglen, 10);
245 }
246
247
248 static void
249 print_p2r_getparameters (const unsigned char *msg, size_t msglen)
250 {
251   print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen);
252   print_pr_data (msg, msglen, 7);
253 }
254
255
256 static void
257 print_p2r_resetparameters (const unsigned char *msg, size_t msglen)
258 {
259   print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen);
260   print_pr_data (msg, msglen, 7);
261 }
262
263
264 static void
265 print_p2r_setparameters (const unsigned char *msg, size_t msglen)
266 {
267   print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
268   if (msglen < 10)
269     return;
270   printf ("  bProtocolNum ......: 0x%02x\n", msg[7]);
271   print_pr_data (msg, msglen, 8);
272 }
273
274
275 static void
276 print_p2r_escape (const unsigned char *msg, size_t msglen)
277 {
278   if (skip_escape)
279     return;
280   print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
281   print_pr_data (msg, msglen, 7);
282 }
283
284
285 static void
286 print_p2r_iccclock (const unsigned char *msg, size_t msglen)
287 {
288   print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
289   if (msglen < 10)
290     return;
291   printf ("  bClockCommand .....: 0x%02x\n", msg[7]);
292   print_pr_data (msg, msglen, 8);
293 }
294
295
296 static void
297 print_p2r_to0apdu (const unsigned char *msg, size_t msglen)
298 {
299   print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen);
300   if (msglen < 10)
301     return;
302   printf ("  bmChanges .........: 0x%02x\n", msg[7]);
303   printf ("  bClassGetResponse .: 0x%02x\n", msg[8]);
304   printf ("  bClassEnvelope ....: 0x%02x\n", msg[9]);
305   print_pr_data (msg, msglen, 10);
306 }
307
308
309 static void
310 print_p2r_secure (const unsigned char *msg, size_t msglen)
311 {
312   unsigned int val;
313
314   print_p2r_header ("PC_to_RDR_Secure", msg, msglen);
315   if (msglen < 10)
316     return;
317   printf ("  bBMI ..............: 0x%02x\n", msg[7]);
318   val = convert_le_u16 (msg+8);
319   printf ("  wLevelParameter ...: 0x%04x%s\n", val,
320           val == 1? " (continued)":
321           val == 2? " (continues+ends)":
322           val == 3? " (continues+continued)":
323           val == 16? " (DataBlock-expected)":"");
324   print_pr_data (msg, msglen, 10);
325 }
326
327
328 static void
329 print_p2r_mechanical (const unsigned char *msg, size_t msglen)
330 {
331   print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
332   if (msglen < 10)
333     return;
334   printf ("  bFunction .........: 0x%02x\n", msg[7]);
335   print_pr_data (msg, msglen, 8);
336 }
337
338
339 static void
340 print_p2r_abort (const unsigned char *msg, size_t msglen)
341 {
342   print_p2r_header ("PC_to_RDR_Abort", msg, msglen);
343   print_pr_data (msg, msglen, 7);
344 }
345
346
347 static void
348 print_p2r_setdatarate (const unsigned char *msg, size_t msglen)
349 {
350   print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen);
351   if (msglen < 10)
352     return;
353   print_pr_data (msg, msglen, 7);
354 }
355
356
357 static void
358 print_p2r_unknown (const unsigned char *msg, size_t msglen)
359 {
360   char buf[100];
361
362   snprintf (buf, sizeof buf, "Unknown PC_to_RDR command 0x%02X", 
363             msglen? msg[0]:0);
364   print_p2r_header (buf, msg, msglen);
365   if (msglen < 10)
366     return;
367   print_pr_data (msg, msglen, 0);
368 }
369
370
371 static void
372 print_p2r (const unsigned char *msg, size_t msglen)
373 {
374   switch (msglen? msg[0]:0)
375     {
376     case PC_to_RDR_IccPowerOn:
377       print_p2r_iccpoweron (msg, msglen);
378       break;
379     case PC_to_RDR_IccPowerOff:
380       print_p2r_iccpoweroff (msg, msglen);
381       break;
382     case PC_to_RDR_GetSlotStatus:
383       print_p2r_getslotstatus (msg, msglen);
384       break;
385     case PC_to_RDR_XfrBlock:
386       print_p2r_xfrblock (msg, msglen);
387       break;
388     case PC_to_RDR_GetParameters:
389       print_p2r_getparameters (msg, msglen);
390       break;
391     case PC_to_RDR_ResetParameters:
392       print_p2r_resetparameters (msg, msglen);
393       break;
394     case PC_to_RDR_SetParameters:
395       print_p2r_setparameters (msg, msglen);
396       break;
397     case PC_to_RDR_Escape:
398       print_p2r_escape (msg, msglen);
399       break;
400     case PC_to_RDR_IccClock:
401       print_p2r_iccclock (msg, msglen);
402       break;
403     case PC_to_RDR_T0APDU:
404       print_p2r_to0apdu (msg, msglen);
405       break;
406     case PC_to_RDR_Secure:
407       print_p2r_secure (msg, msglen);
408       break;
409     case PC_to_RDR_Mechanical:
410       print_p2r_mechanical (msg, msglen);
411       break;
412     case PC_to_RDR_Abort:
413       print_p2r_abort (msg, msglen);
414       break;
415     case PC_to_RDR_SetDataRate:
416       print_p2r_setdatarate (msg, msglen);
417       break;
418     default:
419       print_p2r_unknown (msg, msglen);
420       break;
421     }
422 }
423
424
425 static void
426 print_r2p_header (const char *name, const unsigned char *msg, size_t msglen)
427 {
428   printf ("%s:\n", name);
429   if (msglen < 9)
430     return;
431   printf ("  dwLength ..........: %u\n", convert_le_u32 (msg+1));
432   printf ("  bSlot .............: %u\n", msg[5]);
433   printf ("  bSeq ..............: %u\n", msg[6]);
434   printf ("  bStatus ...........: %u\n", msg[7]);
435   if (msg[8])
436     printf ("  bError ............: %u\n", msg[8]);
437 }
438
439
440 static void
441 print_r2p_datablock (const unsigned char *msg, size_t msglen)
442 {
443   print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen);
444   if (msglen < 10)
445     return;
446   if (msg[9])
447     printf ("  bChainParameter ...: 0x%02x%s\n", msg[9],
448             msg[9] == 1? " (continued)":
449             msg[9] == 2? " (continues+ends)":
450             msg[9] == 3? " (continues+continued)":
451             msg[9] == 16? " (XferBlock-expected)":"");
452   print_pr_data (msg, msglen, 10);
453 }
454
455
456 static void
457 print_r2p_slotstatus (const unsigned char *msg, size_t msglen)
458 {
459   print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen);
460   if (msglen < 10)
461     return;
462   printf ("  bClockStatus ......: 0x%02x%s\n", msg[9],
463           msg[9] == 0? " (running)":
464           msg[9] == 1? " (stopped-L)":
465           msg[9] == 2? " (stopped-H)":
466           msg[9] == 3? " (stopped)":"");
467   print_pr_data (msg, msglen, 10);
468 }
469   
470
471 static void
472 print_r2p_parameters (const unsigned char *msg, size_t msglen)
473 {
474   print_r2p_header ("RDR_to_PC_Parameters", msg, msglen);
475   if (msglen < 10)
476     return;
477
478   printf ("  protocol ..........: T=%d\n", msg[9]);
479   if (msglen == 17 && msg[9] == 1)
480     {
481       /* Protocol T=1.  */
482       printf ("  bmFindexDindex ....: %02X\n", msg[10]);
483       printf ("  bmTCCKST1 .........: %02X\n", msg[11]);
484       printf ("  bGuardTimeT1 ......: %02X\n", msg[12]);
485       printf ("  bmWaitingIntegersT1: %02X\n", msg[13]);
486       printf ("  bClockStop ........: %02X\n", msg[14]);
487       printf ("  bIFSC .............: %d\n", msg[15]);
488       printf ("  bNadValue .........: %d\n", msg[16]);
489     }
490   else
491     print_pr_data (msg, msglen, 10);
492 }
493
494
495 static void
496 print_r2p_escape (const unsigned char *msg, size_t msglen)
497 {
498   if (skip_escape)
499     return;
500   print_r2p_header ("RDR_to_PC_Escape", msg, msglen);
501   if (msglen < 10)
502     return;
503   printf ("  buffer[9] .........: %02X\n", msg[9]);
504   print_pr_data (msg, msglen, 10);
505 }
506
507
508 static void
509 print_r2p_datarate (const unsigned char *msg, size_t msglen)
510 {
511   print_r2p_header ("RDR_to_PC_DataRate", msg, msglen);
512   if (msglen < 10)
513     return;
514   if (msglen >= 18)
515     {
516       printf ("  dwClockFrequency ..: %u\n", convert_le_u32 (msg+10));
517       printf ("  dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14));
518       print_pr_data (msg, msglen, 18);
519     }
520   else
521     print_pr_data (msg, msglen, 10);
522 }
523
524
525 static void
526 print_r2p_unknown (const unsigned char *msg, size_t msglen)
527 {
528   char buf[100];
529
530   snprintf (buf, sizeof buf, "Unknown RDR_to_PC command 0x%02X", 
531             msglen? msg[0]:0);
532   print_r2p_header (buf, msg, msglen);
533   if (msglen < 10)
534     return;
535   printf ("  bMessageType ......: %02X\n", msg[0]);
536   printf ("  buffer[9] .........: %02X\n", msg[9]);
537   print_pr_data (msg, msglen, 10);
538 }
539
540
541 static void
542 print_r2p (const unsigned char *msg, size_t msglen)
543 {
544   switch (msglen? msg[0]:0)
545     {
546     case RDR_to_PC_DataBlock:
547       print_r2p_datablock (msg, msglen);
548       break;
549     case RDR_to_PC_SlotStatus:
550       print_r2p_slotstatus (msg, msglen);
551       break;
552     case RDR_to_PC_Parameters:
553       print_r2p_parameters (msg, msglen);
554       break;
555     case RDR_to_PC_Escape:
556       print_r2p_escape (msg, msglen);
557       break;
558     case RDR_to_PC_DataRate:
559       print_r2p_datarate (msg, msglen);
560       break;
561     default:
562       print_r2p_unknown (msg, msglen);
563       break;
564     }
565
566 }
567
568
569 static void
570 flush_data (void)
571 {
572   if (!databuffer.count)
573     return;
574   
575   if (verbose)
576     printf ("Address: %s\n", databuffer.address);
577   if (databuffer.is_bi)
578     {
579       print_r2p (databuffer.data, databuffer.count);
580       if (verbose)
581         putchar ('\n');
582     }
583   else
584     print_p2r (databuffer.data, databuffer.count);
585
586   databuffer.count = 0;
587 }
588
589 static void
590 collect_data (char *hexdata, const char *address, unsigned int lineno)
591 {
592   size_t length;
593   int is_bi;
594   char *s;
595   unsigned int value;
596
597   is_bi = (*address && address[1] == 'i');
598
599   if (databuffer.is_bi != is_bi || strcmp (databuffer.address, address))
600     flush_data ();
601   databuffer.is_bi = is_bi;
602   if (strlen (address) >= sizeof databuffer.address)
603     die ("address field too long");
604   strcpy (databuffer.address, address);
605
606   length = databuffer.count;
607   for (s=hexdata; *s; s++ )
608     {
609       if (ascii_isspace (*s))
610         continue;
611       if (!hexdigitp (*s))
612         {
613           err ("invalid hex digit in line %u - line skipped", lineno);
614           break;
615         }
616       value = xtoi_1 (*s) * 16;
617       s++;
618       if (!hexdigitp (*s))
619         {
620           err ("invalid hex digit in line %u - line skipped", lineno);
621           break;
622         }
623       value += xtoi_1 (*s);
624
625       if (length >= sizeof (databuffer.data))
626         {
627           err ("too much data at line %u - can handle only up to % bytes",
628                lineno, sizeof (databuffer.data));
629           break;
630         }
631       databuffer.data[length++] = value;
632     }
633   databuffer.count = length;
634 }
635
636
637 static void
638 parse_line (char *line, unsigned int lineno)
639 {
640   char *p;
641   char *event_type, *address, *data, *status, *datatag;
642
643   if (debug)
644     printf ("line[%u] =`%s'\n", lineno, line);
645
646   p = strtok (line, " ");
647   if (!p)
648     die ("invalid line %d (no URB)");
649   p = strtok (NULL, " ");
650   if (!p)
651     die ("invalid line %d (no timestamp)");
652   event_type = strtok (NULL, " ");
653   if (!event_type)
654     die ("invalid line %d (no event type)");
655   address = strtok (NULL, " ");
656   if (!address)
657     die ("invalid line %d (no address");
658   if (usb_bus || usb_dev)
659     {
660       int bus, dev;
661
662       p = strchr (address, ':');
663       if (!p)
664         die ("invalid line %d (invalid address");
665       p++;
666       bus = atoi (p);
667       p = strchr (p, ':');
668       if (!p)
669         die ("invalid line %d (invalid address");
670       p++;
671       dev = atoi (p);
672
673       if ((usb_bus && usb_bus != bus) || (usb_dev && usb_dev != dev))
674         return;  /* We don't want that one.  */
675     }
676   if (*address != 'B' || (address[1] != 'o' && address[1] != 'i'))
677     return; /* We only want block in and block out.  */
678   status = strtok (NULL, " ");
679   if (!status)
680     return;
681   if (!strchr ("-0123456789", *status))
682     return; /* Setup packet.  */
683   /* We don't support "Z[io]" types thus we don't need to check here.  */
684   p = strtok (NULL, " ");
685   if (!p)
686     return; /* No data length.  */
687   
688   datatag = strtok (NULL, " ");
689   if (datatag && *datatag == '=')
690     {
691       data = strtok (NULL, "");
692       collect_data (data?data:"", address, lineno);
693     }
694 }
695
696
697 static void
698 parse_line_sniffusb (char *line, unsigned int lineno)
699 {
700   char *p;
701
702   if (debug)
703     printf ("line[%u] =`%s'\n", lineno, line);
704
705   p = strtok (line, " \t");
706   if (!p)
707     return;
708   p = strtok (NULL, " \t");
709   if (!p)
710     return; 
711   p = strtok (NULL, " \t");
712   if (!p)
713     return; 
714
715   if (hexdigitp (p[0]) && hexdigitp (p[1])
716       && hexdigitp (p[2]) && hexdigitp (p[3])
717       && p[4] == ':' && !p[5])
718     {
719       size_t length;
720       unsigned int value;
721       
722       length = databuffer.count;
723       while ((p=strtok (NULL, " \t")))
724         {
725           if (!hexdigitp (p[0]) || !hexdigitp (p[1]))
726             {
727               err ("invalid hex digit in line %u (%s)", lineno,p);
728               break;
729             }
730           value = xtoi_1 (p[0]) * 16 + xtoi_1 (p[1]);
731
732           if (length >= sizeof (databuffer.data))
733             {
734               err ("too much data at line %u - can handle only up to % bytes",
735                    lineno, sizeof (databuffer.data));
736               break;
737             }
738           databuffer.data[length++] = value;
739         }
740       databuffer.count = length;
741
742     }
743   else if (!strcmp (p, "TransferFlags"))
744     {
745       flush_data ();
746
747       *databuffer.address = 0;
748       while ((p=strtok (NULL, " \t(,)")))
749         {
750           if (!strcmp (p, "USBD_TRANSFER_DIRECTION_IN"))
751             {
752               databuffer.is_bi = 1;
753               break;
754             }
755           else if (!strcmp (p, "USBD_TRANSFER_DIRECTION_OUT"))
756             {
757               databuffer.is_bi = 0;
758               break;
759             }
760         }
761     }
762
763 }
764
765
766 static void
767 parse_input (FILE *fp)
768 {
769   char line[2000];
770   size_t length;
771   unsigned int lineno = 0;
772
773   while (fgets (line, sizeof (line), fp))
774     {
775       lineno++;
776       length = strlen (line);
777       if (length && line[length - 1] == '\n')
778         line[--length] = 0;
779       else
780         err ("line number %u too long or last line not terminated", lineno);
781       if (length && line[length - 1] == '\r')
782         line[--length] = 0;
783       if (sniffusb)
784         parse_line_sniffusb (line, lineno);
785       else
786         parse_line (line, lineno);
787     }
788   flush_data ();
789   if (ferror (fp))
790     err ("error reading input at line %u: %s", lineno, strerror (errno));
791 }
792
793
794 int 
795 main (int argc, char **argv)
796 {
797   int last_argc = -1;
798
799   if (argc)
800     {
801       argc--; argv++;
802     }
803   while (argc && last_argc != argc )
804     {
805       last_argc = argc;
806       if (!strcmp (*argv, "--"))
807         {
808           argc--; argv++;
809           break;
810         }
811       else if (!strcmp (*argv, "--version"))
812         {
813           fputs (PGM " (GnuPG) " PACKAGE_VERSION "\n", stdout);
814           exit (0);
815         }
816       else if (!strcmp (*argv, "--help"))
817         {
818           puts ("Usage: " PGM " [BUS:DEV]\n"
819                 "Parse the output of usbmod assuming it is CCID compliant.\n\n"
820                 "  --skip-escape  do not show escape packets\n"
821                 "  --sniffusb     Assume output from Sniffusb.exe\n"
822                 "  --verbose      enable extra informational output\n"
823                 "  --debug        enable additional debug output\n"
824                 "  --help         display this help and exit\n\n"
825                 "Report bugs to " PACKAGE_BUGREPORT ".");
826           exit (0);
827         }
828       else if (!strcmp (*argv, "--verbose"))
829         {
830           verbose = 1;
831           argc--; argv++;
832         }
833       else if (!strcmp (*argv, "--debug"))
834         {
835           verbose = debug = 1;
836           argc--; argv++;
837         }
838       else if (!strcmp (*argv, "--skip-escape"))
839         {
840           skip_escape = 1;
841           argc--; argv++;
842         }
843       else if (!strcmp (*argv, "--sniffusb"))
844         {
845           sniffusb = 1;
846           argc--; argv++;
847         }
848     }          
849
850   if (argc && sniffusb)
851     die ("no arguments expected when using --sniffusb\n");
852   else if (argc > 1)
853     die ("usage: " PGM " [BUS:DEV]  (try --help for more information)\n");
854
855   if (argc == 1)
856     {
857       const char *s = strchr (argv[0], ':');
858       
859       usb_bus = atoi (argv[0]);
860       if (s)
861         usb_dev =  atoi (s+1);
862       if (usb_bus < 1 || usb_bus > 999 || usb_dev < 1 || usb_dev > 999)
863         die ("invalid bus:dev specified");
864     }
865   
866
867   signal (SIGPIPE, SIG_IGN);
868
869   parse_input (stdin);
870
871   return any_error? 1:0;
872 }
873
874
875 /*
876 Local Variables:
877 compile-command: "gcc -Wall -Wno-pointer-sign -g -o ccidmon ccidmon.c"
878 End:
879 */