diff options
Diffstat (limited to 'module/menu.c')
-rw-r--r-- | module/menu.c | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/module/menu.c b/module/menu.c new file mode 100644 index 0000000..f42f505 --- /dev/null +++ b/module/menu.c @@ -0,0 +1,618 @@ +/* Encoding menu */ + +#include <ctype.h> +#ifdef MTEST +#include <stdio.h> +#endif +#include <stdlib.h> +#include <string.h> + +#include <unicode/charsets.h> +#include <unicode/encoding.h> + +#include "module.h" + +#define menu_HEADER_SIZE (28) +#define menu_ENTRY_SIZE (24) + +typedef struct _menu { + char head[menu_HEADER_SIZE]; /* We don't care about this */ + struct { + int menu_flags; + int *sub_menu; + int icon_flags; + struct { /* Only handle indirected text */ + char *text; + char *validation; + int size; + } indirected_text; + } entries[1]; +} wimp_menu; + +struct menu_desc { + char *title; + int n_entries; + const char *entries[1]; +}; + +#define menudesc(N) \ + struct { \ + char *title; \ + int n_entries; \ + char *entries[(N)]; \ + } + +/* Menu descriptions. + * A number of magic characters are permitted at the start of entry names: + * + * Character: Meaning: + * ^ Insert separator after this entry + * > Can open submenu, even if shaded + * {..} Has submenu, named ".." + * $ This entry is shaded + * + * Magic characters are examined in the order above. + * The submenu name is the title of the submenu. + * The first alphanumeric character is taken as the start of the entry name. + */ +static const char *val = ""; /* Validation string */ + +#define N_LATIN (30) +static const menudesc(N_LATIN) latin_menu = { + "Latin", N_LATIN, + { + "Western (ISO 8859-1: Latin-1)", + "Eastern (ISO 8859-2: Latin-2)", + "Southern (ISO 8859-3: Latin-3)", + "Nordic (ISO 8859-4: Latin-4)", + "Turkish (ISO 8859-9: Latin-5)", + "Nordic (ISO 8859-10: Latin-6)", + "Baltic rim (ISO 8859-13: Latin-7)", + "Celtic (ISO 8859-14: Latin-8)", + "^Western (ISO 8859-15: Latin-9)", + "Welsh (ISO-IR 182)", + "^Sami (ISO-IR 197)", + "Microsoft Latin-1 (CP1252)", + "Microsoft Latin-2 (CP1250)", + "Microsoft Baltic (CP1257)", + "^Microsoft Turkish (CP1254)", + "Apple Macintosh Roman", + "Apple Macintosh Croatian", + "Apple Macintosh Icelandic", + "Apple Macintosh Romanian", + "Apple Macintosh Turkish", + "^Apple Macintosh Central European", + "^Acorn Latin-1", + "DOS Latin-1 (CP850)", + "DOS Latin-2 (CP852)", + "DOS Baltic rim (CP775)", + "DOS Turkish (CP857)", + "DOS Portuguese (CP860)", + "DOS Icelandic (CP861)", + "DOS CanadaF (CP863)", + "DOS Nordic (CP865)", + } +}; + +#define N_ARABIC (4) +static const menudesc(N_ARABIC) arabic_menu = { + "Arabic", N_ARABIC, + { + "$ISO 8859-6", + "Microsoft Arabic (CP1256)", + "Apple Macintosh Arabic", + "DOS Arabic (CP864)", + } +}; + +#define N_CYRILLIC (10) +static const menudesc(N_CYRILLIC) cyrillic_menu = { + "Cyrillic", N_CYRILLIC, + { + "ISO 8859-5", + "KOI8-R", + "KOI8-RU", + "KOI8-T", + "^KOI8-U", + "^Microsoft Cyrillic (CP1251)", + "Apple Macintosh Cyrillic", + "^Apple Macintosh Ukrainian", + "DOS Cyrillic (CP855)", + "DOS Cyrillic Russian (CP866)", + } +}; + +#define N_GREEK (5) +static const menudesc(N_GREEK) greek_menu = { + "Greek", N_GREEK, + { + "ISO 8859-7", + "Microsoft Greek (CP1253)", + "Apple Macintosh Greek", + "DOS Greek (CP737)", + "DOS Greek2 (CP869)", + } +}; + +#define N_HEBREW (4) +static const menudesc(N_HEBREW) hebrew_menu = { + "Hebrew", N_HEBREW, + { + "ISO 8859-8", + "$Microsoft Hebrew (CP1255)", + "Apple Macintosh Hebrew", + "DOS Hebrew (CP862)", + } +}; + +#define N_CHINESE (3) +static const menudesc(N_CHINESE) chinese_menu = { + "Chinese", N_CHINESE, + { + "ISO 2022-CN", + "^GB 2312 (EUC-CN)", + "Big Five", + } +}; + +#define N_JAPANESE (3) +static const menudesc(N_JAPANESE) japanese_menu = { + "Japanese", N_JAPANESE, + { + "ISO 2022-JP", + "EUC-JP", + "Shift-JIS", + } +}; + +#define N_KOREAN (3) +static const menudesc(N_KOREAN) korean_menu = { + "Korean", N_KOREAN, + { + "ISO 2022-KR", + "EUC-KR", + "Johab", + } +}; + +#define N_THAI (3) +static const menudesc(N_THAI) thai_menu = { + "Thai", N_THAI, + { + "ISO 8859-11", + "Apple Macintosh Thai", + "DOS Thai (CP874)", + } +}; + +#define N_VIETNAMESE (1) +static const menudesc(N_VIETNAMESE) vietnamese_menu = { + "Vietnamese", N_VIETNAMESE, + { + "$Microsoft Vietnamese (CP1258)", + } +}; + +#define N_UNIVERSAL (4) +static const menudesc(N_UNIVERSAL) universal_menu = { + "Universal", N_UNIVERSAL, + { + "UTF-8 (ASCII-compatible)", + "UCS-2 / UTF-16 (16-bit)", + "^UCS-4 (31-bit)", + "ISO-2022", + } +}; + +#define N_ENC (11) +static const menudesc(N_ENC) enc_menu = { + "Encodings", N_ENC, + { + "^{Latin}Latin", + "{Arabic}Arabic", + "{Cyrillic}Cyrillic", + "{Greek}Greek", + "^{Hebrew}Hebrew", + "{Chinese}Chinese", + "{Japanese}Japanese", + "{Korean}Korean", + "{Thai}Thai", + "^>{Vietnamese}$Vietnamese", + "{Universal}Universal", + } +}; + +/* This struct is a lookup table between menu entries and charset numbers + * It is ordered as per the menus. */ +static const struct csmap { + short latin[N_LATIN]; + short arabic[N_ARABIC]; + short greek[N_GREEK]; + short hebrew[N_HEBREW]; + short cyrillic[N_CYRILLIC]; + short chinese[N_CHINESE]; + short japanese[N_JAPANESE]; + short korean[N_KOREAN]; + short thai[N_THAI]; + short vietnamese[N_VIETNAMESE]; + short universal[N_UNIVERSAL]; +} csmap = { + { csISOLatin1, csISOLatin2, csISOLatin3, csISOLatin4, csISOLatin5, + csISOLatin6, csISOLatin7, csISOLatin8, csISOLatin9, csWelsh, + csSami, csWindows1252, csWindows1250, csWindows1257, csWindows1254, + csMacintosh, 3019, 3022, 3023, 3025, csMacCentEuro, csAcornLatin1, + csPC850Multilingual, csPCp852, csPC775Baltic, csIBM857, csIBM860, + csIBM861, csIBM863, csIBM865 }, + { csISOLatinArabic, csWindows1256, 3018, csIBM864 }, + { csISOLatinGreek, csWindows1253, 3020, 3000, csIBM869 }, + { csISOLatinHebrew, csWindows1255, 3021, csPC862LatinHebrew }, + { csISOLatinCyrillic, csKOI8R, 3016, 3017, 2088, csWindows1251, + csMacCyrillic, csMacUkrainian, csIBM855, csIBM866 }, + { csISO2022CN, csGB2312, csBig5 }, + { csISO2022JP, csEUCPkdFmtJapanese, csShiftJIS }, + { csISO2022KR, csEUCKR, csJohab }, + { csISOLatinThai, 3024, 3004 }, + { csWindows1258 }, + { csUTF8, csUnicode11, csUCS4, csVenturaMath } +}; + +/* Sub menu lookup table - Must be sorted alphabetically */ +static const struct sub_menu { + char name[12]; + const struct menu_desc *desc; + const short *lut; +} sub_menus[] = { + { "Arabic", (const struct menu_desc *)&arabic_menu, + csmap.arabic }, + { "Chinese", (const struct menu_desc *)&chinese_menu, + csmap.chinese }, + { "Cyrillic", (const struct menu_desc *)&cyrillic_menu, + csmap.cyrillic }, + { "Greek", (const struct menu_desc *)&greek_menu, csmap.greek }, + { "Hebrew", (const struct menu_desc *)&hebrew_menu, + csmap.hebrew }, + { "Japanese", (const struct menu_desc *)&japanese_menu, + csmap.japanese }, + { "Korean", (const struct menu_desc *)&korean_menu, + csmap.korean }, + { "Latin", (const struct menu_desc *)&latin_menu, csmap.latin }, + { "Thai", (const struct menu_desc *)&thai_menu, csmap.thai }, + { "Universal", (const struct menu_desc *)&universal_menu, + csmap.universal }, + { "Vietnamese", (const struct menu_desc *)&vietnamese_menu, + csmap.vietnamese }, +}; +#define SUB_MENU_COUNT (sizeof(sub_menus) / sizeof(sub_menus[0])) + + + +#define MAX_SUBMENUS (16) /* Maximum number of submenus each menu can have */ + +#define MENU_COUNT_SIZE (0x00) +#define MENU_CREATE (0x01) +#define MENU_CLEAR_SELECTIONS (0x02) +/** + * Perform an operation on a menu + * + * \param d The description + * \param buf Location to write menu to + * \param parent Parent menu + * \param which Which parent entry this menu is linked from + * \param flags Flags word + * Bit: Meaning: + * 0 Create menu + * 1 Clear existing selections (charset != 0) + * \param charset Charset identifier of selected charset + * \param lut Selection lookup table + * \param data Location to write indirected data to + * \return Pointer to location after menu data + */ +static char *menu_op(const struct menu_desc *d, char *buf, + wimp_menu *parent, size_t which, size_t flags, + size_t charset, const short *lut, char **data) +{ + size_t e, top = 0; + struct { size_t e; const char *name; } submenus[MAX_SUBMENUS]; + char *bp = buf; + char *dp; + + if (data) + dp = *data; + + if (!buf && (flags & 0x02)) + return buf; + + if ((flags & MENU_CREATE)) { + /* copy menu title */ + strncpy(bp, d->title, 12); + bp += 12; + + /* colours */ + *bp++ = 7; *bp++ = 2; *bp++ = 7; *bp++ = 0; + + /* width, height, gap */ + *((int *)bp) = 200; bp += 4; + *((int *)bp) = 44; bp += 4; + *((int *)bp) = 0; bp += 4; + + memcpy(dp, val, strlen(val) + 1); + dp += strlen(val) + 1; + } else { + bp += menu_HEADER_SIZE; + dp += strlen(val) + 1; + } + + /* now the entries */ + for (e = 0; e != d->n_entries; e++) { + int menuf = 0, icon = (7 << 24) | 0x121; + const char *pos = 0; + + /* parse description string */ + for (pos = d->entries[e]; !isalnum(*pos); pos++) { + if (*pos == '^') + menuf |= 0x2; + else if (*pos == '>') + menuf |= 0x10; + else if (*pos == '{') { + if (top < MAX_SUBMENUS) { + submenus[top].e = e; + submenus[top++].name = pos+1; + } + while (*pos != '}') + pos++; + } + else if (*pos == '$') + icon |= (1<<22); + } + + if (e == d->n_entries - 1) + /* last item */ + menuf |= 0x80; + + if (charset != 0 && lut && lut[e] == charset) { + menuf |= 0x1; + if (parent) + parent->entries[which].menu_flags |= 0x1; + } + else + menuf &= ~0x1; + + if (flags & MENU_CLEAR_SELECTIONS) { + ((wimp_menu *)buf)->entries[e].menu_flags = menuf; + } + + if ((flags & MENU_CREATE)) { + *((int *)bp) = menuf; bp += 4; + *((int *)bp) = -1; bp += 4; + *((int *)bp) = icon; bp += 4; + *((int *)bp) = (int)(dp); bp += 4; + *((int *)bp) = (int)(*data); bp += 4; + *((int *)bp) = strlen(pos) + 1; bp += 4; + + memcpy(dp, pos, strlen(pos) + 1); + dp += strlen(pos) + 1; + } else { + bp += menu_ENTRY_SIZE; + dp += strlen(pos) + 1; + } + } + + /* fixup parent's pointer to this menu */ + if (parent && (flags & MENU_CREATE)) + parent->entries[which].sub_menu = (int *)buf; + + /* and recurse */ + for (e = 0; e < top; e++) { + struct sub_menu *s; + size_t len = (strchr(submenus[e].name, '}') - + submenus[e].name); + char child[len + 1]; + + strncpy(child, submenus[e].name, len); + child[len] = '\0'; + + s = bsearch(child, sub_menus, SUB_MENU_COUNT, + sizeof(sub_menus[0]), + (int (*)(const void *, const void *))strcmp); + if (s) + bp = menu_op(s->desc, bp, (wimp_menu *)buf, + submenus[e].e, flags, + charset, s->lut, &dp); + } + + if (data) + (*data) = dp; + + return bp; +} + +/** + * Iconv_CreateMenu SWI - Creates a menu structure of supported encodings + * + * \param flags Flags word - all reserved + * \param buf Pointer to buffer in which to store menu data, or NULL to + * read required buffer size. + * \param len Length of buffer, in bytes + * \param selected Pointer to name of selected encoding, or NULL if none + * \param data Pointer to buffer in which to store indirected data, or NULL + * to read required buffer size. + * \param dlen Pointer to length of data buffer, in bytes + * \return length of data written in buffer, or 0 if insufficient space + */ +size_t iconv_createmenu(size_t flags, char *buf, size_t len, + const char *selected, char *data, size_t *dlen) +{ + size_t reqlen, datalen; + char *bp = buf, *dp = NULL; + int sel = 0; + struct canon *c; + + UNUSED(flags); + + /* sanity check arguments */ + if ((!buf && data) || !dlen) + return 0; + + /* get required size */ + reqlen = (int)menu_op((const struct menu_desc *)&enc_menu, 0, + NULL, 0, MENU_COUNT_SIZE, 0, NULL, &dp); + + datalen = (size_t)dp; + + /* buffer length requested, so return it */ + if (!buf) { + *dlen = datalen; + return reqlen; + } + + /* insufficient room in buffer */ + if (reqlen > len) + return 0; + + /* Selected entry? */ + if (selected) { + sel = iconv_eightbit_number_from_name(selected) & ~(1<<30); + + if (!sel) { + c = alias_canonicalise(selected); + if (c) { + sel = encoding_number_from_name(c->name); + } + } + } + +#ifdef TEST + printf("selected: '%s' : %d\n", selected, sel); +#endif + + dp = data; + bp = menu_op((const struct menu_desc *)&enc_menu, buf, + NULL, 0, MENU_CREATE, sel, NULL, &dp); + + (*dlen) = datalen; + + return reqlen; +} + +/** + * Iconv_DecodeMenu SWI - Decodes a selection in a menu generated by + * Iconv_CreateMenu. + * + * \param flags Bitfield of flags - all reserved + * \param menu Menu definition + * \param selections Menu selections + * \param buf Pointer to output buffer, or NULL to read required length + * \param buflen Length of output buffer + * \return Required length of output buffer, or 0 if no selections + */ +size_t iconv_decodemenu(size_t flags, void *menu, int *selections, + char *buf, size_t buflen) +{ + const char *text, *t; + size_t len; + struct sub_menu *s; + + UNUSED(flags); + + if (!menu || !selections) + return 0; + + /* out of range */ + if (selections[0] == -1 || selections[0] >= enc_menu.n_entries) + return 0; + + /* Grab sub menu name */ + t = strchr(enc_menu.entries[selections[0]], '{') + 1; + len = (strchr(t, '}') - t); + + /* copy to temporary buffer */ + char child[len + 1]; + strncpy(child, t, len); + child[len] = '\0'; + + /* look for submenu */ + s = bsearch(child, sub_menus, SUB_MENU_COUNT, sizeof(sub_menus[0]), + (int (*)(const void *, const void *))strcmp); + if (!s) + return 0; + + if (selections[1] == -1 || selections[1] >= s->desc->n_entries) + return 0; + + /* lookup encoding name from number */ + text = mibenum_to_name(s->lut[selections[1]]); + + /* not found */ + if (!text) + return 0; + +#ifdef MTEST + printf("%p : '%s'\n", text, text); +#endif + + if (buf && buflen < strlen(text) + 1) + /* insufficient buffer space */ + return 0; + + + if (buf) { + strcpy(buf, text); + buf[strlen(text)] = '\0'; + } + + menu_op((const struct menu_desc *)&enc_menu, menu, NULL, 0, + MENU_CLEAR_SELECTIONS, s->lut[selections[1]], + NULL, NULL); + + return strlen(text) + 1; +} + + +#ifdef MTEST +int main(void) +{ + int len, slen, dlen; + char *buf, *dbuf, *selected; + int selection[3] = { 0, 5, -1}; + + if (!create_alias_data("Unicode:Files.Aliases")) + return 1; + + + len = iconv_createmenu(0, 0, 0, 0, 0, (size_t *)&dlen); + + buf = calloc(len, sizeof(char)); + if (!buf) + return 1; + + dbuf = calloc(dlen, sizeof(char)); + if (!dbuf) + return 1; + + printf("%p: %d\n", buf, iconv_createmenu(0, buf, len, "UTF-16", + dbuf, (size_t *)&dlen)); + + FILE *fp = fopen("$.dump", "w"); + fwrite(buf, len, sizeof(char), fp); + fclose(fp); + + fp = fopen("$.dump1", "w"); + fwrite(dbuf, dlen, sizeof(char), fp); + fclose(fp); + + slen = iconv_decodemenu(0, (wimp_menu*)buf, selection, 0, 0); + + selected = calloc(slen, sizeof(char)); + if (!selected) + return 1; + + printf("%p: %d\n", selected, iconv_decodemenu(0, (wimp_menu*)buf, + selection, selected, slen)); + + printf("'%s'\n", selected); + + free_alias_data(); + + return 0; +} +#endif |