darling-xnu/libkern/c++/OSUnserialize.y
2023-05-16 21:41:14 -07:00

710 lines
15 KiB
Plaintext

/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/* OSUnserialize.y created by rsulack on Nov 21 1998 */
// "classic" parser for unserializing OSContainer objects
//
// XXX - this code should really be removed!
// - the XML format is now prefered
// - this code leaks on syntax errors, the XML doesn't
// - "classic" looks, reads, ... much better than XML :-(
// - well except the XML is more efficent on OSData
//
//
// to build :
// bison -p OSUnserialize OSUnserialize.y
// head -50 OSUnserialize.y > OSUnserialize.cpp
// sed -e "s/stdio.h/stddef.h/" < OSUnserialize.tab.c >> OSUnserialize.cpp
//
// when changing code check in both OSUnserialize.y and OSUnserialize.cpp
//
//
//
//
// DO NOT EDIT OSUnserialize.tab.cpp!
//
// this means you!
//
//
//
//
//
%{
#include <libkern/c++/OSMetaClass.h>
#include <libkern/c++/OSContainers.h>
#include <libkern/c++/OSLib.h>
typedef struct object {
struct object *next;
struct object *prev;
void *object;
int size; // for data
union {
void *key; // for dictionary
long long offset; // for offset
} u;
} object_t;
static int yyerror(const char *s);
static int yylex();
static object_t * newObject();
static void freeObject(object_t *o);
static OSObject *buildOSDictionary(object_t *);
static OSObject *buildOSArray(object_t *);
static OSObject *buildOSSet(object_t *);
static OSObject *buildOSString(object_t *);
static OSObject *buildOSData(object_t *);
static OSObject *buildOSOffset(object_t *);
static OSObject *buildOSBoolean(object_t *o);
static void rememberObject(int, object_t *);
static OSObject *retrieveObject(int);
// temp variable to use during parsing
static object_t *oo;
// resultant object of parsed text
static OSObject *parsedObject;
#define YYSTYPE object_t *
__BEGIN_DECLS
#include <kern/kalloc.h>
__END_DECLS
#define malloc(size) malloc_impl(size)
static inline void *
malloc_impl(size_t size)
{
if (size == 0) {
return NULL;
}
return kheap_alloc_tag_bt(KHEAP_DEFAULT, size,
(zalloc_flags_t) (Z_WAITOK | Z_ZERO),
VM_KERN_MEMORY_LIBKERN);
}
#define free(addr) free_impl(addr)
static inline void
free_impl(void *addr)
{
kheap_free_addr(KHEAP_DEFAULT, addr);
}
static inline void
safe_free(void *addr, size_t size)
{
if(addr) {
assert(size != 0);
kheap_free(KHEAP_DEFAULT, addr, size);
}
}
#define realloc(addr, osize, nsize) realloc_impl(addr, osize, nsize)
static inline void *
realloc_impl(void *addr, size_t osize, size_t nsize)
{
if (!addr) {
return malloc(nsize);
}
if (nsize == osize) {
return addr;
}
void *nmem = malloc(nsize);
if (!nmem) {
safe_free(addr, osize);
return NULL;
}
(void)memcpy(nmem, addr, (nsize > osize) ? osize : nsize);
safe_free(addr, osize);
return nmem;
}
%}
%token NUMBER
%token STRING
%token DATA
%token BOOLEAN
%token SYNTAX_ERROR
%% /* Grammar rules and actions follow */
input: /* empty */ { parsedObject = (OSObject *)NULL; YYACCEPT; }
| object { parsedObject = (OSObject *)$1; YYACCEPT; }
| SYNTAX_ERROR { yyerror("syntax error"); YYERROR; }
;
object: dict { $$ = (object_t *)buildOSDictionary($1); }
| array { $$ = (object_t *)buildOSArray($1); }
| set { $$ = (object_t *)buildOSSet($1); }
| string { $$ = (object_t *)buildOSString($1); }
| data { $$ = (object_t *)buildOSData($1); }
| offset { $$ = (object_t *)buildOSOffset($1); }
| boolean { $$ = (object_t *)buildOSBoolean($1); }
| '@' NUMBER { $$ = (object_t *)retrieveObject($2->u.offset);
if ($$) {
((OSObject *)$$)->retain();
} else {
yyerror("forward reference detected");
YYERROR;
}
freeObject($2);
}
| object '@' NUMBER { $$ = $1;
rememberObject($3->u.offset, $1);
freeObject($3);
}
;
//------------------------------------------------------------------------------
dict: '{' '}' { $$ = NULL; }
| '{' pairs '}' { $$ = $2; }
;
pairs: pair
| pairs pair { $2->next = $1; $1->prev = $2; $$ = $2; }
;
pair: object '=' object ';' { $$ = newObject();
$$->next = NULL;
$$->prev = NULL;
$$->u.key = $1;
$$->object = $3;
}
;
//------------------------------------------------------------------------------
array: '(' ')' { $$ = NULL; }
| '(' elements ')' { $$ = $2; }
;
set: '[' ']' { $$ = NULL; }
| '[' elements ']' { $$ = $2; }
;
elements: object { $$ = newObject();
$$->object = $1;
$$->next = NULL;
$$->prev = NULL;
}
| elements ',' object { oo = newObject();
oo->object = $3;
oo->next = $1;
oo->prev = NULL;
$1->prev = oo;
$$ = oo;
}
;
//------------------------------------------------------------------------------
offset: NUMBER ':' NUMBER { $$ = $1;
$$->size = $3->u.offset;
freeObject($3);
}
;
//------------------------------------------------------------------------------
data: DATA
;
//------------------------------------------------------------------------------
string: STRING
;
//------------------------------------------------------------------------------
boolean: BOOLEAN
;
%%
static int lineNumber = 0;
static const char *parseBuffer;
static int parseBufferIndex;
#define currentChar() (parseBuffer[parseBufferIndex])
#define nextChar() (parseBuffer[++parseBufferIndex])
#define prevChar() (parseBuffer[parseBufferIndex - 1])
#define isSpace(c) ((c) == ' ' || (c) == '\t')
#define isAlpha(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
#define isDigit(c) ((c) >= '0' && (c) <= '9')
#define isAlphaDigit(c) ((c) >= 'a' && (c) <= 'f')
#define isHexDigit(c) (isDigit(c) || isAlphaDigit(c))
#define isAlphaNumeric(c) (isAlpha(c) || isDigit(c) || ((c) == '-'))
static char yyerror_message[128];
int
yyerror(const char *s) /* Called by yyparse on error */
{
snprintf(yyerror_message, sizeof(yyerror_message), "OSUnserialize: %s near line %d\n", s, lineNumber);
return 0;
}
int
yylex()
{
int c;
if (parseBufferIndex == 0) lineNumber = 1;
top:
c = currentChar();
/* skip white space */
if (isSpace(c)) while ((c = nextChar()) != 0 && isSpace(c)) {};
/* skip over comments */
if (c == '#') while ((c = nextChar()) != 0 && c != '\n') {};
/* keep track of line number, don't return \n's */
if (c == '\n') {
lineNumber++;
(void)nextChar();
goto top;
}
/* parse boolean */
if (c == '.') {
bool boolean = false;
if (nextChar() == 't') {
if (nextChar() != 'r') return SYNTAX_ERROR;
if (nextChar() != 'u') return SYNTAX_ERROR;
if (nextChar() != 'e') return SYNTAX_ERROR;
boolean = true;
} else {
if (currentChar() != 'f') return SYNTAX_ERROR;
if (nextChar() != 'a') return SYNTAX_ERROR;
if (nextChar() != 'l') return SYNTAX_ERROR;
if (nextChar() != 's') return SYNTAX_ERROR;
if (nextChar() != 'e') return SYNTAX_ERROR;
}
if (nextChar() != '.') return SYNTAX_ERROR;
/* skip over dot */
(void)nextChar();
yylval = (object_t *)boolean;
return BOOLEAN;
}
/* parse unquoted string */
if (isAlpha(c)) {
int start, length;
char * tempString;
start = parseBufferIndex;
/* find end of string */
while (isAlphaNumeric(c)) {
c = nextChar();
}
length = parseBufferIndex - start;
/* copy to null terminated buffer */
tempString = (char *)malloc(length + 1);
if (tempString == NULL) {
printf("OSUnserialize: can't alloc temp memory\n");
return 0;
}
bcopy(&parseBuffer[start], tempString, length);
tempString[length] = 0;
yylval = (object_t *)tempString;
return STRING;
}
/* parse quoted string */
if (c == '"' || c == '\'') {
int start, length;
char * tempString;
char quoteChar = c;
start = parseBufferIndex + 1; // skip quote
/* find end of string, line, buffer */
while ((c = nextChar()) != quoteChar) {
if (c == '\\') c = nextChar();
if (c == '\n') lineNumber++;
if (c == 0) return SYNTAX_ERROR;
}
length = parseBufferIndex - start;
/* skip over trailing quote */
(void)nextChar();
/* copy to null terminated buffer */
tempString = (char *)malloc(length + 1);
if (tempString == NULL) {
printf("OSUnserialize: can't alloc temp memory\n");
return 0;
}
int to = 0;
for (int from=start; from < parseBufferIndex; from++) {
// hack - skip over backslashes
if (parseBuffer[from] == '\\') {
length--;
continue;
}
tempString[to] = parseBuffer[from];
to++;
}
tempString[length] = 0;
yylval = (object_t *)tempString;
return STRING;
}
/* process numbers */
if (isDigit (c))
{
unsigned long long n = 0;
int base = 10;
if (c == '0') {
c = nextChar();
if (c == 'x') {
base = 16;
c = nextChar();
}
}
if (base == 10) {
while(isDigit(c)) {
n = (n * base + c - '0');
c = nextChar();
}
} else {
while(isHexDigit(c)) {
if (isDigit(c)) {
n = (n * base + c - '0');
} else {
n = (n * base + 0xa + c - 'a');
}
c = nextChar();
}
}
yylval = newObject();
yylval->u.offset = n;
return NUMBER;
}
#define OSDATA_ALLOC_SIZE 4096
/* process data */
if (c == '<') {
unsigned char *d, *start, *lastStart;
size_t buflen = OSDATA_ALLOC_SIZE;
start = lastStart = d = (unsigned char *)malloc(buflen);
c = nextChar(); // skip over '<'
while (c != 0 && c != '>') {
if (isSpace(c)) while ((c = nextChar()) != 0 && isSpace(c)) {};
if (c == '#') while ((c = nextChar()) != 0 && c != '\n') {};
if (c == '\n') {
lineNumber++;
c = nextChar();
continue;
}
// get high nibble
if (!isHexDigit(c)) break;
if (isDigit(c)) {
*d = (c - '0') << 4;
} else {
*d = (0xa + (c - 'a')) << 4;
}
// get low nibble
c = nextChar();
if (!isHexDigit(c)) break;
if (isDigit(c)) {
*d |= c - '0';
} else {
*d |= 0xa + (c - 'a');
}
d++;
if ((d - lastStart) >= OSDATA_ALLOC_SIZE) {
int oldsize = d - start;
assert(buflen == oldsize);
start = (unsigned char *)realloc(start, oldsize, buflen);
d = lastStart = start + oldsize;
}
c = nextChar();
}
if (c != '>' ) {
safe_free(start, buflen);
return SYNTAX_ERROR;
}
// got it!
yylval = newObject();
yylval->object = start;
yylval->size = d - start;
(void)nextChar(); // skip over '>'
return DATA;
}
/* return single chars, move pointer to next char */
(void)nextChar();
return c;
}
// !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!#
// !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!#
// !@$&)(^Q$&*^!$(*!@$_(^%_(*Q#$(_*&!$_(*&!$_(*&!#$(*!@&^!@#%!_!#
#if DEBUG
int debugUnserializeAllocCount = 0;
#endif
object_t *
newObject()
{
#if DEBUG
debugUnserializeAllocCount++;
#endif
return (object_t *)malloc(sizeof(object_t));
}
void
freeObject(object_t *o)
{
#if DEBUG
debugUnserializeAllocCount--;
#endif
safe_free(o, sizeof(object_t));
}
static OSDictionary *tags;
static void
rememberObject(int tag, object_t *o)
{
char key[16];
snprintf(key, sizeof(key), "%u", tag);
tags->setObject(key, (OSObject *)o);
}
static OSObject *
retrieveObject(int tag)
{
char key[16];
snprintf(key, sizeof(key), "%u", tag);
return tags->getObject(key);
}
OSObject *
buildOSDictionary(object_t *o)
{
object_t *temp, *last = o;
int count = 0;
// get count and last object
while (o) {
count++;
last = o;
o = o->next;
}
o = last;
OSDictionary *d = OSDictionary::withCapacity(count);
while (o) {
#ifdef metaclass_stuff_worksXXX
if (((OSObject *)o->u.key)->metaCast("OSSymbol")) {
// XXX the evil frontdoor
d->setObject((OSSymbol *)o->u.key, (OSObject *)o->object);
} else {
// If it isn't a symbol, I hope it's a string!
d->setObject((OSString *)o->u.key, (OSObject *)o->object);
}
#else
d->setObject((OSString *)o->u.key, (OSObject *)o->object);
#endif
((OSObject *)o->object)->release();
((OSObject *)o->u.key)->release();
temp = o;
o = o->prev;
freeObject(temp);
}
return d;
};
OSObject *
buildOSArray(object_t *o)
{
object_t *temp, *last = o;
int count = 0;
// get count and last object
while (o) {
count++;
last = o;
o = o->next;
}
o = last;
OSArray *a = OSArray::withCapacity(count);
while (o) {
a->setObject((OSObject *)o->object);
((OSObject *)o->object)->release();
temp = o;
o = o->prev;
freeObject(temp);
}
return a;
};
OSObject *
buildOSSet(object_t *o)
{
OSArray *a = (OSArray *)buildOSArray(o);
OSSet *s = OSSet::withArray(a, a->getCapacity());
a->release();
return s;
};
OSObject *
buildOSString(object_t *o)
{
OSString *s = OSString::withCString((char *)o);
safe_free(o, strlen((char *)o) + 1);
return s;
};
OSObject *
buildOSData(object_t *o)
{
OSData *d;
if (o->size) {
d = OSData::withBytes(o->object, o->size);
} else {
d = OSData::withCapacity(0);
}
safe_free(o->object, o->size);
freeObject(o);
return d;
};
OSObject *
buildOSOffset(object_t *o)
{
OSNumber *off = OSNumber::withNumber(o->u.offset, o->size);
freeObject(o);
return off;
};
OSObject *
buildOSBoolean(object_t *o)
{
OSBoolean *b = OSBoolean::withBoolean((bool)o);
return b;
};
__BEGIN_DECLS
#include <kern/locks.h>
__END_DECLS
static lck_mtx_t *lock = 0;
extern lck_grp_t *IOLockGroup;
OSObject*
OSUnserialize(const char *buffer, OSString **errorString)
{
OSObject *object;
if (!lock) {
lock = lck_mtx_alloc_init(IOLockGroup, LCK_ATTR_NULL);
lck_mtx_lock(lock);
} else {
lck_mtx_lock(lock);
}
#if DEBUG
debugUnserializeAllocCount = 0;
#endif
yyerror_message[0] = 0; //just in case
parseBuffer = buffer;
parseBufferIndex = 0;
tags = OSDictionary::withCapacity(128);
if (yyparse() == 0) {
object = parsedObject;
if (errorString) *errorString = NULL;
} else {
object = NULL;
if (errorString)
*errorString = OSString::withCString(yyerror_message);
}
tags->release();
#if DEBUG
if (debugUnserializeAllocCount) {
printf("OSUnserialize: allocation check failed, count = %d.\n",
debugUnserializeAllocCount);
}
#endif
lck_mtx_unlock(lock);
return object;
}
//
//
//
//
//
// DO NOT EDIT OSUnserialize.cpp!
//
// this means you!
//
//
//
//
//