2 * PROJECT: ReactOS Build Tools [Keyboard Layout Compiler]
3 * LICENSE: BSD - See COPYING.BSD in the top level directory
4 * FILE: tools/kbdtool/parser.c
5 * PURPOSE: Parsing Logic
6 * PROGRAMMERS: ReactOS Foundation
9 /* INCLUDES *******************************************************************/
13 /* GLOBALS ********************************************************************/
15 /* Internal parser data about everything that was parsed */
19 CHAR gDescription
[256];
21 CHAR gLocaleName
[256];
24 ULONG gKbdLayoutVersion
;
28 /* Table of keywords the parser recognizes */
29 PCHAR KeyWordList
[KEYWORD_COUNT
] =
50 /* FUNCTIONS ******************************************************************/
57 /* Check if we know this keyword */
58 for (i
= 0; i
< KEYWORD_COUNT
; i
++) if (strcmp(KeyWordList
[i
], p
) == 0) break;
60 /* If we didn't find anything, i will be KEYWORD_COUNT, which is invalid */
65 getVKName(IN ULONG VirtualKey
,
70 /* Loop for standard virtual key */
71 if (((VirtualKey
>= 'A') && (VirtualKey
<= 'Z')) ||
72 ((VirtualKey
>= '0') && (VirtualKey
<= '9')))
74 /* Fill out the name */
76 gVKeyName
[1] = VirtualKey
;
82 /* Check if a prefix is required */
86 strcpy(gVKeyName
, "VK_");
90 /* Otherwise, don't add anything */
91 strcpy(gVKeyName
, "");
94 /* Loop all virtual keys */
95 for (i
= 0; i
< 36; i
++)
97 /* Check if this key matches */
98 if (VKName
[i
].VirtualKey
== VirtualKey
)
100 /* Copy the key's name into the buffer */
101 strcat(gVKeyName
, VKName
[i
].Name
);
106 /* If we got here, then we failed, so print out an error name */
107 strcpy(gVKeyName
, "#ERROR#");
118 /* Compute the length of the string */
120 if (!Length
) return -1;
122 /* Check if this is is a simple key */
125 /* If it's a number, return it now */
126 if ((*p
>= '0') && (*p
<= '9')) return *p
;
128 /* Otherwise, convert the letter to upper case */
131 /* And make sure it's a valid letter */
132 if ((*p
>= 'A') && (*p
<='Z')) return *p
;
134 /* Otherwise, fail */
138 /* Otherwise, scan our virtual key names */
139 for (i
= 0; i
< 36; i
++)
141 /* Check if we have a match */
142 if (!strcmp(VKName
[i
].Name
, p
)) return VKName
[i
].VirtualKey
;
145 /* Check if this is a hex string */
146 if ((*p
== '0') && ((*(p
+ 1) == 'x') || (*(p
+ 1) == 'X')))
148 /* Get the key number from the hex string */
150 if (sscanf(p
, "0x%x", &KeyNumber
) == 1) return KeyNumber
;
158 getCharacterInfo(IN PCHAR State
,
159 OUT PULONG EntryChar
,
160 OUT PCHAR LigatureChar
)
163 ULONG CharInfo
= CHAR_NORMAL_KEY
;
167 /* Calculate the length of the state */
168 Length
= strlen(State
);
170 /* Check if this is at least a simple key state */
173 /* Read the first character and check if it's a dead key */
174 StateChar
= State
[Length
- 1];
175 if (StateChar
== '@')
177 /* This is a dead key */
178 CharInfo
= CHAR_DEAD_KEY
;
180 else if (StateChar
== '%')
182 /* This is another key */
183 CharInfo
= CHAR_OTHER_KEY
;
187 /* Check if this is a numerical key state */
188 if ((Length
- 1) >= 2)
190 /* Scan for extended character code entry */
191 if ((sscanf(State
, "%6x", &CharCode
) == 1) &&
192 (((Length
== 5) && (State
[0] == '0')) ||
193 ((Length
== 6) && ((State
[0] == '0') && (State
[1] == '0')))))
195 /* Handle a ligature key */
196 CharInfo
= CHAR_LIGATURE_KEY
;
198 /* Not yet handled */
199 printf("Ligatured character entries not yet supported!\n");
204 /* Get the normal character entry */
205 if (sscanf(State
, "%4x", &CharCode
) == 1)
207 /* Does the caller want the key? */
208 if (EntryChar
) *EntryChar
= CharCode
;
212 /* The entry is totally invalid */
213 if (Verbose
) printf("An unparseable character entry '%s' was found.\n", State
);
214 if (EntryChar
) *EntryChar
= 0;
215 CharInfo
= CHAR_INVALID_KEY
;
221 /* Save the key if the caller requested it */
222 if (EntryChar
) *EntryChar
= *State
;
225 /* Return the type of character this is */
230 NextLine(PCHAR LineBuffer
,
237 while (fgets(LineBuffer
, BufferSize
, File
))
242 /* Reset the pointer at the beginning of the line */
245 /* Now bypass all whitespace (and tabspace) */
246 while ((*p
) && ((*p
== ' ') || (*p
== '\t'))) p
++;
248 /* If this is an old-style comment, skip the line */
249 if (*p
== ';') continue;
251 /* Otherwise, check for new-style comment */
252 pp
= strstr(p
, "//");
255 /* We have a comment, so terminate there (unless the whole line is one) */
256 if (pp
== p
) continue;
261 /* No comment, so find the new line and terminate there */
266 /* We have a line! */
278 CHAR KeyWordChars
[32];
280 /* Scan each line, skipping it if it's not a keyword */
281 while (NextLine(gBuf
, sizeof(gBuf
), gfpInput
))
283 /* Read a single word */
284 if (sscanf(gBuf
, "%s", KeyWordChars
) == 1)
286 /* If the word is a keyword, stop skipping lines */
287 KeyWord
= isKeyWord(KeyWordChars
);
288 if (KeyWord
< KEYWORD_COUNT
) return KeyWord
;
292 /* We skipped all the possible lines, not finding anything */
293 return KEYWORD_COUNT
;
299 /* On Unicode files, we need to find the Unicode marker (FEEF) */
300 ASSERT(UnicodeFile
== FALSE
);
304 *gDescription
= '\0';
306 /* Scan for the values */
307 if (sscanf(gBuf
, "KBD %8s \"%40[^\"]\" %d", gKBDName
, gDescription
, &gID
) < 2)
309 /* Couldn't find them */
310 printf("Unable to read keyboard name or description.\n");
315 DPRINT1("KBD Name: [%8s] Description: [%40s] ID: [%d]\n", gKBDName
, gDescription
, gID
);
322 /* Scan for the value */
323 if (sscanf(gBuf
, "VERSION %d", &gKbdLayoutVersion
) < 1)
325 /* Couldn't find them */
326 printf("Unable to read keyboard version information.\n");
330 DPRINT1("VERSION [%d]\n", gKbdLayoutVersion
);
340 /* Scan for the value */
341 if (sscanf(gBuf
, "COPYRIGHT \"%40[^\"]\"", gCopyright
) < 1)
343 /* Couldn't find them */
344 printf("Unable to read the specified COPYRIGHT string.\n");
348 DPRINT1("COPYRIGHT [%40s]\n", gCopyright
);
358 /* Scan for the value */
359 if (sscanf(gBuf
, "COMPANY \"%85[^\"]\"", gCompany
) < 1)
361 /* Couldn't find them */
362 printf("Unable to read the specified COMPANY name.\n");
366 DPRINT1("COMPANY [%85s]\n", gCompany
);
376 /* Scan for the value */
377 if (sscanf(gBuf
, "LOCALENAME \"%40[^\"]\"", gLocaleName
) < 1)
379 /* Couldn't find them */
380 printf("Unable to read the specified COPYRIGHT string.\n");
384 DPRINT1("LOCALENAME [%40s]\n", gLocaleName
);
389 DoDESCRIPTIONS(IN PKEYNAME
* DescriptionData
)
395 PKEYNAME Description
;
398 *DescriptionData
= 0;
401 while (NextLine(gBuf
, 256, gfpInput
))
403 /* Search for token */
404 if (sscanf(gBuf
, "%s", Token
) != 1) continue;
406 /* Make sure it's not just a comment */
407 if (*Token
== ';') continue;
409 /* Make sure it's not a keyword */
410 KeyWord
= isKeyWord(Token
);
411 if (KeyWord
< KEYWORD_COUNT
) break;
413 /* Now scan for the language code */
414 if (sscanf(Token
, " %4x", &LanguageCode
) != 1)
417 printf("An invalid LANGID was specified.\n");
421 /* Now get the actual description */
422 if (sscanf(gBuf
, " %*4x %s[^\n]", Token
) != 1)
425 printf("A language description is missing.\n");
429 /* Get the description string and find the ending */
430 p
= strstr(gBuf
, Token
);
431 pp
= strchr(p
, '\n');
432 if (!pp
) pp
= strchr(p
, '\r');
434 /* Terminate the description string here */
437 /* Now allocate the description */
438 Description
= malloc(sizeof(KEYNAME
));
442 printf("Unable to allocate the KEYNAME struct (out of memory?).\n");
446 /* Fill out the structure */
447 Description
->Code
= LanguageCode
;
448 Description
->Name
= strdup(p
);
449 Description
->Next
= NULL
;
452 DPRINT1("LANGID: [%4x] Description: [%s]\n", Description
->Code
, Description
->Name
);
454 /* Point to it and advance the pointer */
455 *DescriptionData
= Description
;
456 DescriptionData
= &Description
->Next
;
464 DoLANGUAGENAMES(IN PKEYNAME
* LanguageData
)
476 while (NextLine(gBuf
, 256, gfpInput
))
478 /* Search for token */
479 if (sscanf(gBuf
, "%s", Token
) != 1) continue;
481 /* Make sure it's not just a comment */
482 if (*Token
== ';') continue;
484 /* Make sure it's not a keyword */
485 KeyWord
= isKeyWord(Token
);
486 if (KeyWord
< KEYWORD_COUNT
) break;
488 /* Now scan for the language code */
489 if (sscanf(Token
, " %4x", &LanguageCode
) != 1)
492 printf("An invalid LANGID was specified.\n");
496 /* Now get the actual language */
497 if (sscanf(gBuf
, " %*4x %s[^\n]", Token
) != 1)
500 printf("A language name is missing\n");
504 /* Get the language string and find the ending */
505 p
= strstr(gBuf
, Token
);
506 pp
= strchr(p
, '\n');
507 if (!pp
) pp
= strchr(p
, '\r');
509 /* Terminate the language string here */
512 /* Now allocate the language */
513 Language
= malloc(sizeof(KEYNAME
));
517 printf("Unable to allocate the KEYNAME struct (out of memory?).\n");
521 /* Fill out the structure */
522 Language
->Code
= LanguageCode
;
523 Language
->Name
= strdup(p
);
524 Language
->Next
= NULL
;
527 DPRINT1("LANGID: [%4x] Name: [%s]\n", Language
->Code
, Language
->Name
);
529 /* Point to it and advance the pointer */
530 *LanguageData
= Language
;
531 LanguageData
= &Language
->Next
;
539 DoKEYNAME(IN PKEYNAME
* KeyNameData
)
551 while (NextLine(gBuf
, 256, gfpInput
))
553 /* Search for token */
554 if (sscanf(gBuf
, "%s", Token
) != 1) continue;
556 /* Make sure it's not just a comment */
557 if (*Token
== ';') continue;
559 /* Make sure it's not a keyword */
560 KeyWord
= isKeyWord(Token
);
561 if (KeyWord
< KEYWORD_COUNT
) break;
563 /* Now scan for the character code */
564 if (sscanf(Token
, " %4x", &CharacterCode
) != 1)
567 printf("An invalid character code was specified.\n");
571 /* Now get the actual key name */
572 if (sscanf(gBuf
, " %*4x %s[^\n]", Token
) != 1)
575 printf("A key name is missing\n");
579 /* Get the key name string and find the ending */
580 p
= strstr(gBuf
, Token
);
581 pp
= strchr(p
, '\n');
582 if (!pp
) pp
= strchr(p
, '\r');
584 /* Terminate the key name string here */
587 /* Now allocate the language */
588 KeyName
= malloc(sizeof(KEYNAME
));
592 printf("Unable to allocate the KEYNAME struct (out of memory?).\n");
596 /* Fill out the structure */
597 KeyName
->Code
= CharacterCode
;
598 KeyName
->Name
= strdup(p
);
599 KeyName
->Next
= NULL
;
602 DPRINT1("CHARCODE: [%4x] Name: [%s]\n", KeyName
->Code
, KeyName
->Name
);
604 /* Point to it and advance the pointer */
605 *KeyNameData
= KeyName
;
606 KeyNameData
= &KeyName
->Next
;
614 DoSHIFTSTATE(IN PULONG StateCount
,
615 IN OUT PULONG ShiftStates
)
622 /* Reset the shift states */
623 for (i
= 0; i
< 8; i
++) ShiftStates
[i
] = -1;
625 /* Start with no states */
628 /* Scan for shift states */
629 while (NextLine(gBuf
, 256, gfpInput
))
631 /* Search for token */
632 if (sscanf(gBuf
, "%s", Token
) != 1) continue;
634 /* Make sure it's not a keyword */
635 KeyWord
= isKeyWord(Token
);
636 if (KeyWord
< KEYWORD_COUNT
) break;
638 /* Now scan for the shift state */
639 if (sscanf(gBuf
, " %1s[012367]", Token
) != 1)
641 /* We failed -- should we warn? */
642 if (Verbose
) printf("An invalid shift state '%s' was found (use 0, 1, 2, 3, 6, or 7.)\n", Token
);
646 /* Now read the state */
647 ShiftState
= atoi(Token
);
649 /* Scan existing states */
650 for (i
= 0; i
< *StateCount
; i
++)
652 /* Check for duplicate */
653 if ((ShiftStates
[i
] == ShiftState
) && (Verbose
))
656 printf("The state '%d' was duplicated for this Virtual Key.\n", ShiftStates
[i
]);
661 /* Make sure we won't overflow */
664 /* Save this state */
665 ShiftStates
[(*StateCount
)++] = ShiftState
;
669 /* Too many states -- should we warn? */
670 if (Verbose
) printf("There were too many states (you defined %d).\n", *StateCount
);
675 DPRINT1("Found %d Shift States: [", *StateCount
);
676 for (i
= 0; i
< *StateCount
; i
++) DPRINT1("%d ", ShiftStates
[i
]);
684 DoLIGATURE(PVOID LigatureData
)
686 printf("LIGATURE support is not yet implemented. Please bug Arch to fix it\n");
691 DoATTRIBUTES(PVOID AttributeData
)
693 printf("ATTRIBUTES support is not yet implemented. Please bug Arch to fix it\n");
700 printf("MODIFIERS support is not yet implemented. Please bug Arch to fix it\n");
705 DoDEADKEY(PVOID DeadKeyData
)
707 printf("DEADKEY support is not yet implemented. Please bug Arch to fix it\n");
712 DoLAYOUT(IN PLAYOUT LayoutData
,
713 IN PVOID LigatureData
,
714 IN PULONG ShiftStates
,
720 ULONG ScanCode
, CurrentCode
;
727 ULONG ScanCodeCount
= -1;
732 /* Zero out the layout */
733 memset(LayoutData
, 0, sizeof(LAYOUT
));
736 Entry
= &LayoutData
->Entry
[0];
737 while (NextLine(gBuf
, 256, gfpInput
))
739 /* Search for token */
740 if (sscanf(gBuf
, "%s", Token
) != 1) continue;
742 /* Make sure it's not just a comment */
743 if (*Token
== ';') continue;
745 /* Make sure it's not a keyword */
746 KeyWord
= isKeyWord(Token
);
747 if (KeyWord
< KEYWORD_COUNT
) break;
749 /* Now read the entry */
750 TokenCount
= sscanf(gBuf
, " %x %s %s", &ScanCode
, Token
, Cap
);
753 /* Full entry with cap */
756 else if (TokenCount
!= 2)
758 /* Fail, invalid LAYOUT entry */
759 printf("There are not enough columns in the layout list.\n");
764 /* Simplified layout with no cap */
769 DPRINT1("RAW ENTRY: [%x %s %s]\n", ScanCode
, Token
, Cap
);
771 if (++ScanCodeCount
>= 110)
774 printf("ScanCode %02x - too many scancodes here to parse.\n", ScanCode
);
778 /* Fill out this entry */
779 Entry
->ScanCode
= ScanCode
;
780 Entry
->LineCount
= gLineCount
;
782 /* Loop scancode table */
783 for (i
= 0; i
< 110; i
++)
785 /* Get the current code */
786 CurrentCode
= ScVk
[i
].ScanCode
;
787 if (CurrentCode
== 0xFFFF)
790 if (Verbose
) printf("A new scancode is being defined: 0x%2X, %s\n", Entry
->ScanCode
, Token
);
792 /* Fill out the entry */
793 Entry
->VirtualKey
= getVKNum(Token
);
796 else if (ScanCode
== CurrentCode
)
798 /* Make sure we didn't already process it */
799 if (ScVk
[i
].Processed
)
802 printf("Scancode %X was previously defined.\n", ScanCode
);
806 /* Check if there is a valid virtual key */
807 if (ScVk
[i
].VirtualKey
== 0xFFFF)
810 printf("The Scancode you tried to use (%X) is reserved.\n", ScanCode
);
814 /* Fill out the entry */
815 Entry
->OriginalVirtualKey
= ScVk
[i
].VirtualKey
;
816 Entry
->Name
= ScVk
[i
].Name
;
821 /* The entry is now processed */
822 Entry
->Processed
= TRUE
;
823 ScVk
[i
].Processed
= TRUE
;
825 /* Get the virtual key from the entry */
826 VirtualKey
= getVKNum(Token
);
827 Entry
->VirtualKey
= VirtualKey
;
828 DPRINT1("ENTRY: [%x %x %x %s] with ",
829 Entry
->VirtualKey
, Entry
->OriginalVirtualKey
, Entry
->ScanCode
, Entry
->Name
);
831 /* Make sure it's valid */
832 if (VirtualKey
== 0xFFFF)
835 if (Verbose
) printf("An invalid Virtual Key '%s' was defined.\n", Token
);
839 /* Is this a full entry */
842 /* Do we have SGCAP data? Set cap mode to 2 */
843 if (!strcmp(Cap
, "SGCAP")) *Cap
= '2';
845 /* Read the cap mode */
846 if (sscanf(Cap
, "%1d[012]", &Entry
->Cap
) != 1)
848 /* Invalid cap mode */
849 printf("invalid Cap specified (%s). Must be 0, 1, or 2.\n", Cap
);
854 /* Read the states */
856 " %*s %*s %*s %s %s %s %s %s %s %s %s",
865 Entry
->StateCount
= Count
;
866 DPRINT1("%d STATES: [", Count
);
868 /* Check if there are less than 2 states */
869 if ((Count
< 2) && (FullEntry
))
872 printf("You must have at least 2 characters.\n");
876 /* Loop all states */
877 for (i
= 0; i
< Count
; i
++)
879 /* Check if this is an undefined state */
880 DPRINT1("%s ", State
[i
]);
881 if (!strcmp(State
[i
], "-1"))
883 /* No data for this state */
884 Entry
->CharData
[i
] = -1;
888 /* Otherwise, check what kind of character this is */
889 CharacterType
= getCharacterInfo(State
[i
],
892 if (CharacterType
== CHAR_DEAD_KEY
)
894 /* Save it as such */
895 Entry
->DeadCharData
[i
] = 1;
897 else if (CharacterType
== CHAR_OTHER_KEY
)
899 /* Save it as such */
900 Entry
->OtherCharData
[i
] = 1;
904 /* Check for sanity checks */
908 /* Not yet handled... */
909 printf("Sanity checks not yet handled!\n");
913 /* Check if we had SGCAP data */
916 /* Not yet handled... */
917 printf("SGCAP state not yet handled!\n");
922 /* Check if we have found any ScanCode in the file */
924 if (ScanCodeCount
== -1)
926 printf("No ScanCode found!\n");
930 /* Process the scan code table */
931 Entry
= &LayoutData
->Entry
[ScanCodeCount
];
932 for (i
= 0; i
< 110; i
++)
934 /* Get the scan code */
935 CurrentCode
= ScVk
[i
].ScanCode
;
936 if (CurrentCode
== 0xFFFF) break;
938 /* Check if this entry had been processed */
939 if (ScVk
[i
].Processed
)
942 ScVk
[i
].Processed
= FALSE
;
946 /* Do we have too many? */
947 if (++ScanCodeCount
>= 110)
950 printf("ScanCode %02x - too many scancodes here to parse.\n", CurrentCode
);
954 /* Build an entry for it */
956 Entry
->ScanCode
= CurrentCode
;
957 Entry
->VirtualKey
= ScVk
[i
].VirtualKey
;
958 Entry
->OriginalVirtualKey
= ScVk
[i
].VirtualKey
;
959 Entry
->Name
= ScVk
[i
].Name
;
960 Entry
->Processed
= TRUE
;
961 Entry
->LineCount
= 0;
962 DPRINT1("AUTOMATIC ENTRY: [%x %x %s]\n",
963 Entry
->VirtualKey
, Entry
->ScanCode
, Entry
->Name
);
967 /* Skip what's left */
974 ULONG KeyWords
[KEYWORD_COUNT
];
977 ULONG ShiftStates
[8];
978 PKEYNAME DescriptionData
= NULL
, LanguageData
= NULL
;
979 PKEYNAME KeyNameData
= NULL
, KeyNameExtData
= NULL
, KeyNameDeadData
= NULL
;
980 PVOID AttributeData
= NULL
, LigatureData
= NULL
, DeadKeyData
= NULL
;
984 KeyWord
= SkipLines();
985 if (KeyWord
>= KEYWORD_COUNT
)
987 /* Invalid keyword count, fail */
989 printf("No keywords were found in '%s'.\n", gpszFileName
);
993 /* Now parse the keywords */
994 memset(KeyWords
, 0, sizeof(KeyWords
));
995 while (KeyWord
< (KEYWORD_COUNT
- 1))
997 /* Save this keyword */
1000 /* Check for duplicate entires, other than DEADKEY, which is okay */
1001 if ((KeyWord
!= 9) && (KeyWords
[KeyWord
] > 1) && (Verbose
))
1003 /* On a verbose run, warn the user */
1004 printf("The '%s' keyword appeared multiple times.\n",
1005 KeyWordList
[KeyWord
]);
1008 /* Now parse this keyword */
1014 DPRINT1("Found KBD section\n");
1021 DPRINT1("Found VERSION section\n");
1022 KeyWord
= DoVERSION();
1028 DPRINT1("Found COPYRIGHT section\n");
1029 KeyWord
= DoCOPYRIGHT();
1035 DPRINT1("Found COMPANY section\n");
1036 KeyWord
= DoCOMPANY();
1042 DPRINT1("Found LOCALENAME section\n");
1043 KeyWord
= DoLOCALENAME();
1049 DPRINT1("Found MODIFIERS section\n");
1050 KeyWord
= DoMODIFIERS();
1056 DPRINT1("Found SHIFTSTATE section\n");
1057 KeyWord
= DoSHIFTSTATE(&StateCount
, ShiftStates
);
1070 DPRINT1("Found ATTRIBUTES section\n");
1071 KeyWord
= DoATTRIBUTES(&AttributeData
);
1077 DPRINT1("Found LAYOUT section\n");
1078 KeyWord
= DoLAYOUT(&g_Layout
,
1087 DPRINT1("Found DEADKEY section\n");
1088 KeyWord
= DoDEADKEY(&DeadKeyData
);
1094 DPRINT1("Found LIGATURE section\n");
1095 KeyWord
= DoLIGATURE(&LigatureData
);
1101 DPRINT1("Found KEYNAME section\n");
1102 KeyWord
= DoKEYNAME(&KeyNameData
);
1108 DPRINT1("Found KEYNAME_EXT section\n");
1109 KeyWord
= DoKEYNAME(&KeyNameExtData
);
1115 DPRINT1("Found KEYNAME_DEAD section\n");
1116 KeyWord
= DoKEYNAME(&KeyNameDeadData
);
1122 DPRINT1("Found DESCRIPTIONS section\n");
1123 KeyWord
= DoDESCRIPTIONS(&DescriptionData
);
1129 DPRINT1("Found LANGUAGENAMES section\n");
1130 KeyWord
= DoLANGUAGENAMES(&LanguageData
);
1136 DPRINT1("Found ENDKBD section\n");
1137 KeyWord
= SkipLines();
1149 /* Now enter the output phase */
1150 return DoOutput(StateCount
,