18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Internationalization implementation.  Includes definitions of English
38c2ecf20Sopenharmony_ci * string arrays, and the i18n pointer.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/slab.h>		/* For kmalloc. */
78c2ecf20Sopenharmony_ci#include <linux/ctype.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/string.h>
108c2ecf20Sopenharmony_ci#include "speakup.h"
118c2ecf20Sopenharmony_ci#include "spk_priv.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic char *speakup_msgs[MSG_LAST_INDEX];
148c2ecf20Sopenharmony_cistatic char *speakup_default_msgs[MSG_LAST_INDEX] = {
158c2ecf20Sopenharmony_ci	[MSG_BLANK] = "blank",
168c2ecf20Sopenharmony_ci	[MSG_IAM_ALIVE] = "I'm aLive!",
178c2ecf20Sopenharmony_ci	[MSG_YOU_KILLED_SPEAKUP] = "You killed speakup!",
188c2ecf20Sopenharmony_ci	[MSG_HEY_THATS_BETTER] = "hey. That's better!",
198c2ecf20Sopenharmony_ci	[MSG_YOU_TURNED_ME_OFF] = "You turned me off!",
208c2ecf20Sopenharmony_ci	[MSG_PARKED] = "parked!",
218c2ecf20Sopenharmony_ci	[MSG_UNPARKED] = "unparked!",
228c2ecf20Sopenharmony_ci	[MSG_MARK] = "mark",
238c2ecf20Sopenharmony_ci	[MSG_CUT] = "cut",
248c2ecf20Sopenharmony_ci	[MSG_MARK_CLEARED] = "mark, cleared",
258c2ecf20Sopenharmony_ci	[MSG_PASTE] = "paste",
268c2ecf20Sopenharmony_ci	[MSG_BRIGHT] = "bright",
278c2ecf20Sopenharmony_ci	[MSG_ON_BLINKING] = "on blinking",
288c2ecf20Sopenharmony_ci	[MSG_OFF] = "off",
298c2ecf20Sopenharmony_ci	[MSG_ON] = "on",
308c2ecf20Sopenharmony_ci	[MSG_NO_WINDOW] = "no window",
318c2ecf20Sopenharmony_ci	[MSG_CURSORING_OFF] = "cursoring off",
328c2ecf20Sopenharmony_ci	[MSG_CURSORING_ON] = "cursoring on",
338c2ecf20Sopenharmony_ci	[MSG_HIGHLIGHT_TRACKING] = "highlight tracking",
348c2ecf20Sopenharmony_ci	[MSG_READ_WINDOW] = "read windo",
358c2ecf20Sopenharmony_ci	[MSG_READ_ALL] = "read all",
368c2ecf20Sopenharmony_ci	[MSG_EDIT_DONE] = "edit done",
378c2ecf20Sopenharmony_ci	[MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset",
388c2ecf20Sopenharmony_ci	[MSG_END_BEFORE_START] = "error end before start",
398c2ecf20Sopenharmony_ci	[MSG_WINDOW_CLEARED] = "window cleared",
408c2ecf20Sopenharmony_ci	[MSG_WINDOW_SILENCED] = "window silenced",
418c2ecf20Sopenharmony_ci	[MSG_WINDOW_SILENCE_DISABLED] = "window silence disabled",
428c2ecf20Sopenharmony_ci	[MSG_ERROR] = "error",
438c2ecf20Sopenharmony_ci	[MSG_GOTO_CANCELED] = "goto canceled",
448c2ecf20Sopenharmony_ci	[MSG_GOTO] = "go to?",
458c2ecf20Sopenharmony_ci	[MSG_LEAVING_HELP] = "leaving help",
468c2ecf20Sopenharmony_ci	[MSG_IS_UNASSIGNED] = "is unassigned",
478c2ecf20Sopenharmony_ci	[MSG_HELP_INFO] =
488c2ecf20Sopenharmony_ci	"press space to exit, up or down to scroll, or a letter to go to a command",
498c2ecf20Sopenharmony_ci	[MSG_EDGE_TOP] = "top,",
508c2ecf20Sopenharmony_ci	[MSG_EDGE_BOTTOM] = "bottom,",
518c2ecf20Sopenharmony_ci	[MSG_EDGE_LEFT] = "left,",
528c2ecf20Sopenharmony_ci	[MSG_EDGE_RIGHT] = "right,",
538c2ecf20Sopenharmony_ci	[MSG_NUMBER] = "number",
548c2ecf20Sopenharmony_ci	[MSG_SPACE] = "space",
558c2ecf20Sopenharmony_ci	[MSG_START] = "start",
568c2ecf20Sopenharmony_ci	[MSG_END] = "end",
578c2ecf20Sopenharmony_ci	[MSG_CTRL] = "control-",
588c2ecf20Sopenharmony_ci	[MSG_DISJUNCTION] = "or",
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* Messages with embedded format specifiers. */
618c2ecf20Sopenharmony_ci	[MSG_POS_INFO] = "line %ld, col %ld, t t y %d",
628c2ecf20Sopenharmony_ci	[MSG_CHAR_INFO] = "hex %02x, decimal %d",
638c2ecf20Sopenharmony_ci	[MSG_REPEAT_DESC] = "times %d .",
648c2ecf20Sopenharmony_ci	[MSG_REPEAT_DESC2] = "repeated %d .",
658c2ecf20Sopenharmony_ci	[MSG_WINDOW_LINE] = "window is line %d",
668c2ecf20Sopenharmony_ci	[MSG_WINDOW_BOUNDARY] = "%s at line %d, column %d",
678c2ecf20Sopenharmony_ci	[MSG_EDIT_PROMPT] = "edit  %s, press space when done",
688c2ecf20Sopenharmony_ci	[MSG_NO_COMMAND] = "no commands for %c",
698c2ecf20Sopenharmony_ci	[MSG_KEYDESC] = "is %s",
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* Control keys. */
728c2ecf20Sopenharmony_ci	/* Most of these duplicate the entries in state names. */
738c2ecf20Sopenharmony_ci	[MSG_CTL_SHIFT] = "shift",
748c2ecf20Sopenharmony_ci	[MSG_CTL_ALTGR] = "altgr",
758c2ecf20Sopenharmony_ci	[MSG_CTL_CONTROL] = "control",
768c2ecf20Sopenharmony_ci	[MSG_CTL_ALT] = "alt",
778c2ecf20Sopenharmony_ci	[MSG_CTL_LSHIFT] = "l shift",
788c2ecf20Sopenharmony_ci	[MSG_CTL_SPEAKUP] = "speakup",
798c2ecf20Sopenharmony_ci	[MSG_CTL_LCONTROL] = "l control",
808c2ecf20Sopenharmony_ci	[MSG_CTL_RCONTROL] = "r control",
818c2ecf20Sopenharmony_ci	[MSG_CTL_CAPSSHIFT] = "caps shift",
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Color names. */
848c2ecf20Sopenharmony_ci	[MSG_COLOR_BLACK] = "black",
858c2ecf20Sopenharmony_ci	[MSG_COLOR_BLUE] = "blue",
868c2ecf20Sopenharmony_ci	[MSG_COLOR_GREEN] = "green",
878c2ecf20Sopenharmony_ci	[MSG_COLOR_CYAN] = "cyan",
888c2ecf20Sopenharmony_ci	[MSG_COLOR_RED] = "red",
898c2ecf20Sopenharmony_ci	[MSG_COLOR_MAGENTA] = "magenta",
908c2ecf20Sopenharmony_ci	[MSG_COLOR_YELLOW] = "yellow",
918c2ecf20Sopenharmony_ci	[MSG_COLOR_WHITE] = "white",
928c2ecf20Sopenharmony_ci	[MSG_COLOR_GREY] = "grey",
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Names of key states. */
958c2ecf20Sopenharmony_ci	[MSG_STATE_DOUBLE] = "double",
968c2ecf20Sopenharmony_ci	[MSG_STATE_SPEAKUP] = "speakup",
978c2ecf20Sopenharmony_ci	[MSG_STATE_ALT] = "alt",
988c2ecf20Sopenharmony_ci	[MSG_STATE_CONTROL] = "ctrl",
998c2ecf20Sopenharmony_ci	[MSG_STATE_ALTGR] = "altgr",
1008c2ecf20Sopenharmony_ci	[MSG_STATE_SHIFT] = "shift",
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Key names. */
1038c2ecf20Sopenharmony_ci	[MSG_KEYNAME_ESC] = "escape",
1048c2ecf20Sopenharmony_ci	[MSG_KEYNAME_1] = "1",
1058c2ecf20Sopenharmony_ci	[MSG_KEYNAME_2] = "2",
1068c2ecf20Sopenharmony_ci	[MSG_KEYNAME_3] = "3",
1078c2ecf20Sopenharmony_ci	[MSG_KEYNAME_4] = "4",
1088c2ecf20Sopenharmony_ci	[MSG_KEYNAME_5] = "5",
1098c2ecf20Sopenharmony_ci	[MSG_KEYNAME_6] = "6",
1108c2ecf20Sopenharmony_ci	[MSG_KEYNAME_7] = "7",
1118c2ecf20Sopenharmony_ci	[MSG_KEYNAME_8] = "8",
1128c2ecf20Sopenharmony_ci	[MSG_KEYNAME_9] = "9",
1138c2ecf20Sopenharmony_ci	[MSG_KEYNAME_0] = "0",
1148c2ecf20Sopenharmony_ci	[MSG_KEYNAME_DASH] = "minus",
1158c2ecf20Sopenharmony_ci	[MSG_KEYNAME_EQUAL] = "equal",
1168c2ecf20Sopenharmony_ci	[MSG_KEYNAME_BS] = "back space",
1178c2ecf20Sopenharmony_ci	[MSG_KEYNAME_TAB] = "tab",
1188c2ecf20Sopenharmony_ci	[MSG_KEYNAME_Q] = "q",
1198c2ecf20Sopenharmony_ci	[MSG_KEYNAME_W] = "w",
1208c2ecf20Sopenharmony_ci	[MSG_KEYNAME_E] = "e",
1218c2ecf20Sopenharmony_ci	[MSG_KEYNAME_R] = "r",
1228c2ecf20Sopenharmony_ci	[MSG_KEYNAME_T] = "t",
1238c2ecf20Sopenharmony_ci	[MSG_KEYNAME_Y] = "y",
1248c2ecf20Sopenharmony_ci	[MSG_KEYNAME_U] = "u",
1258c2ecf20Sopenharmony_ci	[MSG_KEYNAME_I] = "i",
1268c2ecf20Sopenharmony_ci	[MSG_KEYNAME_O] = "o",
1278c2ecf20Sopenharmony_ci	[MSG_KEYNAME_P] = "p",
1288c2ecf20Sopenharmony_ci	[MSG_KEYNAME_LEFTBRACE] = "left brace",
1298c2ecf20Sopenharmony_ci	[MSG_KEYNAME_RIGHTBRACE] = "right brace",
1308c2ecf20Sopenharmony_ci	[MSG_KEYNAME_ENTER] = "enter",
1318c2ecf20Sopenharmony_ci	[MSG_KEYNAME_LEFTCTRL] = "left control",
1328c2ecf20Sopenharmony_ci	[MSG_KEYNAME_A] = "a",
1338c2ecf20Sopenharmony_ci	[MSG_KEYNAME_S] = "s",
1348c2ecf20Sopenharmony_ci	[MSG_KEYNAME_D] = "d",
1358c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F] = "f",
1368c2ecf20Sopenharmony_ci	[MSG_KEYNAME_G] = "g",
1378c2ecf20Sopenharmony_ci	[MSG_KEYNAME_H] = "h",
1388c2ecf20Sopenharmony_ci	[MSG_KEYNAME_J] = "j",
1398c2ecf20Sopenharmony_ci	[MSG_KEYNAME_K] = "k",
1408c2ecf20Sopenharmony_ci	[MSG_KEYNAME_L] = "l",
1418c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SEMICOLON] = "semicolon",
1428c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SINGLEQUOTE] = "apostrophe",
1438c2ecf20Sopenharmony_ci	[MSG_KEYNAME_GRAVE] = "accent",
1448c2ecf20Sopenharmony_ci	[MSG_KEYNAME_LEFTSHFT] = "left shift",
1458c2ecf20Sopenharmony_ci	[MSG_KEYNAME_BACKSLASH] = "back slash",
1468c2ecf20Sopenharmony_ci	[MSG_KEYNAME_Z] = "z",
1478c2ecf20Sopenharmony_ci	[MSG_KEYNAME_X] = "x",
1488c2ecf20Sopenharmony_ci	[MSG_KEYNAME_C] = "c",
1498c2ecf20Sopenharmony_ci	[MSG_KEYNAME_V] = "v",
1508c2ecf20Sopenharmony_ci	[MSG_KEYNAME_B] = "b",
1518c2ecf20Sopenharmony_ci	[MSG_KEYNAME_N] = "n",
1528c2ecf20Sopenharmony_ci	[MSG_KEYNAME_M] = "m",
1538c2ecf20Sopenharmony_ci	[MSG_KEYNAME_COMMA] = "comma",
1548c2ecf20Sopenharmony_ci	[MSG_KEYNAME_DOT] = "dot",
1558c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SLASH] = "slash",
1568c2ecf20Sopenharmony_ci	[MSG_KEYNAME_RIGHTSHFT] = "right shift",
1578c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPSTAR] = "keypad asterisk",
1588c2ecf20Sopenharmony_ci	[MSG_KEYNAME_LEFTALT] = "left alt",
1598c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SPACE] = "space",
1608c2ecf20Sopenharmony_ci	[MSG_KEYNAME_CAPSLOCK] = "caps lock",
1618c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F1] = "f1",
1628c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F2] = "f2",
1638c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F3] = "f3",
1648c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F4] = "f4",
1658c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F5] = "f5",
1668c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F6] = "f6",
1678c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F7] = "f7",
1688c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F8] = "f8",
1698c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F9] = "f9",
1708c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F10] = "f10",
1718c2ecf20Sopenharmony_ci	[MSG_KEYNAME_NUMLOCK] = "num lock",
1728c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SCROLLLOCK] = "scroll lock",
1738c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP7] = "keypad 7",
1748c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP8] = "keypad 8",
1758c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP9] = "keypad 9",
1768c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPMINUS] = "keypad minus",
1778c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP4] = "keypad 4",
1788c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP5] = "keypad 5",
1798c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP6] = "keypad 6",
1808c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPPLUS] = "keypad plus",
1818c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP1] = "keypad 1",
1828c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP2] = "keypad 2",
1838c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP3] = "keypad 3",
1848c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KP0] = "keypad 0",
1858c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPDOT] = "keypad dot",
1868c2ecf20Sopenharmony_ci	[MSG_KEYNAME_103RD] = "103rd",
1878c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F13] = "f13",
1888c2ecf20Sopenharmony_ci	[MSG_KEYNAME_102ND] = "102nd",
1898c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F11] = "f11",
1908c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F12] = "f12",
1918c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F14] = "f14",
1928c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F15] = "f15",
1938c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F16] = "f16",
1948c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F17] = "f17",
1958c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F18] = "f18",
1968c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F19] = "f19",
1978c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F20] = "f20",
1988c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPENTER] = "keypad enter",
1998c2ecf20Sopenharmony_ci	[MSG_KEYNAME_RIGHTCTRL] = "right control",
2008c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPSLASH] = "keypad slash",
2018c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SYSRQ] = "sysrq",
2028c2ecf20Sopenharmony_ci	[MSG_KEYNAME_RIGHTALT] = "right alt",
2038c2ecf20Sopenharmony_ci	[MSG_KEYNAME_LF] = "line feed",
2048c2ecf20Sopenharmony_ci	[MSG_KEYNAME_HOME] = "home",
2058c2ecf20Sopenharmony_ci	[MSG_KEYNAME_UP] = "up",
2068c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PGUP] = "page up",
2078c2ecf20Sopenharmony_ci	[MSG_KEYNAME_LEFT] = "left",
2088c2ecf20Sopenharmony_ci	[MSG_KEYNAME_RIGHT] = "right",
2098c2ecf20Sopenharmony_ci	[MSG_KEYNAME_END] = "end",
2108c2ecf20Sopenharmony_ci	[MSG_KEYNAME_DOWN] = "down",
2118c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PGDN] = "page down",
2128c2ecf20Sopenharmony_ci	[MSG_KEYNAME_INS] = "insert",
2138c2ecf20Sopenharmony_ci	[MSG_KEYNAME_DEL] = "delete",
2148c2ecf20Sopenharmony_ci	[MSG_KEYNAME_MACRO] = "macro",
2158c2ecf20Sopenharmony_ci	[MSG_KEYNAME_MUTE] = "mute",
2168c2ecf20Sopenharmony_ci	[MSG_KEYNAME_VOLDOWN] = "volume down",
2178c2ecf20Sopenharmony_ci	[MSG_KEYNAME_VOLUP] = "volume up",
2188c2ecf20Sopenharmony_ci	[MSG_KEYNAME_POWER] = "power",
2198c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPEQUAL] = "keypad equal",
2208c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPPLUSDASH] = "keypad plusminus",
2218c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PAUSE] = "pause",
2228c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F21] = "f21",
2238c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F22] = "f22",
2248c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F23] = "f23",
2258c2ecf20Sopenharmony_ci	[MSG_KEYNAME_F24] = "f24",
2268c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPCOMMA] = "keypad comma",
2278c2ecf20Sopenharmony_ci	[MSG_KEYNAME_LEFTMETA] = "left meta",
2288c2ecf20Sopenharmony_ci	[MSG_KEYNAME_RIGHTMETA] = "right meta",
2298c2ecf20Sopenharmony_ci	[MSG_KEYNAME_COMPOSE] = "compose",
2308c2ecf20Sopenharmony_ci	[MSG_KEYNAME_STOP] = "stop",
2318c2ecf20Sopenharmony_ci	[MSG_KEYNAME_AGAIN] = "again",
2328c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PROPS] = "props",
2338c2ecf20Sopenharmony_ci	[MSG_KEYNAME_UNDO] = "undo",
2348c2ecf20Sopenharmony_ci	[MSG_KEYNAME_FRONT] = "front",
2358c2ecf20Sopenharmony_ci	[MSG_KEYNAME_COPY] = "copy",
2368c2ecf20Sopenharmony_ci	[MSG_KEYNAME_OPEN] = "open",
2378c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PASTE] = "paste",
2388c2ecf20Sopenharmony_ci	[MSG_KEYNAME_FIND] = "find",
2398c2ecf20Sopenharmony_ci	[MSG_KEYNAME_CUT] = "cut",
2408c2ecf20Sopenharmony_ci	[MSG_KEYNAME_HELP] = "help",
2418c2ecf20Sopenharmony_ci	[MSG_KEYNAME_MENU] = "menu",
2428c2ecf20Sopenharmony_ci	[MSG_KEYNAME_CALC] = "calc",
2438c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SETUP] = "setup",
2448c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SLEEP] = "sleep",
2458c2ecf20Sopenharmony_ci	[MSG_KEYNAME_WAKEUP] = "wakeup",
2468c2ecf20Sopenharmony_ci	[MSG_KEYNAME_FILE] = "file",
2478c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SENDFILE] = "send file",
2488c2ecf20Sopenharmony_ci	[MSG_KEYNAME_DELFILE] = "delete file",
2498c2ecf20Sopenharmony_ci	[MSG_KEYNAME_XFER] = "transfer",
2508c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PROG1] = "prog1",
2518c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PROG2] = "prog2",
2528c2ecf20Sopenharmony_ci	[MSG_KEYNAME_WWW] = "www",
2538c2ecf20Sopenharmony_ci	[MSG_KEYNAME_MSDOS] = "msdos",
2548c2ecf20Sopenharmony_ci	[MSG_KEYNAME_COFFEE] = "coffee",
2558c2ecf20Sopenharmony_ci	[MSG_KEYNAME_DIRECTION] = "direction",
2568c2ecf20Sopenharmony_ci	[MSG_KEYNAME_CYCLEWINDOWS] = "cycle windows",
2578c2ecf20Sopenharmony_ci	[MSG_KEYNAME_MAIL] = "mail",
2588c2ecf20Sopenharmony_ci	[MSG_KEYNAME_BOOKMARKS] = "bookmarks",
2598c2ecf20Sopenharmony_ci	[MSG_KEYNAME_COMPUTER] = "computer",
2608c2ecf20Sopenharmony_ci	[MSG_KEYNAME_BACK] = "back",
2618c2ecf20Sopenharmony_ci	[MSG_KEYNAME_FORWARD] = "forward",
2628c2ecf20Sopenharmony_ci	[MSG_KEYNAME_CLOSECD] = "close cd",
2638c2ecf20Sopenharmony_ci	[MSG_KEYNAME_EJECTCD] = "eject cd",
2648c2ecf20Sopenharmony_ci	[MSG_KEYNAME_EJECTCLOSE] = "eject close cd",
2658c2ecf20Sopenharmony_ci	[MSG_KEYNAME_NEXTSONG] = "next song",
2668c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PLAYPAUSE] = "play pause",
2678c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PREVSONG] = "previous song",
2688c2ecf20Sopenharmony_ci	[MSG_KEYNAME_STOPCD] = "stop cd",
2698c2ecf20Sopenharmony_ci	[MSG_KEYNAME_RECORD] = "record",
2708c2ecf20Sopenharmony_ci	[MSG_KEYNAME_REWIND] = "rewind",
2718c2ecf20Sopenharmony_ci	[MSG_KEYNAME_PHONE] = "phone",
2728c2ecf20Sopenharmony_ci	[MSG_KEYNAME_ISO] = "iso",
2738c2ecf20Sopenharmony_ci	[MSG_KEYNAME_CONFIG] = "config",
2748c2ecf20Sopenharmony_ci	[MSG_KEYNAME_HOMEPG] = "home page",
2758c2ecf20Sopenharmony_ci	[MSG_KEYNAME_REFRESH] = "refresh",
2768c2ecf20Sopenharmony_ci	[MSG_KEYNAME_EXIT] = "exit",
2778c2ecf20Sopenharmony_ci	[MSG_KEYNAME_MOVE] = "move",
2788c2ecf20Sopenharmony_ci	[MSG_KEYNAME_EDIT] = "edit",
2798c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SCROLLUP] = "scroll up",
2808c2ecf20Sopenharmony_ci	[MSG_KEYNAME_SCROLLDN] = "scroll down",
2818c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPLEFTPAR] = "keypad left paren",
2828c2ecf20Sopenharmony_ci	[MSG_KEYNAME_KPRIGHTPAR] = "keypad right paren",
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* Function names. */
2858c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_ATTRIB_BLEEP_DEC] = "attribute bleep decrement",
2868c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_ATTRIB_BLEEP_INC] = "attribute bleep increment",
2878c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_BLEEPS_DEC] = "bleeps decrement",
2888c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_BLEEPS_INC] = "bleeps increment",
2898c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CHAR_FIRST] = "character, first",
2908c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CHAR_LAST] = "character, last",
2918c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CHAR_CURRENT] = "character, say current",
2928c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CHAR_HEX_AND_DEC] = "character, say hex and decimal",
2938c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CHAR_NEXT] = "character, say next",
2948c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CHAR_PHONETIC] = "character, say phonetic",
2958c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CHAR_PREVIOUS] = "character, say previous",
2968c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CURSOR_PARK] = "cursor park",
2978c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_CUT] = "cut",
2988c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_EDIT_DELIM] = "edit delimiters",
2998c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_EDIT_EXNUM] = "edit exnum",
3008c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_EDIT_MOST] = "edit most",
3018c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_EDIT_REPEATS] = "edit repeats",
3028c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_EDIT_SOME] = "edit some",
3038c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_GOTO] = "go to",
3048c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_GOTO_BOTTOM] = "go to bottom edge",
3058c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_GOTO_LEFT] = "go to left edge",
3068c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_GOTO_RIGHT] = "go to right edge",
3078c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_GOTO_TOP] = "go to top edge",
3088c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_HELP] = "help",
3098c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_LINE_SAY_CURRENT] = "line, say current",
3108c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_LINE_SAY_NEXT] = "line, say next",
3118c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_LINE_SAY_PREVIOUS] = "line, say previous",
3128c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_LINE_SAY_WITH_INDENT] = "line, say with indent",
3138c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_PASTE] = "paste",
3148c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_PITCH_DEC] = "pitch decrement",
3158c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_PITCH_INC] = "pitch increment",
3168c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_PUNC_DEC] = "punctuation decrement",
3178c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_PUNC_INC] = "punctuation increment",
3188c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_PUNC_LEVEL_DEC] = "punc level decrement",
3198c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_PUNC_LEVEL_INC] = "punc level increment",
3208c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_QUIET] = "quiet",
3218c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_RATE_DEC] = "rate decrement",
3228c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_RATE_INC] = "rate increment",
3238c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_READING_PUNC_DEC] = "reading punctuation decrement",
3248c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_READING_PUNC_INC] = "reading punctuation increment",
3258c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SAY_ATTRIBUTES] = "say attributes",
3268c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SAY_FROM_LEFT] = "say from left",
3278c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SAY_FROM_TOP] = "say from top",
3288c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SAY_POSITION] = "say position",
3298c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SAY_SCREEN] = "say screen",
3308c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SAY_TO_BOTTOM] = "say to bottom",
3318c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SAY_TO_RIGHT] = "say to right",
3328c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPEAKUP] = "speakup",
3338c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPEAKUP_LOCK] = "speakup lock",
3348c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPEAKUP_OFF] = "speakup off",
3358c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPEECH_KILL] = "speech kill",
3368c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPELL_DELAY_DEC] = "spell delay decrement",
3378c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPELL_DELAY_INC] = "spell delay increment",
3388c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPELL_WORD] = "spell word",
3398c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_SPELL_WORD_PHONETICALLY] = "spell word phonetically",
3408c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_TONE_DEC] = "tone decrement",
3418c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_TONE_INC] = "tone increment",
3428c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_VOICE_DEC] = "voice decrement",
3438c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_VOICE_INC] = "voice increment",
3448c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_VOLUME_DEC] = "volume decrement",
3458c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_VOLUME_INC] = "volume increment",
3468c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_WINDOW_CLEAR] = "window, clear",
3478c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_WINDOW_SAY] = "window, say",
3488c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_WINDOW_SET] = "window, set",
3498c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_WINDOW_SILENCE] = "window, silence",
3508c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_WORD_SAY_CURRENT] = "word, say current",
3518c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_WORD_SAY_NEXT] = "word, say next",
3528c2ecf20Sopenharmony_ci	[MSG_FUNCNAME_WORD_SAY_PREVIOUS] = "word, say previous",
3538c2ecf20Sopenharmony_ci};
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic struct msg_group_t all_groups[] = {
3568c2ecf20Sopenharmony_ci	{
3578c2ecf20Sopenharmony_ci		.name = "ctl_keys",
3588c2ecf20Sopenharmony_ci		.start = MSG_CTL_START,
3598c2ecf20Sopenharmony_ci		.end = MSG_CTL_END,
3608c2ecf20Sopenharmony_ci	},
3618c2ecf20Sopenharmony_ci	{
3628c2ecf20Sopenharmony_ci		.name = "colors",
3638c2ecf20Sopenharmony_ci		.start = MSG_COLORS_START,
3648c2ecf20Sopenharmony_ci		.end = MSG_COLORS_END,
3658c2ecf20Sopenharmony_ci	},
3668c2ecf20Sopenharmony_ci	{
3678c2ecf20Sopenharmony_ci		.name = "formatted",
3688c2ecf20Sopenharmony_ci		.start = MSG_FORMATTED_START,
3698c2ecf20Sopenharmony_ci		.end = MSG_FORMATTED_END,
3708c2ecf20Sopenharmony_ci	},
3718c2ecf20Sopenharmony_ci	{
3728c2ecf20Sopenharmony_ci		.name = "function_names",
3738c2ecf20Sopenharmony_ci		.start = MSG_FUNCNAMES_START,
3748c2ecf20Sopenharmony_ci		.end = MSG_FUNCNAMES_END,
3758c2ecf20Sopenharmony_ci	},
3768c2ecf20Sopenharmony_ci	{
3778c2ecf20Sopenharmony_ci		.name = "key_names",
3788c2ecf20Sopenharmony_ci		.start = MSG_KEYNAMES_START,
3798c2ecf20Sopenharmony_ci		.end = MSG_KEYNAMES_END,
3808c2ecf20Sopenharmony_ci	},
3818c2ecf20Sopenharmony_ci	{
3828c2ecf20Sopenharmony_ci		.name = "announcements",
3838c2ecf20Sopenharmony_ci		.start = MSG_ANNOUNCEMENTS_START,
3848c2ecf20Sopenharmony_ci		.end = MSG_ANNOUNCEMENTS_END,
3858c2ecf20Sopenharmony_ci	},
3868c2ecf20Sopenharmony_ci	{
3878c2ecf20Sopenharmony_ci		.name = "states",
3888c2ecf20Sopenharmony_ci		.start = MSG_STATES_START,
3898c2ecf20Sopenharmony_ci		.end = MSG_STATES_END,
3908c2ecf20Sopenharmony_ci	},
3918c2ecf20Sopenharmony_ci};
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic const  int num_groups = ARRAY_SIZE(all_groups);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cichar *spk_msg_get(enum msg_index_t index)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	return speakup_msgs[index];
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/*
4018c2ecf20Sopenharmony_ci * Function: next_specifier
4028c2ecf20Sopenharmony_ci * Finds the start of the next format specifier in the argument string.
4038c2ecf20Sopenharmony_ci * Return value: pointer to start of format
4048c2ecf20Sopenharmony_ci * specifier, or NULL if no specifier exists.
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_cistatic char *next_specifier(char *input)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	int found = 0;
4098c2ecf20Sopenharmony_ci	char *next_percent = input;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	while (next_percent && !found) {
4128c2ecf20Sopenharmony_ci		next_percent = strchr(next_percent, '%');
4138c2ecf20Sopenharmony_ci		if (next_percent) {
4148c2ecf20Sopenharmony_ci			/* skip over doubled percent signs */
4158c2ecf20Sopenharmony_ci			while (next_percent[0] == '%' &&
4168c2ecf20Sopenharmony_ci			       next_percent[1] == '%')
4178c2ecf20Sopenharmony_ci				next_percent += 2;
4188c2ecf20Sopenharmony_ci			if (*next_percent == '%')
4198c2ecf20Sopenharmony_ci				found = 1;
4208c2ecf20Sopenharmony_ci			else if (*next_percent == '\0')
4218c2ecf20Sopenharmony_ci				next_percent = NULL;
4228c2ecf20Sopenharmony_ci		}
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return next_percent;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci/* Skip over 0 or more flags. */
4298c2ecf20Sopenharmony_cistatic char *skip_flags(char *input)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	while ((*input != '\0') && strchr(" 0+-#", *input))
4328c2ecf20Sopenharmony_ci		input++;
4338c2ecf20Sopenharmony_ci	return input;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/* Skip over width.precision, if it exists. */
4378c2ecf20Sopenharmony_cistatic char *skip_width(char *input)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	while (isdigit(*input))
4408c2ecf20Sopenharmony_ci		input++;
4418c2ecf20Sopenharmony_ci	if (*input == '.') {
4428c2ecf20Sopenharmony_ci		input++;
4438c2ecf20Sopenharmony_ci		while (isdigit(*input))
4448c2ecf20Sopenharmony_ci			input++;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci	return input;
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/*
4508c2ecf20Sopenharmony_ci * Skip past the end of the conversion part.
4518c2ecf20Sopenharmony_ci * Note that this code only accepts a handful of conversion specifiers:
4528c2ecf20Sopenharmony_ci * c d s x and ld.  Not accidental; these are exactly the ones used in
4538c2ecf20Sopenharmony_ci * the default group of formatted messages.
4548c2ecf20Sopenharmony_ci */
4558c2ecf20Sopenharmony_cistatic char *skip_conversion(char *input)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	if ((input[0] == 'l') && (input[1] == 'd'))
4588c2ecf20Sopenharmony_ci		input += 2;
4598c2ecf20Sopenharmony_ci	else if ((*input != '\0') && strchr("cdsx", *input))
4608c2ecf20Sopenharmony_ci		input++;
4618c2ecf20Sopenharmony_ci	return input;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci/*
4658c2ecf20Sopenharmony_ci * Function: find_specifier_end
4668c2ecf20Sopenharmony_ci * Return a pointer to the end of the format specifier.
4678c2ecf20Sopenharmony_ci */
4688c2ecf20Sopenharmony_cistatic char *find_specifier_end(char *input)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	input++;		/* Advance over %. */
4718c2ecf20Sopenharmony_ci	input = skip_flags(input);
4728c2ecf20Sopenharmony_ci	input = skip_width(input);
4738c2ecf20Sopenharmony_ci	input = skip_conversion(input);
4748c2ecf20Sopenharmony_ci	return input;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/*
4788c2ecf20Sopenharmony_ci * Function: compare_specifiers
4798c2ecf20Sopenharmony_ci * Compare the format specifiers pointed to by *input1 and *input2.
4808c2ecf20Sopenharmony_ci * Return true if they are the same, false otherwise.
4818c2ecf20Sopenharmony_ci * Advance *input1 and *input2 so that they point to the character following
4828c2ecf20Sopenharmony_ci * the end of the specifier.
4838c2ecf20Sopenharmony_ci */
4848c2ecf20Sopenharmony_cistatic bool compare_specifiers(char **input1, char **input2)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	bool same = false;
4878c2ecf20Sopenharmony_ci	char *end1 = find_specifier_end(*input1);
4888c2ecf20Sopenharmony_ci	char *end2 = find_specifier_end(*input2);
4898c2ecf20Sopenharmony_ci	size_t length1 = end1 - *input1;
4908c2ecf20Sopenharmony_ci	size_t length2 = end2 - *input2;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if ((length1 == length2) && !memcmp(*input1, *input2, length1))
4938c2ecf20Sopenharmony_ci		same = true;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	*input1 = end1;
4968c2ecf20Sopenharmony_ci	*input2 = end2;
4978c2ecf20Sopenharmony_ci	return same;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci/*
5018c2ecf20Sopenharmony_ci * Function: fmt_validate
5028c2ecf20Sopenharmony_ci * Check that two format strings contain the same number of format specifiers,
5038c2ecf20Sopenharmony_ci * and that the order of specifiers is the same in both strings.
5048c2ecf20Sopenharmony_ci * Return true if the condition holds, false if it doesn't.
5058c2ecf20Sopenharmony_ci */
5068c2ecf20Sopenharmony_cistatic bool fmt_validate(char *template, char *user)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	bool valid = true;
5098c2ecf20Sopenharmony_ci	bool still_comparing = true;
5108c2ecf20Sopenharmony_ci	char *template_ptr = template;
5118c2ecf20Sopenharmony_ci	char *user_ptr = user;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	while (still_comparing && valid) {
5148c2ecf20Sopenharmony_ci		template_ptr = next_specifier(template_ptr);
5158c2ecf20Sopenharmony_ci		user_ptr = next_specifier(user_ptr);
5168c2ecf20Sopenharmony_ci		if (template_ptr && user_ptr) {
5178c2ecf20Sopenharmony_ci			/* Both have at least one more specifier. */
5188c2ecf20Sopenharmony_ci			valid = compare_specifiers(&template_ptr, &user_ptr);
5198c2ecf20Sopenharmony_ci		} else {
5208c2ecf20Sopenharmony_ci			/* No more format specifiers in one or both strings. */
5218c2ecf20Sopenharmony_ci			still_comparing = false;
5228c2ecf20Sopenharmony_ci			/* See if one has more specifiers than the other. */
5238c2ecf20Sopenharmony_ci			if (template_ptr || user_ptr)
5248c2ecf20Sopenharmony_ci				valid = false;
5258c2ecf20Sopenharmony_ci		}
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci	return valid;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/*
5318c2ecf20Sopenharmony_ci * Function: msg_set
5328c2ecf20Sopenharmony_ci * Description: Add a user-supplied message to the user_messages array.
5338c2ecf20Sopenharmony_ci * The message text is copied to a memory area allocated with kmalloc.
5348c2ecf20Sopenharmony_ci * If the function fails, then user_messages is untouched.
5358c2ecf20Sopenharmony_ci * Arguments:
5368c2ecf20Sopenharmony_ci * - index: a message number, as found in i18n.h.
5378c2ecf20Sopenharmony_ci * - text:  text of message.  Not NUL-terminated.
5388c2ecf20Sopenharmony_ci * - length: number of bytes in text.
5398c2ecf20Sopenharmony_ci * Failure conditions:
5408c2ecf20Sopenharmony_ci * -EINVAL -  Invalid format specifiers in formatted message or illegal index.
5418c2ecf20Sopenharmony_ci * -ENOMEM -  Unable to allocate memory.
5428c2ecf20Sopenharmony_ci */
5438c2ecf20Sopenharmony_cissize_t spk_msg_set(enum msg_index_t index, char *text, size_t length)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	char *newstr = NULL;
5468c2ecf20Sopenharmony_ci	unsigned long flags;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if ((index < MSG_FIRST_INDEX) || (index >= MSG_LAST_INDEX))
5498c2ecf20Sopenharmony_ci		return -EINVAL;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	newstr = kmalloc(length + 1, GFP_KERNEL);
5528c2ecf20Sopenharmony_ci	if (!newstr)
5538c2ecf20Sopenharmony_ci		return -ENOMEM;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	memcpy(newstr, text, length);
5568c2ecf20Sopenharmony_ci	newstr[length] = '\0';
5578c2ecf20Sopenharmony_ci	if (index >= MSG_FORMATTED_START &&
5588c2ecf20Sopenharmony_ci	    index <= MSG_FORMATTED_END &&
5598c2ecf20Sopenharmony_ci	    !fmt_validate(speakup_default_msgs[index], newstr)) {
5608c2ecf20Sopenharmony_ci		kfree(newstr);
5618c2ecf20Sopenharmony_ci		return -EINVAL;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
5648c2ecf20Sopenharmony_ci	if (speakup_msgs[index] != speakup_default_msgs[index])
5658c2ecf20Sopenharmony_ci		kfree(speakup_msgs[index]);
5668c2ecf20Sopenharmony_ci	speakup_msgs[index] = newstr;
5678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci/*
5728c2ecf20Sopenharmony_ci * Find a message group, given its name.  Return a pointer to the structure
5738c2ecf20Sopenharmony_ci * if found, or NULL otherwise.
5748c2ecf20Sopenharmony_ci */
5758c2ecf20Sopenharmony_cistruct msg_group_t *spk_find_msg_group(const char *group_name)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	struct msg_group_t *group = NULL;
5788c2ecf20Sopenharmony_ci	int i;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	for (i = 0; i < num_groups; i++) {
5818c2ecf20Sopenharmony_ci		if (!strcmp(all_groups[i].name, group_name)) {
5828c2ecf20Sopenharmony_ci			group = &all_groups[i];
5838c2ecf20Sopenharmony_ci			break;
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci	return group;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_civoid spk_reset_msg_group(struct msg_group_t *group)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	unsigned long flags;
5928c2ecf20Sopenharmony_ci	enum msg_index_t i;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	for (i = group->start; i <= group->end; i++) {
5978c2ecf20Sopenharmony_ci		if (speakup_msgs[i] != speakup_default_msgs[i])
5988c2ecf20Sopenharmony_ci			kfree(speakup_msgs[i]);
5998c2ecf20Sopenharmony_ci		speakup_msgs[i] = speakup_default_msgs[i];
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci/* Called at initialization time, to establish default messages. */
6058c2ecf20Sopenharmony_civoid spk_initialize_msgs(void)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	memcpy(speakup_msgs, speakup_default_msgs,
6088c2ecf20Sopenharmony_ci	       sizeof(speakup_default_msgs));
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/* Free user-supplied strings when module is unloaded: */
6128c2ecf20Sopenharmony_civoid spk_free_user_msgs(void)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	enum msg_index_t index;
6158c2ecf20Sopenharmony_ci	unsigned long flags;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&speakup_info.spinlock, flags);
6188c2ecf20Sopenharmony_ci	for (index = MSG_FIRST_INDEX; index < MSG_LAST_INDEX; index++) {
6198c2ecf20Sopenharmony_ci		if (speakup_msgs[index] != speakup_default_msgs[index]) {
6208c2ecf20Sopenharmony_ci			kfree(speakup_msgs[index]);
6218c2ecf20Sopenharmony_ci			speakup_msgs[index] = speakup_default_msgs[index];
6228c2ecf20Sopenharmony_ci		}
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&speakup_info.spinlock, flags);
6258c2ecf20Sopenharmony_ci}
626