Beispiel #1
0
void
identifyDriver (
  const char *type,
  const DriverDefinition *definition,
  int full
) {
  {
    char buffer[0X100];

    STR_BEGIN(buffer, sizeof(buffer));
    STR_PRINTF("%s %s Driver:", definition->name, type);

    if (definition->version && *definition->version) {
      STR_PRINTF(" version %s", definition->version);
    }

    if (full) {
      STR_PRINTF(" [compiled on %s at %s]", definition->date, definition->time);
    }

    STR_END;
    logMessage(LOG_NOTICE, "%s", buffer);
  }

  if (full) {
    if (definition->developers && *definition->developers)
      logMessage(LOG_INFO, "   Developed by %s", definition->developers);
  }
}
Beispiel #2
0
size_t
formatKeyName (KeyTable *table, char *buffer, size_t size, const KeyValue *value) {
  const KeyNameEntry *kne = findKeyNameEntry(table, value);

  size_t length;
  STR_BEGIN(buffer, size);

  if (kne) {
    STR_PRINTF("%s", kne->name);
  } else if (value->number != KTB_KEY_ANY) {
    const KeyValue anyKey = {
      .group = value->group,
      .number = KTB_KEY_ANY
    };

    if ((kne = findKeyNameEntry(table, &anyKey))) {
      STR_PRINTF("%s.%u", kne->name, value->number+1);
    }
  }

  if (STR_LENGTH == 0) STR_PRINTF("?");
  length = STR_LENGTH;
  STR_END;
  return length;
}

static int
putKeyName (ListGenerationData *lgd, const KeyValue *value) {
  char name[0X100];

  formatKeyName(lgd->keyTable, name, sizeof(name), value);
  return putUtf8String(lgd, name);
}
Beispiel #3
0
size_t
formatCharacterDescription (char *buffer, size_t size, int column, int row) {
  static char *const colours[] = {
    strtext("black"),
    strtext("blue"),
    strtext("green"),
    strtext("cyan"),
    strtext("red"),
    strtext("magenta"),
    strtext("brown"),
    strtext("light grey"),
    strtext("dark grey"),
    strtext("light blue"),
    strtext("light green"),
    strtext("light cyan"),
    strtext("light red"),
    strtext("light magenta"),
    strtext("yellow"),
    strtext("white")
  };

  size_t length;
  ScreenCharacter character;

  readScreen(column, row, 1, 1, &character);
  STR_BEGIN(buffer, size);

  {
    uint32_t text = character.text;

    STR_PRINTF("char %" PRIu32 " (U+%04" PRIX32 "): %s on %s",
               text, text,
               gettext(colours[character.attributes & 0X0F]),
               gettext(colours[(character.attributes & 0X70) >> 4]));
  }

  if (character.attributes & SCR_ATTR_BLINK) {
    STR_PRINTF(" %s", gettext("blink"));
  }

#ifdef HAVE_ICU
  {
    char name[0X40];
    UErrorCode error = U_ZERO_ERROR;

    u_charName(character.text, U_EXTENDED_CHAR_NAME, name, sizeof(name), &error);
    if (U_SUCCESS(error)) {
      STR_PRINTF(" [%s]", name);
    }
  }
#endif /* HAVE_ICU */

  length = STR_LENGTH;
  STR_END;
  return length;
}
Beispiel #4
0
static size_t
formatTitle_HelpScreen (char *buffer, size_t size) {
  size_t length;

  STR_BEGIN(buffer, size);
  STR_PRINTF("%s", gettext("Help Screen"));
  length = STR_LENGTH;
  STR_END
  return length;
}
Beispiel #5
0
static void
usbLogLineCoding_CDC_ACM (const USB_CDC_ACM_LineCoding *lineCoding) {
  char log[0X80];

  STR_BEGIN(log, sizeof(log));
  STR_PRINTF("CDC ACM line coding:");

  { // baud (bits per second)
    uint32_t baud = getLittleEndian32(lineCoding->dwDTERate);
    STR_PRINTF(" Baud:%" PRIu32, baud);
  }

  { // number of data bits
    STR_PRINTF(" Data:%u", lineCoding->bDataBits);
  }

  { // number of stop bits
    const char *bits;

#define USB_CDC_ACM_STOP(value,name) \
case USB_CDC_ACM_STOP_##value: bits = #name; break;
    switch (lineCoding->bCharFormat) {
      USB_CDC_ACM_STOP(1  , 1  )
      USB_CDC_ACM_STOP(1_5, 1.5)
      USB_CDC_ACM_STOP(2  , 2  )
      default: bits = "?"; break;
    }
#undef USB_CDC_ACM_STOP

    STR_PRINTF(" Stop:%s", bits);
  }

  { // type of parity
    const char *parity;

#define USB_CDC_ACM_PARITY(value,name) \
case USB_CDC_ACM_PARITY_##value: parity = #name; break;
    switch (lineCoding->bParityType) {
      USB_CDC_ACM_PARITY(NONE , none )
      USB_CDC_ACM_PARITY(ODD  , odd  )
      USB_CDC_ACM_PARITY(EVEN , even )
      USB_CDC_ACM_PARITY(MARK , mark )
      USB_CDC_ACM_PARITY(SPACE, space)
      default: parity = "?"; break;
    }
#undef USB_CDC_ACM_PARITY

    STR_PRINTF(" Parity:%s", parity);
  }

  STR_END;
  logMessage(LOG_CATEGORY(USB_IO), "%s", log);
}
Beispiel #6
0
size_t
formatInputError (char *buffer, size_t size, const char *file, const int *line, const char *format, va_list argp) {
  size_t length;

  STR_BEGIN(buffer, size);
  if (file) STR_PRINTF("%s", file);
  if (line) STR_PRINTF("[%d]", *line);
  if (STR_LENGTH) STR_PRINTF(": ");
  STR_VPRINTF(format, argp);
  length = STR_LENGTH;
  STR_END
  return length;
}
Beispiel #7
0
static int
monitorKeyboard (KeyboardInstanceObject *kio) {
  const char *deviceName = locatePathName(kio->kix->device.path);

  if ((kio->kix->file.descriptor = open(kio->kix->device.path, O_RDONLY)) != -1) {
    struct stat status;

    if (fstat(kio->kix->file.descriptor, &status) != -1) {
      if (S_ISCHR(status.st_mode)) {
        {
          char description[0X100];

          STR_BEGIN(description, sizeof(description));
          STR_PRINTF("%s:", deviceName);

          {
            struct input_id identity;

            if (ioctl(kio->kix->file.descriptor, EVIOCGID, &identity) != -1) {
              STR_PRINTF(" bus=%04X vnd=%04X prd=%04X ver=%04X",
                         identity.bustype, identity.vendor, identity.product, identity.version);

              {
                static const KeyboardType typeTable[] = {
  #ifdef BUS_I8042
                  [BUS_I8042] = KBD_TYPE_PS2,
  #endif /* BUS_I8042 */

  #ifdef BUS_USB
                  [BUS_USB] = KBD_TYPE_USB,
  #endif /* BUS_USB */

  #ifdef BUS_BLUETOOTH
                  [BUS_BLUETOOTH] = KBD_TYPE_Bluetooth,
  #endif /* BUS_BLUETOOTH */
                };

                if (identity.bustype < ARRAY_COUNT(typeTable)) {
                  kio->actualProperties.type = typeTable[identity.bustype];
                }
              }

              kio->actualProperties.vendor = identity.vendor;
              kio->actualProperties.product = identity.product;
            } else if (errno != ENOTTY) {
              logMessage(LOG_WARNING, "cannot get input device identity: %s: %s",
                         deviceName, strerror(errno));
            }
          }
Beispiel #8
0
static size_t
usbFormatURB (char *buffer, size_t size, const void *data) {
  const UsbFormatUrbData *fud = data;
  const struct usbdevfs_urb *urb = fud->urb;
  size_t length;

  STR_BEGIN(buffer, size);
  STR_PRINTF("%s URB:", fud->action);

  STR_PRINTF(" Adr:%p", urb);
  STR_PRINTF(" Ept:%02X", urb->endpoint);

  STR_PRINTF(" Typ:%u", urb->type);
  {
    static const char *const types[] = {
      [USBDEVFS_URB_TYPE_CONTROL] = "ctl",
      [USBDEVFS_URB_TYPE_BULK] = "blk",
      [USBDEVFS_URB_TYPE_INTERRUPT] = "int",
      [USBDEVFS_URB_TYPE_ISO] = "iso"
    };

    if (urb->type < ARRAY_COUNT(types)) {
      const char *type = types[urb->type];
      if (type) STR_PRINTF("(%s)", type);
    }
  }

  STR_PRINTF(" Flg:%02X", urb->flags);
  {
    typedef struct {
      unsigned char bit;
      const char *name;
    } UrbFlagEntry;

    static const UrbFlagEntry urbFlagTable[] = {
#ifdef USBDEVFS_URB_SHORT_NOT_OK
      { .bit = USBDEVFS_URB_SHORT_NOT_OK,
        .name = "spd"
      },
#endif /* USBDEVFS_URB_SHORT_NOT_OK */

#ifdef USBDEVFS_URB_ISO_ASAP
      { .bit = USBDEVFS_URB_ISO_ASAP,
        .name = "isa"
      },
#endif /* USBDEVFS_URB_ISO_ASAP */

#ifdef USBDEVFS_URB_BULK_CONTINUATION
      { .bit = USBDEVFS_URB_BULK_CONTINUATION,
Beispiel #9
0
static size_t
usbFormatLogSetupPacket (char *buffer, size_t size, const void *data) {
  const UsbSetupPacket *setup = data;
  size_t length;
  STR_BEGIN(buffer, size);

  STR_PRINTF("setup packet: Typ:%02X Req:%02X Val:%04X Idx:%04X Len:%04X",
             setup->bRequestType, setup->bRequest,
             getLittleEndian16(setup->wValue),
             getLittleEndian16(setup->wIndex),
             getLittleEndian16(setup->wLength));

  length = STR_LENGTH;
  STR_END;
  return length;
}
Beispiel #10
0
static size_t
formatLogCommandData (char *buffer, size_t size, const void *data) {
  const LogCommandData *cmd = data;
  size_t length;

  STR_BEGIN(buffer, size);
  STR_PRINTF("command: ");

  {
    size_t sublength = formatCommand(STR_NEXT, STR_LEFT, cmd->command);
    STR_ADJUST(sublength);
  }

  length = STR_LENGTH;
  STR_END
  return length;
}
Beispiel #11
0
static size_t
formatCommandModifiers (char *buffer, size_t size, int command, const CommandModifierEntry *modifiers) {
  const CommandModifierEntry *modifier = modifiers;
  size_t length;
  STR_BEGIN(buffer, size);

  while (modifier->name) {
    if (command & modifier->bit) {
      STR_PRINTF(" + %s", modifier->name);
    }

    modifier += 1;
  }

  length = STR_LENGTH;
  STR_END;
  return length;
}
Beispiel #12
0
static size_t
formatLogSpeechActionData (char *buffer, size_t size, const void *data) {
    const LogSpeechActionData *lsa = data;
    size_t length;

    STR_BEGIN(buffer, size);
    STR_PRINTF("%s speech %s: ", lsa->action, lsa->type);

    if (lsa->name) {
        STR_PRINTF("%s", lsa->name);
    } else {
        STR_PRINTF("%u", lsa->value);
    }

    length = STR_LENGTH;
    STR_END;
    return length;
}
Beispiel #13
0
static size_t
formatCommand (char *buffer, size_t size, int command) {
  size_t length;
  STR_BEGIN(buffer, size);

  STR_PRINTF("%06X (", command);

  {
    size_t length = describeCommand(command, STR_NEXT, STR_LEFT, 
                                    CDO_IncludeName | CDO_IncludeOperand);
    STR_ADJUST(length);
  }

  STR_PRINTF(")");

  length = STR_LENGTH;
  STR_END;
  return length;
}
Beispiel #14
0
static int
insertKey_ScreenScreen (ScreenKey key) {
  char *sequence;
  char buffer[0X10];

  setScreenKeyModifiers(&key, 0);
  wchar_t character = key & SCR_KEY_CHAR_MASK;

  if (isSpecialKey(key)) {
    const unsigned char flags = getAuxiliaryData()[1];

#define KEY(key,string) case (key): sequence = (string); break
#define CURSOR_KEY(key,string1,string2) KEY((key), ((flags & 0X01)? (string1): (string2)))

    switch (character) {
      KEY(SCR_KEY_ENTER, "\r");
      KEY(SCR_KEY_TAB, "\t");
      KEY(SCR_KEY_BACKSPACE, "\x7f");
      KEY(SCR_KEY_ESCAPE, "\x1b");

      CURSOR_KEY(SCR_KEY_CURSOR_LEFT , "\x1bOD", "\x1b[D");
      CURSOR_KEY(SCR_KEY_CURSOR_RIGHT, "\x1bOC", "\x1b[C");
      CURSOR_KEY(SCR_KEY_CURSOR_UP   , "\x1bOA", "\x1b[A");
      CURSOR_KEY(SCR_KEY_CURSOR_DOWN , "\x1bOB", "\x1b[B");

      KEY(SCR_KEY_PAGE_UP, "\x1b[5~");
      KEY(SCR_KEY_PAGE_DOWN, "\x1b[6~");
      KEY(SCR_KEY_HOME, "\x1b[1~");
      KEY(SCR_KEY_END, "\x1b[4~");
      KEY(SCR_KEY_INSERT, "\x1b[2~");
      KEY(SCR_KEY_DELETE, "\x1b[3~");
      KEY(SCR_KEY_FUNCTION+0, "\x1bOP");
      KEY(SCR_KEY_FUNCTION+1, "\x1bOQ");
      KEY(SCR_KEY_FUNCTION+2, "\x1bOR");
      KEY(SCR_KEY_FUNCTION+3, "\x1bOS");
      KEY(SCR_KEY_FUNCTION+4, "\x1b[15~");
      KEY(SCR_KEY_FUNCTION+5, "\x1b[17~");
      KEY(SCR_KEY_FUNCTION+6, "\x1b[18~");
      KEY(SCR_KEY_FUNCTION+7, "\x1b[19~");
      KEY(SCR_KEY_FUNCTION+8, "\x1b[20~");
      KEY(SCR_KEY_FUNCTION+9, "\x1b[21~");
      KEY(SCR_KEY_FUNCTION+10, "\x1b[23~");
      KEY(SCR_KEY_FUNCTION+11, "\x1b[24~");
      KEY(SCR_KEY_FUNCTION+12, "\x1b[25~");
      KEY(SCR_KEY_FUNCTION+13, "\x1b[26~");
      KEY(SCR_KEY_FUNCTION+14, "\x1b[28~");
      KEY(SCR_KEY_FUNCTION+15, "\x1b[29~");
      KEY(SCR_KEY_FUNCTION+16, "\x1b[31~");
      KEY(SCR_KEY_FUNCTION+17, "\x1b[32~");
      KEY(SCR_KEY_FUNCTION+18, "\x1b[33~");
      KEY(SCR_KEY_FUNCTION+19, "\x1b[34~");

      default:
        logMessage(LOG_WARNING, "unsuported key: %04X", key);
        return 0;
    }

#undef CURSOR_KEY
#undef KEY
  } else {
    int byte = convertWcharToChar(character);

    if (byte == EOF) {
      logMessage(LOG_WARNING, "character not supported in local character set: 0X%04X", key);
      return 0;
    }

    STR_BEGIN(buffer, sizeof(buffer));
    if (key & SCR_KEY_ALT_LEFT) STR_PRINTF("%c", ESC);
    STR_PRINTF("\\%03o", byte);
    STR_END;

    sequence = buffer;
  }

  logBytes(LOG_CATEGORY(SCREEN_DRIVER), "insert bytes", sequence, strlen(sequence));
  return doScreenCommand("stuff", sequence, NULL);
}
Beispiel #15
0
void
formatAddress (char *buffer, size_t bufferSize, const void *address, int addressSize) {
#ifdef AF_INET
  const struct sockaddr *sa = address;
  switch (sa->sa_family) {
#ifndef __MINGW32__
    case AF_LOCAL: {
      const struct sockaddr_un *local = address;
      if (addressSize <= sizeof(sa_family_t)) {
        snprintf(buffer, bufferSize, "local <unnamed>");
      } else {
        snprintf(buffer, bufferSize, "local %s", local->sun_path);
      }
      break;
    }
#endif /* __MINGW32__ */

    case AF_INET: {
      const struct sockaddr_in *inet = address;
      snprintf(buffer, bufferSize, "inet %s:%d", inet_ntoa(inet->sin_addr), ntohs(inet->sin_port));
      break;
    }

    default:
#if defined(HAVE_GETNAMEINFO) && !defined(WINDOWS)
      {
        char host[NI_MAXHOST];
        char service[NI_MAXSERV];
        int err;

        if (!(err = getnameinfo(address, addressSize,
                                host, sizeof(host), service, sizeof(service),
                                NI_NUMERICHOST | NI_NUMERICSERV))) {
          snprintf(buffer, bufferSize, "af=%d %s:%s", sa->sa_family, host, service);
          break;
        }

        if (err != EAI_FAMILY) {
#ifdef HAVE_GAI_STRERROR
          snprintf(buffer, bufferSize, "reverse lookup error for address family %d: %s", 
                   sa->sa_family,
#ifdef EAI_SYSTEM
                   (err == EAI_SYSTEM)? strerror(errno):
#endif /* EAI_SYSTEM */
                   gai_strerror(err));
#else /* HAVE_GAI_STRERROR */
          snprintf(buffer, bufferSize, "reverse lookup error %d for address family %d.",
                   err, sa->sa_family);
#endif /* HAVE_GAI_STRERROR */
          break;
        }
      }
#endif /* GETNAMEINFO */

      {
        STR_BEGIN(buffer, bufferSize);
        STR_PRINTF("address family %d:", sa->sa_family);

        {
          const unsigned char *byte = address;
          const unsigned char *end = byte + addressSize;

          while (byte < end) STR_PRINTF(" %02X", *byte++);
        }

        STR_END
      }
      break;
  }
#else /* AF_INET */
  snprintf(buffer, bufferSize, "unknown");
#endif /* AF_INET */
}
Beispiel #16
0
void
logData (int level, LogDataFormatter *formatLogData, const void *data) {
  const char *prefix = NULL;

  if (level & LOG_FLG_CATEGORY) {
    int category = level & LOG_MSK_CATEGORY;

    if (!logCategoryFlags[category]) return;
    level = categoryLogLevel;

    {
      const LogCategoryEntry *ctg = &logCategoryTable[category];

      prefix = ctg->prefix;
    }
  }

  {
    int write = level <= systemLogLevel;
    int print = level <= stderrLogLevel;

    if (write || print) {
      int oldErrno = errno;
      char record[0X1000];

      STR_BEGIN(record, sizeof(record));
      if (prefix) STR_PRINTF("%s: ", prefix);
      {
        size_t sublength = formatLogData(STR_NEXT, STR_LEFT, data);
        STR_ADJUST(sublength);
      }
      STR_END

      if (write) {
        writeLogRecord(record);

#if defined(WINDOWS)
        if (windowsEventLog != INVALID_HANDLE_VALUE) {
          const char *strings[] = {record};
          ReportEvent(windowsEventLog, toWindowsEventType(level), 0, 0, NULL,
                      ARRAY_COUNT(strings), 0, strings, NULL);
        }

#elif defined(__MSDOS__)

#elif defined(__ANDROID__)
        __android_log_write(toAndroidLogPriority(level), PACKAGE_TARNAME, record);

#elif defined(HAVE_SYSLOG_H)
        if (syslogOpened) syslog(level, "%s", record);
#endif /* write system log */
      }

      if (print) {
        FILE *stream = stderr;
        lockStream(stream);

        if (logPrefixStack) {
          const char *prefix = logPrefixStack->prefix;

          if (*prefix) {
            fputs(prefix, stream);
            fputs(": ", stream);
          }
        }

        fputs(record, stream);
        fputc('\n', stream);

        flushStream(stream);
        unlockStream(stream);
      }

      errno = oldErrno;
    }
  }
Beispiel #17
0
size_t
describeCommand (int command, char *buffer, size_t size, CommandDescriptionOption options) {
  size_t length;
  STR_BEGIN(buffer, size);

  unsigned int arg = BRL_ARG_GET(command);
  unsigned int arg1 = BRL_CODE_GET(ARG, command);
  unsigned int arg2 = BRL_CODE_GET(EXT, command);
  const CommandEntry *cmd = getCommandEntry(command);

  if (!cmd) {
    STR_PRINTF("%s: %06X", gettext("unknown command"), command);
  } else {
    if (options & CDO_IncludeName) {
      STR_PRINTF("%s: ", cmd->name);
    }

    if (cmd->isToggle && (command & BRL_FLG_TOGGLE_MASK)) {
      const char *text = gettext(cmd->description);
      size_t length = strlen(text);
      char buffer[length + 1];
      char *delimiter;

      strcpy(buffer, text);
      delimiter = strchr(buffer, '/');

      if (delimiter) {
        char *source;
        char *target;

        if (command & BRL_FLG_TOGGLE_ON) {
          target = delimiter;
          if (!(source = strchr(target, ' '))) source = target + strlen(target);
        } else if (command & BRL_FLG_TOGGLE_OFF) {
          {
            char oldDelimiter = *delimiter;
            *delimiter = 0;
            target = strrchr(buffer, ' ');
            *delimiter = oldDelimiter;
          }

          target = target? (target + 1): buffer;
          source = delimiter + 1;
        } else {
          goto toggleReady;
        }

        memmove(target, source, (strlen(source) + 1));
      }

    toggleReady:
      STR_PRINTF("%s", buffer);
    } else {
      STR_PRINTF("%s", gettext(cmd->description));
    }

    if (options & CDO_IncludeOperand) {
      if (cmd->isCharacter) {
        STR_PRINTF(" [U+%04X]", arg);
      }

      if (cmd->isBraille) {
        int none = 1;
        STR_PRINTF(" [");

        {
          static const int dots[] = {
            BRL_DOTC,
            BRL_DOT1, BRL_DOT2, BRL_DOT3, BRL_DOT4,
            BRL_DOT5, BRL_DOT6, BRL_DOT7, BRL_DOT8,
            0
          };
          const int *dot = dots;

          while (*dot) {
            if (command & *dot) {
              none = 0;

              if (dot == dots) {
                STR_PRINTF("C");
              } else {
                unsigned int number = dot - dots;
                STR_PRINTF("%u", number);
              }
            }

            dot += 1;
          }
        }

        if (none) STR_PRINTF("%s", gettext("space"));
        STR_PRINTF("]");
      }

      if (cmd->isKeyboard) {
        STR_PRINTF(" [\\X%02X]", arg1);
      }

      if (cmd->isColumn && !cmd->isRouting && (
           (arg == BRL_MSK_ARG) /* key event processing */
         ||
           ((options & CDO_DefaultOperand) && !arg) /* key table listing */
         )) {
        STR_PRINTF(" %s", gettext("at cursor"));
      } else if (cmd->isColumn || cmd->isRow || cmd->isOffset) {
        STR_PRINTF(" #%u", arg - (cmd->code & BRL_MSK_ARG) + 1);
      } else if (cmd->isRange) {
        STR_PRINTF(" #%u-%u", arg1, arg2);
      }

      if (cmd->isInput) {
        size_t length = formatCommandModifiers(STR_NEXT, STR_LEFT, command, commandModifierTable_input);
        STR_ADJUST(length);
      }

      if (cmd->isCharacter) {
        size_t length = formatCommandModifiers(STR_NEXT, STR_LEFT, command, commandModifierTable_character);
        STR_ADJUST(length);
      }

      if (cmd->isKeyboard) {
        size_t length = formatCommandModifiers(STR_NEXT, STR_LEFT, command, commandModifierTable_keyboard);
        STR_ADJUST(length);
      }
    }

    if (cmd->isMotion) {
      if (cmd->isRow) {
        if (command & BRL_FLG_LINE_TOLEFT) {
          STR_PRINTF(", %s", gettext("left margin"));
        }

        if (command & BRL_FLG_LINE_SCALED) {
          STR_PRINTF(", %s", gettext("normalized position"));
        }
      }

      if (command & BRL_FLG_MOTION_ROUTE) {
        STR_PRINTF(", %s", gettext("drag cursor"));
      }
    }
  }

  length = STR_LENGTH;
  STR_END;
  return length;
}
Beispiel #18
0
size_t
formatBrailleTime (char *buffer, size_t size, const TimeFormattingData *fmt) {
  size_t length;
  char time[0X40];
  STR_BEGIN(buffer, size);

  {
    const char *hourFormat = "%02" PRIu8;
    const char *minuteFormat = "%02" PRIu8;
    const char *secondFormat = "%02" PRIu8;
    char separator;

    switch (prefs.timeSeparator) {
      default:
      case tsColon:
        separator = ':';
        break;

      case tsDot:
        separator = '.';
        break;
    }

    switch (prefs.timeFormat) {
      default:
      case tf24Hour:
        break;

      case tf12Hour:
        hourFormat = "%" PRIu8;
        break;
    }

    STR_BEGIN(time, sizeof(time));
    STR_PRINTF(hourFormat, fmt->components.hour);
    STR_PRINTF("%c", separator);
    STR_PRINTF(minuteFormat, fmt->components.minute);

    if (prefs.showSeconds) {
      STR_PRINTF("%c", separator);
      STR_PRINTF(secondFormat, fmt->components.second);
    }

    if (fmt->meridian) STR_PRINTF("%s", fmt->meridian);
    STR_END
  }

  if (prefs.datePosition == dpNone) {
    STR_PRINTF("%s", time);
  } else {
    char date[0X40];

    {
      const char *yearFormat = "%04" PRIu16;
      const char *monthFormat = "%02" PRIu8;
      const char *dayFormat = "%02" PRIu8;

      uint16_t year = fmt->components.year;
      uint8_t month = fmt->components.month + 1;
      uint8_t day = fmt->components.day + 1;

      char separator;

      switch (prefs.dateSeparator) {
        default:
        case dsDash:
          separator = '-';
          break;

        case dsSlash:
          separator = '/';
          break;

        case dsDot:
          separator = '.';
          break;
      }

      STR_BEGIN(date, sizeof(date));
      switch (prefs.dateFormat) {
        default:
        case dfYearMonthDay:
          STR_PRINTF(yearFormat, year);
          STR_PRINTF("%c", separator);
          STR_PRINTF(monthFormat, month);
          STR_PRINTF("%c", separator);
          STR_PRINTF(dayFormat, day);
          break;

        case dfMonthDayYear:
          STR_PRINTF(monthFormat, month);
          STR_PRINTF("%c", separator);
          STR_PRINTF(dayFormat, day);
          STR_PRINTF("%c", separator);
          STR_PRINTF(yearFormat, year);
          break;

        case dfDayMonthYear:
          STR_PRINTF(dayFormat, day);
          STR_PRINTF("%c", separator);
          STR_PRINTF(monthFormat, month);
          STR_PRINTF("%c", separator);
          STR_PRINTF(yearFormat, year);
          break;
      }
      STR_END

      switch (prefs.datePosition) {
        case dpBeforeTime:
          STR_PRINTF("%s %s", date, time);
          break;

        case dpAfterTime:
          STR_PRINTF("%s %s", time, date);
          break;

        default:
          STR_PRINTF("%s", date);
          break;
      }
    }
  }

  length = STR_LENGTH;
  STR_END
  return length;
}