text:
/* -----------------------------------------------------------------------------
 *                              File Picker
 * ---------------------------------------------------------------------------*/
enum file_type { FILE_DEFAULT, FILE_FOLDER };
enum file_groups {
  FILE_GRP_OTHER,
  FILE_GRP_DIR,
  FILE_GRP_TXT,
  FILE_GRP_PROG,
  FILE_GRP_AUDIO,
  FILE_GRP_VIDEO,
  FILE_GRP_IMAGE,
  FILE_GRP_FONT,
  FILE_GRP_3D,
  FILE_GRP_EXEC,
  FILE_GRP_ARCHIVE,
  FILE_GRP_CNT,
};
struct file_def {
  const char *suffix;
  const char *name;
  const char *mime;
  int grp;
};
struct file_group_def {
  const char *name;
  const char *description;
  enum res_ico_id icon;
  const struct file_def *files;
  int cnt;
};
#define FS_FILE_PERM_LEN 10
enum file_elm_type {
  FILE_DEF,
  FILE_DIR,
  FILE_SOCK,
  FILE_LNK,
  FILE_FIFO,
};
struct file_elm {
  const struct file_def *type;
  struct str fullpath;
  struct str path;
  struct str name;
  struct str ext;

  size_t size;
  time_t mtime;

  enum res_ico_id ico;
  char perms[FS_FILE_PERM_LEN];
  enum file_elm_type file_type;
  unsigned isvalid : 1;
  unsigned isdir : 1;
};
struct file_tbl_col_def {
  struct str title;
  struct gui_split_lay_slot ui;
  sort_f sort[2];
};
enum file_tbl_hdr_col {
  FILE_TBL_NAME,
  FILE_TBL_TYPE,
  FILE_TBL_SIZE,
  FILE_TBL_PERM,
  FILE_TBL_DATE,
  FILE_TBL_MAX,
};
struct file_tbl {
  int cnt;
  struct gui_tbl_sort sort;
  int state[GUI_TBL_CAP(FILE_TBL_MAX)];
};
#define MAX_FILTER 64
struct file_list_view {
  unsigned rev;
  struct arena mem;

  dyn(struct file_elm) elms;
  dyn(unsigned long) fltr;
  int sel_idx;

  double off[2];
  struct file_tbl tbl;

  /* navigation */
  dyn(char) full_path;
  dyn(char) nav_path;
  dyn(char) fnd_buf;

  struct gui_txt_ed nav_ed;
  struct gui_txt_ed fnd_ed;
};
struct file_tree_node {
  struct file_tree_node *parent;
  struct lst_elm hook;
  struct lst_elm sub;

  unsigned long long id;
  struct str fullpath;
  int depth;
};
struct file_tree_view {
  unsigned rev;
  struct arena mem;
  struct file_tree_node root;
  dyn(struct file_tree_node*) lst;
  struct lst_elm del_lst;
  unsigned long long *exp;

  int sel;
  double off[2];

  unsigned jmp:1;
  unsigned long jmp_to;
};
#define FILE_SPLIT_MAX 2
struct file_view {
  int state;
  struct arena *tmp_arena;
  struct str home;
  int split[GUI_SPLIT_CAP(FILE_SPLIT_MAX)];

  unsigned lst_rev;
  struct file_list_view lst;

  unsigned tree_rev;
  struct file_tree_view tree;
};

/* =============================================================================
 *
 *                                File Picker
 *
 * =============================================================================
 */
// clang-format off
static const struct file_def file_unkown_defs[] = {{"","Unknown","application/octet-stream",FILE_GRP_OTHER}};
static const struct file_def file_folder_defs[] = {{NULL,"Folder","application/octet-stream",FILE_GRP_DIR}};
static const struct file_def file_link_defs[] = {{NULL,"Link","application/octet-stream",FILE_GRP_DIR}};
static const struct file_def file_sock_defs[] = {{NULL,"Socket","application/octet-stream",FILE_GRP_DIR}};
static const struct file_def file_fifo_defs[] = {{NULL,"Fifo","application/octet-stream",FILE_GRP_DIR}};
static const struct file_def file_text_defs[] = {
  {.suffix = "txt",   .name = "Text",                         .mime = "text/plain",                   .grp = FILE_GRP_TXT},
  {.suffix = "html",  .name = "Hyper Text Markup Language",   .mime = "text/html",                    .grp = FILE_GRP_TXT},
  {.suffix = "css",   .name = "CSS",                          .mime = "text/css",                     .grp = FILE_GRP_TXT},
  {.suffix = "md",    .name = "Markdown",                     .mime = "text/plain",                   .grp = FILE_GRP_TXT},
  {.suffix = "json",  .name = "JavaScript Object Notation",   .mime = "application/json",             .grp = FILE_GRP_TXT},
  {.suffix = "xml",   .name = "Extensive Markup Language",    .mime = "application/xml",              .grp = FILE_GRP_TXT},
  {.suffix = "pdf",   .name = "Portable Document Format",     .mime = "application/pdf",              .grp = FILE_GRP_TXT},
  {.suffix = "csv",   .name = "Comma-separated Values",       .mime = "text/csv",                     .grp = FILE_GRP_TXT},
  {.suffix = "chm",   .name = "Compiled HTML Help",           .mime = "application/vnd.ms-htmlhelp",  .grp = FILE_GRP_TXT},
};
static const struct file_def file_prog_defs[] = {
  {.suffix = "c",     .name = "C Source",         .mime = "text/x-c",                 .grp = FILE_GRP_TXT},
  {.suffix = "cpp",   .name = "C++ Source",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "cxx",   .name = "C++ Source",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "m",     .name = "ObjC Source",      .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "h",     .name = "C Header",         .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "hpp",   .name = "C++ Header",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "hxx",   .name = "C++ Header",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "py",    .name = "Python Script",    .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "rb",    .name = "Ruby Script",      .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "java",  .name = "Java Source",      .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "lua",   .name = "Lua Script",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "js",    .name = "JavaScript",       .mime = "application/javascript",   .grp = FILE_GRP_TXT},
  {.suffix = "php",   .name = "PHP Script",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "r",     .name = "R Script",         .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "tcl",   .name = "TCL Script",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "pl",    .name = "Perl Script",      .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "ada",   .name = "Ada Source",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "asm",   .name = "Assembly Source",  .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "clj",   .name = "Clojure Source",   .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "cs",    .name = "C# Source",        .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "d",     .name = "D Source",         .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "el",    .name = "Emacs Lisp",       .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "go",    .name = "Golang Source",    .mime = "text/plain",               .grp = FILE_GRP_TXT},
  {.suffix = "hs",    .name = "Haskell Source",   .mime = "text/plain",               .grp = FILE_GRP_TXT},
};
static const struct file_def file_audio_defs[] = {
  {.suffix = "mp3",   .name = "MP3 Audio File",   .mime = "audio/mpeg",   .grp = FILE_GRP_AUDIO},
  {.suffix = "wav",   .name = "WAV Audio File",   .mime = "audio/x-wav",  .grp = FILE_GRP_AUDIO},
  {.suffix = "ogg",   .name = "OGG Audio File",   .mime = "audio/ogg",    .grp = FILE_GRP_AUDIO},
};
static const struct file_def file_video_defs[] = {
  {.suffix = "asf",   .name = "ASF Video Container",          .mime = "video/x-ms-asf",               .grp = FILE_GRP_VIDEO},
  {.suffix = "AVI",   .name = "Audio Video Interleave",       .mime = "video/x-msvideo",              .grp = FILE_GRP_VIDEO},
  {.suffix = "BIK",   .name = "Bink Video",                   .mime = "application/octet-stream",     .grp = FILE_GRP_VIDEO},
  {.suffix = "flv",   .name = "Flash Video",                  .mime = "video/x-flv",                  .grp = FILE_GRP_VIDEO},
  {.suffix = "mkv",   .name = "Matroska Video Container",     .mime = "application/octet-stream",     .grp = FILE_GRP_VIDEO},
  {.suffix = "mov",   .name = "QuickTime Video Container",    .mime = "video/quicktime",              .grp = FILE_GRP_VIDEO},
  {.suffix = "mpeg",  .name = "MPEG Video",                   .mime = "video/mpeg",                   .grp = FILE_GRP_VIDEO},
  {.suffix = "mpg",   .name = "MPEG Video",                   .mime = "video/mpeg",                   .grp = FILE_GRP_VIDEO},
  {.suffix = "mp4",   .name = "MPEG Video",                   .mime = "video/mp4",                    .grp = FILE_GRP_VIDEO},
  {.suffix = "mpe",   .name = "MPEG Video",                   .mime = "video/mpeg",                   .grp = FILE_GRP_VIDEO},
  {.suffix = "thp",   .name = "Nintendo Video",               .mime = "application/octet-stream",     .grp = FILE_GRP_VIDEO},
  {.suffix = "roq",   .name = "Quake 3 Video",                .mime = "application/octet-stream",     .grp = FILE_GRP_VIDEO},
  {.suffix = "swf",   .name = "Macromedia Flash",             .mime = "application/octet-stream",     .grp = FILE_GRP_VIDEO},
  {.suffix = "webm",  .name = "Web Video",                    .mime = "application/octet-stream",     .grp = FILE_GRP_VIDEO},
  {.suffix = "ogg",   .name = "Ogg Video",                    .mime = "video/ogg",                    .grp = FILE_GRP_VIDEO},
};
static const struct file_def file_images_defs[] = {
  {.suffix = "bmp",   .name = "Bitmap",                   .mime = "image/bmp",                  .grp = FILE_GRP_IMAGE},
  {.suffix = "pbm",   .name = "Portable Bitmap",          .mime = "image/x-portable-bitmap",    .grp = FILE_GRP_IMAGE},
  {.suffix = "png",   .name = "PNG",                      .mime = "image/png",                  .grp = FILE_GRP_IMAGE},
  {.suffix = "jpg",   .name = "JPEG",                     .mime = "image/jpeg",                 .grp = FILE_GRP_IMAGE},
  {.suffix = "jp2",   .name = "JPEG 2000",                .mime = "image/jpeg",                 .grp = FILE_GRP_IMAGE},
  {.suffix = "pcx",   .name = "Picture Exchange Format",  .mime = "image/x-pcx",                .grp = FILE_GRP_IMAGE},
  {.suffix = "tga",   .name = "Truevision TGA",           .mime = "application/octet-stream",   .grp = FILE_GRP_IMAGE},
  {.suffix = "gif",   .name = "Graphic Interchange",      .mime = "image/gif",                  .grp = FILE_GRP_IMAGE},
  {.suffix = "psd",   .name = "Adobe Photoshop",          .mime = "application/octet-stream",   .grp = FILE_GRP_IMAGE},
  {.suffix = "raw",   .name = "Raw Image Data",           .mime = "application/octet-stream",   .grp = FILE_GRP_IMAGE},
  {.suffix = "tif",   .name = "Tagged Image Format",      .mime = "image/tiff",                 .grp = FILE_GRP_IMAGE},
  {.suffix = "xbm",   .name = "X Pixmap",                 .mime = "image/x-xbitmap",            .grp = FILE_GRP_IMAGE},
  {.suffix = "xpm",   .name = "X Pixmap",                 .mime = "image/x-xpixmap",            .grp = FILE_GRP_IMAGE},
  {.suffix = "xcf",   .name = "GIMP Image",               .mime = "application/octet-stream",   .grp = FILE_GRP_IMAGE},
  {.suffix = "ico",   .name = "Windows Icon",             .mime = "image/x-icon",               .grp = FILE_GRP_IMAGE},
  {.suffix = "bti",   .name = "Nintendo Texture",         .mime = "application/octet-stream",   .grp = FILE_GRP_IMAGE},
  {.suffix = "blp",   .name = "Blizzard Texture",         .mime = "application/octet-stream",   .grp = FILE_GRP_IMAGE},
  {.suffix = "vtf",   .name = "Valve Texture",            .mime = "application/octet-stream",   .grp = FILE_GRP_IMAGE},
};
static const struct file_def file_3d_defs[] = {
  {.suffix = "3ds",   .name = "Legacy 3DS Max Model", .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "b3d",   .name = "Blitz3D Model",        .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "blend", .name = "Blender Model",        .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "bmd",   .name = "Nintendo 3d Model",    .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "bdl",   .name = "Nintendo 3d Model",    .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "brres", .name = "Nintendo 3d Model",    .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "c4d",   .name = "Cinema 4d Model",      .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "cob",   .name = "Caligary Object",      .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "dae",   .name = "Collada Model",        .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "fbx",   .name = "Autodesk FBX",         .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "max",   .name = "Autodesk 3DS Max",     .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "md2",   .name = "Quake 2 Model",        .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "md3",   .name = "Quake 3 Model",        .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "md5",   .name = "Doom 3 Model",         .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "mdx",   .name = "Blizzard Model ",      .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "obj",   .name = "Wavefront .obj",       .mime = "application/octet-stream", .grp = FILE_GRP_3D},
  {.suffix = "smd",   .name = "Valves Studiomdl",     .mime = "application/octet-stream", .grp = FILE_GRP_3D},
};
static const struct file_def file_font_defs[] = {
  {.suffix = "ttf",   .name = "TrueType Font",            .mime = "application/x-font-ttf",   .grp = FILE_GRP_FONT},
  {.suffix = "abf",   .name = "Adobe Binary Screen Font", .mime = "application/octet-stream", .grp = FILE_GRP_FONT},
  {.suffix = "afm",   .name = "Adobe Font Metrics",       .mime = "application/octet-stream", .grp = FILE_GRP_FONT},
  {.suffix = "otf",   .name = "OpenType Font",            .mime = "application/x-font-otf",   .grp = FILE_GRP_FONT},
};
static const struct file_def file_exe_defs[] = {
  {.suffix = "o",     .name = "Unix Object Format",           .mime = "application/octet-stream",                 .grp = FILE_GRP_EXEC},
  {.suffix = "so",    .name = "Shared Object Format",         .mime = "application/octet-stream",                 .grp = FILE_GRP_EXEC},
  {.suffix = "apk",   .name = "Android Application Package",  .mime = "application/vnd.android.package-archive",  .grp = FILE_GRP_EXEC},
  {.suffix = "dll",   .name = "Windows Dynamic Library",      .mime = "application/octet-stream",                 .grp = FILE_GRP_EXEC},
  {.suffix = "exe",   .name = "Windows Executable",           .mime = "application/x-msdownload",                 .grp = FILE_GRP_EXEC},
  {.suffix = "xbe",   .name = "Xbox Executable",              .mime = "application/x-msdownload",                 .grp = FILE_GRP_EXEC},
  {.suffix = "xex",   .name = "Xbox 360 Executable",          .mime = "application/x-msdownload",                 .grp = FILE_GRP_EXEC},
  {.suffix = "sh",    .name = "Shell Script",                 .mime = "application/x-sh",                         .grp = FILE_GRP_EXEC},
};
static const struct file_def file_archive_defs[] = {
  {.suffix = "zip",   .name = "Zip Archive",                  .mime = "application/zip",              .grp = FILE_GRP_ARCHIVE},
  {.suffix = "7z",    .name = "7Zip Archive",                 .mime = "application/x-7z-compressed",  .grp = FILE_GRP_ARCHIVE},
  {.suffix = "rar",   .name = "RAR Archive",                  .mime = "application/x-rar-compressed", .grp = FILE_GRP_ARCHIVE},
  {.suffix = "gz",    .name = "Compressed TAR Archive",       .mime = "application/x-gtar",           .grp = FILE_GRP_ARCHIVE},
  {.suffix = "bz2",   .name = "Compressed TAR Archive",       .mime = "application/x-bzip2",          .grp = FILE_GRP_ARCHIVE},
  {.suffix = "tar",   .name = "TAR Archive",                  .mime = "application/x-gtar",           .grp = FILE_GRP_ARCHIVE},
  {.suffix = "jar",   .name = "Jave Zipped Container",        .mime = "application/java-archive",     .grp = FILE_GRP_ARCHIVE},
  {.suffix = "pk3",   .name = "Quake 3 Archive",              .mime = "application/zip",              .grp = FILE_GRP_ARCHIVE},
  {.suffix = "pk4",   .name = "Doom 3 Archive",               .mime = "application/zip",              .grp = FILE_GRP_ARCHIVE},
  {.suffix = "mpq",   .name = "Blizzard Archive",             .mime = "application/octet-stream",     .grp = FILE_GRP_ARCHIVE},
  {.suffix = "bin",   .name = "Binary File",                  .mime = "application/octet-stream",     .grp = FILE_GRP_ARCHIVE},
  {.suffix = "iso",   .name = "Optical Media",                .mime = "application/octet-stream",     .grp = FILE_GRP_ARCHIVE},
};
static const struct file_group_def file_groups[] = {
  [FILE_GRP_OTHER]      = {.name = "Other",         .icon = RES_ICO_FILE,         .files = file_unkown_defs,  .cnt = cntof(file_unkown_defs)},
  [FILE_GRP_DIR]        = {.name = "Folder",        .icon = RES_ICO_FOLDER_OPEN,  .files = file_folder_defs,  .cnt = cntof(file_folder_defs)},
  [FILE_GRP_TXT]        = {.name = "Text",          .icon = RES_ICO_FILE_TXT,     .files = file_text_defs,    .cnt = cntof(file_text_defs)},
  [FILE_GRP_PROG]       = {.name = "Programming",   .icon = RES_ICO_FILE_CODE,    .files = file_prog_defs,    .cnt = cntof(file_prog_defs)},
  [FILE_GRP_AUDIO]      = {.name = "Audio",         .icon = RES_ICO_FILE_AUDIO,   .files = file_audio_defs,   .cnt = cntof(file_audio_defs)},
  [FILE_GRP_VIDEO]      = {.name = "Video",         .icon = RES_ICO_FILE_VIDEO,   .files = file_video_defs,   .cnt = cntof(file_video_defs)},
  [FILE_GRP_IMAGE]      = {.name = "Image",         .icon = RES_ICO_FILE_IMG,     .files = file_images_defs,  .cnt = cntof(file_images_defs)},
  [FILE_GRP_FONT]       = {.name = "Font",          .icon = RES_ICO_FILE,         .files = file_font_defs,    .cnt = cntof(file_font_defs)},
  [FILE_GRP_3D]         = {.name = "3DModel",       .icon = RES_ICO_FILE,         .files = file_3d_defs,      .cnt = cntof(file_3d_defs)},
  [FILE_GRP_EXEC]       = {.name = "Executable",    .icon = RES_ICO_FILE,         .files = file_exe_defs,     .cnt = cntof(file_exe_defs)},
  [FILE_GRP_ARCHIVE]    = {.name = "Archive",       .icon = RES_ICO_FILE_ARCHIVE, .files = file_archive_defs, .cnt = cntof(file_archive_defs)},
};
// clang-format on

static int file_cmp_asc(const void *a, const void *b);
static int file_cmp_desc(const void *a, const void *b);
static int file_cmp_name_asc(const void *a, const void *b);
static int file_cmp_name_desc(const void *a, const void *b);
static int file_cmp_type_asc(const void *a, const void *b);
static int file_cmp_type_desc(const void *a, const void *b);
static int file_cmp_size_asc(const void *a, const void *b);
static int file_cmp_size_desc(const void *a, const void *b);
static int file_cmp_perm_asc(const void *a, const void *b);
static int file_cmp_perm_desc(const void *a, const void *b);
static int file_cmp_time_asc(const void *a, const void *b);
static int file_cmp_time_desc(const void *a, const void *b);

// clang-format off
static const struct file_tbl_col_def file_tbl_def[FILE_TBL_MAX] = {
  [FILE_TBL_NAME] =  {.title = strv("Name"),           .sort = {file_cmp_asc,      file_cmp_desc},       .ui = {.type = GUI_LAY_SLOT_DYN, .size =   1, .con = {200, 800}}},
  [FILE_TBL_TYPE] =  {.title = strv("Type"),           .sort = {file_cmp_type_asc, file_cmp_type_desc},  .ui = {.type = GUI_LAY_SLOT_FIX, .size = 100, .con = {100, 800}}},
  [FILE_TBL_SIZE] =  {.title = strv("Size"),           .sort = {file_cmp_size_asc, file_cmp_size_desc},  .ui = {.type = GUI_LAY_SLOT_FIX, .size = 120, .con = {100, 800}}},
  [FILE_TBL_PERM] =  {.title = strv("Permission"),     .sort = {file_cmp_perm_asc, file_cmp_perm_desc},  .ui = {.type = GUI_LAY_SLOT_FIX, .size = 160, .con = {100, 800}}},
  [FILE_TBL_DATE] =  {.title = strv("Date Modified"),  .sort = {file_cmp_time_asc, file_cmp_time_desc},  .ui = {.type = GUI_LAY_SLOT_FIX, .size = 300, .con = {200, 800}}},
};
static const struct gui_split_lay_slot file_split_def[FILE_SPLIT_MAX] = {
  {.type = GUI_LAY_SLOT_FIX, .size = 400, .con = {100, 800}},
  {.type = GUI_LAY_SLOT_DYN, .size =   1, .con = {100, 10000}},
};
// clang-format on

static const struct file_def *
file_type(struct str ext) {
  const struct file_group_def *grp = 0;
  for_arrv(grp, file_groups) {
    const struct file_def *def = 0;
    for_arr(def, grp->files, grp->cnt) {
      if (def->suffix && str_cmp(str0(def->suffix), ext) == 0) {
        return def;
      }
    }
  }
  return file_unkown_defs;
}
static enum res_ico_id
file_icon(const struct file_def *type) {
  assert(type);
  assert(type->grp >= 0 && type->grp < cntof(file_groups));
  return file_groups[type->grp].icon;
}
static int
file_cmp_asc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  if (fa->isdir && !fb->isdir) {
    return -1;
  } else if (!fa->isdir && fb->isdir) {
    return 1;
  }
  return str_cmp(fa->name, fb->name);
}
static int
file_cmp_desc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  if (fa->isdir && !fb->isdir) {
    return 1;
  } else if (!fa->isdir && fb->isdir) {
    return -1;
  }
  return str_cmp(fb->name, fa->name);
}
static int
file_cmp_name_asc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return str_cmp(fa->name, fb->name);
}
static int
file_cmp_name_desc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return str_cmp(fb->name, fa->name);
}
static int
file_cmp_type_asc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  if (fa->isdir && !fb->isdir) {
    return -1;
  } else if (!fa->isdir && fb->isdir) {
    return 1;
  } else if (fa->type == fb->type) {
    return str_cmp(fa->name, fb->name);
  }
  return strcmp(fa->type->suffix, fb->type->suffix);
}
static int
file_cmp_type_desc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  if (fa->isdir && !fb->isdir) {
    return 1;
  } else if (!fa->isdir && fb->isdir) {
    return -1;
  } else if (fa->type == fb->type) {
    return str_cmp(fa->name, fb->name);
  }
  return strcmp(fb->type->suffix, fa->type->suffix);
}
static int
file_cmp_size_asc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return cast(int, fa->size - fb->size);
}
static int
file_cmp_size_desc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return cast(int, fb->size - fa->size);
}
static int
file_cmp_perm_asc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return strcmp(fa->perms, fb->perms);
}
static int
file_cmp_perm_desc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return strcmp(fb->perms, fa->perms);
}
static int
file_cmp_time_asc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return cast(int, fa->mtime - fb->mtime);
}
static int
file_cmp_time_desc(const void *a, const void *b) {
  const struct file_elm *fa = (const struct file_elm *)a;
  const struct file_elm *fb = (const struct file_elm *)b;
  return cast(int, fb->mtime - fa->mtime);
}
static struct lst_elm*
file_tree_node_sort_after_elm(struct file_tree_node *n,
                              struct file_tree_node *s) {
  assert(s);
  assert(n);

  struct lst_elm *elm = 0;
  for_lst(elm, &n->sub) {
    struct file_tree_node *it = 0;
    it = lst_get(elm, struct file_tree_node, hook);
    if (str_cmp(s->fullpath, it->fullpath) < 0) {
      break;
    }
  }
  return elm;
}
static void
file_tree_node_lnk(struct file_tree_node *n,
                   struct file_tree_node *s) {
  assert(s);
  assert(n);

  struct lst_elm *elm = file_tree_node_sort_after_elm(n, s);
  lst_del(&s->hook);
  lst_init(&s->hook);
  lst__add(&s->hook, elm->prv, elm);
}
static void
file_tree_node_init(struct sys *_sys, struct arena *mem, struct file_tree_node *s,
                    struct file_tree_node *n, struct str p, unsigned long long id) {
  assert(_sys);
  assert(mem);
  assert(s);
  assert(n);

  s->id = id;
  s->parent = n;
  s->depth = n->depth + 1;
  s->fullpath = arena_str(mem, _sys, p);

  lst_init(&s->hook);
  lst_init(&s->sub);
  file_tree_node_lnk(n, s);
}
static struct file_tree_node*
file_node_alloc(struct file_tree_view *tree, struct sys *_sys, struct arena *mem) {
  assert(tree);
  assert(_sys);
  assert(mem);

  struct file_tree_node *s = 0;
  if (lst_any(&tree->del_lst)) {
    s = lst_get(lst_first(&tree->del_lst), struct file_tree_node, hook);
    lst_del(&s->hook);
    memset(s, 0, sizeof(*s));
  } else {
    s = arena_alloc(mem, _sys, szof(*s));
  }
  return s;
}
static struct file_tree_node*
file_view_tree_node_new(struct file_tree_view *tree, struct sys *_sys,
                        struct arena *mem, struct file_tree_node *n,
                        struct str path, unsigned long long id) {
  assert(tree);
  assert(_sys);
  assert(mem);
  assert(n);

  struct file_tree_node *s = 0;
  s = file_node_alloc(tree, _sys, mem);
  file_tree_node_init(_sys, mem, s, n, path, id);
  return s;
}
static void
file_view_tree_node_del(struct file_tree_view *tree, struct file_tree_node *n) {
  assert(n);
  assert(tree);

  struct lst_elm *elm = 0;
  struct lst_elm *item = 0;
  for_lst_safe(elm, item, &n->sub) {
    struct file_tree_node *s = 0;
    s = lst_get(elm, struct file_tree_node, hook);
    file_view_tree_node_del(tree, s);
  }
  lst_del(&n->hook);
  lst_init(&n->hook);
  lst_add(&tree->del_lst, &n->hook);
}
static int
file_view_tree_build(struct file_tree_view *tree,
                     struct file_tree_node *n, struct sys *_sys,
                     struct arena *mem, struct arena *tmp) {
  assert(tree);
  assert(_sys);
  assert(mem);
  assert(tmp);
  assert(n);

  int upt = 0;
  if (n->parent && !set_fnd(tree->exp, n->parent->id)) {
    return upt;
  }
  scp_mem(tmp, _sys) {
    unsigned long long *set = arena_set(tmp, _sys, 1024);
    struct lst_elm *elm = 0;
    for_lst(elm, &n->sub) {
      struct file_tree_node *s;
      s = lst_get(elm, struct file_tree_node, hook);
      set_put(set, _sys, s->id);
    }
    /* validate child nodes */
    struct sys_dir_iter it = {0};
    for_dir_lst(_sys, &it, tmp, n->fullpath) {
      if (it.name.str[0] == '.' || !_sys->dir.exists(_sys, it.fullpath, tmp)) {
        continue;
      }
      unsigned long long id = str_hash(it.fullpath);
      if (!set_fnd(set, id)) {
        file_view_tree_node_new(tree, _sys, mem, n, it.fullpath, id);
        upt = 1;
      }
      set_del(set, id);
    }
    /* remove deleted directory nodes */
    struct lst_elm *safe = 0;
    for_lst_safe(elm, safe, &n->sub) {
      struct file_tree_node *s;
      s = lst_get(elm, struct file_tree_node, hook);
      if (!set_fnd(set, s->id)) {
        continue;
      }
      file_view_tree_node_del(tree, s);
    }
    set_free(set, _sys);
  }
  /* recurse child directory nodes */
  struct lst_elm *it = 0;
  for_lst(it, &n->sub) {
    struct file_tree_node *s;
    s = lst_get(it, struct file_tree_node, hook);
    upt |= file_view_tree_build(tree, s, _sys, mem, tmp);
  }
  return upt;
}
static struct file_tree_node**
file_view_tree_serial(struct file_tree_view *tree,
                      struct file_tree_node *n,
                      struct file_tree_node **lst,
                      struct sys *_sys) {
  assert(tree);
  assert(lst);
  assert(_sys);
  assert(n);

  dyn_add(lst, _sys, n);
  if (set_fnd(tree->exp, n->id)) {
    struct lst_elm *it = 0;
    for_lst(it, &n->sub) {
      struct file_tree_node *s = 0;
      s = lst_get(it, struct file_tree_node, hook);
      lst = file_view_tree_serial(tree, s, lst, _sys);
    }
  }
  return lst;
}
static void
file_tree_update(struct file_view *fs, struct sys *_sys) {
  assert(fs);
  assert(_sys);

  struct arena *tmp = fs->tmp_arena;
  file_view_tree_build(&fs->tree, &fs->tree.root, _sys, &fs->tree.mem, tmp);
  dyn_clr(fs->tree.lst);
  fs->tree.lst = file_view_tree_serial(&fs->tree, &fs->tree.root, fs->tree.lst, _sys);
}
static struct file_tree_node*
file_tree_node_fnd(struct file_tree_node *n, struct str str) {
  assert(n);
  struct lst_elm *elm = 0;
  struct file_tree_node *s = 0;
  for_lst(elm, &n->sub) {
    s = lst_get(elm, struct file_tree_node, hook);
    struct str file_name = path_file(s->fullpath);
    if (str_eq(str, file_name)) {
      return s;
    }
  }
  return s;
}
static void
file_view_tree_open(struct file_view *fs, struct file_tree_view *tree,
                    struct sys *_sys, struct str p) {
  assert(tree);
  assert(_sys);
  assert(fs);

  int off = fs->home.len + 1;
  if (p.len < off) {
    return;
  }
  struct file_tree_node *n = &tree->root;
  struct str path = str_rhs(p, off);
  for_str_tok(it, _, path, strv("/")) {
    struct str fullpath = strp(p.str, it.end);
    if (!_sys->dir.exists(_sys, fullpath, fs->tmp_arena)) {
      break;
    }
    /* create or find node to dig deeper */
    struct file_tree_node *s = file_tree_node_fnd(n, it);
    if (!s) {
      unsigned long long id = str_hash(strp(p.str, it.end));
      s = file_view_tree_node_new(tree, _sys, &tree->mem, n, fullpath, id);
    }
    tree->jmp = 1;
    tree->jmp_to = s->id;
    set_put(tree->exp, _sys, s->id);
    n = s;
  }
  file_tree_update(fs, _sys);
}
static void
file_lst_view_clr(struct file_list_view *lst, struct sys *_sys) {
  assert(lst);
  assert(_sys);

  dyn_free(lst->fltr, _sys);
  dyn_free(lst->elms, _sys);
  dyn_free(lst->full_path, _sys);
  dyn_free(lst->fnd_buf, _sys);
  arena_free(&lst->mem, _sys);

  zero2(lst->off);
  lst->fltr = arena_dyn(&lst->mem, _sys, unsigned long, 128);
  lst->elms = arena_dyn(&lst->mem, _sys, struct file_elm, 256);
  lst->nav_path = arena_dyn(&lst->mem, _sys, char, 256);
  lst->full_path = arena_dyn(&lst->mem, _sys, char, 256);
  lst->fnd_buf = arena_dyn(&lst->mem, _sys, char, MAX_FILTER);

  gui.edt.buf.reset(&lst->fnd_ed);
  gui.edt.buf.reset(&lst->nav_ed);
}
static void
file_tree_view_clr(struct file_tree_view *tree, struct sys *_sys) {
  assert(tree);
  assert(_sys);

  dyn_free(tree->lst, _sys);
  set_free(tree->exp, _sys);
  arena_free(&tree->mem, _sys);

  tree->rev = 0;
  tree->lst = 0;
  tree->exp = 0;

  lst_init(&tree->del_lst);
  memset(tree->off, 0, sizeof(tree->off));
}
static void
file_lst_view_add_path(struct file_list_view *lst, struct sys *_sys,
                       struct str path) {
  assert(lst);
  assert(_sys);

  struct file_elm elm = {0};
  struct mem_scp scp = {0};
  mem_scp_begin(&scp, &lst->mem);

  /* parse file/directory name */
  elm.fullpath = arena_str(&lst->mem, _sys, path);
  elm.name = path_file(elm.fullpath);
  elm.path = strp(elm.fullpath.str, elm.name.str);
  elm.ext = str_nil;

  /* extract file stat, extension and type */
  struct sys_file_info info;
  if (!_sys->file.info(_sys, &info, elm.fullpath, &lst->mem)) {
    mem_scp_end(&scp, &lst->mem, _sys);
    return;
  }
  elm.size = info.siz;
  elm.mtime = info.mtime;
  elm.isvalid = 0;
  memcpy(elm.perms, info.perm, sizeof(elm.perms));

  switch (info.type) {
  case SYS_FILE_DEF: {
    struct str ext = path_ext(elm.name);
    elm.ext = arena_str(&lst->mem, _sys, ext);
    elm.type = file_type(elm.ext);
  } break;
  case SYS_FILE_DIR: {
    elm.isdir = 1;
    elm.type = file_folder_defs;
    elm.file_type = FILE_DIR;
  } break;
  case SYS_FILE_SOCK: {
    elm.type = file_sock_defs;
    elm.file_type = FILE_SOCK;
  } break;
  case SYS_FILE_LNK: {
    elm.type = file_link_defs;
    elm.file_type = FILE_LNK;
  } break;
  case SYS_FILE_FIFO: {
    elm.type = file_fifo_defs;
    elm.file_type = FILE_FIFO;
  } break;}
  elm.ico = file_icon(elm.type);

  /* add new file info */
  dyn_add(lst->elms, _sys, elm);
  dyn_fit(lst->fltr, _sys, bits_to_long(dyn_cnt(lst->elms)));
  bit_clr(lst->fltr, dyn_cnt(lst->elms) - 1);
}
static void
file_list_view_fltr(struct file_list_view *lst, struct str fltr) {
  assert(lst);
  struct str_fnd_tbl tbl;
  str_fnd_tbl(&tbl, fltr);
  fori_dyn(i, lst->elms) {
    assert(i < dyn_cnt(lst->elms));
    int has = str_fnd_tbl_has(lst->elms[i].name, fltr, &tbl);
    bit_set_on(lst->fltr, i, has);
  }
}
static void
file_view_cd(struct file_view *fs, struct sys *_sys, struct str path) {
  assert(fs);
  assert(_sys);

  struct arena *tmp = fs->tmp_arena;
  scp_mem(tmp, _sys) {
    struct str sys_path = arena_str(tmp, _sys, path);
    file_lst_view_clr(&fs->lst, _sys);
    dyn_asn_str(fs->lst.full_path, _sys, sys_path);
    dyn_asn_str(fs->lst.nav_path, _sys, sys_path);

    /* add all files in directory */
    struct sys_dir_iter it = {0};
    for_dir_lst(_sys, &it, tmp, sys_path) {
      file_lst_view_add_path(&fs->lst, _sys, it.fullpath);
    }
    /* sort list by name */
    dyn_sort(fs->lst.elms, file_cmp_asc);
    fs->lst.sel_idx = -1;
  }
}
static void
file_list_view_init(struct file_view *fs, struct sys *_sys, struct gui_ctx *ctx) {
  assert(fs);
  assert(_sys);
  assert(ctx);

  fs->lst.elms = arena_dyn(&fs->lst.mem, _sys, struct file_elm, 1024);
  fs->lst.fltr = arena_dyn(&fs->lst.mem, _sys, unsigned long, 128);
  fs->lst.nav_path = arena_dyn(&fs->lst.mem, _sys, char, 1024);
  fs->lst.full_path = arena_dyn(&fs->lst.mem, _sys, char, 1024);
  fs->lst.fnd_buf = arena_dyn(&fs->lst.mem, _sys, char, MAX_FILTER);

  /* setup list table */
  struct gui_split_lay_cfg tbl_cfg = {0};
  tbl_cfg.size = sizeof(struct file_tbl_col_def);
  tbl_cfg.off = offsetof(struct file_tbl_col_def, ui);
  tbl_cfg.slots = file_tbl_def;
  tbl_cfg.cnt = FILE_TBL_MAX;
  gui.tbl.lay(fs->lst.tbl.state, ctx, &tbl_cfg);

  /* open home directory */
  file_view_cd(fs, _sys, fs->home);
}
static void
file_tree_view_init(struct file_view *fs, struct sys *_sys) {
  assert(fs);
  assert(_sys);

  fs->tree.lst = arena_dyn(&fs->tree.mem, _sys, struct file_tree_node*, 1024);
  fs->tree.exp = arena_set(&fs->tree.mem, _sys, 1024);
  lst_init(&fs->tree.del_lst);
  zero2(fs->tree.off);

  /* setup root node */
  fs->tree.root.fullpath = fs->home;
  fs->tree.root.id = str_hash(fs->home);
  lst_init(&fs->tree.root.hook);
  lst_init(&fs->tree.root.sub);
  set_put(fs->tree.exp, _sys, fs->tree.root.id);
}
static struct file_view*
file_view_init(struct sys *_sys, struct gui_ctx *ctx, struct arena *mem,
                struct arena *tmp_arena) {
  assert(_sys);
  assert(ctx);
  assert(mem);
  assert(tmp_arena);

  struct file_view *fs = arena_obj(mem, _sys, struct file_view);
  fs->lst_rev = 1;
  fs->tree_rev = 1;
  fs->tmp_arena = tmp_arena;
  fs->home = arena_str(mem, _sys, str0(getenv("HOME")));

  struct gui_split_lay_cfg cfg = {0};
  cfg.slots = file_split_def;
  cfg.cnt = FILE_SPLIT_MAX;
  gui.splt.lay.bld(fs->split, ctx, &cfg);

  file_list_view_init(fs, _sys, ctx);
  file_tree_view_init(fs, _sys);
  return fs;
}
static void
file_view_update(struct file_view *fs, struct sys *_sys) {
  assert(_sys);
  if (fs->tree_rev != fs->tree.rev) {
    file_tree_update(fs, _sys);
    fs->tree.rev = fs->tree_rev;
  }
}
static void
file_view_free(struct file_view *fs, struct sys *_sys) {
  assert(_sys);
  file_lst_view_clr(&fs->lst, _sys);
  file_tree_view_clr(&fs->tree, _sys);
}

/* -----------------------------------------------------------------------------
 *                                  GUI
 * -----------------------------------------------------------------------------
 */
static void
ui_edit_search(struct gui_ctx *ctx, struct gui_edit_box *edt,
               struct gui_panel *pan, struct gui_panel *parent,
               struct gui_txt_ed *ed, char **buf) {
  assert(ed);
  assert(buf);
  assert(ctx);
  assert(edt);
  assert(pan);
  assert(parent);

  gui.pan.begin(ctx, pan, parent);
  {
    static const int pad[2] = {3, 3};
    if (ctx->pass == GUI_RENDER &&
        pan->state != GUI_HIDDEN) {
      gui.edt.drw(ctx, pan);
    }
    /* icon */
    struct gui_panel ico = {0};
    ico.box.y = gui.bnd.shrink(&pan->box.y, pad[1]);
    ico.box.x = gui.bnd.min_ext(pan->box.x.min, ctx->cfg.item);
    gui.ico.img(ctx, &ico, pan, RES_ICO_SEARCH);

    /* edit */
    edt->pan.focusable = 1;
    edt->pan.box.x = gui.bnd.min_max(ico.box.x.max, pan->box.x.max);
    edt->pan.box.x = gui.bnd.shrink(&edt->pan.box.x, pad[0]);
    edt->pan.box.y = gui.bnd.shrink(&pan->box.y, pad[1]);
    gui.edt.fld(ctx, edt, &edt->pan, pan, ed, buf);
  }
  gui.pan.end(ctx, pan, parent);
}
static void
ui_file_lst_view_fnd(struct file_list_view *lst,
                     struct gui_ctx *ctx, struct gui_panel *pan,
                     struct gui_panel *parent) {
  assert(lst);
  assert(ctx);
  assert(pan);
  assert(parent);

  struct gui_edit_box edt = {.box = pan->box};
  ui_edit_search(ctx, &edt, pan, parent, &lst->fnd_ed, &lst->fnd_buf);
  if (edt.mod) {
    /* filter files by name */
    bit_fill(lst->fltr, 0x00, dyn_cnt(lst->elms));
    if (dyn_any(lst->fnd_buf)) {
      file_list_view_fltr(lst, dyn_str(lst->fnd_buf));
    }
  }
}
static void
ui_file_lst_view_nav_bar(struct file_view *fs, struct file_list_view *lst,
                         struct gui_ctx *ctx, struct gui_panel *pan,
                         struct gui_panel *parent) {
  assert(lst);
  assert(ctx);
  assert(pan);
  assert(parent);

  gui.pan.begin(ctx, pan, parent);
  {
    int gap = ctx->cfg.gap[0];
    struct gui_box lay = pan->box;
    struct gui_btn home = {.box = gui.cut.rhs(&lay, ctx->cfg.item, gap)};
    if (gui.btn.ico(ctx, &home, pan, RES_ICO_HOME)) {
      /* change the directory to home  */
      file_view_tree_open(fs, &fs->tree, ctx->sys, fs->home);
      file_view_cd(fs, ctx->sys, fs->home);
    }
    gui.tooltip(ctx, &home.pan, strv("Goto Home Directory"));

    struct gui_btn up = {.box = gui.cut.rhs(&lay, ctx->cfg.item, gap)};
    if (gui.btn.ico(ctx, &up, pan, RES_ICO_FOLDER)) {
      /* go up to parent directory  */
      struct str file_name = path_file(dyn_str(lst->full_path));
      struct str file_path = strp(lst->full_path, file_name.str);
      if (file_path.len > fs->home.len) {
        file_view_cd(fs, ctx->sys, strp(lst->full_path, file_name.str));
      }
    }
    gui.tooltip(ctx, &up.pan, strv("Move to Parent Directory"));

    struct gui_edit_box edt = {.box = lay};
    gui.edt.txt(ctx, &edt, pan, &lst->nav_ed, &lst->nav_path);
  }
  gui.pan.end(ctx, pan, parent);
}
static void
ui_file_view_tbl_elm(struct gui_ctx *ctx, struct gui_tbl *tbl,
                     const int *lay, struct gui_panel *elm,
                     const struct file_elm *fi, int is_sel) {
  assert(tbl);
  assert(lay);
  assert(elm);
  assert(fi);

  static const struct gui_align algn = {GUI_HALIGN_RIGHT, GUI_VALIGN_MID};
  static const unsigned dir_col = col_rgb_hex(0xeecd4a);
  unsigned long long elm_id = str_hash(fi->fullpath);
  gui.tbl.lst.elm.begin(ctx, tbl, elm, elm_id, is_sel);
  {
    struct gui_cfg_stk stk[1] = {0};
    scp_gui_cfg_pushu_on(&gui,stk,&ctx->cfg.col[GUI_COL_ICO], dir_col, fi->isdir) {
      /* columns */
      struct tm *mod_time = localtime(&fi->mtime);
      gui.tbl.lst.elm.col.txt_ico(ctx, tbl, lay, elm, fi->name, fi->ico);
      gui.tbl.lst.elm.col.txt(ctx, tbl, lay, elm, str0(fi->type->name), 0);
      gui.tbl.lst.elm.col.txtf(ctx, tbl, lay, elm, &algn, "%zu", fi->size);
      gui.tbl.lst.elm.col.txt(ctx, tbl, lay, elm, str0(fi->perms), 0);
      gui.tbl.lst.elm.col.tm(ctx, tbl, lay, elm, "%d/%m/%Y %H:%M:%S", mod_time);
    }
  }
  gui.tbl.lst.elm.end(ctx, tbl, elm);
  gui.tooltip(ctx, elm, fi->name);
}
static void
ui_file_view_tbl(struct file_view *fs, struct file_list_view *lst,
                 struct gui_ctx *ctx, struct gui_panel *pan,
                 struct gui_panel *parent) {
  assert(lst);
  assert(ctx);
  assert(pan);
  assert(parent);

  int chdir = 0, dir = 0;
  gui.pan.begin(ctx, pan, parent);
  {
    struct gui_tbl tbl = {.box = pan->box};
    gui.tbl.begin(ctx, &tbl, pan, lst->off, &lst->tbl.sort);
    {
      /* header */
      int tbl_lay[GUI_TBL_COL(FILE_TBL_MAX)];
      gui.tbl.hdr.begin(ctx, &tbl, tbl_lay, lst->tbl.state);
      for_cnt(i, tbl.cnt) {
        assert(i < cntof(file_tbl_def));
        struct str title = file_tbl_def[i].title;
        gui.tbl.hdr.slot.txt(ctx, &tbl, tbl_lay, lst->tbl.state, title);
      }
      gui.tbl.hdr.end(ctx, &tbl);

      /* sorting */
      if (tbl.resort && lst->elms) {
        assert(tbl.sort.col < cntof(file_tbl_def));
        assert(tbl.sort.order < cntof(file_tbl_def[tbl.sort.col].sort));
        dyn_sort(lst->elms, file_tbl_def[tbl.sort.col].sort[tbl.sort.order]);
        lst->tbl.sort = tbl.sort;
      }
      /* list */
      struct gui_tbl_lst_cfg cfg = {0};
      gui.tbl.lst.cfg(ctx, &cfg, dyn_cnt(lst->elms));
      cfg.sel.src = GUI_LST_SEL_SRC_EXT;
      cfg.fltr.on = GUI_LST_FLTR_ON_ONE;
      cfg.fltr.bitset = lst->fltr;

      gui.tbl.lst.begin(ctx, &tbl, &cfg);
      for_gui_tbl_lst(i,gui,&tbl) {
        struct gui_panel elm = {0};
        int is_sel = lst->sel_idx == i;
        ui_file_view_tbl_elm(ctx, &tbl, tbl_lay, &elm, lst->elms + i, is_sel);

        /* input handling */
        struct gui_input in = {0};
        gui.pan.input(&in, ctx, &elm, GUI_BTN_LEFT);
        if (in.mouse.btn.left.doubled) {
          chdir = 1, dir = i;
        }
      }
      gui.tbl.lst.end(ctx, &tbl);
      if (tbl.lst.sel.mod) {
        /* selection handling */
        struct file_elm *elm = lst->elms + tbl.lst.sel.idx;
        elm->isvalid = !elm->isdir;
        lst->sel_idx = tbl.lst.sel.idx;
      }
    }
    gui.tbl.end(ctx, &tbl, pan, lst->off);
  }
  gui.pan.end(ctx, pan, parent);

  if (chdir) {
    struct file_elm *fi = lst->elms + dir;
    if (fi->isdir) {
      file_view_tree_open(fs, &fs->tree, ctx->sys, fi->fullpath);
      file_view_cd(fs, ctx->sys, fi->fullpath);
      ctx->lst_state.cur_idx = -1;
    }
  }
}
static void
ui_file_view_tree_node(struct gui_ctx *ctx, struct gui_tree_node *node,
                       struct gui_panel *parent, struct file_tree_node *n){
  assert(n);
  assert(ctx);
  assert(node);
  assert(parent);

  gui.tree.begin(ctx, node, parent, n->depth);
  {
    struct gui_cfg_stk stk[1] = {0};
    scp_gui_cfg_pushu(&gui, stk, &ctx->cfg.col[GUI_COL_ICO], col_rgb_hex(0xeecd4a)) {
      struct gui_panel lbl = {.box = node->box};
      struct str txt = path_file(n->fullpath);
      enum res_ico_id ico = node->open ? RES_ICO_FOLDER_OPEN : RES_ICO_FOLDER;
      gui.ico.box(ctx, &lbl, &node->pan, ico, txt);
      gui.tooltip(ctx, &node->pan, n->fullpath);
    }
  }
  gui.tree.end(ctx, node, parent);
}
static int
ui_file_view_tree_elm(struct gui_ctx *ctx, struct file_tree_view *tree,
                      struct file_tree_node *n, struct gui_lst_reg *reg,
                      struct gui_panel *elm, int is_sel) {
  assert(n);
  assert(ctx);
  assert(reg);
  assert(elm);
  assert(tree);

  int ret = 0;
  gui.lst.reg.elm.begin(ctx, reg, elm, n->id, is_sel);
  {
    struct gui_tree_node node = {0};
    node.type = lst_any(&n->sub) ? GUI_TREE_NODE : GUI_TREE_LEAF;
    node.open = set_fnd(tree->exp, n->id);
    ui_file_view_tree_node(ctx, &node, elm, n);
    if (node.changed) {
      if (node.open) {
        set_put(tree->exp, ctx->sys, n->id);
      } else {
        set_del(tree->exp, n->id);
      }
      ret = 1;
    }
  }
  gui.lst.reg.elm.end(ctx, reg, elm);
  return ret;
}
static void
ui_file_view_tree_key(struct file_view *fs, struct file_tree_view *tree,
                      struct gui_ctx *ctx, struct gui_lst *lst) {
  assert(tree);
  assert(ctx);
  assert(lst);
  assert(fs);

  /* tree node expansion */
  int plus = bit_tst_clr(ctx->keys, GUI_KEY_TREE_EXPAND);
  int opening = bit_tst_clr(ctx->keys, GUI_KEY_TREE_RIGHT);
  if (plus || opening) {
    struct file_tree_node *n = tree->lst[tree->sel];
    set_put(tree->exp, ctx->sys, n->id);
    fs->tree_rev++;
    return;
  }
  /* tree node collapse and jump to parent node */
  int minus = bit_tst_clr(ctx->keys, GUI_KEY_TREE_COLLAPSE);
  int closing = bit_tst_clr(ctx->keys, GUI_KEY_TREE_LEFT);
  if (minus || closing) {
    struct file_tree_node *n = tree->lst[tree->sel];
    if (closing && !set_fnd(tree->exp, n->id)) {
      for (int i = tree->sel; i > 0; --i) {
        int nidx = i - 1;
        struct file_tree_node *p = tree->lst[nidx];
        if (p->depth >= n->depth) {
          continue;
        }
        tree->off[1] = gui.lst.lay.clamps(&lst->lay, nidx);
        gui.lst.set_sel_idx(ctx, lst, nidx);
        gui.lst.set_cur_idx(ctx, lst, nidx);
        tree->sel = nidx;
        break;
      }
    } else {
      set_del(tree->exp, n->id);
    }
    fs->tree_rev++;
  }
}
static int
ui_file_view_tree_jmp_elm(struct file_tree_view *tree,
                          unsigned long long jmp_id) {
  fori_dyn(i, tree->lst) {
    assert(i < dyn_cnt(tree->lst));
    struct file_tree_node *n = tree->lst[i];
    if (n->id == jmp_id) {
      return i;
    }
  }
  return dyn_cnt(tree->lst);
}
static void
ui_file_view_tree(struct file_view *fs, struct file_tree_view *tree,
                  struct gui_ctx *ctx, struct gui_panel *pan,
                  struct gui_panel *parent) {
  assert(tree);
  assert(ctx);
  assert(pan);
  assert(parent);

  gui.pan.begin(ctx, pan, parent);
  {
    /* tree list */
    struct gui_lst_cfg cfg = {0};
    gui.lst.cfg(&cfg, dyn_cnt(tree->lst), tree->off[1]);
    cfg.sel.src = GUI_LST_SEL_SRC_EXT;

    struct gui_lst_reg reg = {.box = pan->box};
    gui.lst.reg.begin(ctx, &reg, pan, &cfg, tree->off);
    for_gui_lst_reg(i,gui,&reg) {
      /* tree nodes */
      struct gui_panel elm = {0};
      struct file_tree_node *n = tree->lst[i];
      if (ui_file_view_tree_elm(ctx, tree, n, &reg, &elm, tree->sel == i)) {
        fs->tree_rev++;
      }
    }
    if (tree->jmp) {
      /* jump to list element */
      int idx = ui_file_view_tree_jmp_elm(tree, tree->jmp_to);
      if (idx < dyn_cnt(tree->lst)) {
        gui.lst.reg.ctr(&reg, idx);
        tree->sel = idx;
      }
      tree->jmp = 0;
    }
    gui.lst.reg.end(ctx, &reg, pan, tree->off);
    if (reg.lst.sel.mod) {
      /* selection */
      tree->sel = reg.lst.sel.idx;
      struct file_tree_node *n = tree->lst[tree->sel];
      file_view_cd(fs, ctx->sys, n->fullpath);
    }
    if (reg.lst.ctl.has_focus) {
      /* key handling */
      ui_file_view_tree_key(fs, tree, ctx, &reg.lst);
    }
  }
  gui.pan.end(ctx, pan, parent);
}
static void
ui_file_sel_view(struct file_view *fs, struct file_list_view *lst,
                 struct gui_ctx *ctx, struct gui_panel *pan,
                 struct gui_panel *parent) {
  assert(fs);
  assert(lst);
  assert(ctx);
  assert(pan);
  assert(parent);

  gui.pan.begin(ctx, pan, parent);
  {
    struct gui_panel fnd = {.box = pan->box};
    fnd.box.y = gui.bnd.min_ext(pan->box.y.min, ctx->cfg.item);
    ui_file_lst_view_fnd(lst, ctx, &fnd, pan);

    int gap = ctx->cfg.pan_gap[0];
    struct gui_panel tbl = {.box = pan->box};
    tbl.box.y = gui.bnd.min_max(fnd.box.y.max + gap, pan->box.y.max);
    ui_file_view_tbl(fs, lst, ctx, &tbl, pan);
  }
  gui.pan.end(ctx, pan, parent);
}
static void
ui_file_sel_split(struct file_view *fs, struct gui_ctx *ctx,
                  struct gui_panel *pan, struct gui_panel *parent) {
  assert(ctx);
  assert(pan);
  assert(parent);

  gui.pan.begin(ctx, pan, parent);
  {
    struct gui_split spt = {.box = pan->box};
    int lay[GUI_SPLIT_COL(FILE_SPLIT_MAX)];
    gui.splt.begin(ctx, &spt, pan, GUI_SPLIT_FIT, GUI_HORIZONTAL, lay, fs->split);
    {
      struct gui_panel tree = {.box = spt.item};
      ui_file_view_tree(fs, &fs->tree, ctx, &tree, &spt.pan);
      gui.splt.sep(ctx, &spt, lay, fs->split);

      struct gui_panel tbl = {.box = spt.item};
      ui_file_sel_view(fs, &fs->lst, ctx, &tbl, &spt.pan);
    }
    gui.splt.end(ctx, &spt, pan);
  }
  gui.pan.end(ctx, pan, parent);
}
static int
ui_file_sel(dyn(char) *filepath, struct file_view *fs, struct gui_ctx *ctx,
            struct gui_panel *pan, struct gui_panel *parent) {
  assert(ctx);
  assert(pan);
  assert(parent);
  assert(filepath);

  int ret = 0;
  gui.pan.begin(ctx, pan, parent);
  {
    int gap = ctx->cfg.pan_gap[1];
    struct gui_box lay = pan->box;
    struct gui_btn open = {.box = gui.cut.bot(&lay, ctx->cfg.item, gap)};
    struct gui_panel nav = {.box = gui.cut.top(&lay, ctx->cfg.item, gap)};
    struct gui_panel tbl = {.box = lay};

    ui_file_lst_view_nav_bar(fs, &fs->lst, ctx, &nav, pan);
    ui_file_sel_split(fs, ctx, &tbl, pan);

    /* file selection */
    int dis = fs->lst.sel_idx < 0 || fs->lst.sel_idx >= dyn_cnt(fs->lst.elms);
    if (!dis) {
      const struct file_elm *elm = 0;
      elm = fs->lst.elms + fs->lst.sel_idx;
      dis = elm->isdir || !elm->isvalid;
    }
    scp_gui_disable_on(&gui, ctx, dis) {
      if (gui.btn.ico_txt(ctx, &open, pan, strv("Open"), RES_ICO_IMPORT, -1)) {
        const struct file_elm *elm = fs->lst.elms + fs->lst.sel_idx;
        dyn_asn_str(*filepath, ctx->sys, elm->fullpath);
        ret = 1;
      }
      gui.tooltip(ctx, &open.pan, strv("Open SQLite Database"));
    }
  }
  gui.pan.end(ctx, pan, parent);
  return ret;
}

/* ---------------------------------------------------------------------------
 *                                  API
 * ---------------------------------------------------------------------------
 */
static const struct pck_api pck__api = {
  .init = file_view_init,
  .update = file_view_update,
  .shutdown = file_view_free,
  .ui = ui_file_sel,
};
static void
pck_api(void *export, void *import) {
  unused(import);
  struct pck_api *exp = cast(struct pck_api*, export);
  *exp = pck__api;
}

repo_name: vurtun/tau

repo_language: C

mime_type: text/x-c


text:
#define fmt__uint32 unsigned int
#define fmt__int32 signed int

#ifdef _MSC_VER
#define fmt__uint64 unsigned __int64
#define fmt__int64 signed __int64
#else
#define fmt__uint64 unsigned long long
#define fmt__int64 signed long long
#endif
#define fmt__uint16 unsigned short

#ifndef fmt__uintptr
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || \
    defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
#define fmt__uintptr fmt__uint64
#else
#define fmt__uintptr fmt__uint32
#endif
#endif

#ifndef FMT_MSVC_MODE  // used for MSVC2013 and earlier (MSVC2015 matches GCC)
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define FMT_MSVC_MODE
#endif
#endif

#ifdef FMT_NOUNALIGNED  // define this before inclusion to force fmt_sprintf to
                        // always use aligned accesses
#define FMT__UNALIGNED(code)
#else
#define FMT__UNALIGNED(code) code
#endif

#ifndef FMT_NOFLOAT
// internal float utility functions
static fmt__int32
fmt__real_to_str(char const **start, fmt__uint32 *len, char *out,
                 fmt__int32 *decimal_pos, double value,
                 fmt__uint32 frac_digits);
static fmt__int32
fmt__real_to_parts(fmt__int64 *bits, fmt__int32 *expo, double value);
#define FMT__SPECIAL 0x7000
#endif

#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wcast-align"
#pragma GCC diagnostic ignored "-Wextra-semi-stmt"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion"
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif

static char fmt__period = '.';
static char fmt__comma = ',';
static struct {
  short temp;  // force next field to be 2-byte aligned
  char pair[201];
} fmt__digitpair = {0,
                    "00010203040506070809101112131415161718192021222324"
                    "25262728293031323334353637383940414243444546474849"
                    "50515253545556575859606162636465666768697071727374"
                    "75767778798081828384858687888990919293949596979899"};
static void
fmt_set_sep(char pcomma, char pperiod) {
  fmt__period = pperiod;
  fmt__comma = pcomma;
}

#define FMT__LEFTJUST 1
#define FMT__LEADINGPLUS 2
#define FMT__LEADINGSPACE 4
#define FMT__LEADING_0X 8
#define FMT__LEADINGZERO 16
#define FMT__INTMAX 32
#define FMT__TRIPLET_COMMA 64
#define FMT__NEGATIVE 128
#define FMT__METRIC_SUFFIX 256
#define FMT__HALFWIDTH 512
#define FMT__METRIC_NOSPACE 1024
#define FMT__METRIC_1024 2048
#define FMT__METRIC_JEDEC 4096

static void
fmt__lead_sign(fmt__uint32 fl, char *sign) {
  sign[0] = 0;
  if (fl & FMT__NEGATIVE) {
    sign[0] = 1;
    sign[1] = '-';
  } else if (fl & FMT__LEADINGSPACE) {
    sign[0] = 1;
    sign[1] = ' ';
  } else if (fl & FMT__LEADINGPLUS) {
    sign[0] = 1;
    sign[1] = '+';
  }
}
static int
fmtvscb(FMT_SPRINTFCB *callback, void *user, char *buf, char const *fmt,
        va_list va) {
  static char hex[] = "0123456789abcdefxp";
  static char hexu[] = "0123456789ABCDEFXP";
  char *bf;
  char const *f;
  int tlen = 0;

  bf = buf;
  f = fmt;
  for (;;) {
    fmt__int32 fw, pr, tz;
    fmt__uint32 fl;

// macros for the callback buffer stuff
#define fmt__chk_cb_bufL(bytes)                                  \
  {                                                              \
    int len = (int)(bf - buf);                                   \
    if ((len + (bytes)) >= FMT_MIN) {                            \
      tlen += len;                                               \
      if (0 == (bf = buf = callback(buf, user, len))) goto done; \
    }                                                            \
  }
#define fmt__chk_cb_buf(bytes) \
  {                            \
    if (callback) {            \
      fmt__chk_cb_bufL(bytes); \
    }                          \
  }
#define fmt__flush_cb()            \
  {                                \
    fmt__chk_cb_bufL(FMT_MIN - 1); \
  }  // flush if there is even one byte in the buffer
#define fmt__cb_buf_clamp(cl, v)        \
  cl = v;                               \
  if (callback) {                       \
    int lg = FMT_MIN - (int)(bf - buf); \
    if (cl > lg) cl = lg;               \
  }

    // fast copy everything up to the next % (or end of string)
    for (;;) {
      while (((fmt__uintptr)f) & 3) {
      schk1:
        if (f[0] == '%') goto scandd;
      schk2:
        if (f[0] == 0) goto endfmt;
        fmt__chk_cb_buf(1);
        *bf++ = f[0];
        ++f;
      }
      for (;;) {
        // Check if the next 4 bytes contain %(0x25) or end of string.
        // Using the 'hasless' trick:
        // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
        fmt__uint32 v, c;
        v = *(fmt__uint32 *)f;
        c = (~v) & 0x80808080;
        if (((v ^ 0x25252525) - 0x01010101) & c) goto schk1;
        if ((v - 0x01010101) & c) goto schk2;
        if (callback)
          if ((FMT_MIN - (int)(bf - buf)) < 4) goto schk1;
#ifdef FMT_NOUNALIGNED
        if (((fmt__uintptr)bf) & 3) {
          bf[0] = f[0];
          bf[1] = f[1];
          bf[2] = f[2];
          bf[3] = f[3];
        } else
#endif
        {
          *(fmt__uint32 *)bf = v;
        }
        bf += 4;
        f += 4;
      }
    }
  scandd:

    ++f;

    // ok, we have a percent, read the modifiers first
    fw = 0;
    pr = -1;
    fl = 0;
    tz = 0;

    // flags
    for (;;) {
      switch (f[0]) {
        // if we have left justify
        case '-':
          fl |= FMT__LEFTJUST;
          ++f;
          continue;
        // if we have leading plus
        case '+':
          fl |= FMT__LEADINGPLUS;
          ++f;
          continue;
        // if we have leading space
        case ' ':
          fl |= FMT__LEADINGSPACE;
          ++f;
          continue;
        // if we have leading 0x
        case '#':
          fl |= FMT__LEADING_0X;
          ++f;
          continue;
        // if we have thousand commas
        case '\'':
          fl |= FMT__TRIPLET_COMMA;
          ++f;
          continue;
        // if we have kilo marker (none->kilo->kibi->jedec)
        case '$':
          if (fl & FMT__METRIC_SUFFIX) {
            if (fl & FMT__METRIC_1024) {
              fl |= FMT__METRIC_JEDEC;
            } else {
              fl |= FMT__METRIC_1024;
            }
          } else {
            fl |= FMT__METRIC_SUFFIX;
          }
          ++f;
          continue;
        // if we don't want space between metric suffix and number
        case '_':
          fl |= FMT__METRIC_NOSPACE;
          ++f;
          continue;
        // if we have leading zero
        case '0':
          fl |= FMT__LEADINGZERO;
          ++f;
          goto flags_done;
        default:
          goto flags_done;
      }
    }
  flags_done:

    // get the field width
    if (f[0] == '*') {
      fw = va_arg(va, fmt__uint32);
      ++f;
    } else {
      while ((f[0] >= '0') && (f[0] <= '9')) {
        fw = fw * 10 + f[0] - '0';
        f++;
      }
    }
    // get the precision
    if (f[0] == '.') {
      ++f;
      if (f[0] == '*') {
        pr = va_arg(va, fmt__uint32);
        ++f;
      } else {
        pr = 0;
        while ((f[0] >= '0') && (f[0] <= '9')) {
          pr = pr * 10 + f[0] - '0';
          f++;
        }
      }
    }

    // handle integer size overrides
    switch (f[0]) {
      // are we halfwidth?
      case 'h':
        fl |= FMT__HALFWIDTH;
        ++f;
        if (f[0] == 'h') ++f;  // QUARTERWIDTH
        break;
      // are we 64-bit (unix style)
      case 'l':
        fl |= ((sizeof(long) == 8) ? FMT__INTMAX : 0);
        ++f;
        if (f[0] == 'l') {
          fl |= FMT__INTMAX;
          ++f;
        }
        break;
      // are we 64-bit on intmax? (c99)
      case 'j':
        fl |= (sizeof(size_t) == 8) ? FMT__INTMAX : 0;
        ++f;
        break;
      // are we 64-bit on size_t or ptrdiff_t? (c99)
      case 'z':
        fl |= (sizeof(ptrdiff_t) == 8) ? FMT__INTMAX : 0;
        ++f;
        break;
      case 't':
        fl |= (sizeof(ptrdiff_t) == 8) ? FMT__INTMAX : 0;
        ++f;
        break;
      // are we 64-bit (msft style)
      case 'I':
        if ((f[1] == '6') && (f[2] == '4')) {
          fl |= FMT__INTMAX;
          f += 3;
        } else if ((f[1] == '3') && (f[2] == '2')) {
          f += 3;
        } else {
          fl |= ((sizeof(void *) == 8) ? FMT__INTMAX : 0);
          ++f;
        }
        break;
      default:
        break;
    }

    // handle each replacement
    switch (f[0]) {
#define FMT__NUMSZ 512  // big enough for e308 (with commas) or e-307
      char num[FMT__NUMSZ];
      char lead[8];
      char tail[8];
      char *s;
      char const *h;
      fmt__uint32 l, n, cs;
      fmt__uint64 n64;
#ifndef FMT_NOFLOAT
      double fv;
#endif
      fmt__int32 dp;
      char const *sn;

      case 's':
        // get the string
        s = va_arg(va, char *);
        if (s == 0) s = (char *)"null";
        // get the length
        sn = s;
        for (;;) {
          if ((((fmt__uintptr)sn) & 3) == 0) break;
        lchk:
          if (sn[0] == 0) goto ld;
          ++sn;
        }
        n = 0xffffffff;
        if (pr >= 0) {
          n = (fmt__uint32)(sn - s);
          if (n >= (fmt__uint32)pr) goto ld;
          n = ((fmt__uint32)(pr - n)) >> 2;
        }
        while (n) {
          fmt__uint32 v = *(fmt__uint32 *)sn;
          if ((v - 0x01010101) & (~v) & 0x80808080UL) goto lchk;
          sn += 4;
          --n;
        }
        goto lchk;
      ld:

        l = (fmt__uint32)(sn - s);
        // clamp to precision
        if (l > (fmt__uint32)pr) l = pr;
        lead[0] = 0;
        tail[0] = 0;
        pr = 0;
        dp = 0;
        cs = 0;
        // copy the string in
        goto scopy;

      case 'c':  // char
        // get the character
        s = num + FMT__NUMSZ - 1;
        *s = (char)va_arg(va, int);
        l = 1;
        lead[0] = 0;
        tail[0] = 0;
        pr = 0;
        dp = 0;
        cs = 0;
        goto scopy;

      case 'n':  // weird write-bytes specifier
      {
        int *d = va_arg(va, int *);
        *d = tlen + (int)(bf - buf);
      } break;

#ifdef FMT_NOFLOAT
      case 'A':              // float
      case 'a':              // hex float
      case 'G':              // float
      case 'g':              // float
      case 'E':              // float
      case 'e':              // float
      case 'f':              // float
        va_arg(va, double);  // eat it
        s = (char *)"No float";
        l = 8;
        lead[0] = 0;
        tail[0] = 0;
        pr = 0;
        dp = 0;
        cs = 0;
        goto scopy;
#else
      case 'A':  // hex float
      case 'a':  // hex float
        h = (f[0] == 'A') ? hexu : hex;
        fv = va_arg(va, double);
        if (pr == -1) pr = 6;  // default is 6
        // read the double into a string
        if (fmt__real_to_parts((fmt__int64 *)&n64, &dp, fv))
          fl |= FMT__NEGATIVE;

        s = num + 64;

        fmt__lead_sign(fl, lead);

        if (dp == -1023)
          dp = (n64) ? -1022 : 0;
        else
          n64 |= (((fmt__uint64)1) << 52);
        n64 <<= (64 - 56);
        if (pr < 15) n64 += ((((fmt__uint64)8) << 56) >> (pr * 4));
          // add leading chars

#ifdef FMT_MSVC_MODE
        *s++ = '0';
        *s++ = 'x';
#else
        lead[1 + lead[0]] = '0';
        lead[2 + lead[0]] = 'x';
        lead[0] += 2;
#endif
        *s++ = h[(n64 >> 60) & 15];
        n64 <<= 4;
        if (pr) *s++ = fmt__period;
        sn = s;

        // print the bits
        n = pr;
        if (n > 13) n = 13;
        if (pr > (fmt__int32)n) tz = pr - n;
        pr = 0;
        while (n--) {
          *s++ = h[(n64 >> 60) & 15];
          n64 <<= 4;
        }

        // print the expo
        tail[1] = h[17];
        if (dp < 0) {
          tail[2] = '-';
          dp = -dp;
        } else
          tail[2] = '+';
        n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));
        tail[0] = (char)n;
        for (;;) {
          tail[n] = '0' + dp % 10;
          if (n <= 3) break;
          --n;
          dp /= 10;
        }

        dp = (int)(s - sn);
        l = (int)(s - (num + 64));
        s = num + 64;
        cs = 1 + (3 << 24);
        goto scopy;

      case 'G':  // float
      case 'g':  // float
        h = (f[0] == 'G') ? hexu : hex;
        fv = va_arg(va, double);
        if (pr == -1)
          pr = 6;
        else if (pr == 0)
          pr = 1;  // default is 6
        // read the double into a string
        if (fmt__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))
          fl |= FMT__NEGATIVE;

        // clamp the precision and delete extra zeros after clamp
        n = pr;
        if (l > (fmt__uint32)pr) l = pr;
        while ((l > 1) && (pr) && (sn[l - 1] == '0')) {
          --pr;
          --l;
        }

        // should we use %e
        if ((dp <= -4) || (dp > (fmt__int32)n)) {
          if (pr > (fmt__int32)l)
            pr = l - 1;
          else if (pr)
            --pr;  // when using %e, there is one digit before the decimal
          goto doexpfromg;
        }
        // this is the insane action to get the pr to match %g semantics for %f
        if (dp > 0) {
          pr = (dp < (fmt__int32)l) ? l - dp : 0;
        } else {
          pr = -dp + ((pr > (fmt__int32)l) ? (fmt__int32)l : pr);
        }
        goto dofloatfromg;

      case 'E':  // float
      case 'e':  // float
        h = (f[0] == 'E') ? hexu : hex;
        fv = va_arg(va, double);
        if (pr == -1) pr = 6;  // default is 6
        // read the double into a string
        if (fmt__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))
          fl |= FMT__NEGATIVE;
      doexpfromg:
        tail[0] = 0;
        fmt__lead_sign(fl, lead);
        if (dp == FMT__SPECIAL) {
          s = (char *)sn;
          cs = 0;
          pr = 0;
          goto scopy;
        }
        s = num + 64;
        // handle leading chars
        *s++ = sn[0];

        if (pr) *s++ = fmt__period;

        // handle after decimal
        if ((l - 1) > (fmt__uint32)pr) l = pr + 1;
        for (n = 1; n < l; n++) *s++ = sn[n];
        // trailing zeros
        tz = pr - (l - 1);
        pr = 0;
        // dump expo
        tail[1] = h[0xe];
        dp -= 1;
        if (dp < 0) {
          tail[2] = '-';
          dp = -dp;
        } else
          tail[2] = '+';
#ifdef FMT_MSVC_MODE
        n = 5;
#else
        n = (dp >= 100) ? 5 : 4;
#endif
        tail[0] = (char)n;
        for (;;) {
          tail[n] = '0' + dp % 10;
          if (n <= 3) break;
          --n;
          dp /= 10;
        }
        cs = 1 + (3 << 24);  // how many tens
        goto flt_lead;

      case 'f':  // float
        fv = va_arg(va, double);
      doafloat:
        // do kilos
        if (fl & FMT__METRIC_SUFFIX) {
          double divisor;
          divisor = 1000.0f;
          if (fl & FMT__METRIC_1024) divisor = 1024.0;
          while (fl < 0x4000000) {
            if ((fv < divisor) && (fv > -divisor)) break;
            fv /= divisor;
            fl += 0x1000000;
          }
        }
        if (pr == -1) pr = 6;  // default is 6
        // read the double into a string
        if (fmt__real_to_str(&sn, &l, num, &dp, fv, pr)) fl |= FMT__NEGATIVE;
      dofloatfromg:
        tail[0] = 0;
        fmt__lead_sign(fl, lead);
        if (dp == FMT__SPECIAL) {
          s = (char *)sn;
          cs = 0;
          pr = 0;
          goto scopy;
        }
        s = num + 64;

        // handle the three decimal varieties
        if (dp <= 0) {
          fmt__int32 i;
          // handle 0.000*000xxxx
          *s++ = '0';
          if (pr) *s++ = fmt__period;
          n = -dp;
          if ((fmt__int32)n > pr) n = pr;
          i = n;
          while (i) {
            if ((((fmt__uintptr)s) & 3) == 0) break;
            *s++ = '0';
            --i;
          }
          while (i >= 4) {
            *(fmt__uint32 *)s = 0x30303030;
            s += 4;
            i -= 4;
          }
          while (i) {
            *s++ = '0';
            --i;
          }
          if ((fmt__int32)(l + n) > pr) l = pr - n;
          i = l;
          while (i) {
            *s++ = *sn++;
            --i;
          }
          tz = pr - (n + l);
          cs = 1 + (3 << 24);  // how many tens did we write (for commas below)
        } else {
          cs = (fl & FMT__TRIPLET_COMMA) ? ((600 - (fmt__uint32)dp) % 3) : 0;
          if ((fmt__uint32)dp >= l) {
            // handle xxxx000*000.0
            n = 0;
            for (;;) {
              if ((fl & FMT__TRIPLET_COMMA) && (++cs == 4)) {
                cs = 0;
                *s++ = fmt__comma;
              } else {
                *s++ = sn[n];
                ++n;
                if (n >= l) break;
              }
            }
            if (n < (fmt__uint32)dp) {
              n = dp - n;
              if ((fl & FMT__TRIPLET_COMMA) == 0) {
                while (n) {
                  if ((((fmt__uintptr)s) & 3) == 0) break;
                  *s++ = '0';
                  --n;
                }
                while (n >= 4) {
                  *(fmt__uint32 *)s = 0x30303030;
                  s += 4;
                  n -= 4;
                }
              }
              while (n) {
                if ((fl & FMT__TRIPLET_COMMA) && (++cs == 4)) {
                  cs = 0;
                  *s++ = fmt__comma;
                } else {
                  *s++ = '0';
                  --n;
                }
              }
            }
            cs = (int)(s - (num + 64)) + (3 << 24);  // cs is how many tens
            if (pr) {
              *s++ = fmt__period;
              tz = pr;
            }
          } else {
            // handle xxxxx.xxxx000*000
            n = 0;
            for (;;) {
              if ((fl & FMT__TRIPLET_COMMA) && (++cs == 4)) {
                cs = 0;
                *s++ = fmt__comma;
              } else {
                *s++ = sn[n];
                ++n;
                if (n >= (fmt__uint32)dp) break;
              }
            }
            cs = (int)(s - (num + 64)) + (3 << 24);  // cs is how many tens
            if (pr) *s++ = fmt__period;
            if ((l - dp) > (fmt__uint32)pr) l = pr + dp;
            while (n < l) {
              *s++ = sn[n];
              ++n;
            }
            tz = pr - (l - dp);
          }
        }
        pr = 0;

        // handle k,m,g,t
        if (fl & FMT__METRIC_SUFFIX) {
          char idx;
          idx = 1;
          if (fl & FMT__METRIC_NOSPACE) idx = 0;
          tail[0] = idx;
          tail[1] = ' ';
          {
            if (fl >> 24) {  // SI kilo is 'k', JEDEC and SI kibits are 'K'.
              if (fl & FMT__METRIC_1024)
                tail[idx + 1] = "_KMGT"[fl >> 24];
              else
                tail[idx + 1] = "_kMGT"[fl >> 24];
              idx++;
              // If printing kibits and not in jedec, add the 'i'.
              if (fl & FMT__METRIC_1024 && !(fl & FMT__METRIC_JEDEC)) {
                tail[idx + 1] = 'i';
                idx++;
              }
              tail[0] = idx;
            }
          }
        };

      flt_lead:
        // get the length that we copied
        l = (fmt__uint32)(s - (num + 64));
        s = num + 64;
        goto scopy;
#endif

      case 'B':  // upper binary
      case 'b':  // lower binary
        h = (f[0] == 'B') ? hexu : hex;
        lead[0] = 0;
        if (fl & FMT__LEADING_0X) {
          lead[0] = 2;
          lead[1] = '0';
          lead[2] = h[0xb];
        }
        l = (8 << 4) | (1 << 8);
        goto radixnum;

      case 'o':  // octal
        h = hexu;
        lead[0] = 0;
        if (fl & FMT__LEADING_0X) {
          lead[0] = 1;
          lead[1] = '0';
        }
        l = (3 << 4) | (3 << 8);
        goto radixnum;

      case 'p':  // pointer
        fl |= (sizeof(void *) == 8) ? FMT__INTMAX : 0;
        pr = sizeof(void *) * 2;
        fl &= ~FMT__LEADINGZERO;  // 'p' only prints the pointer with zeros
                                  // fall through - to X

      case 'X':  // upper hex
      case 'x':  // lower hex
        h = (f[0] == 'X') ? hexu : hex;
        l = (4 << 4) | (4 << 8);
        lead[0] = 0;
        if (fl & FMT__LEADING_0X) {
          lead[0] = 2;
          lead[1] = '0';
          lead[2] = h[16];
        }
      radixnum:
        // get the number
        if (fl & FMT__INTMAX)
          n64 = va_arg(va, fmt__uint64);
        else
          n64 = va_arg(va, fmt__uint32);

        s = num + FMT__NUMSZ;
        dp = 0;
        // clear tail, and clear leading if value is zero
        tail[0] = 0;
        if (n64 == 0) {
          lead[0] = 0;
          if (pr == 0) {
            l = 0;
            cs = (((l >> 4) & 15)) << 24;
            goto scopy;
          }
        }
        // convert to string
        for (;;) {
          *--s = h[n64 & ((1 << (l >> 8)) - 1)];
          n64 >>= (l >> 8);
          if (!((n64) || ((fmt__int32)((num + FMT__NUMSZ) - s) < pr))) break;
          if (fl & FMT__TRIPLET_COMMA) {
            ++l;
            if ((l & 15) == ((l >> 4) & 15)) {
              l &= ~15;
              *--s = fmt__comma;
            }
          }
        };
        // get the tens and the comma pos
        cs = (fmt__uint32)((num + FMT__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);
        // get the length that we copied
        l = (fmt__uint32)((num + FMT__NUMSZ) - s);
        // copy it
        goto scopy;

      case 'u':  // unsigned
      case 'i':
      case 'd':  // integer
        // get the integer and abs it
        if (fl & FMT__INTMAX) {
          fmt__int64 i64 = va_arg(va, fmt__int64);
          n64 = (fmt__uint64)i64;
          if ((f[0] != 'u') && (i64 < 0)) {
            n64 = (fmt__uint64)-i64;
            fl |= FMT__NEGATIVE;
          }
        } else {
          fmt__int32 i = va_arg(va, fmt__int32);
          n64 = (fmt__uint32)i;
          if ((f[0] != 'u') && (i < 0)) {
            n64 = (fmt__uint32)-i;
            fl |= FMT__NEGATIVE;
          }
        }

#ifndef FMT_NOFLOAT
        if (fl & FMT__METRIC_SUFFIX) {
          if (n64 < 1024)
            pr = 0;
          else if (pr == -1)
            pr = 1;
          fv = (double)(fmt__int64)n64;
          goto doafloat;
        }
#endif

        // convert to string
        s = num + FMT__NUMSZ;
        l = 0;

        for (;;) {
          // do in 32-bit chunks (avoid lots of 64-bit divides even with
          // constant denominators)
          char *o = s - 8;
          if (n64 >= 100000000) {
            n = (fmt__uint32)(n64 % 100000000);
            n64 /= 100000000;
          } else {
            n = (fmt__uint32)n64;
            n64 = 0;
          }
          if ((fl & FMT__TRIPLET_COMMA) == 0) {
            do {
              s -= 2;
              *(fmt__uint16 *)s =
                  *(fmt__uint16 *)&fmt__digitpair.pair[(n % 100) * 2];
              n /= 100;
            } while (n);
          }
          while (n) {
            if ((fl & FMT__TRIPLET_COMMA) && (l++ == 3)) {
              l = 0;
              *--s = fmt__comma;
              --o;
            } else {
              *--s = (char)(n % 10) + '0';
              n /= 10;
            }
          }
          if (n64 == 0) {
            if ((s[0] == '0') && (s != (num + FMT__NUMSZ))) ++s;
            break;
          }
          while (s != o)
            if ((fl & FMT__TRIPLET_COMMA) && (l++ == 3)) {
              l = 0;
              *--s = fmt__comma;
              --o;
            } else {
              *--s = '0';
            }
        }

        tail[0] = 0;
        fmt__lead_sign(fl, lead);

        // get the length that we copied
        l = (fmt__uint32)((num + FMT__NUMSZ) - s);
        if (l == 0) {
          *--s = '0';
          l = 1;
        }
        cs = l + (3 << 24);
        if (pr < 0) pr = 0;

      scopy:
        // get fw=leading/trailing space, pr=leading zeros
        if (pr < (fmt__int32)l) pr = l;
        n = pr + lead[0] + tail[0] + tz;
        if (fw < (fmt__int32)n) fw = n;
        fw -= n;
        pr -= l;

        // handle right justify and leading zeros
        if ((fl & FMT__LEFTJUST) == 0) {
          if (fl & FMT__LEADINGZERO)  // if leading zeros, everything is in pr
          {
            pr = (fw > pr) ? fw : pr;
            fw = 0;
          } else {
            fl &= ~FMT__TRIPLET_COMMA;  // if no leading zeros, then no commas
          }
        }

        // copy the spaces and/or zeros
        if (fw + pr) {
          fmt__int32 i;
          fmt__uint32 c;

          // copy leading spaces (or when doing %8.4d stuff)
          if ((fl & FMT__LEFTJUST) == 0)
            while (fw > 0) {
              fmt__cb_buf_clamp(i, fw);
              fw -= i;
              while (i) {
                if ((((fmt__uintptr)bf) & 3) == 0) break;
                *bf++ = ' ';
                --i;
              }
              while (i >= 4) {
                *(fmt__uint32 *)bf = 0x20202020;
                bf += 4;
                i -= 4;
              }
              while (i) {
                *bf++ = ' ';
                --i;
              }
              fmt__chk_cb_buf(1);
            }

          // copy leader
          sn = lead + 1;
          while (lead[0]) {
            fmt__cb_buf_clamp(i, lead[0]);
            lead[0] -= (char)i;
            while (i) {
              *bf++ = *sn++;
              --i;
            }
            fmt__chk_cb_buf(1);
          }

          // copy leading zeros
          c = cs >> 24;
          cs &= 0xffffff;
          cs = (fl & FMT__TRIPLET_COMMA)
                   ? ((fmt__uint32)(c - ((pr + cs) % (c + 1))))
                   : 0;
          while (pr > 0) {
            fmt__cb_buf_clamp(i, pr);
            pr -= i;
            if ((fl & FMT__TRIPLET_COMMA) == 0) {
              while (i) {
                if ((((fmt__uintptr)bf) & 3) == 0) break;
                *bf++ = '0';
                --i;
              }
              while (i >= 4) {
                *(fmt__uint32 *)bf = 0x30303030;
                bf += 4;
                i -= 4;
              }
            }
            while (i) {
              if ((fl & FMT__TRIPLET_COMMA) && (cs++ == c)) {
                cs = 0;
                *bf++ = fmt__comma;
              } else
                *bf++ = '0';
              --i;
            }
            fmt__chk_cb_buf(1);
          }
        }

        // copy leader if there is still one
        sn = lead + 1;
        while (lead[0]) {
          fmt__int32 i;
          fmt__cb_buf_clamp(i, lead[0]);
          lead[0] -= (char)i;
          while (i) {
            *bf++ = *sn++;
            --i;
          }
          fmt__chk_cb_buf(1);
        }

        // copy the string
        n = l;
        while (n) {
          fmt__int32 i;
          fmt__cb_buf_clamp(i, n);
          n -= i;
          FMT__UNALIGNED(while (i >= 4) {
            *(fmt__uint32 volatile *)bf = *(fmt__uint32 volatile *)s;
            bf += 4;
            s += 4;
            i -= 4;
          })
          while (i) {
            *bf++ = *s++;
            --i;
          }
          fmt__chk_cb_buf(1);
        }

        // copy trailing zeros
        while (tz) {
          fmt__int32 i;
          fmt__cb_buf_clamp(i, tz);
          tz -= i;
          while (i) {
            if ((((fmt__uintptr)bf) & 3) == 0) break;
            *bf++ = '0';
            --i;
          }
          while (i >= 4) {
            *(fmt__uint32 *)bf = 0x30303030;
            bf += 4;
            i -= 4;
          }
          while (i) {
            *bf++ = '0';
            --i;
          }
          fmt__chk_cb_buf(1);
        }

        // copy tail if there is one
        sn = tail + 1;
        while (tail[0]) {
          fmt__int32 i;
          fmt__cb_buf_clamp(i, tail[0]);
          tail[0] -= (char)i;
          while (i) {
            *bf++ = *sn++;
            --i;
          }
          fmt__chk_cb_buf(1);
        }

        // handle the left justify
        if (fl & FMT__LEFTJUST)
          if (fw > 0) {
            while (fw) {
              fmt__int32 i;
              fmt__cb_buf_clamp(i, fw);
              fw -= i;
              while (i) {
                if ((((fmt__uintptr)bf) & 3) == 0) break;
                *bf++ = ' ';
                --i;
              }
              while (i >= 4) {
                *(fmt__uint32 *)bf = 0x20202020;
                bf += 4;
                i -= 4;
              }
              while (i--) *bf++ = ' ';
              fmt__chk_cb_buf(1);
            }
          }
        break;

      default:  // unknown, just copy code
        s = num + FMT__NUMSZ - 1;
        *s = f[0];
        l = 1;
        fw = fl = 0;
        lead[0] = 0;
        tail[0] = 0;
        pr = 0;
        dp = 0;
        cs = 0;
        goto scopy;
    }
    ++f;
  }
endfmt:

  if (!callback)
    *bf = 0;
  else
    fmt__flush_cb();

done:
  return tlen + (int)(bf - buf);
}

// cleanup
#undef FMT__LEFTJUST
#undef FMT__LEADINGPLUS
#undef FMT__LEADINGSPACE
#undef FMT__LEADING_0X
#undef FMT__LEADINGZERO
#undef FMT__INTMAX
#undef FMT__TRIPLET_COMMA
#undef FMT__NEGATIVE
#undef FMT__METRIC_SUFFIX
#undef FMT__NUMSZ
#undef fmt__chk_cb_bufL
#undef fmt__chk_cb_buf
#undef fmt__flush_cb
#undef fmt__cb_buf_clamp

// ============================================================================
//   wrapper functions

static int
fmts(char *buf, char const *fmt, ...) {
  int result;
  va_list va;
  va_start(va, fmt);
  result = fmtvscb(0, 0, buf, fmt, va);
  va_end(va);
  return result;
}

typedef struct fmt__context {
  char *buf;
  int count;
  int length;
  char tmp[FMT_MIN];
} fmt__context;

static char *
fmt__clamp_callback(const char *buf, void *user, int len) {
  fmt__context *c = (fmt__context *)user;
  c->length += len;

  if (len > c->count) len = c->count;

  if (len) {
    if (buf != c->buf) {
      const char *s, *se;
      char *d;
      d = c->buf;
      s = buf;
      se = buf + len;
      do {
        *d++ = *s++;
      } while (s < se);
    }
    c->buf += len;
    c->count -= len;
  }
  if (c->count <= 0) return c->tmp;
  // go direct into buffer if you can
  return (c->count >= FMT_MIN) ? c->buf : c->tmp;
}
static char *
fmt__count_clamp_callback(const char *buf, void *user, int len) {
  fmt__context *c = (fmt__context *)user;
  (void)sizeof(buf);

  c->length += len;
  return c->tmp;  // go direct into buffer if you can
}
static int
fmtvsn(char *buf, int count, char const *fmt, va_list va) {
  fmt__context c;
  if ((count == 0) && !buf) {
    c.length = 0;
    fmtvscb(fmt__count_clamp_callback, &c, c.tmp, fmt, va);
  } else {
    int l;

    c.buf = buf;
    c.count = count;
    c.length = 0;
    fmtvscb(fmt__clamp_callback, &c, fmt__clamp_callback(0, &c, 0), fmt, va);

    // zero-terminate
    l = (int)(c.buf - buf);
    if (l >= count)  // should never be greater, only equal (or less) than count
      l = count - 1;
    buf[l] = 0;
  }
  return c.length;
}
static int
fmtsn(char *buf, int count, char const *fmt, ...) {
  int result;
  va_list va;
  va_start(va, fmt);
  result = fmtvsn(buf, count, fmt, va);
  va_end(va);
  return result;
}

static int
fmtvs(char *buf, char const *fmt, va_list va) {
  return fmtvscb(0, 0, buf, fmt, va);
}

// =======================================================================
//   low level float utility functions

#ifndef FMT_NOFLOAT

// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
#define FMT__COPYFP(dest, src)                                           \
  {                                                                      \
    int cn;                                                              \
    for (cn = 0; cn < 8; cn++) ((char *)&dest)[cn] = ((char *)&src)[cn]; \
  }

// get float info
static fmt__int32
fmt__real_to_parts(fmt__int64 *bits, fmt__int32 *expo, double value) {
  double d;
  fmt__int64 b = 0;

  // load value and round at the frac_digits
  d = value;

  FMT__COPYFP(b, d);

  *bits = b & ((((fmt__uint64)1) << 52) - 1);
  *expo = (fmt__int32)(((b >> 52) & 2047) - 1023);

  return (fmt__int32)((fmt__uint64)b >> 63);
}

static double const fmt__bot[23] = {
    1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007,
    1e+008, 1e+009, 1e+010, 1e+011, 1e+012, 1e+013, 1e+014, 1e+015,
    1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022};
static double const fmt__negbot[22] = {
    1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008,
    1e-009, 1e-010, 1e-011, 1e-012, 1e-013, 1e-014, 1e-015, 1e-016,
    1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022};
static double const fmt__negboterr[22] = {
    -5.551115123125783e-018,  -2.0816681711721684e-019,
    -2.0816681711721686e-020, -4.7921736023859299e-021,
    -8.1803053914031305e-022, 4.5251888174113741e-023,
    4.5251888174113739e-024,  -2.0922560830128471e-025,
    -6.2281591457779853e-026, -3.6432197315497743e-027,
    6.0503030718060191e-028,  2.0113352370744385e-029,
    -3.0373745563400371e-030, 1.1806906454401013e-032,
    -7.7705399876661076e-032, 2.0902213275965398e-033,
    -7.1542424054621921e-034, -7.1542424054621926e-035,
    2.4754073164739869e-036,  5.4846728545790429e-037,
    9.2462547772103625e-038,  -4.8596774326570872e-039};
static double const fmt__top[13] = {1e+023, 1e+046, 1e+069, 1e+092, 1e+115,
                                    1e+138, 1e+161, 1e+184, 1e+207, 1e+230,
                                    1e+253, 1e+276, 1e+299};
static double const fmt__negtop[13] = {1e-023, 1e-046, 1e-069, 1e-092, 1e-115,
                                       1e-138, 1e-161, 1e-184, 1e-207, 1e-230,
                                       1e-253, 1e-276, 1e-299};
static double const fmt__toperr[13] = {8388608,
                                       6.8601809640529717e+028,
                                       -7.253143638152921e+052,
                                       -4.3377296974619174e+075,
                                       -1.5559416129466825e+098,
                                       -3.2841562489204913e+121,
                                       -3.7745893248228135e+144,
                                       -1.7356668416969134e+167,
                                       -3.8893577551088374e+190,
                                       -9.9566444326005119e+213,
                                       6.3641293062232429e+236,
                                       -5.2069140800249813e+259,
                                       -5.2504760255204387e+282};
static double const fmt__negtoperr[13] = {
    3.9565301985100693e-040,  -2.299904345391321e-063,
    3.6506201437945798e-086,  1.1875228833981544e-109,
    -5.0644902316928607e-132, -6.7156837247865426e-155,
    -2.812077463003139e-178,  -5.7778912386589953e-201,
    7.4997100559334532e-224,  -4.6439668915134491e-247,
    -6.3691100762962136e-270, -9.436808465446358e-293,
    8.0970921678014997e-317};

#if defined(_MSC_VER) && (_MSC_VER <= 1200)
static fmt__uint64 const fmt__powten[20] = {1,
                                            10,
                                            100,
                                            1000,
                                            10000,
                                            100000,
                                            1000000,
                                            10000000,
                                            100000000,
                                            1000000000,
                                            10000000000,
                                            100000000000,
                                            1000000000000,
                                            10000000000000,
                                            100000000000000,
                                            1000000000000000,
                                            10000000000000000,
                                            100000000000000000,
                                            1000000000000000000,
                                            10000000000000000000U};
#define fmt__tento19th ((fmt__uint64)1000000000000000000)
#else
static fmt__uint64 const fmt__powten[20] = {1,
                                            10,
                                            100,
                                            1000,
                                            10000,
                                            100000,
                                            1000000,
                                            10000000,
                                            100000000,
                                            1000000000,
                                            10000000000ULL,
                                            100000000000ULL,
                                            1000000000000ULL,
                                            10000000000000ULL,
                                            100000000000000ULL,
                                            1000000000000000ULL,
                                            10000000000000000ULL,
                                            100000000000000000ULL,
                                            1000000000000000000ULL,
                                            10000000000000000000ULL};
#define fmt__tento19th (1000000000000000000ULL)
#endif

#define fmt__ddmulthi(oh, ol, xh, yh)                            \
  {                                                              \
    double ahi = 0, alo, bhi = 0, blo;                           \
    fmt__int64 bt = 0;                                           \
    oh = xh * yh;                                                \
    FMT__COPYFP(bt, xh);                                         \
    bt &= ((~(fmt__uint64)0) << 27);                             \
    FMT__COPYFP(ahi, bt);                                        \
    alo = xh - ahi;                                              \
    FMT__COPYFP(bt, yh);                                         \
    bt &= ((~(fmt__uint64)0) << 27);                             \
    FMT__COPYFP(bhi, bt);                                        \
    blo = yh - bhi;                                              \
    ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \
  }

#define fmt__ddtoS64(ob, xh, xl)        \
  {                                     \
    double ahi = 0, alo, vh, t;         \
    ob = (fmt__int64)ph;                \
    vh = (double)ob;                    \
    ahi = (xh - vh);                    \
    t = (ahi - xh);                     \
    alo = (xh - (ahi - t)) - (vh + t);  \
    ob += (fmt__int64)(ahi + alo + xl); \
  }

#define fmt__ddrenorm(oh, ol) \
  {                           \
    double s;                 \
    s = oh + ol;              \
    ol = ol - (s - oh);       \
    oh = s;                   \
  }

#define fmt__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);

#define fmt__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);

static void
fmt__raise_to_power10(double *ohi, double *olo, double d,
                      fmt__int32 power)  // power can be -323 to +350
{
  double ph, pl;
  if ((power >= 0) && (power <= 22)) {
    fmt__ddmulthi(ph, pl, d, fmt__bot[power]);
  } else {
    fmt__int32 e, et, eb;
    double p2h, p2l;

    e = power;
    if (power < 0) e = -e;
    et = (e * 0x2c9) >> 14; /* %23 */
    if (et > 13) et = 13;
    eb = e - (et * 23);

    ph = d;
    pl = 0.0;
    if (power < 0) {
      if (eb) {
        --eb;
        fmt__ddmulthi(ph, pl, d, fmt__negbot[eb]);
        fmt__ddmultlos(ph, pl, d, fmt__negboterr[eb]);
      }
      if (et) {
        fmt__ddrenorm(ph, pl);
        --et;
        fmt__ddmulthi(p2h, p2l, ph, fmt__negtop[et]);
        fmt__ddmultlo(p2h, p2l, ph, pl, fmt__negtop[et], fmt__negtoperr[et]);
        ph = p2h;
        pl = p2l;
      }
    } else {
      if (eb) {
        e = eb;
        if (eb > 22) eb = 22;
        e -= eb;
        fmt__ddmulthi(ph, pl, d, fmt__bot[eb]);
        if (e) {
          fmt__ddrenorm(ph, pl);
          fmt__ddmulthi(p2h, p2l, ph, fmt__bot[e]);
          fmt__ddmultlos(p2h, p2l, fmt__bot[e], pl);
          ph = p2h;
          pl = p2l;
        }
      }
      if (et) {
        fmt__ddrenorm(ph, pl);
        --et;
        fmt__ddmulthi(p2h, p2l, ph, fmt__top[et]);
        fmt__ddmultlo(p2h, p2l, ph, pl, fmt__top[et], fmt__toperr[et]);
        ph = p2h;
        pl = p2l;
      }
    }
  }
  fmt__ddrenorm(ph, pl);
  *ohi = ph;
  *olo = pl;
}

// given a float value, returns the significant bits in bits, and the position
// of the
//   decimal point in decimal_pos.  +/-INF and NAN are specified by special
//   values returned in the decimal_pos parameter.
// frac_digits is absolute normally, but if you want from first significant
// digits (got %g and %e), or in 0x80000000
static fmt__int32
fmt__real_to_str(char const **start, fmt__uint32 *len, char *out,
                 fmt__int32 *decimal_pos, double value,
                 fmt__uint32 frac_digits) {
  double d;
  fmt__int64 bits = 0;
  fmt__int32 expo, e, ng, tens;

  d = value;
  FMT__COPYFP(bits, d);
  expo = (fmt__int32)((bits >> 52) & 2047);
  ng = (fmt__int32)((fmt__uint64)bits >> 63);
  if (ng) d = -d;

  if (expo == 2047)  // is nan or inf?
  {
    *start = (bits & ((((fmt__uint64)1) << 52) - 1)) ? "NaN" : "Inf";
    *decimal_pos = FMT__SPECIAL;
    *len = 3;
    return ng;
  }

  if (expo == 0)  // is zero or denormal
  {
    if (((fmt__uint64)bits << 1) == 0)  // do zero
    {
      *decimal_pos = 1;
      *start = out;
      out[0] = '0';
      *len = 1;
      return ng;
    }
    // find the right expo for denormals
    {
      fmt__int64 v = ((fmt__uint64)1) << 51;
      while ((bits & v) == 0) {
        --expo;
        v >>= 1;
      }
    }
  }

  // find the decimal exponent as well as the decimal bits of the value
  {
    double ph, pl;

    // log10 estimate - very specifically tweaked to hit or undershoot by no
    // more than 1 of log10 of all expos 1..2046
    tens = expo - 1023;
    tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);

    // move the significant bits into position and stick them into an int
    fmt__raise_to_power10(&ph, &pl, d, 18 - tens);

    // get full as much precision from double-double as possible
    fmt__ddtoS64(bits, ph, pl);

    // check if we undershot
    if (((fmt__uint64)bits) >= fmt__tento19th) ++tens;
  }

  // now do the rounding in integer land
  frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1)
                                           : (tens + frac_digits);
  if ((frac_digits < 24)) {
    fmt__uint32 dg = 1;
    if ((fmt__uint64)bits >= fmt__powten[9]) dg = 10;
    while ((fmt__uint64)bits >= fmt__powten[dg]) {
      ++dg;
      if (dg == 20) goto noround;
    }
    if (frac_digits < dg) {
      fmt__uint64 r;
      // add 0.5 at the right position and round
      e = dg - frac_digits;
      if ((fmt__uint32)e >= 24) goto noround;
      r = fmt__powten[e];
      bits = bits + (r / 2);
      if ((fmt__uint64)bits >= fmt__powten[dg]) ++tens;
      bits /= r;
    }
  noround:;
  }

  // kill long trailing runs of zeros
  if (bits) {
    fmt__uint32 n;
    for (;;) {
      if (bits <= 0xffffffff) break;
      if (bits % 1000) goto donez;
      bits /= 1000;
    }
    n = (fmt__uint32)bits;
    while ((n % 1000) == 0) n /= 1000;
    bits = n;
  donez:;
  }

  // convert to string
  out += 64;
  e = 0;
  for (;;) {
    fmt__uint32 n;
    char *o = out - 8;
    // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it,
    // constant denomiators be damned)
    if (bits >= 100000000) {
      n = (fmt__uint32)(bits % 100000000);
      bits /= 100000000;
    } else {
      n = (fmt__uint32)bits;
      bits = 0;
    }
    while (n) {
      out -= 2;
      *(fmt__uint16 *)out = *(fmt__uint16 *)&fmt__digitpair.pair[(n % 100) * 2];
      n /= 100;
      e += 2;
    }
    if (bits == 0) {
      if ((e) && (out[0] == '0')) {
        ++out;
        --e;
      }
      break;
    }
    while (out != o) {
      *--out = '0';
      ++e;
    }
  }

  *decimal_pos = tens;
  *start = out;
  *len = e;
  return ng;
}

#undef fmt__ddmulthi
#undef fmt__ddrenorm
#undef fmt__ddmultlo
#undef fmt__ddmultlos
#undef FMT__SPECIAL
#undef FMT__COPYFP

#endif  // FMT_NOFLOAT

// clean up
#undef fmt__uint16
#undef fmt__uint32
#undef fmt__int32
#undef fmt__uint64
#undef fmt__int64
#undef FMT__UNALIGNED

#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif