From a041d9898e6d699bd8c0c25482ec574feb03c547 Mon Sep 17 00:00:00 2001 From: "John Ankarstr\\xf6m" Date: Sat, 29 May 2021 12:54:47 +0200 Subject: First commit This is the original state of the released tarball for JWM 1.8, which will serve as my starting point for further modifications. --- src/lex.c | 572 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 572 insertions(+) create mode 100644 src/lex.c (limited to 'src/lex.c') diff --git a/src/lex.c b/src/lex.c new file mode 100644 index 0000000..0e0f65d --- /dev/null +++ b/src/lex.c @@ -0,0 +1,572 @@ +/***************************************************************************** + * XML lexer functions. + * Copyright (C) 2004 Joe Wingbermuehle + *****************************************************************************/ + +#include "jwm.h" +#include "lex.h" +#include "error.h" +#include "misc.h" + +static const int BLOCK_SIZE = 16; + +/* Order is important! The order must match the order in lex.h */ +static const char *TOKEN_MAP[] = { + "[invalid]", + "ActiveBackground", + "ActiveForeground", + "Background", + "BorderStyle", + "Class", + "Clock", + "ClockStyle", + "Close", + "Desktops", + "Dock", + "DoubleClickSpeed", + "DoubleClickDelta", + "Exit", + "FocusModel", + "Font", + "Foreground", + "Group", + "Height", + "IconPath", + "Include", + "JWM", + "Key", + "Kill", + "Layer", + "Maximize", + "Menu", + "MenuStyle", + "Minimize", + "Mouse", + "Move", + "MoveMode", + "Name", + "Option", + "Outline", + "Pager", + "PagerStyle", + "Popup", + "PopupStyle", + "Program", + "Resize", + "ResizeMode", + "Restart", + "RestartCommand", + "RootMenu", + "SendTo", + "Separator", + "Shade", + "ShutdownCommand", + "SnapMode", + "StartupCommand", + "Stick", + "Swallow", + "TaskListStyle", + "TaskList", + "Theme", + "ThemePath", + "Tray", + "TrayButton", + "TrayButtonStyle", + "TrayStyle", + "Width" +}; + +static TokenNode *head, *current; + +static TokenNode *CreateNode(TokenNode *parent, const char *file, int line); +static AttributeNode *CreateAttribute(TokenNode *np); + +static int IsElementEnd(char ch); +static int IsValueEnd(char ch); +static int IsAttributeEnd(char ch); +static int IsSpace(char ch, int *lineNumber); +static char *ReadElementName(const char *line); +static char *ReadElementValue(const char *line, + const char *file, int *lineNumber); +static char *ReadAttributeValue(const char *line, const char *file, + int *lineNumber); +static int ParseEntity(const char *entity, char *ch, + const char *file, int line); +static TokenType LookupType(const char *name, TokenNode *np); + +/***************************************************************************** + *****************************************************************************/ +TokenNode *Tokenize(const char *line, const char *fileName) { + + TokenNode *np; + AttributeNode *ap; + char *temp; + int inElement; + int x; + int found; + int lineNumber; + + head = NULL; + current = NULL; + inElement = 0; + lineNumber = 1; + + x = 0; + /* Skip any initial white space */ + while(IsSpace(line[x], &lineNumber)) ++x; + + /* Skip any XML stuff */ + if(!strncmp(line + x, "", 2)) { + x += 2; + break; + } + ++x; + } + } + + while(line[x]) { + + do { + + while(IsSpace(line[x], &lineNumber)) ++x; + + /* Skip comments */ + found = 0; + if(!strncmp(line + x, "", 3)) { + x += 3; + found = 1; + break; + } + ++x; + } + } + } while(found); + + switch(line[x]) { + case '<': + ++x; + if(line[x] == '/') { + ++x; + temp = ReadElementName(line + x); + + if(current) { + + if(temp) { + + if(current->type != LookupType(temp, NULL)) { + Warning("%s[%d]: close tag \"%s\" does not " + "match open tag \"%s\"", + fileName, lineNumber, temp, + GetTokenName(current)); + } + + } else { + Warning("%s[%d]: unexpected and invalid close tag", + fileName, lineNumber); + } + + current = current->parent; + } else { + if(temp) { + Warning("%s[%d]: close tag \"%s\" without open " + "tag", fileName, lineNumber, temp); + } else { + Warning("%s[%d]: invalid close tag", fileName, lineNumber); + } + } + + if(temp) { + x += strlen(temp); + Release(temp); + } + + } else { + np = current; + current = NULL; + np = CreateNode(np, fileName, lineNumber); + temp = ReadElementName(line + x); + if(temp) { + x += strlen(temp); + LookupType(temp, np); + Release(temp); + } else { + Warning("%s[%d]: invalid open tag", fileName, lineNumber); + } + } + inElement = 1; + break; + case '/': + if(inElement) { + ++x; + if(line[x] == '>' && current) { + ++x; + current = current->parent; + inElement = 0; + } else { + Warning("%s[%d]: invalid tag", fileName, lineNumber); + } + } else { + goto ReadDefault; + } + break; + case '>': + ++x; + inElement = 0; + break; + default: +ReadDefault: + if(inElement) { + ap = CreateAttribute(current); + ap->name = ReadElementName(line + x); + if(ap->name) { + x += strlen(ap->name); + if(line[x] == '=') { + ++x; + } + if(line[x] == '\"') { + ++x; + } + ap->value = ReadAttributeValue(line + x, fileName, + &lineNumber); + if(ap->value) { + x += strlen(ap->value); + } + if(line[x] == '\"') { + ++x; + } + } + } else { + temp = ReadElementValue(line + x, fileName, &lineNumber); + if(temp) { + x += strlen(temp); + if(current) { + if(current->value) { + current->value = Reallocate(current->value, + strlen(current->value) + strlen(temp) + 1); + strcat(current->value, temp); + Release(temp); + } else { + current->value = temp; + } + } else { + if(temp[0]) { + Warning("%s[%d]: unexpected text: \"%s\"", + fileName, lineNumber, temp); + } + Release(temp); + } + } + } + break; + } + } + + return head; +} + +/***************************************************************************** + * Parse an entity reference. + * The entity value is returned in ch and the length of the entity + * is returned as the value of the function. + *****************************************************************************/ +int ParseEntity(const char *entity, char *ch, const char *file, int line) { + char *temp; + int x; + + if(!strncmp(""", entity, 6)) { + *ch = '\"'; + return 6; + } else if(!strncmp("<", entity, 4)) { + *ch = '<'; + return 4; + } else if(!strncmp(">", entity, 4)) { + *ch = '>'; + return 4; + } else if(!strncmp("&", entity, 5)) { + *ch = '&'; + return 5; + } else if(!strncmp("'", entity, 6)) { + *ch = '\''; + return 6; + } else { + for(x = 0; entity[x]; x++) { + if(entity[x] == ';') { + break; + } + } + temp = AllocateStack(x + 2); + strncpy(temp, entity, x + 1); + temp[x + 1] = 0; + Warning("%s[%d]: invalid entity: \"%.8s\"", file, line, temp); + ReleaseStack(temp); + *ch = '&'; + return 1; + } +} + +/***************************************************************************** + *****************************************************************************/ +int IsElementEnd(char ch) { + switch(ch) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\"': + case '>': + case '<': + case '/': + case '=': + case 0: + return 1; + default: + return 0; + } +} + +/***************************************************************************** + *****************************************************************************/ +int IsAttributeEnd(char ch) { + switch(ch) { + case 0: + case '\"': + return 1; + default: + return 0; + } +} + +/***************************************************************************** + *****************************************************************************/ +int IsValueEnd(char ch) { + switch(ch) { + case 0: + case '<': + return 1; + default: + return 0; + } +} + +/***************************************************************************** + *****************************************************************************/ +int IsSpace(char ch, int *lineNumber) { + switch(ch) { + case ' ': + case '\t': + case '\r': + return 1; + case '\n': + ++*lineNumber; + return 1; + default: + return 0; + } +} + +/***************************************************************************** + *****************************************************************************/ +char *ReadElementName(const char *line) { + char *buffer; + int len, max; + int x; + + len = 0; + max = BLOCK_SIZE; + buffer = Allocate(max + 1); + + for(x = 0; !IsElementEnd(line[x]); x++) { + buffer[len++] = line[x]; + if(len >= max) { + max += BLOCK_SIZE; + buffer = Reallocate(buffer, max + 1); + } + } + buffer[len] = 0; + + return buffer; +} + +/***************************************************************************** + *****************************************************************************/ +char *ReadElementValue(const char *line, const char *file, int *lineNumber) { + char *buffer; + char ch; + int len, max; + int x; + + len = 0; + max = BLOCK_SIZE; + buffer = Allocate(max + 1); + + for(x = 0; !IsValueEnd(line[x]); x++) { + if(line[x] == '&') { + x += ParseEntity(line + x, &ch, file, *lineNumber) - 1; + if(ch) { + buffer[len] = ch; + } else { + buffer[len] = line[x]; + } + } else { + if(line[x] == '\n') { + ++*lineNumber; + } + buffer[len] = line[x]; + } + ++len; + if(len >= max) { + max += BLOCK_SIZE; + buffer = Reallocate(buffer, max + 1); + } + } + buffer[len] = 0; + Trim(buffer); + + return buffer; +} + +/***************************************************************************** + *****************************************************************************/ +char *ReadAttributeValue(const char *line, const char *file, + int *lineNumber) { + + char *buffer; + char ch; + int len, max; + int x; + + len = 0; + max = BLOCK_SIZE; + buffer = Allocate(max + 1); + + for(x = 0; !IsAttributeEnd(line[x]); x++) { + if(line[x] == '&') { + x += ParseEntity(line + x, &ch, file, *lineNumber) - 1; + if(ch) { + buffer[len] = ch; + } else { + buffer[len] = line[x]; + } + } else { + if(line[x] == '\n') { + ++*lineNumber; + } + buffer[len] = line[x]; + } + ++len; + if(len >= max) { + max += BLOCK_SIZE; + buffer = Reallocate(buffer, max + 1); + } + } + buffer[len] = 0; + + return buffer; +} + +/***************************************************************************** + *****************************************************************************/ +TokenType LookupType(const char *name, TokenNode *np) { + unsigned int x; + + Assert(name); + + for(x = 0; x < sizeof(TOKEN_MAP) / sizeof(char*); x++) { + if(!strcmp(name, TOKEN_MAP[x])) { + if(np) { + np->type = x; + } + return x; + } + } + + if(np) { + np->type = TOK_INVALID; + np->invalidName = CopyString(name); + } + + return TOK_INVALID; + +} + +/***************************************************************************** + *****************************************************************************/ +const char *GetTokenName(const TokenNode *tp) { + if(tp->invalidName) { + return tp->invalidName; + } else if(tp->type >= sizeof(TOKEN_MAP) / sizeof(const char*)) { + return "[invalid]"; + } else { + return TOKEN_MAP[tp->type]; + } +} + +/***************************************************************************** + *****************************************************************************/ +const char *GetTokenTypeName(TokenType type) { + return TOKEN_MAP[type]; +} + +/***************************************************************************** + *****************************************************************************/ +TokenNode *CreateNode(TokenNode *parent, const char *file, int line) { + TokenNode *np; + + np = Allocate(sizeof(TokenNode)); + np->type = TOK_INVALID; + np->value = NULL; + np->attributes = NULL; + np->subnodeHead = NULL; + np->subnodeTail = NULL; + np->parent = parent; + np->next = NULL; + + np->fileName = Allocate(strlen(file) + 1); + strcpy(np->fileName, file); + np->line = line; + np->invalidName = NULL; + + if(!head) { + head = np; + } + if(parent) { + if(parent->subnodeHead) { + parent->subnodeTail->next = np; + } else { + parent->subnodeHead = np; + } + parent->subnodeTail = np; + } else if(current) { + current->next = np; + } + current = np; + + return np; +} + +/***************************************************************************** + *****************************************************************************/ +AttributeNode *CreateAttribute(TokenNode *np) { + AttributeNode *ap; + + ap = Allocate(sizeof(AttributeNode)); + ap->name = NULL; + ap->value = NULL; + + ap->next = np->attributes; + np->attributes = ap; + + return ap; +} + + -- cgit v1.2.3