/* Returns whether the `sym' is being pressed according to `mask'. * The comparison ignores the case of `sym', and it does not have * to include the "KEY_" and "BTN_" prefix. */ static int match(unsigned char const *mask, char const *sym) { unsigned i; /* TODO This linear search is quite ineffective. */ for (i = 0; i < MEMBS_OF(Symbols); i++) { /* All symbolic names in `Symbols' are prefixed * either with "KEY_" or "BTN_", so addressing * the fourth character is safe. */ if (!Symbols[i] || !BIT_OF(mask, i)) continue; if (!strcasecmp(&Symbols[i][4], sym)) return 1; if (!strcasecmp(&Symbols[i][0], sym)) return 1; } return 0; } /* match */
static size_t date_strftime_with_tmx(char *s, size_t maxsize, const char *format, const struct tmx *tmx) { char *endp = s + maxsize; char *start = s; const char *sp, *tp; auto char tbuf[100]; ptrdiff_t i; int v, w; size_t colons; int precision, flags; char padding; /* LOCALE_[OE] and COLONS are actually modifiers, not flags */ enum {LEFT, CHCASE, LOWER, UPPER, LOCALE_O, LOCALE_E, COLONS}; #define BIT_OF(n) (1U<<(n)) /* various tables for locale C */ static const char days_l[][10] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; static const char months_l[][10] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; static const char ampm[][3] = { "AM", "PM", }; if (s == NULL || format == NULL || tmx == NULL || maxsize == 0) return 0; /* quick check if we even need to bother */ if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) { err: errno = ERANGE; return 0; } for (; *format && s < endp - 1; format++) { #define FLAG_FOUND() do { \ if (precision > 0 || flags & (BIT_OF(LOCALE_E) | BIT_OF(LOCALE_O) | BIT_OF(COLONS))) \ goto unknown; \ } while (0) #define NEEDS(n) do if (s >= endp || (n) >= endp - s - 1) goto err; while (0) #define FILL_PADDING(i) do { \ if (!(flags & BIT_OF(LEFT)) && precision > (i)) { \ NEEDS(precision); \ memset(s, padding ? padding : ' ', precision - (i)); \ s += precision - (i); \ } \ else { \ NEEDS(i); \ } \ } while (0); #define FMT(def_pad, def_prec, fmt, val) \ do { \ int l; \ if (precision <= 0) precision = (def_prec); \ if (flags & BIT_OF(LEFT)) precision = 1; \ l = snprintf(s, endp - s, \ ((padding == '0' || (!padding && (def_pad) == '0')) ? \ "%0*"fmt : "%*"fmt), \ precision, (val)); \ if (l < 0) goto err; \ s += l; \ } while (0) #define STRFTIME(fmt) \ do { \ i = date_strftime_with_tmx(s, endp - s, (fmt), tmx); \ if (!i) return 0; \ if (flags & BIT_OF(UPPER)) \ upcase(s, i); \ if (!(flags & BIT_OF(LEFT)) && precision > i) { \ if (start + maxsize < s + precision) { \ errno = ERANGE; \ return 0; \ } \ memmove(s + precision - i, s, i); \ memset(s, padding ? padding : ' ', precision - i); \ s += precision; \ } \ else s += i; \ } while (0) #define FMTV(def_pad, def_prec, fmt, val) \ do { \ VALUE tmp = (val); \ if (FIXNUM_P(tmp)) { \ FMT((def_pad), (def_prec), "l"fmt, FIX2LONG(tmp)); \ } \ else { \ VALUE args[2], result; \ size_t l; \ if (precision <= 0) precision = (def_prec); \ if (flags & BIT_OF(LEFT)) precision = 1; \ args[0] = INT2FIX(precision); \ args[1] = (val); \ if (padding == '0' || (!padding && (def_pad) == '0')) \ result = rb_str_format(2, args, rb_str_new2("%0*"fmt)); \ else \ result = rb_str_format(2, args, rb_str_new2("%*"fmt)); \ l = strlcpy(s, StringValueCStr(result), endp - s); \ if ((size_t)(endp - s) <= l) \ goto err; \ s += l; \ } \ } while (0) if (*format != '%') { *s++ = *format; continue; } tp = tbuf; sp = format; precision = -1; flags = 0; padding = 0; colons = 0; again: switch (*++format) { case '\0': format--; goto unknown; case 'A': /* full weekday name */ case 'a': /* abbreviated weekday name */ if (flags & BIT_OF(CHCASE)) { flags &= ~(BIT_OF(LOWER) | BIT_OF(CHCASE)); flags |= BIT_OF(UPPER); } { int wday = tmx_wday; if (wday < 0 || wday > 6) i = 1, tp = "?"; else { if (*format == 'A') i = strlen(tp = days_l[wday]); else i = 3, tp = days_l[wday]; } } break; case 'B': /* full month name */ case 'b': /* abbreviated month name */ case 'h': /* same as %b */ if (flags & BIT_OF(CHCASE)) { flags &= ~(BIT_OF(LOWER) | BIT_OF(CHCASE)); flags |= BIT_OF(UPPER); } { int mon = tmx_mon; if (mon < 1 || mon > 12) i = 1, tp = "?"; else { if (*format == 'B') i = strlen(tp = months_l[mon - 1]); else i = 3, tp = months_l[mon - 1]; } } break; case 'C': /* century (year/100) */ FMTV('0', 2, "d", div(tmx_year, INT2FIX(100))); continue; case 'c': /* appropriate date and time representation */ STRFTIME("%a %b %e %H:%M:%S %Y"); continue; case 'D': STRFTIME("%m/%d/%y"); continue; case 'd': /* day of the month, 01 - 31 */ case 'e': /* day of month, blank padded */ v = range(1, tmx_mday, 31); FMT((*format == 'd') ? '0' : ' ', 2, "d", v); continue; case 'F': STRFTIME("%Y-%m-%d"); continue; case 'G': /* year of ISO week with century */ case 'Y': /* year with century */ { VALUE year = (*format == 'G') ? tmx_cwyear : tmx_year; if (FIXNUM_P(year)) { long y = FIX2LONG(year); FMT('0', 0 <= y ? 4 : 5, "ld", y); } else { FMTV('0', 4, "d", year); } } continue; case 'g': /* year of ISO week without a century */ case 'y': /* year without a century */ v = NUM2INT(mod((*format == 'g') ? tmx_cwyear : tmx_year, INT2FIX(100))); FMT('0', 2, "d", v); continue; case 'H': /* hour, 24-hour clock, 00 - 23 */ case 'k': /* hour, 24-hour clock, blank pad */ v = range(0, tmx_hour, 23); FMT((*format == 'H') ? '0' : ' ', 2, "d", v); continue; case 'I': /* hour, 12-hour clock, 01 - 12 */ case 'l': /* hour, 12-hour clock, 1 - 12, blank pad */ v = range(0, tmx_hour, 23); if (v == 0) v = 12; else if (v > 12) v -= 12; FMT((*format == 'I') ? '0' : ' ', 2, "d", v); continue; case 'j': /* day of the year, 001 - 366 */ v = range(1, tmx_yday, 366); FMT('0', 3, "d", v); continue; case 'L': /* millisecond */ case 'N': /* nanosecond */ if (*format == 'L') w = 3; else w = 9; if (precision <= 0) precision = w; NEEDS(precision); { VALUE subsec = tmx_sec_fraction; int ww; long n; ww = precision; while (9 <= ww) { subsec = mul(subsec, INT2FIX(1000000000)); ww -= 9; } n = 1; for (; 0 < ww; ww--) n *= 10; if (n != 1) subsec = mul(subsec, INT2FIX(n)); subsec = div(subsec, INT2FIX(1)); if (FIXNUM_P(subsec)) { (void)snprintf(s, endp - s, "%0*ld", precision, FIX2LONG(subsec)); s += precision; } else { VALUE args[2], result; args[0] = INT2FIX(precision); args[1] = subsec; result = rb_str_format(2, args, rb_str_new2("%0*d")); (void)strlcpy(s, StringValueCStr(result), endp - s); s += precision; } } continue; case 'M': /* minute, 00 - 59 */ v = range(0, tmx_min, 59); FMT('0', 2, "d", v); continue; case 'm': /* month, 01 - 12 */ v = range(1, tmx_mon, 12); FMT('0', 2, "d", v); continue; case 'n': /* same as \n */ FILL_PADDING(1); *s++ = '\n'; continue; case 't': /* same as \t */ FILL_PADDING(1); *s++ = '\t'; continue; case 'P': /* am or pm based on 12-hour clock */ case 'p': /* AM or PM based on 12-hour clock */ if ((*format == 'p' && (flags & BIT_OF(CHCASE))) || (*format == 'P' && !(flags & (BIT_OF(CHCASE) | BIT_OF(UPPER))))) { flags &= ~(BIT_OF(UPPER) | BIT_OF(CHCASE)); flags |= BIT_OF(LOWER); } v = range(0, tmx_hour, 23); if (v < 12) tp = ampm[0]; else tp = ampm[1]; i = 2; break; case 'Q': /* milliseconds since Unix epoch */ FMTV('0', 1, "d", tmx_msecs); continue; case 'R': STRFTIME("%H:%M"); continue; case 'r': STRFTIME("%I:%M:%S %p"); continue; case 'S': /* second, 00 - 59 */ v = range(0, tmx_sec, 59); FMT('0', 2, "d", v); continue; case 's': /* seconds since Unix epoch */ FMTV('0', 1, "d", tmx_secs); continue; case 'T': STRFTIME("%H:%M:%S"); continue; case 'U': /* week of year, Sunday is first day of week */ case 'W': /* week of year, Monday is first day of week */ v = range(0, (*format == 'U') ? tmx_wnum0 : tmx_wnum1, 53); FMT('0', 2, "d", v); continue; case 'u': /* weekday, Monday == 1, 1 - 7 */ v = range(1, tmx_cwday, 7); FMT('0', 1, "d", v); continue; case 'V': /* week of year according ISO 8601 */ v = range(1, tmx_cweek, 53); FMT('0', 2, "d", v); continue; case 'v': STRFTIME("%e-%b-%Y"); continue; case 'w': /* weekday, Sunday == 0, 0 - 6 */ v = range(0, tmx_wday, 6); FMT('0', 1, "d", v); continue; case 'X': /* appropriate time representation */ STRFTIME("%H:%M:%S"); continue; case 'x': /* appropriate date representation */ STRFTIME("%m/%d/%y"); continue; case 'Z': /* time zone name or abbreviation */ if (flags & BIT_OF(CHCASE)) { flags &= ~(BIT_OF(UPPER) | BIT_OF(CHCASE)); flags |= BIT_OF(LOWER); } { char *zone = tmx_zone; if (zone == NULL) tp = ""; else tp = zone; i = strlen(tp); } break; case 'z': /* offset from UTC */ { long off, aoff; int hl, hw; off = tmx_offset; aoff = off; if (aoff < 0) aoff = -off; if ((aoff / 3600) < 10) hl = 1; else hl = 2; hw = 2; if (flags & BIT_OF(LEFT) && hl == 1) hw = 1; switch (colons) { case 0: /* %z -> +hhmm */ precision = precision <= (3 + hw) ? hw : precision - 3; NEEDS(precision + 3); break; case 1: /* %:z -> +hh:mm */ precision = precision <= (4 + hw) ? hw : precision - 4; NEEDS(precision + 4); break; case 2: /* %::z -> +hh:mm:ss */ precision = precision <= (7 + hw) ? hw : precision - 7; NEEDS(precision + 7); break; case 3: /* %:::z -> +hh[:mm[:ss]] */ { if (aoff % 3600 == 0) { precision = precision <= (1 + hw) ? hw : precision - 1; NEEDS(precision + 3); } else if (aoff % 60 == 0) { precision = precision <= (4 + hw) ? hw : precision - 4; NEEDS(precision + 4); } else { precision = precision <= (7 + hw) ? hw : precision - 7; NEEDS(precision + 7); } } break; default: format--; goto unknown; } if (padding == ' ' && precision > hl) { i = snprintf(s, endp - s, "%*s", precision - hl, ""); precision = hl; if (i < 0) goto err; s += i; } if (off < 0) { off = -off; *s++ = '-'; } else { *s++ = '+'; } i = snprintf(s, endp - s, "%.*ld", precision, off / 3600); if (i < 0) goto err; s += i; off = off % 3600; if (colons == 3 && off == 0) continue; if (1 <= colons) *s++ = ':'; i = snprintf(s, endp - s, "%02d", (int)(off / 60)); if (i < 0) goto err; s += i; off = off % 60; if (colons == 3 && off == 0) continue; if (2 <= colons) { *s++ = ':'; i = snprintf(s, endp - s, "%02d", (int)off); if (i < 0) goto err; s += i; } } continue; case '+': STRFTIME("%a %b %e %H:%M:%S %Z %Y"); continue; case 'E': /* POSIX locale extensions, ignored for now */ flags |= BIT_OF(LOCALE_E); if (*(format + 1) && strchr("cCxXyY", *(format + 1))) goto again; goto unknown; case 'O': /* POSIX locale extensions, ignored for now */ flags |= BIT_OF(LOCALE_O); if (*(format + 1) && strchr("deHkIlmMSuUVwWy", *(format + 1))) goto again; goto unknown; case ':': flags |= BIT_OF(COLONS); { size_t l = strspn(format, ":"); format += l; if (*format == 'z') { colons = l; format--; goto again; } format -= l; } goto unknown; case '_': FLAG_FOUND(); padding = ' '; goto again; case '-': FLAG_FOUND(); flags |= BIT_OF(LEFT); goto again; case '^': FLAG_FOUND(); flags |= BIT_OF(UPPER); goto again; case '#': FLAG_FOUND(); flags |= BIT_OF(CHCASE); goto again; case '0': FLAG_FOUND(); padding = '0'; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { char *e; precision = (int)strtoul(format, &e, 10); format = e - 1; goto again; } case '%': FILL_PADDING(1); *s++ = '%'; continue; default: unknown: i = format - sp + 1; tp = sp; precision = -1; flags = 0; padding = 0; colons = 0; break; } if (i) { FILL_PADDING(i); memcpy(s, tp, i); switch (flags & (BIT_OF(UPPER) | BIT_OF(LOWER))) { case BIT_OF(UPPER): upcase(s, i); break; case BIT_OF(LOWER): downcase(s, i); break; } s += i; } } if (s >= endp) { goto err; } if (*format == '\0') { *s = '\0'; return (s - start); } return 0; }
/* The main function */ int main(int argc, char const *argv[0]) { int hdev; char const *dev; unsigned char kbits[BITMAP(MEMBS_OF(Symbols))]; /* Parse the "-d" command line argument. * dev := path to the evdev device */ #ifdef __ARMEL__ dev = "/dev/input/event2"; #else dev = "/dev/input/event0"; #endif if (argv[1] && !strcmp(argv[1], "-d")) { if (!argv[2]) { fputs("getkey: Required argument missing.\n", stderr); exit(255); } dev = argv[2]; argv += 2; argc -= 2; } /* Open the evdev interface. * hdev := file descriptor of the evdev device */ if ((hdev = open(dev, O_RDONLY)) < 0) { fprintf(stderr, "getkey: %s: %s\n", dev, strerror(errno)); exit(255); } /* kbits := bitmask of all keys pressed at the moment */ memset(kbits, 0, sizeof(kbits)); if (ioctl(hdev, EVIOCGKEY(sizeof(kbits)), kbits) < 0) { fprintf(stderr, "getkey: %s: EVIOCGKEY: %s\n", dev, strerror(errno)); exit(255); } /* Process the rest of the command line. */ if (!argv[1]) { unsigned i; /* Get mode: print the symbolic names of * all keys currently pressed. */ for (i = 0; i < MEMBS_OF(Symbols); i++) if (BIT_OF(kbits, i)) printf("%s 0x%.4X %u\n", Symbols[i] ? Symbols[i] : "<unknown>", i, i); exit(0); } else { int allok, checkany; unsigned i, exitcode; /* Test mode: test whether all key specified * on the command line block are pressed and * exit with the appropriate code. */ allok = 1; checkany = 0; exitcode = 0; for (i = 1; ; i++) { if (!argv[i] || !strcmp(argv[i], "-o")) { /* End of command line block */ if (checkany && allok) /* All of the keys in the block * are being pushed. */ break; exitcode++; if (!argv[i]) /* Out of blocks */ break; /* Process the next block. */ allok = 1; checkany = 0; } else { /* Test wheter argv[i] is pressed * and reset `allok' if it isn't. * There's no point in testing if * the block has already failed. */ checkany = 1; if (allok && !match(kbits, argv[i])) allok = 0; } /* if */ } /* for */ exit(exitcode); } /* if */ /* Not reached */ } /* main */