chiark / gitweb /
hostside: more length for bavarian
[trains.git] / hostside / evdev-manip.c
1 /*
2  * evdev-manip [<options> <device> ...]
3  *
4  *  modes:
5  *     --dump-raw      default
6  *     --redact        print redacted version
7  *                      output is   <path|label> <value> <str> <str>...
8  *                      where <str>... identifies the button, axis, etc.
9  *
10  *  global options:
11  *     --stdin-monitor      quit if stdin becomes readable
12  *
13  *  redaction mode options:
14  *
15  *     --redaction <str>... --suppress  do not print these
16  *     --redaction <str>... --show      print these normally
17  *
18  *        Longest matching <str>... applies.  Later identical
19  *        <str>... paths override earlier ones.
20  *
21  *     --new-redactions     resets everything to default (which is --show)
22  *
23  *        Every device specified after --new-redactions gets the new
24  *        set of redactions, which includes even redactions specified
25  *        _after_ the device but _before_ the next --new-redactions.
26  *        It is not possible to selectively edit the redactions list;
27  *        devices which need different redaction lists need them
28  *        respecifying.
29  *
30  *  per-device options (applies only to next device):
31  *    --label LABEL         use LABEL instead of path in redacted output
32  *
33  *  per-device options (apply to all subsequent):
34  *     --evdev                     subsequent devices are evdev
35  *     --hiddev                    subsequent devices are hiddev
36  *
37  *  per-evdev options (apply to all subsequent):
38  *     --[no-]grab                 --nograb is default
39  *     --expect-sysfs /sys/class/input/inputX/eventY/dev
40  *                        ^^^^^^^^^^^^^^^^^^^^
41  *                          this part is in /proc/bus/usb/devices
42  *                           and can thus be specified by caller
43  *                          for evdev devices
44  *
45  *     evdev <str>... are type and code, each split up into
46  *     prefix and tail, eg EV_KEY KEY_EQUAL becomes EV KEY KEY EQUAL.
47  *     If the type or code is not found in our number-to-string
48  *     tables, the respective two <strs> are 0x and the value in hex.
49  *
50  *  per-hiddev options (apply to all subsequent):
51  *     --elide|show-unchanged      --elide-unchanged is default
52  *
53  *     hiddev <str>... are application and field.  In each case, page
54  *     and usage, both in hex, both at least two digits with 0x in
55  *     from of page only.  This matches what is shown in the USB HID
56  *     usage tables specification which can be found at
57  *     http://www.usb.org/developers/hidpage/
58  */
59
60 #include "common.h"
61
62 #include <oop-read.h>
63 #include <poll.h>
64 #include <sys/fcntl.h>
65 #include <search.h>
66 #include <inttypes.h>
67
68 #include <linux/input.h>
69 #include <linux/hiddev.h>
70
71 typedef struct InputEventState InputEventState;
72 #include "input-codes.h" /* not really a header */
73
74 typedef struct Device Device;
75 typedef struct KindInfo KindInfo;
76 typedef struct ModeInfo ModeInfo;
77 typedef const ModeInfo *Mode;
78 typedef const KindInfo *Kind;
79
80 typedef enum { RA_Show, RA_IntermediateNode, RA_Suppress } RedactionAction;
81
82 typedef struct RedactionNode {
83   const char *str;
84   void *children; /* tsearch tree of RedactionNodes */
85   RedactionAction act;
86 } RedactionNode;
87
88 struct ModeInfo {
89   void (*evdev_event)(Device *d, const struct input_event *ie);
90   void (*evdev_readable)(Device *d);
91   void (*evdev_synch)(Device *d, struct timeval tv);
92   void (*hiddev_event)(Device *d, const struct hiddev_usage_ref *ur);
93   int (*hiddev_xflags)(void);
94   void (*opened)(Device *d);
95   void (*redacted)(Device *d, int nstrs, const char *strs[nstrs], int value);
96   void (*died)(Device *d, int revents, int readr, int readc, int e)
97        __attribute__((noreturn));
98   void (*mainloop)(void);
99 };
100
101 struct KindInfo {
102   void (*prepare)(Device*);
103   void (*readable)(Device*);
104 };
105
106 typedef struct {
107   int elide;
108   RedactionNode redactions; /* root; .str is 0 and irrelevant */
109 } DeviceOptions;
110
111 typedef struct {
112   struct hiddev_field_info fi;
113   uint32_t maxusage;
114   int *lastvalues;
115 } HiddevField;
116
117 struct Device {
118   char *path;
119   const char *label;
120   int fd;
121   const KindInfo *kind;
122   DeviceOptions opts;
123   union {
124     struct {
125       void *froot;
126       HiddevField *fbuf;
127     } hiddev;
128     struct {
129       int nabsinfos;
130       struct input_absinfo **absinfos;
131     } evdev;
132   } forkind;
133 };
134
135 /*---------- globals ----------*/
136
137 /* command line options */
138 static Mode mode;
139 static Kind kind;
140 static int grab, stdinmonitor;
141 static const char *expect_sysfs, *label;
142 static DeviceOptions dopts;
143
144 static int ndevices;
145 static Device *devices;
146
147 /*---------- generally useful ----------*/
148
149 static void pr_hex(unsigned long value) { printf("%#lx",value); }
150
151 #define PR_TABLE_STR(tab, val) (pr_table_str(iesis_##tab, ien_##tab, (val)))
152 static void pr_table_str(const InputEventStringInfo *strings, int nstrings,
153                          unsigned long value) {
154   const InputEventStringInfo *string;
155   if (value > nstrings) { pr_hex(value); return; }
156   string= &strings[value];
157   if (!string->prefix) { pr_hex(value); return; }
158   printf("%s_%s", string->prefix, string->main);
159 }
160
161 static void pr_time(struct timeval tv) {
162   printf("%ju.%06d", (uintmax_t)tv.tv_sec, (int)tv.tv_usec);
163 }
164
165 static void mread(Device *d, void *buf, size_t l) {
166   char *p;
167   int r, remain;
168
169   for (p=buf, remain=l;
170        remain;
171        p+=r, remain-=r) {
172     r= read(d->fd, p, remain);
173     if (r<=0) { mode->died(d, POLLIN, r, -1, errno); abort(); }
174     assert(r <= remain);
175   }
176 }
177
178 static void dump_vpv(unsigned vendor, unsigned product, unsigned version) {
179   printf(" vendor %#x product %#x version %#x",
180          vendor, product, version);
181 }
182 #define DUMP_VPV(t) (dump_vpv((t).vendor, (t).product, (t).version))
183
184 /*---------- evdev kind ----------*/
185
186 static const struct input_absinfo *evdev_getabsinfo(Device *d, uint16_t code) {
187   struct input_absinfo **aip, *ai;
188   int i, r;
189
190   if (code >= 0x80) return 0; /* absurd ioctl scheme! */
191
192   if (code >= d->forkind.evdev.nabsinfos) {
193     int newsize= code+10;
194     d->forkind.evdev.absinfos= mrealloc(d->forkind.evdev.absinfos,
195            newsize * sizeof(*d->forkind.evdev.absinfos));
196     for (i=d->forkind.evdev.nabsinfos; i<newsize; i++)
197       d->forkind.evdev.absinfos[i]= 0;
198   }
199   aip= &d->forkind.evdev.absinfos[code];
200   ai= *aip;
201   if (ai) return ai;
202
203   *aip= ai= mmalloc(sizeof(*ai));
204   r= ioctl(d->fd, EVIOCGABS(code), ai);
205   ai->value= r ? errno : 0;
206   return ai;
207 }
208
209 static void evdev_dump(Device *d, const struct input_event *ie) {
210   const InputEventTypeInfo *t;
211   const struct input_absinfo *ai;
212   printf("evdev ");
213   pr_time(ie->time);
214
215   printf(" ");
216   PR_TABLE_STR(ev, ie->type);
217
218   printf(" ");
219   if (ie->type >= IETIN) {
220     t= 0;
221   } else {
222     t= &ietis[ie->type];
223     if (!t->strings) t= 0;
224   }
225   if (t) pr_table_str(t->strings, t->nstrings, ie->code);
226   else pr_hex(ie->code);
227
228   printf(" ");
229   switch (ie->type) {
230   case EV_ABS:
231     ai= evdev_getabsinfo(d, ie->code);
232     if (!ai) {
233       printf("?");
234     } else if (ai->value) {
235       printf("?%ld [%s]", (long)ie->value, strerror(ai->value));
236       break;
237     } else if ((ai->minimum==-1 || ai->minimum== 0) &&
238         (ai->maximum== 0 || ai->maximum==+1)) {
239     } else if (ai) {
240       printf("%.7f",
241              (double)(ie->value - ai->minimum)/(ai->maximum - ai->minimum));
242       break;
243     }
244     /* fall through */
245   case EV_REL:
246     printf("%ld",(long)ie->value);
247     break;
248   default:
249     printf("%lx",(unsigned long)ie->value);
250     break;
251   }
252   printf("\n");
253 }
254
255 #define MAXTABSTRH 20
256 static void tab_redact(const InputEventStringInfo *strings, int nstrings,
257                        unsigned long value, char hexbuf[MAXTABSTRH],
258                        const char *sb[2]) {
259   const InputEventStringInfo *string;
260   if (value < nstrings &&
261       (string= &strings[value],
262        string->prefix)) {
263     sb[0]= string->prefix;
264     sb[1]= string->main;
265   } else {
266     snprintf(hexbuf,sizeof(hexbuf),"%lx",value);
267     sb[0]= "0x";
268     sb[1]= hexbuf;
269   }
270 }
271
272 static void evdev_redact(Device *d, const struct input_event *ie) {
273   const InputEventTypeInfo *t;
274   char sbh_type[MAXTABSTRH];
275   char sbh_code[MAXTABSTRH];
276   const char *strs[4];
277   
278   tab_redact(iesis_ev, ien_ev, ie->type, sbh_type, &strs[0]);
279
280   if (ie->type >= IETIN) {
281     t= 0;
282   } else {
283     t= &ietis[ie->type];
284     if (!t->strings) t= 0;
285   }
286   tab_redact(t ? t->strings : 0,
287              t ? t->nstrings : 0,
288              ie->code, sbh_code, &strs[2]);
289
290   mode->redacted(d, 4,strs, ie->value);
291 }
292
293 static void evdev_readable(Device *d) {
294   struct input_event ie;
295
296   if (mode->evdev_readable) mode->evdev_readable(d);
297
298   for (;;) {
299     mread(d, &ie, sizeof(ie));
300     if (ie.type == EV_SYN) {
301       if (mode->evdev_synch) mode->evdev_synch(d, ie.time);
302       break;
303     }
304
305     mode->evdev_event(d, &ie);
306   }
307 }
308
309 static void evdev_readable_dump(Device *d) {
310   printf("report-from device %s\n",d->path);
311 }
312 static void evdev_synch_dump(Device *d, struct timeval tv) {
313   printf("synch ");
314   pr_time(tv);
315   printf("\n");
316 }
317
318 static void check_expect_sysfs(int fd, const char *path, const char *efn) {
319   char buf[50], *ep;
320   unsigned long maj, min;
321   struct stat stab;
322   FILE *sysfs;
323   int r;
324     
325   r= fstat(fd, &stab);  if (r) diee("%s: fstat failed", path);
326   if (!S_ISCHR(stab.st_mode)) die("%s: not a character device", path);
327     
328   sysfs= fopen(efn,"r");
329   if (!sysfs) diee("%s: failed to open sysfs %s", path, efn);
330   if (!fgets(buf,sizeof(buf)-1,sysfs)) {
331     if (ferror(sysfs)) diee("%s: failed to read sysfs %s", path, efn);
332     assert(feof(sysfs)); die("%s: eof on sysfs %s", path, efn);
333   }
334   buf[sizeof(buf)-1]= 0;
335   errno=0; maj=strtoul(buf,&ep,0);
336   if (errno || *ep!=':') die("%s: bad major number or no colon in sysfs"
337                              " dev file %s", path, efn);
338   errno=0; min=strtoul(ep+1,&ep,0);
339   if (errno || *ep!='\n') die("%s: bad minor number or no colon in sysfs"
340                               " dev file %s", path, efn);
341
342   if (maj != major(stab.st_rdev) || min != minor(stab.st_rdev))
343     die("%s: is %lu:%lu, expected %lu:%lu", path,
344         (unsigned long)major(stab.st_rdev),
345         (unsigned long)minor(stab.st_rdev),
346         maj, min);
347
348   if (fclose(sysfs)) die("%s: failed to close sysfs %s", path, efn);
349 }
350
351 static void evdev_prepare(Device *d) {
352   int r;
353   struct input_id iid;
354   
355   if (expect_sysfs) {
356     check_expect_sysfs(d->fd, d->path, expect_sysfs);
357     expect_sysfs= 0;
358   }
359
360   r= ioctl(d->fd, EVIOCGID, &iid);
361   if (r) diee("%s: failed to get id",d->path);
362
363   mode->opened(d);
364   printf(" bustype ");
365   PR_TABLE_STR(bus, iid.bustype);
366   DUMP_VPV(iid);
367   putchar('\n');
368   mflushstdout();
369
370   if (grab) {
371     r= ioctl(d->fd, EVIOCGRAB, 1);
372     if (r) diee("%s: failed to grab",d->path);
373   }
374
375   d->forkind.evdev.nabsinfos= 0;
376   d->forkind.evdev.absinfos= 0;
377 }
378
379 static const KindInfo kind_evdev= { evdev_prepare, evdev_readable };
380
381 /*---------- hiddev kind ----------*/
382
383 static int hiddev_f_compar(const void *a_v, const void *b_v) {
384   const HiddevField *a=a_v, *b=b_v;
385   /* these are all unsigned 0..0xffff so the differences fit nicely */
386   return (int)a->fi.report_type - (int)b->fi.report_type ? :
387          (int)a->fi.report_id   - (int)b->fi.report_id   ? :
388          (int)a->fi.field_index - (int)b->fi.field_index;
389 }
390
391 static HiddevField *hiddev_get_f(Device *d,
392                                  const struct hiddev_usage_ref *ur) {
393   HiddevField *f;
394   void **fvp;
395   int r;
396   
397   if (ur->field_index == HID_FIELD_INDEX_NONE)
398     return 0;
399
400   if (!d->forkind.hiddev.fbuf) {
401     d->forkind.hiddev.fbuf= mmalloc(sizeof(*d->forkind.hiddev.fbuf));
402   }
403
404   memset(&d->forkind.hiddev.fbuf->fi,0x55,sizeof(d->forkind.hiddev.fbuf->fi));
405   d->forkind.hiddev.fbuf->fi.report_type= ur->report_type;
406   d->forkind.hiddev.fbuf->fi.report_id=   ur->report_id;
407   d->forkind.hiddev.fbuf->fi.field_index= ur->field_index;
408   fvp= tsearch(d->forkind.hiddev.fbuf,
409                &d->forkind.hiddev.froot,
410                hiddev_f_compar);
411   if (!fvp) diee("tsearch hiddev type/id/index");
412   f= *fvp;
413
414   if (f == d->forkind.hiddev.fbuf) {
415     d->forkind.hiddev.fbuf= 0;
416     
417     r= ioctl(d->fd, HIDIOCGFIELDINFO, &f->fi);
418     if (r) diee("%s: ioctl HIDIOCGFIELDINFO %#x %#x %#x", d->path,
419                 f->fi.report_type, f->fi.report_id, f->fi.field_index);
420
421     f->maxusage= f->fi.maxusage;
422
423     size_t sz= sizeof(*f->lastvalues) * f->maxusage;
424     f->lastvalues= mmalloc(sz);
425     memset(f->lastvalues,0,sz);
426   }
427   if (ur->usage_index >= f->maxusage) {
428     uint32_t newmax= ur->usage_index + 1;
429     fprintf(stderr,"%s: usage_index %"PRIu32" >= maxusage %"PRIu32"\n",
430             d->path, ur->usage_index, f->maxusage);
431     f->lastvalues= mrealloc(f->lastvalues, sizeof(*f->lastvalues) * newmax);
432     memset(f->lastvalues + f->maxusage, 0,
433            sizeof(*f->lastvalues) * (newmax - f->maxusage));
434     f->maxusage= newmax;
435   }
436
437   return f;
438 }
439
440 static int hiddev_elide(Device *d, const struct hiddev_usage_ref *ur,
441                         HiddevField *f) {
442   if (!f)
443     return 0;
444   
445   if (!d->opts.elide)
446     return 0;
447
448   unsigned relevant_flags= f->fi.flags &
449     (HID_FIELD_RELATIVE|HID_FIELD_BUFFERED_BYTE);
450
451   if (relevant_flags == HID_FIELD_RELATIVE &&
452       !ur->value) {
453     return 1;
454   }
455   if (relevant_flags == 0) {
456     if (ur->value == f->lastvalues[ur->usage_index])
457       return 1;
458     f->lastvalues[ur->usage_index]= ur->value;
459   }
460   return 0;
461 }
462
463 static void hiddev_dump(Device *d, const struct hiddev_usage_ref *ur) {
464   HiddevField *f;
465
466   f= hiddev_get_f(d, ur);
467
468   if (hiddev_elide(d, ur, f))
469     return;
470
471   printf("hiddev type %04x id %04x", ur->report_type, ur->report_id);
472   if (ur->field_index == HID_FIELD_INDEX_NONE) {
473     printf(" field index NONE\n");
474     return;
475   }
476
477   printf(" field index %04x"
478          " usage index %04x code %04x value %08lx ",
479          ur->field_index,
480          ur->usage_index, ur->usage_code,
481          (unsigned long)ur->value);
482
483   printf(" maxusage %04x flags %04x"
484          " physical %04x %08lx..%08lx"
485          " logical %04x %08lx..%08lx"
486          " application %04x"
487          " unit %04x exponent %04x",
488          f->fi.maxusage, f->fi.flags,
489          f->fi.physical, (unsigned long)f->fi.physical_minimum,
490          (unsigned long)f->fi.physical_maximum,
491          f->fi.logical, (unsigned long)f->fi.logical_minimum,
492          (unsigned long)f->fi.logical_maximum,
493          f->fi.application,
494          f->fi.unit, f->fi.unit_exponent);
495
496   putchar('\n');
497 }
498
499 static void hiddev_redact_pageusage(unsigned long code,
500                                     char sb_buf[2][7],
501                                     const char *strs[2]) {
502   assert(code <= 0xffffffffUL);
503   sprintf(sb_buf[0], "%#04lx", code >> 16);
504   sprintf(sb_buf[1], "%02lx", code & 0xffff);
505   strs[0]= sb_buf[0];
506   strs[1]= sb_buf[1];
507 }
508
509 static void hiddev_redact(Device *d, const struct hiddev_usage_ref *ur) {
510   HiddevField *f;
511   char sb_app[2][7], sb_usage[2][7];
512   const char *strs[4];
513
514   if (ur->field_index == HID_FIELD_INDEX_NONE)
515     return;
516
517   f= hiddev_get_f(d, ur);
518   if (hiddev_elide(d, ur, f))
519     return;
520
521   hiddev_redact_pageusage(f->fi.application, sb_app,   &strs[0]);
522   hiddev_redact_pageusage(ur->usage_code,    sb_usage, &strs[2]);
523
524   mode->redacted(d, 4,strs, ur->value);
525 }
526
527 static void hiddev_readable(Device *d) {
528   struct hiddev_usage_ref ur;
529   mread(d, &ur, sizeof(ur));
530   mode->hiddev_event(d, &ur);
531 }
532
533 static void hiddev_prepare(Device *d) {
534   int r, flags;
535   struct hiddev_devinfo di;
536
537   flags= HIDDEV_FLAG_UREF;
538   if (mode->hiddev_xflags) flags |= mode->hiddev_xflags();
539   r= ioctl(d->fd, HIDIOCSFLAG, &flags);
540   if (r) diee("hiddev %s: ioctl HIDIOCSFLAG", d->path);
541
542   r= ioctl(d->fd, HIDIOCGDEVINFO, &di);
543   if (r) diee("hiddev %s: ioctl HIDIOCGDEVINFO", d->path);
544
545   mode->opened(d);
546   printf(" bustype ");
547   PR_TABLE_STR(bus, di.bustype);
548   printf(" bus %d dev %d if %d", di.busnum, di.devnum, di.ifnum);
549   DUMP_VPV(di);
550   printf(" napplications %d\n", di.num_applications);
551
552   d->forkind.hiddev.froot= 0;
553   d->forkind.hiddev.fbuf= 0;
554 }
555
556 static int hiddev_xflags_dump(void) { return HIDDEV_FLAG_REPORT; }
557
558 static const KindInfo kind_hiddev= { hiddev_prepare, hiddev_readable };
559
560 /*---------- mode dump ----------*/
561
562 static void dump_died(Device *d, int revents, int readr, int readc, int e)
563      __attribute__((noreturn));
564 static void dump_died(Device *d, int revents, int readr, int readc, int e) {
565   printf("device-terminated %s %#x ", d->path, revents);
566   if (readr<0) printf("err %s", strerror(e));
567   else if (readr==0) printf("eof");
568   else printf("%#x",readc);
569   printf("\n");
570   mflushstdout();
571   exit(0);
572 }
573
574 static void dump_opened(Device *d) {
575   printf("device %s", d->path);
576 }
577
578 static void mainloop(void) {
579   struct pollfd *polls;
580   int i, r, npolls;
581
582   npolls= ndevices + stdinmonitor;
583   polls= mmalloc(sizeof(*polls)*npolls);
584   for (i=0; i<ndevices; i++) {
585     polls[i].fd= devices[i].fd;
586     polls[i].events= POLLIN;
587   }
588   if (stdinmonitor) {
589     polls[ndevices].fd= 0;
590     polls[ndevices].events= POLLIN;
591   }
592
593   for (;;) {
594     for (i=0; i<npolls; i++)
595       polls[i].revents= 0;
596
597     r= poll(polls,npolls,-1);
598     if (r==-1) {
599       if (errno==EINTR) continue;
600       diee("poll failed");
601     }
602     assert(r>0);
603
604     if (stdinmonitor) {
605       if (polls[ndevices].revents) {
606         printf("quitting-stdin-polled %#x\n", polls[ndevices].revents);
607         exit(0);
608       }
609     }
610
611     for (i=0; i<ndevices; i++) {
612       if (polls[i].revents & ~POLLIN) {
613         unsigned char dummy;
614         r= oop_fd_nonblock(polls[i].fd, 1);
615         if (r) diee("nonblock %s during poll unepxected %#x",
616                     devices[i].path, polls[i].revents);
617         r= read(polls[i].fd, &dummy,1);
618         mode->died(&devices[i], polls[i].revents, r, dummy, errno);
619         abort();
620       }
621       if (polls[i].revents) {
622         Device *d= &devices[i];
623         d->kind->readable(d);
624         mflushstdout();
625       }
626     }
627   }
628 }
629
630 static const ModeInfo mode_dump= {
631   evdev_dump, evdev_readable_dump, evdev_synch_dump,
632   hiddev_dump, hiddev_xflags_dump,
633   dump_opened, 0, dump_died, mainloop
634 };
635
636 /*---------- mode redact ----------*/
637
638 static int redaction_node_compar(const void *a_v, const void *b_v) {
639   const RedactionNode *a=a_v, *b=b_v;
640   return strcmp(a->str, b->str);
641 }
642
643 static RedactionNode *redact_find_node(int nstrs, const char *strs[nstrs]) {
644   RedactionNode key, *lastfound, *search;
645   void **rn_vp;
646
647   lastfound= search= &dopts.redactions;
648   for (;;) {
649     if (!nstrs) return lastfound;
650     key.str= strs[0];
651     rn_vp= tfind(&key, &search->children, redaction_node_compar);
652     if (!rn_vp) return lastfound;
653     search= *rn_vp;
654     if (search->act != RA_IntermediateNode)
655       lastfound= search;
656     strs++;
657     nstrs--;
658   }
659 }
660
661 static void redact_redacted(Device *d, int nstrs, const char *strs[nstrs],
662                             int value) {
663   int i;
664   RedactionNode *rn;
665
666   rn= redact_find_node(nstrs, strs);
667   switch (rn->act) {
668   case RA_Show: break;
669   case RA_Suppress: return;
670   default: abort();
671   }
672
673   printf("%s %d", d->label, value);
674   for (i=0; i<nstrs; i++)
675     printf(" %s", strs[i]);
676   putchar('\n');
677 }
678
679 static void redact_opened(Device *d) {
680   printf("%s opened", d->label);
681 }
682
683 static const ModeInfo mode_redact= {
684   evdev_redact, 0, 0,
685   hiddev_redact, 0,
686   redact_opened, redact_redacted, dump_died, mainloop
687 };
688
689 static void redaction(const char ***argv) {
690   RedactionNode *path, *newnode;
691   void **searched;
692   const char *arg;
693
694   path= &dopts.redactions;
695   for (;;) {
696     arg= *++(*argv);
697     if (!arg) badusage("missing str or action for --redaction");
698     if (arg[0]=='-') break;
699     newnode= mmalloc(sizeof(*newnode));
700     newnode->str= arg;
701     searched= tsearch(newnode, &path->children, redaction_node_compar);
702     if (!searched) diee("allocate new redaction node");
703     if (*searched == newnode) {
704       newnode->children= 0;
705       newnode->act= RA_IntermediateNode;
706       path= newnode;
707     } else {
708       free(newnode);
709       path= *searched;
710     }
711   }
712   if (!strcmp(arg,"--suppress")) {
713     path->act= RA_Suppress;
714   } else if (!strcmp(arg,"--show")) {
715     path->act= RA_Show;
716   } else {
717     badusage("unknown or missing action for --redaction");
718   }
719 }
720
721 /*---------- main program ----------*/
722
723 static void getdevice(const char *path) {
724   Device *d;
725   ndevices++;
726   devices= mrealloc(devices, sizeof(*devices)*ndevices);
727   d= &devices[ndevices-1];
728
729   d->path= mstrdup(path);
730   d->fd= open(path, O_RDONLY);  if (d->fd<0) diee("%s: failed to open",path);
731   d->kind= kind;
732   d->opts= dopts;
733
734   if (label) {
735     d->label= label;
736     label= 0;
737   } else {
738     d->label= d->path;
739   }
740
741   kind->prepare(d);
742 }
743
744 int main(int argc, const char **argv) {
745   const char *arg;
746
747   mode= &mode_dump;
748   kind= &kind_evdev;
749   dopts.elide= 1;
750
751   while ((arg= *++argv)) {
752     if (arg[0] != '-') {
753       getdevice(arg);
754     }
755     else if (!strcmp(arg,"--expect-sysfs")) {
756       if (!(expect_sysfs= *++argv)) badusage("missing arg for --expect-sysfs");
757     }
758     else if (!strcmp(arg,"--label")) {
759       if (!(label= *++argv)) badusage("missing arg for --expect-sysfs");
760     }
761     else if (!strcmp(arg,"--redaction")) {
762       redaction(&argv);
763     }
764     else if (!strcmp(arg,"--new-redactions")) {
765       dopts.redactions.children= 0;
766       dopts.redactions.act= RA_Show;
767     }
768     else if (!strcmp(arg,"--dump-raw")) { mode= &mode_dump; }
769     else if (!strcmp(arg,"--redact")) { mode= &mode_redact; }
770     else if (!strcmp(arg,"--evdev")) { kind= &kind_evdev; }
771     else if (!strcmp(arg,"--hiddev")) { kind= &kind_hiddev; }
772     else if (!strcmp(arg,"--grab")) { grab= 1; }
773     else if (!strcmp(arg,"--no-grab")) { grab= 0; }
774     else if (!strcmp(arg,"--show-unchanged")) { dopts.elide= 0; }
775     else if (!strcmp(arg,"--elide-unchanged")) { dopts.elide= 1; }
776     else if (!strcmp(arg,"--stdin-monitor")) { stdinmonitor= 1; }
777     else badusage("unknown option");
778   }
779   mode->mainloop();
780   return 0;
781 }
782
783 const char *progname= "evdev-manip";
784 void die_hook(void) { }
785 void die_vprintf_hook(const char *fmt, va_list al) { }