Eric, I do not see how system firmware can reliably support multiple RegEx implementations using a single Regular_Expression_Protocol instance. That was definitely not the intention of, nor was it called for in the UEFI 2.5 specification.
The library was just to allow someone to easily replace a single RegEx implementation with another. If that is not seen as important, we can easily drop the library, and keep a single driver implementation that produces a single instance of the protocol with a single RegEx implementation. Other drivers (most likely not in system firmware) can implement other flavors with new instances of Regular Expression Protocol. Does this sound reasonable? -----Original Message----- From: Dong, Eric [mailto:eric.d...@intel.com] Sent: Wednesday, May 27, 2015 12:05 AM To: El-Haj-Mahmoud, Samer; edk2-devel@lists.sourceforge.net; Doman, Jonathan Cc: Rothman, Michael A; Gao, Liming; Ni, Ruiyu Subject: RE: [edk2] [PATCH] MdeModulePkg: Regular expression protocol Samer & Jonathan, Add my comments below. Thanks, Eric -----Original Message----- From: El-Haj-Mahmoud, Samer [mailto:samer.el-haj-mahm...@hp.com] Sent: Wednesday, May 27, 2015 4:39 AM To: edk2-devel@lists.sourceforge.net; Dong, Eric Subject: RE: [edk2] [PATCH] MdeModulePkg: Regular expression protocol Eric, The original design from UEFI 2.5 (as discussed in USWG) is to allow multiple implementations of the EFI_REGULAR_EXPRESSION_PROTOCOL by multiple drivers. This includes the ability for a UEFI driver, say from an IHV provided card firmware, extend the capabilities of the base driver supplied by the system firmware. That architecture (multiple RegEx protocols from different drivers) does not need the EDK2 router library design. The language in the UEFI spec (and the code in the HII Browser) is clearly expecting this design (looping through all RegEx protocol handles and trying them out one by one). [[[Eric]]] yes, agreed. I see that BIOS will provide only 1 RegEx implementation (there is REALLY no need for multiple implementations in BIOS). Other implementations can be provided by ISVs and IHVs by installing new instances of the RegEx protocol. [[[Eric]]] I don't know the direction now whether the bios need to support only one RegEx or more. If the bios only need to provide one RegEx implementation, I think we don't need the library, just add these code to the driver is enough. If we add the library to support later add more regex support, I think my design is more flexibility and has the capability add more regex support later. Hope this helps clarify the intention behind this implementation. Thanks, --Samer -----Original Message----- From: Doman, Jonathan Sent: Tuesday, May 26, 2015 12:41 PM To: Dong, Eric; edk2-devel@lists.sourceforge.net Subject: Re: [edk2] [PATCH] MdeModulePkg: Regular expression protocol I don't quite understand why the extra indirection layer is useful. The driver can be connected to a specific library B in the dsc file. If you later want to switch to a library C that provides more features, then update the dsc along with the syntax guids in the driver code. I think this is sufficient, and a little simpler, but I will consider your suggestion. [[[Eric]]] If follow your design, later when others provide another regex support, this design can't support both library at same time. Or it must copy all your code and add his new code to support more. But if follow my design, he just need to register his new regex(in library C) to library A and the system can support both library B and library C. It has the capability to accept more regex without change current code infrastructure. ________________________________________ From: Dong, Eric [eric.d...@intel.com] Sent: Monday, May 25, 2015 03:21 To: edk2-devel@lists.sourceforge.net; Doman, Jonathan Subject: RE: [edk2] [PATCH] MdeModulePkg: Regular expression protocol Doman, I think this infrastructure don't have the flexibility to add more regex syntax support later. I think we need to enhance the design. Maybe we need to add two libraries for RegularExpressionDxe driver. One library (Library A) is the router library and used by RegularExpressionDxe driver. This other library (Library B) provide Slre regex implementation, and just register itself to the library A. later we can add library C provide another regex support and also register in library A. Edkii already has similar design for TPM2, we can reference it. The sample code in Edk2\SecurityPkg\Library\Tpm2DeviceLibRouter\Tpm2DeviceLibRouterDxe.inf and Edk2\SecurityPkg\Library\Tpm2DeviceLibDTpm\ Tpm2InstanceLibDTpm.inf. Tpm2DeviceLibRouter is library A and Tpm2DeviceLibDTpm is library B. What do you think? Thanks, Eric -----Original Message----- From: Jonathan Doman [mailto:jonathan.do...@hp.com] Sent: Thursday, May 21, 2015 1:57 AM To: edk2-devel@lists.sourceforge.net Subject: [edk2] [PATCH] MdeModulePkg: Regular expression protocol Add RegularExpressionDxe driver. Add RegExLib library header and sample implementation SlreRegExLib, based on open-source SLRE library (old version with permissive license). Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jonathan Doman <jonathan.do...@hp.com> --- MdeModulePkg/Include/Library/RegExLib.h | 165 +++++ MdeModulePkg/Library/SlreRegExLib/SLRE/License.txt | 9 + .../Library/SlreRegExLib/SLRE/SlreUefiPort.h | 31 + MdeModulePkg/Library/SlreRegExLib/SLRE/slre.c | 684 +++++++++++++++++++++ MdeModulePkg/Library/SlreRegExLib/SLRE/slre.h | 104 ++++ MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.c | 301 +++++++++ MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.inf | 38 ++ MdeModulePkg/MdeModulePkg.dec | 3 + MdeModulePkg/MdeModulePkg.dsc | 2 + .../RegularExpressionDxe/RegularExpressionDxe.c | 306 +++++++++ .../RegularExpressionDxe/RegularExpressionDxe.inf | 44 ++ 11 files changed, 1687 insertions(+) create mode 100644 MdeModulePkg/Include/Library/RegExLib.h create mode 100644 MdeModulePkg/Library/SlreRegExLib/SLRE/License.txt create mode 100644 MdeModulePkg/Library/SlreRegExLib/SLRE/SlreUefiPort.h create mode 100644 MdeModulePkg/Library/SlreRegExLib/SLRE/slre.c create mode 100644 MdeModulePkg/Library/SlreRegExLib/SLRE/slre.h create mode 100644 MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.c create mode 100644 MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.inf create mode 100644 MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.c create mode 100644 MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf diff --git a/MdeModulePkg/Include/Library/RegExLib.h b/MdeModulePkg/Include/Library/RegExLib.h new file mode 100644 index 0000000..75c930d --- /dev/null +++ b/MdeModulePkg/Include/Library/RegExLib.h @@ -0,0 +1,165 @@ +/** @file + + Regular Expression Library Class + + Copyright (c) 2014-2015, Hewlett-Packard Development Company, L.P. + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License that accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ +#ifndef REG_EX_LIB_H +#define REG_EX_LIB_H + +#include <Uefi.h> + +typedef VOID* REG_EX_HANDLE; + +/** + Container for returning information about subexpression captures resulting + from successful matches. +**/ +typedef struct { + CONST CHAR8 *Ptr; ///< Beginning of captured subexpression within matched string. + INT32 Length; ///< Length of captured subexpression. +} REG_EX_CAPTURE; + +/** + Compile a regular expression in preparation for matching with RegExMatch. + A call to RegExFree is always required to free resources regardless of + whether compilation was successful. + + @param[out] Handle Pointer to RegExHandle to initialize. Must be freed + with a call to RegExFree when no longer needed. + @param[in] Pattern Pointer to null-terminated ASCII string containing + the desired regular expression. + + @return EFI_SUCCESS Operation was successful. + EFI_ABORTED Pattern could not be compiled. More information + may be found through RegExErrorMessage. + EFI_INVALID_PARAMETER A required parameter was NULL. + EFI_OUT_OF_RESOURCES Memory allocation failed. +**/ +EFI_STATUS +EFIAPI +RegExCompile ( + OUT REG_EX_HANDLE *Handle, + IN CONST CHAR8 *Pattern + ); + +/** + Search the input string for anything that matches the regular expression. + + Be aware that the specific implementation of this library class may not + support all regular expression syntax, which could lead to unexpected results. + + If no match is found, the contents of Captures is undefined (i.e. it may be + overwritten with invalid data). + + @param[in] Handle The compiled regular expression. + @param[in] String Pointer to null-terminated ASCII string to match + against the regular expression. + @param[out] Captures Pointer to array of REG_EX_CAPTURE objects to receive + the captured groups in the event of a match. According + to convention, the full (sub-)string match is put in + Captures[0], and the results of N capturing groups are + put in Captures[1:N]. The caller must allocate the + appropriate sized array (N+1). This parameter is optional + and may be NULL. + @param[in] NumCaptures Number of elements in the Captures array. If Captures + is NULL, this should be zero. + + @return TRUE Match was found. + FALSE No match was found. +**/ +BOOLEAN +EFIAPI +RegExMatch ( + IN REG_EX_HANDLE Handle, + IN CONST CHAR8 *String, + OUT REG_EX_CAPTURE Captures[], OPTIONAL + IN INT32 NumCaptures + ); + +/** + Free any resources associated with this regular expression when it's no + longer needed. + + @param[in] Handle Handle to regular expression to be freed. +**/ +VOID +EFIAPI +RegExFree ( + IN REG_EX_HANDLE Handle + ); + +/** + When compilation fails, the internal implementation may generate an error + string which can be read with this function. However, there may not always + be an error message when it seems like there should be, as this depends + on the specific implementation. + + @param[in] Handle Handle to regular expression. + + @return Pointer to null-terminated ASCII error message. May be NULL or + an empty string if there is no applicable error. +**/ +CONST CHAR8* +EFIAPI +RegExErrorMessage ( + IN REG_EX_HANDLE Handle + ); + +/** + Helper function. Match the string against a regex pattern. + + @param[in] String The string to check. + @param[in] Pattern The regex pattern. + + @retval TRUE The string matches the pattern. + @retval FALSE The string does not match the pattern. +**/ +BOOLEAN +RegExMatchString ( + IN CONST CHAR16 *String, + IN CONST CHAR8 *AsciiPattern + ); + +/** + Match the string against a regex pattern. + + @param[in] AnsiString The string to check. + @param[in] AnsiPattern The regex pattern. + + @retval TRUE The string matches the pattern. + @retval FALSE The string does not match the pattern. +**/ +BOOLEAN +RegExMatchStringAscii ( + IN CONST CHAR8 *AsciiString, + IN CONST CHAR8 *AsciiPattern + ); + +/** + Match the string against a regex pattern. + + If you happen to have a UCS-2 pattern string, this helper function will do + the ASCII conversion for you. + + @param[in] String The string to check. + @param[in] Pattern The regex pattern. + + @retval TRUE The string matches the pattern. + @retval FALSE The string does not match the pattern. +**/ +BOOLEAN +RegExMatchStringUnicode ( + IN CONST CHAR16 *String, + IN CONST CHAR16 *Pattern + ); + +#endif // !REG_EX_LIB_H diff --git a/MdeModulePkg/Library/SlreRegExLib/SLRE/License.txt b/MdeModulePkg/Library/SlreRegExLib/SLRE/License.txt new file mode 100644 index 0000000..fb0df8f --- /dev/null +++ b/MdeModulePkg/Library/SlreRegExLib/SLRE/License.txt @@ -0,0 +1,9 @@ +http://slre.sourceforge.net/ + +Copyright (c) 2004-2005 Sergey Lyubka <vale...@gmail.com> +All rights reserved + +"THE BEER-WARE LICENSE" (Revision 42): +Sergey Lyubka wrote this file. As long as you retain this notice you +can do whatever you want with this stuff. If we meet some day, and you think +this stuff is worth it, you can buy me a beer in return. diff --git a/MdeModulePkg/Library/SlreRegExLib/SLRE/SlreUefiPort.h b/MdeModulePkg/Library/SlreRegExLib/SLRE/SlreUefiPort.h new file mode 100644 index 0000000..e7389d8 --- /dev/null +++ b/MdeModulePkg/Library/SlreRegExLib/SLRE/SlreUefiPort.h @@ -0,0 +1,31 @@ +/** + @file + + Header to rewrite stdlib references within slre + + Copyright (c) 2014-2015, Hewlett-Packard Development Company, L.P.<BR> + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License that accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ +#ifndef SLRE_UEFI_PORT_H +#define SLRE_UEFI_PORT_H + +#include <Library/DebugLib.h> +#include <Library/BaseMemoryLib.h> + +#define assert(x) ASSERT(x); +#define strchr(str,c) ScanMem8(str,sizeof(str),c) +#define memmove(dest,src,len) CopyMem(dest,src,len) +#define memcmp(dest,src,len) CompareMem(dest,src,len) +#define printf(...) + +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#define isspace(c) ((c) == '\t' || (c) == '\n' || (c) == '\v' || (c) == 0xc || (c) == '\r' || (c) == ' ') + +#endif // !SLRE_UEFI_PORT_H diff --git a/MdeModulePkg/Library/SlreRegExLib/SLRE/slre.c b/MdeModulePkg/Library/SlreRegExLib/SLRE/slre.c new file mode 100644 index 0000000..ecb3d30 --- /dev/null +++ b/MdeModulePkg/Library/SlreRegExLib/SLRE/slre.c @@ -0,0 +1,684 @@ +/** + Copyright (c) 2014-2015, Hewlett-Packard Development Company, L.P.<BR> + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License that accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + Copyright (c) 2004-2005 Sergey Lyubka <vale...@gmail.com> + All rights reserved + + "THE BEER-WARE LICENSE" (Revision 42): + Sergey Lyubka wrote this file. As long as you retain this notice you + can do whatever you want with this stuff. If we meet some day, and you think + this stuff is worth it, you can buy me a beer in return. +**/ + +#if 0 +#include <stdio.h> +#include <assert.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#endif + +#include "slre.h" + +enum {END, BRANCH, ANY, EXACT, ANYOF, ANYBUT, OPEN, CLOSE, BOL, EOL, + STAR, PLUS, STARQ, PLUSQ, QUEST, SPACE, NONSPACE, DIGIT}; + +#if 0 +static struct { + const char *name; + int narg; + const char *flags; +} opcodes[] = { + {"END", 0, ""}, /* End of code block or program */ + {"BRANCH", 2, "oo"}, /* Alternative operator, "|" */ + {"ANY", 0, ""}, /* Match any character, "." */ + {"EXACT", 2, "d"}, /* Match exact string */ + {"ANYOF", 2, "D"}, /* Match any from set, "[]" */ + {"ANYBUT", 2, "D"}, /* Match any but from set, "[^]"*/ + {"OPEN ", 1, "i"}, /* Capture start, "(" */ + {"CLOSE", 1, "i"}, /* Capture end, ")" */ + {"BOL", 0, ""}, /* Beginning of string, "^" */ + {"EOL", 0, ""}, /* End of string, "$" */ + {"STAR", 1, "o"}, /* Match zero or more times "*" */ + {"PLUS", 1, "o"}, /* Match one or more times, "+" */ + {"STARQ", 1, "o"}, /* Non-greedy STAR, "*?" */ + {"PLUSQ", 1, "o"}, /* Non-greedy PLUS, "+?" */ + {"QUEST", 1, "o"}, /* Match zero or one time, "?" */ + {"SPACE", 0, ""}, /* Match whitespace, "\s" */ + {"NONSPACE", 0, ""}, /* Match non-space, "\S" */ + {"DIGIT", 0, ""} /* Match digit, "\d" */ +}; +#endif + +/* + * Commands and operands are all unsigned char (1 byte long). All code offsets + * are relative to current address, and positive (always point forward). Data + * offsets are absolute. Commands with operands: + * + * BRANCH offset1 offset2 + * Try to match the code block that follows the BRANCH instruction + * (code block ends with END). If no match, try to match code block that + * starts at offset1. If either of these match, jump to offset2. + * + * EXACT data_offset data_length + * Try to match exact string. String is recorded in data section from + * data_offset, and has length data_length. + * + * OPEN capture_number + * CLOSE capture_number + * If the user have passed 'struct cap' array for captures, OPEN + * records the beginning of the matched substring (cap->ptr), CLOSE + * sets the length (cap->len) for respective capture_number. + * + * STAR code_offset + * PLUS code_offset + * QUEST code_offset + * *, +, ?, respectively. Try to gobble as much as possible from the + * matched buffer, until code block that follows these instructions + * matches. When the longest possible string is matched, + * jump to code_offset + * + * STARQ, PLUSQ are non-greedy versions of STAR and PLUS. + */ + +static const char *meta_chars = "|.^$*+?()[\\"; + +#if 0 +static void +print_character_set(FILE *fp, const unsigned char *p, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i > 0) + (void) fputc(',', fp); + if (p[i] == 0) { + i++; + if (p[i] == 0) + (void) fprintf(fp, "\\x%02x", p[i]); + else + (void) fprintf(fp, "%s", opcodes[p[i]].name); + } else if (isprint(p[i])) { + (void) fputc(p[i], fp); + } else { + (void) fprintf(fp,"\\x%02x", p[i]); + } + } +} + +void +slre_dump(const struct slre *r, FILE *fp) +{ + int i, j, ch, op, pc; + + for (pc = 0; pc < r->code_size; pc++) { + + op = r->code[pc]; + (void) fprintf(fp, "%3d %s ", pc, opcodes[op].name); + + for (i = 0; opcodes[op].flags[i] != '\0'; i++) + switch (opcodes[op].flags[i]) { + case 'i': + (void) fprintf(fp, "%d ", r->code[pc + 1]); + pc++; + break; + case 'o': + (void) fprintf(fp, "%d ", + pc + r->code[pc + 1] - i); + pc++; + break; + case 'D': + print_character_set(fp, r->data + + r->code[pc + 1], r->code[pc + 2]); + pc += 2; + break; + case 'd': + (void) fputc('"', fp); + for (j = 0; j < r->code[pc + 2]; j++) { + ch = r->data[r->code[pc + 1] + j]; + if (isprint(ch)) + (void) fputc(ch, fp); + else + (void) fprintf(fp,"\\x%02x",ch); + } + (void) fputc('"', fp); + pc += 2; + break; + } + + (void) fputc('\n', fp); + } +} +#endif + +static void +set_jump_offset(struct slre *r, int pc, int offset) +{ + assert(offset < r->code_size); + + if (r->code_size - offset > 0xff) { + r->err_str = "Jump offset is too big"; + } else { + r->code[pc] = (unsigned char) (r->code_size - offset); + } +} + +static void +emit(struct slre *r, int code) +{ + if (r->code_size >= (int) (sizeof(r->code) / sizeof(r->code[0]))) + r->err_str = "RE is too long (code overflow)"; + else + r->code[r->code_size++] = (unsigned char) code; +} + +static void +store_char_in_data(struct slre *r, int ch) +{ + if (r->data_size >= (int) sizeof(r->data)) + r->err_str = "RE is too long (data overflow)"; + else + r->data[r->data_size++] = (unsigned char)ch; +} + +static void +exact(struct slre *r, const char **re) +{ + int old_data_size = r->data_size; + + while (**re != '\0' && (strchr(meta_chars, **re)) == NULL) + store_char_in_data(r, *(*re)++); + + emit(r, EXACT); + emit(r, old_data_size); + emit(r, r->data_size - old_data_size); +} + +static int +get_escape_char(const char **re) +{ + int res; + + switch (*(*re)++) { + case 'n': res = '\n'; break; + case 'r': res = '\r'; break; + case 't': res = '\t'; break; + case '0': res = 0; break; + case 'S': res = NONSPACE << 8; break; + case 's': res = SPACE << 8; break; + case 'd': res = DIGIT << 8; break; + default: res = (*re)[-1]; break; + } + + return (res); +} + +static void +anyof(struct slre *r, const char **re) +{ + int esc, old_data_size = r->data_size, op = ANYOF; + + if (**re == '^') { + op = ANYBUT; + (*re)++; + } + + while (**re != '\0') + + switch (*(*re)++) { + case ']': + emit(r, op); + emit(r, old_data_size); + emit(r, r->data_size - old_data_size); + return; + /* NOTREACHED */ + break; + case '\\': + esc = get_escape_char(re); + if ((esc & 0xff) == 0) { + store_char_in_data(r, 0); + store_char_in_data(r, esc >> 8); + } else { + store_char_in_data(r, esc); + } + break; + default: + store_char_in_data(r, (*re)[-1]); + break; + } + + r->err_str = "No closing ']' bracket"; +} + +static void +relocate(struct slre *r, int begin, int shift) +{ + emit(r, END); + memmove(r->code + begin + shift, r->code + begin, r->code_size - begin); + r->code_size += shift; +} + +static void +quantifier(struct slre *r, int prev, int op) +{ + if (r->code[prev] == EXACT && r->code[prev + 2] > 1) { + r->code[prev + 2]--; + emit(r, EXACT); + emit(r, r->code[prev + 1] + r->code[prev + 2]); + emit(r, 1); + prev = r->code_size - 3; + } + relocate(r, prev, 2); + r->code[prev] = (unsigned char)op; + set_jump_offset(r, prev + 1, prev); +} + +static void +exact_one_char(struct slre *r, int ch) +{ + emit(r, EXACT); + emit(r, r->data_size); + emit(r, 1); + store_char_in_data(r, ch); +} + +static void +fixup_branch(struct slre *r, int fixup) +{ + if (fixup > 0) { + emit(r, END); + set_jump_offset(r, fixup, fixup - 2); + } +} + +static void +compile(struct slre *r, const char **re) +{ + int op, esc, branch_start, last_op, fixup, cap_no, level; + + fixup = 0; + level = r->num_caps; + branch_start = last_op = r->code_size; + + for (;;) + switch (*(*re)++) { + case '\0': + (*re)--; + return; + /* NOTREACHED */ + break; + case '^': + emit(r, BOL); + break; + case '$': + emit(r, EOL); + break; + case '.': + last_op = r->code_size; + emit(r, ANY); + break; + case '[': + last_op = r->code_size; + anyof(r, re); + break; + case '\\': + last_op = r->code_size; + esc = get_escape_char(re); + if (esc & 0xff00) { + emit(r, esc >> 8); + } else { + exact_one_char(r, esc); + } + break; + case '(': + last_op = r->code_size; + cap_no = ++r->num_caps; + emit(r, OPEN); + emit(r, cap_no); + + compile(r, re); + if (*(*re)++ != ')') { + r->err_str = "No closing bracket"; + return; + } + + emit(r, CLOSE); + emit(r, cap_no); + break; + case ')': + (*re)--; + fixup_branch(r, fixup); + if (level == 0) { + r->err_str = "Unbalanced brackets"; + return; + } + return; + /* NOTREACHED */ + break; + case '+': + case '*': + op = (*re)[-1] == '*' ? STAR: PLUS; + if (**re == '?') { + (*re)++; + op = op == STAR ? STARQ : PLUSQ; + } + quantifier(r, last_op, op); + break; + case '?': + quantifier(r, last_op, QUEST); + break; + case '|': + fixup_branch(r, fixup); + relocate(r, branch_start, 3); + r->code[branch_start] = BRANCH; + set_jump_offset(r, branch_start + 1, branch_start); + fixup = branch_start + 2; + r->code[fixup] = 0xff; + break; + default: + (*re)--; + last_op = r->code_size; + exact(r, re); + break; + } +} + +int +slre_compile(struct slre *r, const char *re) +{ + r->err_str = NULL; + r->code_size = r->data_size = r->num_caps = r->anchored = 0; + + if (*re == '^') + r->anchored++; + + emit(r, OPEN); /* This will capture what matches full RE */ + emit(r, 0); + + while (*re != '\0') + compile(r, &re); + + if (r->code[2] == BRANCH) + fixup_branch(r, 4); + + emit(r, CLOSE); + emit(r, 0); + emit(r, END); + + return (r->err_str == NULL ? 1 : 0); +} + +static int match(const struct slre *, int, + const char *, int, int *, struct cap *); + +static void +loop_greedy(const struct slre *r, int pc, const char *s, int len, int *ofs) +{ + int saved_offset, matched_offset; + + saved_offset = matched_offset = *ofs; + + while (match(r, pc + 2, s, len, ofs, NULL)) { + saved_offset = *ofs; + if (match(r, pc + r->code[pc + 1], s, len, ofs, NULL)) + matched_offset = saved_offset; + *ofs = saved_offset; + } + + *ofs = matched_offset; +} + +static void +loop_non_greedy(const struct slre *r, int pc, const char *s,int len, int *ofs) +{ + int saved_offset = *ofs; + + while (match(r, pc + 2, s, len, ofs, NULL)) { + saved_offset = *ofs; + if (match(r, pc + r->code[pc + 1], s, len, ofs, NULL)) + break; + } + + *ofs = saved_offset; +} + +static int +is_any_of(const unsigned char *p, int len, const char *s, int *ofs) +{ + int i, ch; + + ch = s[*ofs]; + + for (i = 0; i < len; i++) + if (p[i] == ch) { + (*ofs)++; + return (1); + } + + return (0); +} + +static int +is_any_but(const unsigned char *p, int len, const char *s, int *ofs) +{ + int i, ch; + + ch = s[*ofs]; + + for (i = 0; i < len; i++) + if (p[i] == ch) + return (0); + + (*ofs)++; + return (1); +} + +static int +match(const struct slre *r, int pc, const char *s, int len, + int *ofs, struct cap *caps) +{ + int n, saved_offset, res = 1; + + while (res && r->code[pc] != END) { + + assert(pc < r->code_size); + assert(pc < (int) (sizeof(r->code) / sizeof(r->code[0]))); + + switch (r->code[pc]) { + case BRANCH: + saved_offset = *ofs; + res = match(r, pc + 3, s, len, ofs, caps); + if (res == 0) { + *ofs = saved_offset; + res = match(r, pc + r->code[pc + 1], + s, len, ofs, caps); + } + pc += r->code[pc + 2]; + break; + case EXACT: + res = 0; + n = r->code[pc + 2]; /* String length */ + if (n <= len - *ofs && !memcmp(s + *ofs, r->data + + r->code[pc + 1], n)) { + (*ofs) += n; + res = 1; + } + pc += 3; + break; + case QUEST: + res = 1; + saved_offset = *ofs; + if (!match(r, pc + 2, s, len, ofs, caps)) + *ofs = saved_offset; + pc += r->code[pc + 1]; + break; + case STAR: + res = 1; + loop_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + case STARQ: + res = 1; + loop_non_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + case PLUS: + if ((res = match(r, pc + 2, s, len, ofs, caps)) == 0) + break; + + loop_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + case PLUSQ: + if ((res = match(r, pc + 2, s, len, ofs, caps)) == 0) + break; + + loop_non_greedy(r, pc, s, len, ofs); + pc += r->code[pc + 1]; + break; + case SPACE: + res = 0; + if (*ofs < len && isspace(((unsigned char *)s)[*ofs])) { + (*ofs)++; + res = 1; + } + pc++; + break; + case NONSPACE: + res = 0; + if (*ofs <len && !isspace(((unsigned char *)s)[*ofs])) { + (*ofs)++; + res = 1; + } + pc++; + break; + case DIGIT: + res = 0; + if (*ofs < len && isdigit(((unsigned char *)s)[*ofs])) { + (*ofs)++; + res = 1; + } + pc++; + break; + case ANY: + res = 0; + if (*ofs < len) { + (*ofs)++; + res = 1; + } + pc++; + break; + case ANYOF: + res = 0; + if (*ofs < len) + res = is_any_of(r->data + r->code[pc + 1], + r->code[pc + 2], s, ofs); + pc += 3; + break; + case ANYBUT: + res = 0; + if (*ofs < len) + res = is_any_but(r->data + r->code[pc + 1], + r->code[pc + 2], s, ofs); + pc += 3; + break; + case BOL: + res = *ofs == 0 ? 1 : 0; + pc++; + break; + case EOL: + res = *ofs == len ? 1 : 0; + pc++; + break; + case OPEN: + if (caps != NULL) + caps[r->code[pc + 1]].ptr = s + *ofs; + pc += 2; + break; + case CLOSE: + if (caps != NULL) + caps[r->code[pc + 1]].len = (int)((s + *ofs) - caps[r->code[pc + 1]].ptr); + pc += 2; + break; + case END: + pc++; + break; + default: + printf("unknown cmd (%d) at %d\n", r->code[pc], pc); + assert(0); + break; + } + } + + return (res); +} + +int +slre_match(const struct slre *r, const char *buf, int len, + struct cap *caps) +{ + int i, ofs = 0, res = 0; + + if (r->anchored) { + res = match(r, 0, buf, len, &ofs, caps); + } else { + for (i = 0; i < len && res == 0; i++) { + ofs = i; + res = match(r, 0, buf, len, &ofs, caps); + } + } + + return (res); +} + +#if 0 +#ifdef TEST +int main(int argc, char *argv[]) +{ + struct slre slre; + struct cap caps[20]; + char data[1 * 1024 * 1024]; + FILE *fp; + int i, count, res, len; + + if (argc < 3) { + printf("Usage: %s 'slre' <file> [count]\n", argv[0]); + } else if ((fp = fopen(argv[2], "rb")) == NULL) { + printf("Error: cannot open %s:%s\n", argv[2], strerror(errno)); + } else if (!slre_compile(&slre, argv[1])) { + printf("Error compiling slre: %s\n", slre.err_str); + } else { + slre_dump(&slre, stderr); + + (void) memset(caps, 0, sizeof(caps)); + + /* Read first 128K of file */ + len = fread(data, 1, sizeof(data), fp); + (void) fclose(fp); + + res = 0; + count = argc > 3 ? atoi(argv[3]) : 1; + for (i = 0; i < count; i++) + res = slre_match(&slre, data, len, caps); + + printf("Result: %d\n", res); + + for (i = 0; i < 20; i++) + if (caps[i].len > 0) + printf("Substring %d: [%.*s]\n", i, + caps[i].len, caps[i].ptr); + } + + return (0); +} +#endif /* TEST */ +#endif diff --git a/MdeModulePkg/Library/SlreRegExLib/SLRE/slre.h b/MdeModulePkg/Library/SlreRegExLib/SLRE/slre.h new file mode 100644 index 0000000..9aecdd5 --- /dev/null +++ b/MdeModulePkg/Library/SlreRegExLib/SLRE/slre.h @@ -0,0 +1,104 @@ +/** + Copyright (c) 2014-2015, Hewlett-Packard Development Company, L.P.<BR> + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License that accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + Copyright (c) 2004-2005 Sergey Lyubka <vale...@gmail.com> + All rights reserved + + "THE BEER-WARE LICENSE" (Revision 42): + Sergey Lyubka wrote this file. As long as you retain this notice you + can do whatever you want with this stuff. If we meet some day, and you think + this stuff is worth it, you can buy me a beer in return. +**/ + +/* + * This is a regular expression library that implements a subset of Perl RE. + * Please refer to http://slre.sourceforge.net for detailed description. + * + * Usage example (parsing HTTP request): + * + * struct slre slre; + * struct cap captures[4 + 1]; // Number of braket pairs + 1 + * ... + * + * slre_compile(&slre,"^(GET|POST) (\S+) HTTP/(\S+?)\r\n"); + * + * if (slre_match(&slre, buf, len, captures)) { + * printf("Request line length: %d\n", captures[0].len); + * printf("Method: %.*s\n", captures[1].len, captures[1].ptr); + * printf("URI: %.*s\n", captures[2].len, captures[2].ptr); + * } + * + * Supported syntax: + * ^ Match beginning of a buffer + * $ Match end of a buffer + * () Grouping and substring capturing + * [...] Match any character from set + * [^...] Match any character but ones from set + * \s Match whitespace + * \S Match non-whitespace + * \d Match decimal digit + * \r Match carriage return + * \n Match newline + * + Match one or more times (greedy) + * +? Match one or more times (non-greedy) + * * Match zero or more times (greedy) + * *? Match zero or more times (non-greedy) + * ? Match zero or once + * \xDD Match byte with hex value 0xDD + * \meta Match one of the meta character: ^$().[*+?\ + */ + +#ifndef SLRE_HEADER_DEFINED +#define SLRE_HEADER_DEFINED + +#include "SlreUefiPort.h" + +/* + * Compiled regular expression + */ +struct slre { + unsigned char code[256]; + unsigned char data[256]; + int code_size; + int data_size; + int num_caps; /* Number of bracket pairs */ + int anchored; /* Must match from string start */ + const char *err_str; /* Error string */ +}; + +/* + * Captured substring + */ +struct cap { + const char *ptr; /* Pointer to the substring */ + int len; /* Substring length */ +}; + +/* + * Compile regular expression. If success, 1 is returned. + * If error, 0 is returned and slre.err_str points to the error message. + */ +int slre_compile(struct slre *, const char *re); + +/* + * Return 1 if match, 0 if no match. + * If `captured_substrings' array is not NULL, then it is filled with the + * values of captured substrings. captured_substrings[0] element is always + * a full matched substring. The round bracket captures start from + * captured_substrings[1]. + * It is assumed that the size of captured_substrings array is enough to + * hold all captures. The caller function must make sure it is! So, the + * array_size = number_of_round_bracket_pairs + 1 + */ +int slre_match(const struct slre *, const char *buf, int buf_len, + struct cap *captured_substrings); + +#endif /* SLRE_HEADER_DEFINED */ diff --git a/MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.c b/MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.c new file mode 100644 index 0000000..a912595 --- /dev/null +++ b/MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.c @@ -0,0 +1,301 @@ +/** + @file + + RegExLib implementation using SLRE + + Copyright (c) 2014-2015, Hewlett-Packard Development Company, L.P.<BR> + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License that accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include "SLRE/slre.h" + +#include <Uefi.h> + +#include <Library/RegExLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> + +typedef struct { + struct slre SlreState; + struct cap *SlreCaptures; + INT32 NumCaptures; +} SLRE_REG_EX_STATE; + +/** + Compile a regular expression in preparation for matching with RegExMatch. + A call to RegExFree is always required to free resources regardless of + whether compilation was successful. + + @param[out] Handle Pointer to RegExHandle to initialize. Must be freed + with a call to RegExFree when no longer needed. + @param[in] Pattern Pointer to null-terminated ASCII string containing + the desired regular expression. + + @return EFI_SUCCESS Operation was successful. + EFI_ABORTED Pattern could not be compiled. More information + may be found through RegExErrorMessage. + EFI_INVALID_PARAMETER A required parameter was NULL. + EFI_OUT_OF_RESOURCES Memory allocation failed. +**/ +EFI_STATUS +EFIAPI +RegExCompile ( + OUT REG_EX_HANDLE *Handle, + IN CONST CHAR8 *Pattern + ) +{ + INT32 Result; + SLRE_REG_EX_STATE *SlreRegEx; + + if (Handle == NULL || Pattern == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Handle = AllocatePool (sizeof(SLRE_REG_EX_STATE)); + if (*Handle == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SlreRegEx = *Handle; + SlreRegEx->SlreCaptures = NULL; + SlreRegEx->NumCaptures = 0; + + Result = slre_compile (&SlreRegEx->SlreState, Pattern); + + return (Result == 1) ? EFI_SUCCESS : EFI_ABORTED; +} + +/** + Search the input string for anything that matches the regular expression. + + Be aware that the specific implementation of this library class may not + support all regular expression syntax, which could lead to unexpected results. + + If no match is found, the contents of Captures is undefined (i.e. it may be + overwritten with invalid data). + + @param[in] Handle The compiled regular expression. + @param[in] String Pointer to null-terminated ASCII string to match + against the regular expression. + @param[out] Captures Pointer to array of REG_EX_CAPTURE objects to receive + the captured groups in the event of a match. According + to convention, the full (sub-)string match is put in + Captures[0], and the results of N capturing groups are + put in Captures[1:N]. The caller must allocate the + appropriate sized array (N+1). This parameter is optional + and may be NULL. + @param[in] NumCaptures Number of elements in the Captures array. If Captures + is NULL, this should be zero. + + @return TRUE Match was found. + FALSE No match was found. +**/ +BOOLEAN +EFIAPI +RegExMatch ( + IN REG_EX_HANDLE Handle, + IN CONST CHAR8 *String, + OUT REG_EX_CAPTURE Captures[], OPTIONAL + IN INT32 NumCaptures + ) +{ + SLRE_REG_EX_STATE *SlreHandle; + INT32 Return; + INT32 Index; + struct cap *SlreCaptures; + + SlreHandle = Handle; + + // + // Allocate internal captures buffer, same size as caller-provided buffer. + // Reuse across calls when possible. + // + if (Captures != NULL) { + if (SlreHandle->SlreCaptures != NULL && NumCaptures > SlreHandle->NumCaptures) { + FreePool (SlreHandle->SlreCaptures); + SlreHandle->SlreCaptures = NULL; + } + if (SlreHandle->SlreCaptures == NULL) { + SlreHandle->SlreCaptures = AllocateZeroPool (NumCaptures * sizeof(struct cap)); + ASSERT (SlreHandle->SlreCaptures != NULL); + } + SlreHandle->NumCaptures = NumCaptures; + SlreCaptures = SlreHandle->SlreCaptures; + } else { + ASSERT (NumCaptures == 0); + SlreCaptures = NULL; + } + + Return = slre_match (&SlreHandle->SlreState, String, (INT32)AsciiStrLen (String), SlreCaptures); + + // + // Translate internal captures into RegExLib standard captures + // + if (Return == 1) { + for (Index = 0; Index < NumCaptures; ++Index) { + Captures[Index].Ptr = SlreHandle->SlreCaptures[Index].ptr; + Captures[Index].Length = SlreHandle->SlreCaptures[Index].len; + } + } + + return (Return == 1); +} + +/** + Free any resources associated with this regular expression when it's no + longer needed. + + @param[in] Handle Handle to regular expression to be freed. +**/ +VOID +EFIAPI +RegExFree ( + IN REG_EX_HANDLE Handle + ) +{ + SLRE_REG_EX_STATE *SlreHandle; + + SlreHandle = Handle; + + if (SlreHandle != NULL) { + if (SlreHandle->SlreCaptures != NULL) { + FreePool (SlreHandle->SlreCaptures); + } + FreePool (Handle); + } +} + +/** + When compilation fails, the internal implementation may generate an error + string which can be read with this function. However, there may not always + be an error message when it seems like there should be, as this depends + on the specific implementation. + + @param[in] Handle Handle to regular expression. + + @return Pointer to null-terminated ASCII error message. May be NULL or + an empty string if there is no applicable error. +**/ +CONST CHAR8* +EFIAPI +RegExErrorMessage ( + IN REG_EX_HANDLE Handle + ) +{ + return ((SLRE_REG_EX_STATE*)Handle)->SlreState.err_str; +} + +/** + Match the string against a regex pattern. + + @param[in] AnsiString The string to check. + @param[in] AnsiPattern The regex pattern. + + @retval TRUE The string matches the pattern. + @retval FALSE The string does not match the pattern. +**/ +BOOLEAN +RegExMatchStringAscii ( + IN CONST CHAR8 *AnsiString, + IN CONST CHAR8 *AnsiPattern + ) +{ + REG_EX_HANDLE RegExHandle; + EFI_STATUS Status; + BOOLEAN Result; + + if ((AnsiString == NULL) || (AnsiPattern == NULL)) { + return FALSE; + } + + Status = RegExCompile (&RegExHandle, AnsiPattern); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "SLRE RegEx Compile failed: %r, %a\n", Status, RegExErrorMessage (RegExHandle))); + Result = FALSE; + } else { + Result = RegExMatch (RegExHandle, AnsiString, NULL, 0); + } + + RegExFree (RegExHandle); + + return Result; +} + +/** + Match the string against a regex pattern. + + @param[in] String The string to check. + @param[in] Pattern The regex pattern. + + @retval TRUE The string matches the pattern. + @retval FALSE The string does not match the pattern. +**/ +BOOLEAN +RegExMatchString ( + IN CONST CHAR16 *String, + IN CONST CHAR8 *Pattern + ) +{ + BOOLEAN Result; + CHAR8 *AnsiStr; + + if ((String == NULL) || (Pattern == NULL)) { + return FALSE; + } + + AnsiStr = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8)); + if (AnsiStr == NULL) { + return FALSE; + } + + UnicodeStrToAsciiStr (String, AnsiStr); + Result = RegExMatchStringAscii (AnsiStr, Pattern); + + FreePool (AnsiStr); + + return Result; +} + +/** + Match the string against a regex pattern. + + If you happen to have a UCS-2 pattern string, this helper function will do + the ASCII conversion for you. + + @param[in] String The string to check. + @param[in] Pattern The regex pattern. + + @retval TRUE The string matches the pattern. + @retval FALSE The string does not match the pattern. +**/ +BOOLEAN +RegExMatchStringUnicode ( + IN CONST CHAR16 *String, + IN CONST CHAR16 *Pattern + ) +{ + CHAR8 *AsciiPattern; + BOOLEAN Match; + + Match = FALSE; + + AsciiPattern = AllocatePool (StrLen (Pattern) + 1); + if (AsciiPattern == NULL) { + return FALSE; + } + UnicodeStrToAsciiStr (Pattern, AsciiPattern); + + Match = RegExMatchString (String, AsciiPattern); + + FreePool (AsciiPattern); + + return Match; +} diff --git a/MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.inf b/MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.inf new file mode 100644 index 0000000..a8a6710 --- /dev/null +++ b/MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.inf @@ -0,0 +1,38 @@ +## +# @file +# +# RegExLib implementation using SLRE +# +# Copyright (c) 2014-2015, Hewlett-Packard Development Company, L.P.<BR> +# +# This program and the accompanying materials are licensed and made available +# under the terms and conditions of the BSD License that accompanies this +# distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +## + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = SlreRegExLib + FILE_GUID = 985CB64D-620A-4E45-B84D-E39C5536D76F + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = RegExLib + +[Sources] + SlreRegExLib.c + SLRE/SlreUefiPort.h + SLRE/slre.h + SLRE/slre.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 93ae120..6b5d1ba 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -111,6 +111,9 @@ ## @libraryclass Provides core boot manager functions PlatformBootManagerLib|Include/Library/PlatformBootManagerLib.h + ## @libraryclass Regular expression matching + RegExLib|Include/Library/RegExLib.h + [Guids] ## MdeModule package token space guid # Include/Guid/MdeModulePkgTokenSpace.h diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index d0832bc..94ebbd8 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -95,6 +95,7 @@ S3BootScriptLib|MdeModulePkg/Library/PiDxeS3BootScriptLib/DxeS3BootScriptLib.inf CpuExceptionHandlerLib|MdeModulePkg/Library/CpuExceptionHandlerLibNull/CpuExceptionHandlerLibNull.inf PlatformBootManagerLib|MdeModulePkg/Library/PlatformBootManagerLibNull/PlatformBootManagerLibNull.inf + RegExLib|MdeModulePkg/Library/SlreRegExLib/SlreRegExLib.inf [LibraryClasses.EBC.PEIM] IoLib|MdePkg/Library/PeiIoLibCpuIo/PeiIoLibCpuIo.inf @@ -298,6 +299,7 @@ MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf + MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf diff --git a/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.c b/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.c new file mode 100644 index 0000000..57558c9 --- /dev/null +++ b/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.c @@ -0,0 +1,306 @@ +/** + @file + + EFI_REGULAR_EXPRESSION_PROTOCOL Implementation + + Copyright (c) 2015, Hewlett-Packard Development Company, L.P.<BR> + + This program and the accompanying materials are licensed and made available + under the terms and conditions of the BSD License that accompanies this + distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include <Uefi.h> +#include <Protocol/RegularExpressionProtocol.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/RegExLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DebugLib.h> +#include <Library/BaseLib.h> + +#define ARRAY_SIZE(Array) (sizeof(Array) / sizeof(*Array)) + +STATIC +EFI_REGEX_SYNTAX_TYPE * CONST mSupportedSyntaxes[] = { + &gEfiRegexSyntaxTypePosixExtendedGuid +}; + +/** + POSIX Extended regex syntax implementation. + + @ref RegularExpressionMatch +**/ +STATIC +EFI_STATUS +PosixExtendedMatch ( + IN CHAR16 *String, + IN CHAR16 *Pattern, + OUT BOOLEAN *Result, + OUT EFI_REGEX_CAPTURE **Captures, OPTIONAL + OUT UINTN *CapturesCount + ) +{ + REG_EX_HANDLE Regex; + CHAR8 *AsciiString; + CHAR8 *AsciiPattern; + EFI_STATUS Status; + + ASSERT (String != NULL); + ASSERT (Pattern != NULL); + ASSERT (Result != NULL); + ASSERT (CapturesCount != NULL); + + // + // Our POSIX Extended library only supports ASCII input + // + AsciiString = AllocatePool (StrLen (String) + 1); + if (AsciiString == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeStrToAsciiStr (String, AsciiString); + + AsciiPattern = AllocatePool (StrLen (Pattern) + 1); + if (AsciiPattern == NULL) { + FreePool (AsciiString); + return EFI_OUT_OF_RESOURCES; + } + UnicodeStrToAsciiStr (Pattern, AsciiPattern); + + // + // Compile and run the regex + // + Status = RegExCompile (&Regex, AsciiPattern); + + if (!EFI_ERROR (Status)) { + *Result = RegExMatch (Regex, AsciiString, NULL, 0); + } else { + *Result = FALSE; + } + + // + // Write out the results + // + if (!*Result) { + if (Captures != NULL) { + *Captures = NULL; + } + *CapturesCount = 0; + } else { + // + // Doesn't support sub-expression capture groups yet + // + if (Captures != NULL) { + *Captures = AllocatePool (sizeof(EFI_REGEX_CAPTURE)); + (*Captures)[0].CapturePtr = String; + (*Captures)[0].Length = StrLen (String); + } + *CapturesCount = 1; + } + + RegExFree (Regex); + + FreePool (AsciiPattern); + FreePool (AsciiString); + + return Status; +} + +/** + Returns information about the regular expression syntax types supported + by the implementation. + + This A pointer to the EFI_REGULAR_EXPRESSION_PROTOCOL + instance. + + RegExSyntaxTypeListSize On input, the size in bytes of RegExSyntaxTypeList. + On output with a return code of EFI_SUCCESS, the + size in bytes of the data returned in + RegExSyntaxTypeList. On output with a return code + of EFI_BUFFER_TOO_SMALL, the size of + RegExSyntaxTypeList required to obtain the list. + + RegExSyntaxTypeList A caller-allocated memory buffer filled by the + driver with one EFI_REGEX_SYNTAX_TYPE element + for each supported Regular expression syntax + type. The list must not change across multiple + calls to the same driver. The first syntax + type in the list is the default type for the + driver. + + @retval EFI_SUCCESS The regular expression syntax types list + was returned successfully. + @retval EFI_UNSUPPORTED The service is not supported by this driver. + @retval EFI_DEVICE_ERROR The list of syntax types could not be + retrieved due to a hardware or firmware error. + @retval EFI_BUFFER_TOO_SMALL The buffer RegExSyntaxTypeList is too small + to hold the result. + @retval EFI_INVALID_PARAMETER RegExSyntaxTypeListSize is NULL + +**/ +EFI_STATUS +EFIAPI +RegularExpressionGetInfo ( + IN EFI_REGULAR_EXPRESSION_PROTOCOL *This, + IN OUT UINTN *RegExSyntaxTypeListSize, + OUT EFI_REGEX_SYNTAX_TYPE *RegExSyntaxTypeList + ) +{ + UINTN SyntaxSize; + UINTN Index; + + if (This == NULL || RegExSyntaxTypeListSize == NULL || RegExSyntaxTypeList == NULL) { + return EFI_INVALID_PARAMETER; + } + + SyntaxSize = ARRAY_SIZE (mSupportedSyntaxes) * sizeof(**mSupportedSyntaxes); + + if (*RegExSyntaxTypeListSize < SyntaxSize) { + *RegExSyntaxTypeListSize = SyntaxSize; + return EFI_BUFFER_TOO_SMALL; + } + + for (Index = 0; Index < ARRAY_SIZE (mSupportedSyntaxes); ++Index) { + CopyMem (&RegExSyntaxTypeList[Index], mSupportedSyntaxes[Index], sizeof(**mSupportedSyntaxes)); + } + *RegExSyntaxTypeListSize = SyntaxSize; + + return EFI_SUCCESS; +} + +/** + Checks if the input string matches to the regular expression pattern. + + This A pointer to the EFI_REGULAR_EXPRESSION_PROTOCOL instance. + Type EFI_REGULAR_EXPRESSION_PROTOCOL is defined in Section + XYZ. + + String A pointer to a NULL terminated string to match against the + regular expression string specified by Pattern. + + Pattern A pointer to a NULL terminated string that represents the + regular expression. + + SyntaxType A pointer to the EFI_REGEX_SYNTAX_TYPE that identifies the + regular expression syntax type to use. May be NULL in which + case the function will use its default regular expression + syntax type. + + Result On return, points to TRUE if String fully matches against + the regular expression Pattern using the regular expression + SyntaxType. Otherwise, points to FALSE. + + Captures A Pointer to an array of EFI_REGEX_CAPTURE objects to receive + the captured groups in the event of a match. The full + sub-string match is put in Captures[0], and the results of N + capturing groups are put in Captures[1:N]. If Captures is + NULL, then this function doesn't allocate the memory for the + array and does not build up the elements. It only returns the + number of matching patterns in CapturesCount. If Captures is + not NULL, this function returns a pointer to an array and + builds up the elements in the array. CapturesCount is also + updated to the number of matching patterns found. It is the + caller's responsibility to free the memory pool in Captures + and in each CapturePtr in the array elements. + + CapturesCount On output, CapturesCount is the number of matching patterns + found in String. Zero means no matching patterns were found + in the string. + + @retval EFI_SUCCESS The regular expression string matching + completed successfully. + @retval EFI_UNSUPPORTED The regular expression syntax specified by + SyntaxType is not supported by this driver. + @retval EFI_DEVICE_ERROR The regular expression string matching + failed due to a hardware or firmware error. + @retval EFI_INVALID_PARAMETER String, Pattern, Result, or CapturesCountis + NULL. + +**/ +EFI_STATUS +EFIAPI +RegularExpressionMatch ( + IN EFI_REGULAR_EXPRESSION_PROTOCOL *This, + IN CHAR16 *String, + IN CHAR16 *Pattern, + IN EFI_REGEX_SYNTAX_TYPE *SyntaxType, OPTIONAL + OUT BOOLEAN *Result, + OUT EFI_REGEX_CAPTURE **Captures, OPTIONAL + OUT UINTN *CapturesCount + ) +{ + EFI_STATUS Status; + UINT32 Index; + BOOLEAN Supported; + + if (This == NULL || String == NULL || Pattern == NULL || Result == NULL || CapturesCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Figure out which syntax to use + // + if (SyntaxType == NULL) { + SyntaxType = mSupportedSyntaxes[0]; + } else { + Supported = FALSE; + for (Index = 0; Index < ARRAY_SIZE (mSupportedSyntaxes); ++Index) { + if (CompareGuid (SyntaxType, mSupportedSyntaxes[Index])) { + Supported = TRUE; + break; + } + } + if (!Supported) { + return EFI_UNSUPPORTED; + } + } + + // + // Here is where we determine which underlying library can handle the + // requested syntax. (Or possibly one library can implement all the + // protocols.) + // + if (CompareGuid (SyntaxType, &gEfiRegexSyntaxTypePosixExtendedGuid)) { + Status = PosixExtendedMatch (String, Pattern, Result, Captures, CapturesCount); + } else { + ASSERT (FALSE); + DEBUG ((DEBUG_ERROR, "Regex Syntax %g is supported but unimplemented\n", SyntaxType)); + Status = EFI_DEVICE_ERROR; + } + + return Status; +} + +/** + Entry point for RegularExpressionDxe. +**/ +EFI_STATUS +EFIAPI +RegularExpressionDxeEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + STATIC + CONST EFI_REGULAR_EXPRESSION_PROTOCOL ProtocolInstance = { + RegularExpressionMatch, + RegularExpressionGetInfo + }; + + (VOID)SystemTable; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ImageHandle, + &gEfiRegularExpressionProtocolGuid, + &ProtocolInstance, + NULL + ); + + return Status; +} diff --git a/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf b/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf new file mode 100644 index 0000000..b83f105 --- /dev/null +++ b/MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf @@ -0,0 +1,44 @@ +## +# @file +# +# EFI_REGULAR_EXPRESSION_PROTOCOL Implementation +# +# Copyright (c) 2015, Hewlett-Packard Development Company, L.P.<BR> +# +# This program and the accompanying materials are licensed and made available +# under the terms and conditions of the BSD License that accompanies this +# distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +## + +[Defines] + INF_VERSION = 0x00010018 + BASE_NAME = RegularExpressionDxe + FILE_GUID = 3E197E9C-D8DC-42D3-89CE-B04FA9833756 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = RegularExpressionDxeEntry + +[Sources] + RegularExpressionDxe.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiDriverEntryPoint + MemoryAllocationLib + BaseMemoryLib + DebugLib + RegExLib + +[Guids] + gEfiRegexSyntaxTypePosixExtendedGuid + +[Protocols] + gEfiRegularExpressionProtocolGuid -- 2.4.1 ------------------------------------------------------------------------------ One dashboard for servers and applications across Physical-Virtual-Cloud Widest out-of-the-box monitoring support with 50+ applications Performance metrics, stats and reports that give you Actionable Insights Deep dive visibility with transaction tracing using APM Insight. http://ad.doubleclick.net/ddm/clk/290420510;117567292;y _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel ------------------------------------------------------------------------------ One dashboard for servers and applications across Physical-Virtual-Cloud Widest out-of-the-box monitoring support with 50+ applications Performance metrics, stats and reports that give you Actionable Insights Deep dive visibility with transaction tracing using APM Insight. http://ad.doubleclick.net/ddm/clk/290420510;117567292;y _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel ------------------------------------------------------------------------------ _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel