Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Internationalization implementation.  Includes definitions of English
0003  * string arrays, and the i18n pointer.
0004  */
0005 
0006 #include <linux/slab.h>     /* For kmalloc. */
0007 #include <linux/ctype.h>
0008 #include <linux/module.h>
0009 #include <linux/string.h>
0010 #include "speakup.h"
0011 #include "spk_priv.h"
0012 
0013 static char *speakup_msgs[MSG_LAST_INDEX];
0014 static char *speakup_default_msgs[MSG_LAST_INDEX] = {
0015     [MSG_BLANK] = "blank",
0016     [MSG_IAM_ALIVE] = "I'm aLive!",
0017     [MSG_YOU_KILLED_SPEAKUP] = "You killed speakup!",
0018     [MSG_HEY_THATS_BETTER] = "hey. That's better!",
0019     [MSG_YOU_TURNED_ME_OFF] = "You turned me off!",
0020     [MSG_PARKED] = "parked!",
0021     [MSG_UNPARKED] = "unparked!",
0022     [MSG_MARK] = "mark",
0023     [MSG_CUT] = "cut",
0024     [MSG_MARK_CLEARED] = "mark, cleared",
0025     [MSG_PASTE] = "paste",
0026     [MSG_BRIGHT] = "bright",
0027     [MSG_ON_BLINKING] = "on blinking",
0028     [MSG_OFF] = "off",
0029     [MSG_ON] = "on",
0030     [MSG_NO_WINDOW] = "no window",
0031     [MSG_CURSORING_OFF] = "cursoring off",
0032     [MSG_CURSORING_ON] = "cursoring on",
0033     [MSG_HIGHLIGHT_TRACKING] = "highlight tracking",
0034     [MSG_READ_WINDOW] = "read windo",
0035     [MSG_READ_ALL] = "read all",
0036     [MSG_EDIT_DONE] = "edit done",
0037     [MSG_WINDOW_ALREADY_SET] = "window already set, clear then reset",
0038     [MSG_END_BEFORE_START] = "error end before start",
0039     [MSG_WINDOW_CLEARED] = "window cleared",
0040     [MSG_WINDOW_SILENCED] = "window silenced",
0041     [MSG_WINDOW_SILENCE_DISABLED] = "window silence disabled",
0042     [MSG_ERROR] = "error",
0043     [MSG_GOTO_CANCELED] = "goto canceled",
0044     [MSG_GOTO] = "go to?",
0045     [MSG_LEAVING_HELP] = "leaving help",
0046     [MSG_IS_UNASSIGNED] = "is unassigned",
0047     [MSG_HELP_INFO] =
0048     "press space to exit, up or down to scroll, or a letter to go to a command",
0049     [MSG_EDGE_TOP] = "top,",
0050     [MSG_EDGE_BOTTOM] = "bottom,",
0051     [MSG_EDGE_LEFT] = "left,",
0052     [MSG_EDGE_RIGHT] = "right,",
0053     [MSG_NUMBER] = "number",
0054     [MSG_SPACE] = "space",
0055     [MSG_START] = "start",
0056     [MSG_END] = "end",
0057     [MSG_CTRL] = "control-",
0058     [MSG_DISJUNCTION] = "or",
0059 
0060 /* Messages with embedded format specifiers. */
0061     [MSG_POS_INFO] = "line %ld, col %ld, t t y %d",
0062     [MSG_CHAR_INFO] = "hex %02x, decimal %d",
0063     [MSG_REPEAT_DESC] = "times %d .",
0064     [MSG_REPEAT_DESC2] = "repeated %d .",
0065     [MSG_WINDOW_LINE] = "window is line %d",
0066     [MSG_WINDOW_BOUNDARY] = "%s at line %d, column %d",
0067     [MSG_EDIT_PROMPT] = "edit  %s, press space when done",
0068     [MSG_NO_COMMAND] = "no commands for %c",
0069     [MSG_KEYDESC] = "is %s",
0070 
0071     /* Control keys. */
0072     /* Most of these duplicate the entries in state names. */
0073     [MSG_CTL_SHIFT] = "shift",
0074     [MSG_CTL_ALTGR] = "altgr",
0075     [MSG_CTL_CONTROL] = "control",
0076     [MSG_CTL_ALT] = "alt",
0077     [MSG_CTL_LSHIFT] = "l shift",
0078     [MSG_CTL_SPEAKUP] = "speakup",
0079     [MSG_CTL_LCONTROL] = "l control",
0080     [MSG_CTL_RCONTROL] = "r control",
0081     [MSG_CTL_CAPSSHIFT] = "caps shift",
0082 
0083     /* Color names. */
0084     [MSG_COLOR_BLACK] = "black",
0085     [MSG_COLOR_BLUE] = "blue",
0086     [MSG_COLOR_GREEN] = "green",
0087     [MSG_COLOR_CYAN] = "cyan",
0088     [MSG_COLOR_RED] = "red",
0089     [MSG_COLOR_MAGENTA] = "magenta",
0090     [MSG_COLOR_YELLOW] = "yellow",
0091     [MSG_COLOR_WHITE] = "white",
0092     [MSG_COLOR_GREY] = "grey",
0093     [MSG_COLOR_BRIGHTBLUE] = "bright blue",
0094     [MSG_COLOR_BRIGHTGREEN] = "bright green",
0095     [MSG_COLOR_BRIGHTCYAN] = "bright cyan",
0096     [MSG_COLOR_BRIGHTRED] = "bright red",
0097     [MSG_COLOR_BRIGHTMAGENTA] = "bright magenta",
0098     [MSG_COLOR_BRIGHTYELLOW] = "bright yellow",
0099     [MSG_COLOR_BRIGHTWHITE] = "bright white",
0100 
0101     /* Names of key states. */
0102     [MSG_STATE_DOUBLE] = "double",
0103     [MSG_STATE_SPEAKUP] = "speakup",
0104     [MSG_STATE_ALT] = "alt",
0105     [MSG_STATE_CONTROL] = "ctrl",
0106     [MSG_STATE_ALTGR] = "altgr",
0107     [MSG_STATE_SHIFT] = "shift",
0108 
0109     /* Key names. */
0110     [MSG_KEYNAME_ESC] = "escape",
0111     [MSG_KEYNAME_1] = "1",
0112     [MSG_KEYNAME_2] = "2",
0113     [MSG_KEYNAME_3] = "3",
0114     [MSG_KEYNAME_4] = "4",
0115     [MSG_KEYNAME_5] = "5",
0116     [MSG_KEYNAME_6] = "6",
0117     [MSG_KEYNAME_7] = "7",
0118     [MSG_KEYNAME_8] = "8",
0119     [MSG_KEYNAME_9] = "9",
0120     [MSG_KEYNAME_0] = "0",
0121     [MSG_KEYNAME_DASH] = "minus",
0122     [MSG_KEYNAME_EQUAL] = "equal",
0123     [MSG_KEYNAME_BS] = "back space",
0124     [MSG_KEYNAME_TAB] = "tab",
0125     [MSG_KEYNAME_Q] = "q",
0126     [MSG_KEYNAME_W] = "w",
0127     [MSG_KEYNAME_E] = "e",
0128     [MSG_KEYNAME_R] = "r",
0129     [MSG_KEYNAME_T] = "t",
0130     [MSG_KEYNAME_Y] = "y",
0131     [MSG_KEYNAME_U] = "u",
0132     [MSG_KEYNAME_I] = "i",
0133     [MSG_KEYNAME_O] = "o",
0134     [MSG_KEYNAME_P] = "p",
0135     [MSG_KEYNAME_LEFTBRACE] = "left brace",
0136     [MSG_KEYNAME_RIGHTBRACE] = "right brace",
0137     [MSG_KEYNAME_ENTER] = "enter",
0138     [MSG_KEYNAME_LEFTCTRL] = "left control",
0139     [MSG_KEYNAME_A] = "a",
0140     [MSG_KEYNAME_S] = "s",
0141     [MSG_KEYNAME_D] = "d",
0142     [MSG_KEYNAME_F] = "f",
0143     [MSG_KEYNAME_G] = "g",
0144     [MSG_KEYNAME_H] = "h",
0145     [MSG_KEYNAME_J] = "j",
0146     [MSG_KEYNAME_K] = "k",
0147     [MSG_KEYNAME_L] = "l",
0148     [MSG_KEYNAME_SEMICOLON] = "semicolon",
0149     [MSG_KEYNAME_SINGLEQUOTE] = "apostrophe",
0150     [MSG_KEYNAME_GRAVE] = "accent",
0151     [MSG_KEYNAME_LEFTSHFT] = "left shift",
0152     [MSG_KEYNAME_BACKSLASH] = "back slash",
0153     [MSG_KEYNAME_Z] = "z",
0154     [MSG_KEYNAME_X] = "x",
0155     [MSG_KEYNAME_C] = "c",
0156     [MSG_KEYNAME_V] = "v",
0157     [MSG_KEYNAME_B] = "b",
0158     [MSG_KEYNAME_N] = "n",
0159     [MSG_KEYNAME_M] = "m",
0160     [MSG_KEYNAME_COMMA] = "comma",
0161     [MSG_KEYNAME_DOT] = "dot",
0162     [MSG_KEYNAME_SLASH] = "slash",
0163     [MSG_KEYNAME_RIGHTSHFT] = "right shift",
0164     [MSG_KEYNAME_KPSTAR] = "keypad asterisk",
0165     [MSG_KEYNAME_LEFTALT] = "left alt",
0166     [MSG_KEYNAME_SPACE] = "space",
0167     [MSG_KEYNAME_CAPSLOCK] = "caps lock",
0168     [MSG_KEYNAME_F1] = "f1",
0169     [MSG_KEYNAME_F2] = "f2",
0170     [MSG_KEYNAME_F3] = "f3",
0171     [MSG_KEYNAME_F4] = "f4",
0172     [MSG_KEYNAME_F5] = "f5",
0173     [MSG_KEYNAME_F6] = "f6",
0174     [MSG_KEYNAME_F7] = "f7",
0175     [MSG_KEYNAME_F8] = "f8",
0176     [MSG_KEYNAME_F9] = "f9",
0177     [MSG_KEYNAME_F10] = "f10",
0178     [MSG_KEYNAME_NUMLOCK] = "num lock",
0179     [MSG_KEYNAME_SCROLLLOCK] = "scroll lock",
0180     [MSG_KEYNAME_KP7] = "keypad 7",
0181     [MSG_KEYNAME_KP8] = "keypad 8",
0182     [MSG_KEYNAME_KP9] = "keypad 9",
0183     [MSG_KEYNAME_KPMINUS] = "keypad minus",
0184     [MSG_KEYNAME_KP4] = "keypad 4",
0185     [MSG_KEYNAME_KP5] = "keypad 5",
0186     [MSG_KEYNAME_KP6] = "keypad 6",
0187     [MSG_KEYNAME_KPPLUS] = "keypad plus",
0188     [MSG_KEYNAME_KP1] = "keypad 1",
0189     [MSG_KEYNAME_KP2] = "keypad 2",
0190     [MSG_KEYNAME_KP3] = "keypad 3",
0191     [MSG_KEYNAME_KP0] = "keypad 0",
0192     [MSG_KEYNAME_KPDOT] = "keypad dot",
0193     [MSG_KEYNAME_103RD] = "103rd",
0194     [MSG_KEYNAME_F13] = "f13",
0195     [MSG_KEYNAME_102ND] = "102nd",
0196     [MSG_KEYNAME_F11] = "f11",
0197     [MSG_KEYNAME_F12] = "f12",
0198     [MSG_KEYNAME_F14] = "f14",
0199     [MSG_KEYNAME_F15] = "f15",
0200     [MSG_KEYNAME_F16] = "f16",
0201     [MSG_KEYNAME_F17] = "f17",
0202     [MSG_KEYNAME_F18] = "f18",
0203     [MSG_KEYNAME_F19] = "f19",
0204     [MSG_KEYNAME_F20] = "f20",
0205     [MSG_KEYNAME_KPENTER] = "keypad enter",
0206     [MSG_KEYNAME_RIGHTCTRL] = "right control",
0207     [MSG_KEYNAME_KPSLASH] = "keypad slash",
0208     [MSG_KEYNAME_SYSRQ] = "sysrq",
0209     [MSG_KEYNAME_RIGHTALT] = "right alt",
0210     [MSG_KEYNAME_LF] = "line feed",
0211     [MSG_KEYNAME_HOME] = "home",
0212     [MSG_KEYNAME_UP] = "up",
0213     [MSG_KEYNAME_PGUP] = "page up",
0214     [MSG_KEYNAME_LEFT] = "left",
0215     [MSG_KEYNAME_RIGHT] = "right",
0216     [MSG_KEYNAME_END] = "end",
0217     [MSG_KEYNAME_DOWN] = "down",
0218     [MSG_KEYNAME_PGDN] = "page down",
0219     [MSG_KEYNAME_INS] = "insert",
0220     [MSG_KEYNAME_DEL] = "delete",
0221     [MSG_KEYNAME_MACRO] = "macro",
0222     [MSG_KEYNAME_MUTE] = "mute",
0223     [MSG_KEYNAME_VOLDOWN] = "volume down",
0224     [MSG_KEYNAME_VOLUP] = "volume up",
0225     [MSG_KEYNAME_POWER] = "power",
0226     [MSG_KEYNAME_KPEQUAL] = "keypad equal",
0227     [MSG_KEYNAME_KPPLUSDASH] = "keypad plusminus",
0228     [MSG_KEYNAME_PAUSE] = "pause",
0229     [MSG_KEYNAME_F21] = "f21",
0230     [MSG_KEYNAME_F22] = "f22",
0231     [MSG_KEYNAME_F23] = "f23",
0232     [MSG_KEYNAME_F24] = "f24",
0233     [MSG_KEYNAME_KPCOMMA] = "keypad comma",
0234     [MSG_KEYNAME_LEFTMETA] = "left meta",
0235     [MSG_KEYNAME_RIGHTMETA] = "right meta",
0236     [MSG_KEYNAME_COMPOSE] = "compose",
0237     [MSG_KEYNAME_STOP] = "stop",
0238     [MSG_KEYNAME_AGAIN] = "again",
0239     [MSG_KEYNAME_PROPS] = "props",
0240     [MSG_KEYNAME_UNDO] = "undo",
0241     [MSG_KEYNAME_FRONT] = "front",
0242     [MSG_KEYNAME_COPY] = "copy",
0243     [MSG_KEYNAME_OPEN] = "open",
0244     [MSG_KEYNAME_PASTE] = "paste",
0245     [MSG_KEYNAME_FIND] = "find",
0246     [MSG_KEYNAME_CUT] = "cut",
0247     [MSG_KEYNAME_HELP] = "help",
0248     [MSG_KEYNAME_MENU] = "menu",
0249     [MSG_KEYNAME_CALC] = "calc",
0250     [MSG_KEYNAME_SETUP] = "setup",
0251     [MSG_KEYNAME_SLEEP] = "sleep",
0252     [MSG_KEYNAME_WAKEUP] = "wakeup",
0253     [MSG_KEYNAME_FILE] = "file",
0254     [MSG_KEYNAME_SENDFILE] = "send file",
0255     [MSG_KEYNAME_DELFILE] = "delete file",
0256     [MSG_KEYNAME_XFER] = "transfer",
0257     [MSG_KEYNAME_PROG1] = "prog1",
0258     [MSG_KEYNAME_PROG2] = "prog2",
0259     [MSG_KEYNAME_WWW] = "www",
0260     [MSG_KEYNAME_MSDOS] = "msdos",
0261     [MSG_KEYNAME_COFFEE] = "coffee",
0262     [MSG_KEYNAME_DIRECTION] = "direction",
0263     [MSG_KEYNAME_CYCLEWINDOWS] = "cycle windows",
0264     [MSG_KEYNAME_MAIL] = "mail",
0265     [MSG_KEYNAME_BOOKMARKS] = "bookmarks",
0266     [MSG_KEYNAME_COMPUTER] = "computer",
0267     [MSG_KEYNAME_BACK] = "back",
0268     [MSG_KEYNAME_FORWARD] = "forward",
0269     [MSG_KEYNAME_CLOSECD] = "close cd",
0270     [MSG_KEYNAME_EJECTCD] = "eject cd",
0271     [MSG_KEYNAME_EJECTCLOSE] = "eject close cd",
0272     [MSG_KEYNAME_NEXTSONG] = "next song",
0273     [MSG_KEYNAME_PLAYPAUSE] = "play pause",
0274     [MSG_KEYNAME_PREVSONG] = "previous song",
0275     [MSG_KEYNAME_STOPCD] = "stop cd",
0276     [MSG_KEYNAME_RECORD] = "record",
0277     [MSG_KEYNAME_REWIND] = "rewind",
0278     [MSG_KEYNAME_PHONE] = "phone",
0279     [MSG_KEYNAME_ISO] = "iso",
0280     [MSG_KEYNAME_CONFIG] = "config",
0281     [MSG_KEYNAME_HOMEPG] = "home page",
0282     [MSG_KEYNAME_REFRESH] = "refresh",
0283     [MSG_KEYNAME_EXIT] = "exit",
0284     [MSG_KEYNAME_MOVE] = "move",
0285     [MSG_KEYNAME_EDIT] = "edit",
0286     [MSG_KEYNAME_SCROLLUP] = "scroll up",
0287     [MSG_KEYNAME_SCROLLDN] = "scroll down",
0288     [MSG_KEYNAME_KPLEFTPAR] = "keypad left paren",
0289     [MSG_KEYNAME_KPRIGHTPAR] = "keypad right paren",
0290 
0291     /* Function names. */
0292     [MSG_FUNCNAME_ATTRIB_BLEEP_DEC] = "attribute bleep decrement",
0293     [MSG_FUNCNAME_ATTRIB_BLEEP_INC] = "attribute bleep increment",
0294     [MSG_FUNCNAME_BLEEPS_DEC] = "bleeps decrement",
0295     [MSG_FUNCNAME_BLEEPS_INC] = "bleeps increment",
0296     [MSG_FUNCNAME_CHAR_FIRST] = "character, first",
0297     [MSG_FUNCNAME_CHAR_LAST] = "character, last",
0298     [MSG_FUNCNAME_CHAR_CURRENT] = "character, say current",
0299     [MSG_FUNCNAME_CHAR_HEX_AND_DEC] = "character, say hex and decimal",
0300     [MSG_FUNCNAME_CHAR_NEXT] = "character, say next",
0301     [MSG_FUNCNAME_CHAR_PHONETIC] = "character, say phonetic",
0302     [MSG_FUNCNAME_CHAR_PREVIOUS] = "character, say previous",
0303     [MSG_FUNCNAME_CURSOR_PARK] = "cursor park",
0304     [MSG_FUNCNAME_CUT] = "cut",
0305     [MSG_FUNCNAME_EDIT_DELIM] = "edit delimiters",
0306     [MSG_FUNCNAME_EDIT_EXNUM] = "edit exnum",
0307     [MSG_FUNCNAME_EDIT_MOST] = "edit most",
0308     [MSG_FUNCNAME_EDIT_REPEATS] = "edit repeats",
0309     [MSG_FUNCNAME_EDIT_SOME] = "edit some",
0310     [MSG_FUNCNAME_GOTO] = "go to",
0311     [MSG_FUNCNAME_GOTO_BOTTOM] = "go to bottom edge",
0312     [MSG_FUNCNAME_GOTO_LEFT] = "go to left edge",
0313     [MSG_FUNCNAME_GOTO_RIGHT] = "go to right edge",
0314     [MSG_FUNCNAME_GOTO_TOP] = "go to top edge",
0315     [MSG_FUNCNAME_HELP] = "help",
0316     [MSG_FUNCNAME_LINE_SAY_CURRENT] = "line, say current",
0317     [MSG_FUNCNAME_LINE_SAY_NEXT] = "line, say next",
0318     [MSG_FUNCNAME_LINE_SAY_PREVIOUS] = "line, say previous",
0319     [MSG_FUNCNAME_LINE_SAY_WITH_INDENT] = "line, say with indent",
0320     [MSG_FUNCNAME_PASTE] = "paste",
0321     [MSG_FUNCNAME_PITCH_DEC] = "pitch decrement",
0322     [MSG_FUNCNAME_PITCH_INC] = "pitch increment",
0323     [MSG_FUNCNAME_PUNC_DEC] = "punctuation decrement",
0324     [MSG_FUNCNAME_PUNC_INC] = "punctuation increment",
0325     [MSG_FUNCNAME_PUNC_LEVEL_DEC] = "punc level decrement",
0326     [MSG_FUNCNAME_PUNC_LEVEL_INC] = "punc level increment",
0327     [MSG_FUNCNAME_QUIET] = "quiet",
0328     [MSG_FUNCNAME_RATE_DEC] = "rate decrement",
0329     [MSG_FUNCNAME_RATE_INC] = "rate increment",
0330     [MSG_FUNCNAME_READING_PUNC_DEC] = "reading punctuation decrement",
0331     [MSG_FUNCNAME_READING_PUNC_INC] = "reading punctuation increment",
0332     [MSG_FUNCNAME_SAY_ATTRIBUTES] = "say attributes",
0333     [MSG_FUNCNAME_SAY_FROM_LEFT] = "say from left",
0334     [MSG_FUNCNAME_SAY_FROM_TOP] = "say from top",
0335     [MSG_FUNCNAME_SAY_POSITION] = "say position",
0336     [MSG_FUNCNAME_SAY_SCREEN] = "say screen",
0337     [MSG_FUNCNAME_SAY_TO_BOTTOM] = "say to bottom",
0338     [MSG_FUNCNAME_SAY_TO_RIGHT] = "say to right",
0339     [MSG_FUNCNAME_SPEAKUP] = "speakup",
0340     [MSG_FUNCNAME_SPEAKUP_LOCK] = "speakup lock",
0341     [MSG_FUNCNAME_SPEAKUP_OFF] = "speakup off",
0342     [MSG_FUNCNAME_SPEECH_KILL] = "speech kill",
0343     [MSG_FUNCNAME_SPELL_DELAY_DEC] = "spell delay decrement",
0344     [MSG_FUNCNAME_SPELL_DELAY_INC] = "spell delay increment",
0345     [MSG_FUNCNAME_SPELL_WORD] = "spell word",
0346     [MSG_FUNCNAME_SPELL_WORD_PHONETICALLY] = "spell word phonetically",
0347     [MSG_FUNCNAME_TONE_DEC] = "tone decrement",
0348     [MSG_FUNCNAME_TONE_INC] = "tone increment",
0349     [MSG_FUNCNAME_VOICE_DEC] = "voice decrement",
0350     [MSG_FUNCNAME_VOICE_INC] = "voice increment",
0351     [MSG_FUNCNAME_VOLUME_DEC] = "volume decrement",
0352     [MSG_FUNCNAME_VOLUME_INC] = "volume increment",
0353     [MSG_FUNCNAME_WINDOW_CLEAR] = "window, clear",
0354     [MSG_FUNCNAME_WINDOW_SAY] = "window, say",
0355     [MSG_FUNCNAME_WINDOW_SET] = "window, set",
0356     [MSG_FUNCNAME_WINDOW_SILENCE] = "window, silence",
0357     [MSG_FUNCNAME_WORD_SAY_CURRENT] = "word, say current",
0358     [MSG_FUNCNAME_WORD_SAY_NEXT] = "word, say next",
0359     [MSG_FUNCNAME_WORD_SAY_PREVIOUS] = "word, say previous",
0360 };
0361 
0362 static struct msg_group_t all_groups[] = {
0363     {
0364         .name = "ctl_keys",
0365         .start = MSG_CTL_START,
0366         .end = MSG_CTL_END,
0367     },
0368     {
0369         .name = "colors",
0370         .start = MSG_COLORS_START,
0371         .end = MSG_COLORS_END,
0372     },
0373     {
0374         .name = "formatted",
0375         .start = MSG_FORMATTED_START,
0376         .end = MSG_FORMATTED_END,
0377     },
0378     {
0379         .name = "function_names",
0380         .start = MSG_FUNCNAMES_START,
0381         .end = MSG_FUNCNAMES_END,
0382     },
0383     {
0384         .name = "key_names",
0385         .start = MSG_KEYNAMES_START,
0386         .end = MSG_KEYNAMES_END,
0387     },
0388     {
0389         .name = "announcements",
0390         .start = MSG_ANNOUNCEMENTS_START,
0391         .end = MSG_ANNOUNCEMENTS_END,
0392     },
0393     {
0394         .name = "states",
0395         .start = MSG_STATES_START,
0396         .end = MSG_STATES_END,
0397     },
0398 };
0399 
0400 static const  int num_groups = ARRAY_SIZE(all_groups);
0401 
0402 char *spk_msg_get(enum msg_index_t index)
0403 {
0404     return speakup_msgs[index];
0405 }
0406 
0407 /*
0408  * Function: next_specifier
0409  * Finds the start of the next format specifier in the argument string.
0410  * Return value: pointer to start of format
0411  * specifier, or NULL if no specifier exists.
0412  */
0413 static char *next_specifier(char *input)
0414 {
0415     int found = 0;
0416     char *next_percent = input;
0417 
0418     while (next_percent && !found) {
0419         next_percent = strchr(next_percent, '%');
0420         if (next_percent) {
0421             /* skip over doubled percent signs */
0422             while (next_percent[0] == '%' &&
0423                    next_percent[1] == '%')
0424                 next_percent += 2;
0425             if (*next_percent == '%')
0426                 found = 1;
0427             else if (*next_percent == '\0')
0428                 next_percent = NULL;
0429         }
0430     }
0431 
0432     return next_percent;
0433 }
0434 
0435 /* Skip over 0 or more flags. */
0436 static char *skip_flags(char *input)
0437 {
0438     while ((*input != '\0') && strchr(" 0+-#", *input))
0439         input++;
0440     return input;
0441 }
0442 
0443 /* Skip over width.precision, if it exists. */
0444 static char *skip_width(char *input)
0445 {
0446     while (isdigit(*input))
0447         input++;
0448     if (*input == '.') {
0449         input++;
0450         while (isdigit(*input))
0451             input++;
0452     }
0453     return input;
0454 }
0455 
0456 /*
0457  * Skip past the end of the conversion part.
0458  * Note that this code only accepts a handful of conversion specifiers:
0459  * c d s x and ld.  Not accidental; these are exactly the ones used in
0460  * the default group of formatted messages.
0461  */
0462 static char *skip_conversion(char *input)
0463 {
0464     if ((input[0] == 'l') && (input[1] == 'd'))
0465         input += 2;
0466     else if ((*input != '\0') && strchr("cdsx", *input))
0467         input++;
0468     return input;
0469 }
0470 
0471 /*
0472  * Function: find_specifier_end
0473  * Return a pointer to the end of the format specifier.
0474  */
0475 static char *find_specifier_end(char *input)
0476 {
0477     input++;        /* Advance over %. */
0478     input = skip_flags(input);
0479     input = skip_width(input);
0480     input = skip_conversion(input);
0481     return input;
0482 }
0483 
0484 /*
0485  * Function: compare_specifiers
0486  * Compare the format specifiers pointed to by *input1 and *input2.
0487  * Return true if they are the same, false otherwise.
0488  * Advance *input1 and *input2 so that they point to the character following
0489  * the end of the specifier.
0490  */
0491 static bool compare_specifiers(char **input1, char **input2)
0492 {
0493     bool same = false;
0494     char *end1 = find_specifier_end(*input1);
0495     char *end2 = find_specifier_end(*input2);
0496     size_t length1 = end1 - *input1;
0497     size_t length2 = end2 - *input2;
0498 
0499     if ((length1 == length2) && !memcmp(*input1, *input2, length1))
0500         same = true;
0501 
0502     *input1 = end1;
0503     *input2 = end2;
0504     return same;
0505 }
0506 
0507 /*
0508  * Function: fmt_validate
0509  * Check that two format strings contain the same number of format specifiers,
0510  * and that the order of specifiers is the same in both strings.
0511  * Return true if the condition holds, false if it doesn't.
0512  */
0513 static bool fmt_validate(char *template, char *user)
0514 {
0515     bool valid = true;
0516     bool still_comparing = true;
0517     char *template_ptr = template;
0518     char *user_ptr = user;
0519 
0520     while (still_comparing && valid) {
0521         template_ptr = next_specifier(template_ptr);
0522         user_ptr = next_specifier(user_ptr);
0523         if (template_ptr && user_ptr) {
0524             /* Both have at least one more specifier. */
0525             valid = compare_specifiers(&template_ptr, &user_ptr);
0526         } else {
0527             /* No more format specifiers in one or both strings. */
0528             still_comparing = false;
0529             /* See if one has more specifiers than the other. */
0530             if (template_ptr || user_ptr)
0531                 valid = false;
0532         }
0533     }
0534     return valid;
0535 }
0536 
0537 /*
0538  * Function: msg_set
0539  * Description: Add a user-supplied message to the user_messages array.
0540  * The message text is copied to a memory area allocated with kmalloc.
0541  * If the function fails, then user_messages is untouched.
0542  * Arguments:
0543  * - index: a message number, as found in i18n.h.
0544  * - text:  text of message.  Not NUL-terminated.
0545  * - length: number of bytes in text.
0546  * Failure conditions:
0547  * -EINVAL -  Invalid format specifiers in formatted message or illegal index.
0548  * -ENOMEM -  Unable to allocate memory.
0549  */
0550 ssize_t spk_msg_set(enum msg_index_t index, char *text, size_t length)
0551 {
0552     char *newstr = NULL;
0553     unsigned long flags;
0554 
0555     if ((index < MSG_FIRST_INDEX) || (index >= MSG_LAST_INDEX))
0556         return -EINVAL;
0557 
0558     newstr = kmemdup_nul(text, length, GFP_KERNEL);
0559     if (!newstr)
0560         return -ENOMEM;
0561 
0562     if (index >= MSG_FORMATTED_START &&
0563         index <= MSG_FORMATTED_END &&
0564         !fmt_validate(speakup_default_msgs[index], newstr)) {
0565         kfree(newstr);
0566         return -EINVAL;
0567     }
0568     spin_lock_irqsave(&speakup_info.spinlock, flags);
0569     if (speakup_msgs[index] != speakup_default_msgs[index])
0570         kfree(speakup_msgs[index]);
0571     speakup_msgs[index] = newstr;
0572     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0573     return 0;
0574 }
0575 
0576 /*
0577  * Find a message group, given its name.  Return a pointer to the structure
0578  * if found, or NULL otherwise.
0579  */
0580 struct msg_group_t *spk_find_msg_group(const char *group_name)
0581 {
0582     struct msg_group_t *group = NULL;
0583     int i;
0584 
0585     for (i = 0; i < num_groups; i++) {
0586         if (!strcmp(all_groups[i].name, group_name)) {
0587             group = &all_groups[i];
0588             break;
0589         }
0590     }
0591     return group;
0592 }
0593 
0594 void spk_reset_msg_group(struct msg_group_t *group)
0595 {
0596     unsigned long flags;
0597     enum msg_index_t i;
0598 
0599     spin_lock_irqsave(&speakup_info.spinlock, flags);
0600 
0601     for (i = group->start; i <= group->end; i++) {
0602         if (speakup_msgs[i] != speakup_default_msgs[i])
0603             kfree(speakup_msgs[i]);
0604         speakup_msgs[i] = speakup_default_msgs[i];
0605     }
0606     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0607 }
0608 
0609 /* Called at initialization time, to establish default messages. */
0610 void spk_initialize_msgs(void)
0611 {
0612     memcpy(speakup_msgs, speakup_default_msgs,
0613            sizeof(speakup_default_msgs));
0614 }
0615 
0616 /* Free user-supplied strings when module is unloaded: */
0617 void spk_free_user_msgs(void)
0618 {
0619     enum msg_index_t index;
0620     unsigned long flags;
0621 
0622     spin_lock_irqsave(&speakup_info.spinlock, flags);
0623     for (index = MSG_FIRST_INDEX; index < MSG_LAST_INDEX; index++) {
0624         if (speakup_msgs[index] != speakup_default_msgs[index]) {
0625             kfree(speakup_msgs[index]);
0626             speakup_msgs[index] = speakup_default_msgs[index];
0627         }
0628     }
0629     spin_unlock_irqrestore(&speakup_info.spinlock, flags);
0630 }