MIME Type Reference — C source
A searchable map of common file extensions to their MIME types - text, image, audio, video, application, font, and multipart. Look up by extension or by type, filter by category, and copy the exact Content-Type string for any format.
This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.
/* mime-types — C99 port of the curated extension ↔ MIME table, ported from
* src/lib/mime-types.ts. Display source for CosmoDev's polyglot tool pages.
*
* The table is a static array of 71 entries grouped by the seven top-level
* media types (RFC 2046): text, image, audio, video, application, font, and
* multipart. Each entry's `type` always equals the top-level segment of its
* own `mime`, so the category filter and the Content-Type never disagree.
*
* C has no lists-at-runtime without allocation, so the lookups write their
* matches into a caller-provided buffer and return the match count — the
* idiomatic C shape for "return a filtered list".
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
typedef enum {
MIME_TEXT = 0,
MIME_IMAGE,
MIME_AUDIO,
MIME_VIDEO,
MIME_APPLICATION,
MIME_FONT,
MIME_MULTIPART,
} MimeCategory;
typedef struct {
const char *ext; /* lowercase, no leading dot (e.g. "json") */
const char *mime; /* full Content-Type (e.g. "application/json") */
MimeCategory type; /* always the top-level segment of `mime` */
const char *name; /* human-readable format name (e.g. "JSON") */
} MimeEntry;
/* Display order of the categories (used by the UI filter). */
static const MimeCategory MIME_CATEGORIES[] = {
MIME_TEXT, MIME_IMAGE, MIME_AUDIO, MIME_VIDEO,
MIME_APPLICATION, MIME_FONT, MIME_MULTIPART,
};
static const char *const CATEGORY_LABELS[] = {
"text", "image", "audio", "video", "application", "font", "multipart",
};
/* The curated extension ↔ MIME table. One entry is one (extension, type)
* pair, so an extension with several MIME types (e.g. .js) appears more than
* once, and a MIME with several extensions (e.g. image/jpeg) appears once per
* extension. Values follow IANA / MDN conventions. */
static const MimeEntry MIME[] = {
/* ── text ─────────────────────────────────────────────────────────── */
{"txt", "text/plain", MIME_TEXT, "Plain text"},
{"html", "text/html", MIME_TEXT, "HTML document"},
{"htm", "text/html", MIME_TEXT, "HTML document"},
{"xhtml", "application/xhtml+xml", MIME_APPLICATION, "XHTML"},
{"css", "text/css", MIME_TEXT, "CSS stylesheet"},
{"csv", "text/csv", MIME_TEXT, "Comma-separated values"},
{"xml", "application/xml", MIME_APPLICATION, "XML document"},
{"js", "text/javascript", MIME_TEXT, "JavaScript"},
{"js", "application/javascript", MIME_APPLICATION, "JavaScript (legacy)"},
{"mjs", "text/javascript", MIME_TEXT, "JavaScript module"},
{"json", "application/json", MIME_APPLICATION, "JSON"},
{"md", "text/markdown", MIME_TEXT, "Markdown"},
{"yaml", "text/yaml", MIME_TEXT, "YAML"},
{"yml", "text/yaml", MIME_TEXT, "YAML"},
{"ics", "text/calendar", MIME_TEXT, "iCalendar"},
{"rtf", "application/rtf", MIME_APPLICATION, "Rich text"},
{"tsv", "text/tab-separated-values", MIME_TEXT, "Tab-separated values"},
/* ── image ────────────────────────────────────────────────────────── */
{"jpg", "image/jpeg", MIME_IMAGE, "JPEG image"},
{"jpeg", "image/jpeg", MIME_IMAGE, "JPEG image"},
{"png", "image/png", MIME_IMAGE, "PNG image"},
{"gif", "image/gif", MIME_IMAGE, "GIF image"},
{"webp", "image/webp", MIME_IMAGE, "WebP image"},
{"avif", "image/avif", MIME_IMAGE, "AVIF image"},
{"svg", "image/svg+xml", MIME_IMAGE, "SVG image"},
{"bmp", "image/bmp", MIME_IMAGE, "Bitmap image"},
{"ico", "image/x-icon", MIME_IMAGE, "Icon"},
{"tif", "image/tiff", MIME_IMAGE, "TIFF image"},
{"tiff", "image/tiff", MIME_IMAGE, "TIFF image"},
{"heic", "image/heic", MIME_IMAGE, "HEIC image"},
/* ── audio ────────────────────────────────────────────────────────── */
{"mp3", "audio/mpeg", MIME_AUDIO, "MP3 audio"},
{"wav", "audio/wav", MIME_AUDIO, "WAV audio"},
{"ogg", "audio/ogg", MIME_AUDIO, "Ogg audio"},
{"oga", "audio/ogg", MIME_AUDIO, "Ogg audio"},
{"flac", "audio/flac", MIME_AUDIO, "FLAC audio"},
{"aac", "audio/aac", MIME_AUDIO, "AAC audio"},
{"m4a", "audio/mp4", MIME_AUDIO, "M4A audio"},
{"weba", "audio/webm", MIME_AUDIO, "WebM audio"},
{"mid", "audio/midi", MIME_AUDIO, "MIDI"},
{"midi", "audio/midi", MIME_AUDIO, "MIDI"},
/* ── video ────────────────────────────────────────────────────────── */
{"mp4", "video/mp4", MIME_VIDEO, "MP4 video"},
{"webm", "video/webm", MIME_VIDEO, "WebM video"},
{"avi", "video/x-msvideo", MIME_VIDEO, "AVI video"},
{"mov", "video/quicktime", MIME_VIDEO, "QuickTime video"},
{"mkv", "video/x-matroska", MIME_VIDEO, "Matroska video"},
{"mpeg", "video/mpeg", MIME_VIDEO, "MPEG video"},
{"mpg", "video/mpeg", MIME_VIDEO, "MPEG video"},
{"ogv", "video/ogg", MIME_VIDEO, "Ogg video"},
{"m4v", "video/mp4", MIME_VIDEO, "M4V video"},
/* ── application ──────────────────────────────────────────────────── */
{"pdf", "application/pdf", MIME_APPLICATION, "PDF document"},
{"zip", "application/zip", MIME_APPLICATION, "ZIP archive"},
{"gz", "application/gzip", MIME_APPLICATION, "Gzip archive"},
{"tar", "application/x-tar", MIME_APPLICATION, "Tar archive"},
{"rar", "application/vnd.rar", MIME_APPLICATION, "RAR archive"},
{"7z", "application/x-7z-compressed", MIME_APPLICATION, "7-Zip archive"},
{"bin", "application/octet-stream", MIME_APPLICATION, "Binary data"},
{"exe", "application/x-msdownload", MIME_APPLICATION, "Windows executable"},
{"jar", "application/java-archive", MIME_APPLICATION, "Java archive"},
{"wasm", "application/wasm", MIME_APPLICATION, "WebAssembly"},
{"doc", "application/msword", MIME_APPLICATION, "Word document"},
{"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", MIME_APPLICATION, "Word document (OOXML)"},
{"xls", "application/vnd.ms-excel", MIME_APPLICATION, "Excel spreadsheet"},
{"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", MIME_APPLICATION, "Excel spreadsheet (OOXML)"},
{"ppt", "application/vnd.ms-powerpoint", MIME_APPLICATION, "PowerPoint"},
{"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation", MIME_APPLICATION, "PowerPoint (OOXML)"},
/* ── font ─────────────────────────────────────────────────────────── */
{"woff", "font/woff", MIME_FONT, "WOFF font"},
{"woff2", "font/woff2", MIME_FONT, "WOFF2 font"},
{"ttf", "font/ttf", MIME_FONT, "TrueType font"},
{"otf", "font/otf", MIME_FONT, "OpenType font"},
{"eot", "application/vnd.ms-fontobject", MIME_APPLICATION, "Embedded OpenType"},
/* ── multipart ────────────────────────────────────────────────────── */
{"mht", "multipart/related", MIME_MULTIPART, "MHTML web archive"},
{"mhtml", "multipart/related", MIME_MULTIPART, "MHTML web archive"},
};
#define MIME_COUNT (sizeof MIME / sizeof MIME[0])
/* Trim surrounding whitespace and lowercase in place into `out` (at most
* cap-1 chars plus NUL). Shared by normalize_ext and the filter query. */
static char *canon(const char *s, char *out, size_t cap)
{
size_t n = 0;
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') s++;
for (; *s && n + 1 < cap; s++)
out[n++] = (char)tolower((unsigned char)*s);
while (n > 0 && (out[n - 1] == ' ' || out[n - 1] == '\t' ||
out[n - 1] == '\r' || out[n - 1] == '\n'))
n--;
out[n] = '\0';
return out;
}
/* Lowercase an extension and drop a leading dot so ".JSON" == "json". */
char *normalize_ext(const char *ext, char *out, size_t cap)
{
char *e = canon(ext, out, cap);
char *p = e;
while (*p == '.') p++;
if (p != e) memmove(e, p, strlen(p) + 1);
return e;
}
/* Every entry whose extension matches. The dot is optional and matching is
* case-insensitive, so ".js", "JS", and "js" are equivalent. Matches are
* copied into `out` (at most `cap`); returns the match count. */
size_t lookup_by_ext(const char *ext, MimeEntry *out, size_t cap)
{
char e[64];
normalize_ext(ext, e, sizeof e);
if (e[0] == '\0') return 0;
size_t n = 0;
for (size_t i = 0; i < MIME_COUNT; i++)
if (strcmp(MIME[i].ext, e) == 0 && n < cap) out[n++] = MIME[i];
return n;
}
/* Every entry whose MIME matches, case-insensitively, tolerating surrounding
* whitespace. One MIME commonly maps to several extensions (image/jpeg →
* jpg, jpeg), so all are returned. */
size_t lookup_by_mime(const char *mime, MimeEntry *out, size_t cap)
{
char m[128];
canon(mime, m, sizeof m);
if (m[0] == '\0') return 0;
size_t n = 0;
for (size_t i = 0; i < MIME_COUNT; i++)
if (strcasecmp(MIME[i].mime, m) == 0 && n < cap) out[n++] = MIME[i];
return n;
}
/* Optional constraints: has_type == 0 means "any category" (mirrors
* `undefined` in the TypeScript source); an empty query is ignored. */
typedef struct {
int has_type;
MimeCategory type;
char query[64]; /* raw; lowercased + trimmed by mime_filter */
} MimeFilter;
/* Case-insensitive substring test against a pre-lowercased `needle`. */
static int ci_contains(const char *hay, const char *needle)
{
if (needle[0] == '\0') return 1;
size_t nl = strlen(needle);
for (const char *p = hay; *p; p++) {
size_t i = 0;
while (i < nl && p[i] &&
tolower((unsigned char)p[i]) == (unsigned char)needle[i])
i++;
if (i == nl) return 1;
}
return 0;
}
/* Filter the full table by category and/or a free-text query matched against
* ext, mime, and name. An unknown category value yields nothing. */
size_t mime_filter(const MimeFilter *opts, MimeEntry *out, size_t cap)
{
char q[64];
canon(opts->query, q, sizeof q);
size_t n = 0;
for (size_t i = 0; i < MIME_COUNT; i++) {
const MimeEntry *e = &MIME[i];
if (opts->has_type && e->type != opts->type) continue;
if (q[0] != '\0' &&
!ci_contains(e->ext, q) && !ci_contains(e->mime, q) &&
!ci_contains(e->name, q))
continue;
if (n < cap) out[n++] = MIME[i];
}
return n;
}
int main(void)
{
MimeEntry hits[8];
size_t n = lookup_by_ext(".JSON", hits, 8);
if (n > 0) printf(".JSON -> %s (%s)\n", hits[0].mime, hits[0].name);
n = lookup_by_mime("IMAGE/JPEG", hits, 8);
for (size_t i = 0; i < n; i++)
printf("image/jpeg -> .%s\n", hits[i].ext);
MimeFilter f = {0};
strcpy(f.query, "font");
printf("query \"font\": %zu of %zu entries\n",
mime_filter(&f, hits, 8), (size_t)MIME_COUNT);
return 0;
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →