Beispiel #1
0
/* 23.4.13 */
acl_t
acl_from_text(const char *buf_p)
{
	acl_t acl;
	acl = acl_init(0);
	if (!acl)
		return NULL;
	if (!buf_p) {
		errno = EINVAL;
		return NULL;
	}
	while (*buf_p != '\0') {
		if (parse_acl_entry(&buf_p, &acl) != 0)
			goto fail;
		SKIP_WS(buf_p);
		if (*buf_p == ',') {
			buf_p++;
			SKIP_WS(buf_p);
		}
	}
	if (*buf_p != '\0') {
		errno = EINVAL;
		goto fail;
	}

	return acl;

fail:
	acl_free(acl);
	return NULL;
}
Beispiel #2
0
/* Read argument in macro expansion, starting from one offset from the initial
 * open parenthesis. Stop readin when reaching a comma, and nesting depth is
 * zero. Track nesting depth to allow things like MAX( foo(a), b ).
 */
static struct token *read_arg(
    const struct token *list,
    const struct token **endptr)
{
    size_t n = 0;
    struct token *arg = calloc(1, sizeof(*arg));
    int nesting = 0;

    SKIP_WS(list);
    do {
        if (list->token == END) {
            error("Unexpected end of input in expansion.");
            exit(1);
        }
        if (list->token == '(') {
            nesting++;
        } else if (list->token == ')') {
            nesting--;
            if (nesting < 0) {
                error("Negative nesting depth in expansion.");
                exit(1);
            }
        }
        arg = realloc(arg, (++n + 1) * sizeof(*arg));
        arg[n - 1] = *list++;
        SKIP_WS(list);
    } while (nesting || (list->token != ',' && list->token != ')'));

    arg[n] = token_end;
    *endptr = list;
    return arg;
}
Beispiel #3
0
int getproperty(FILE* html,char* property)
{
    int c=0;
    char* p=property;
    int outword=0;
    int lim=MAX_PROPERTY-2;

    SKIP_WS(html,c);
    do {
        if (IS_SPACE(c)) {outword=1;SKIP_WS(html,c);}
        if (c==EOF) goto END_OF_FILE;
        if (c=='=') break;
        if (outword) {property=p;outword=0;}
        *property++=c;
        if (lim--<0) break;
        c=NEXT_CHAR(html);
        if (c==EOF) goto END_OF_FILE;
    } while (c!='=');

    *property='\0';
    return 1;

    END_OF_FILE:
    *property='\0';
    return 0;
}
Beispiel #4
0
int gettag(FILE* html,char* tag)
{
    int c=0;
    char* p=tag;
    int lim=MAX_TAG-2;

    do {
        c=NEXT_CHAR(html);
        if (c==EOF) break;
    } while (c!='<');

    if (c==EOF) goto END_OF_FILE;

    SKIP_WS(html,c);

    do {
        if (lim--<0) break;
        *tag++=c;
        c=NEXT_CHAR(html);
    } while (!IS_SPACE(c));

    *tag='\0';
    return 1;
    END_OF_FILE:
    *tag='\0';
    return 0;

}
Beispiel #5
0
static bool
parse_line(const char *line, char **key, char **value, char **errmsg)
{
	const char *p, *q;

#define SKIP_WS(x) while (isspace(*x)) { ++x; }

	*key = NULL;
	*value = NULL;

	p = line;
	SKIP_WS(p);
	if (*p == '\0' || *p == '#') {
		return true;
	}
	q = p;
	while (isalpha(*q) || *q == '_') {
		++q;
	}
	*key = x_strndup(p, q - p);
	p = q;
	SKIP_WS(p);
	if (*p != '=') {
		*errmsg = x_strdup("missing equal sign");
		free(*key);
		*key = NULL;
		return false;
	}
	++p;

	/* Skip leading whitespace. */
	SKIP_WS(p);
	q = p;
	while (*q) {
		++q;
	}
	/* Skip trailing whitespace. */
	while (isspace(q[-1])) {
		--q;
	}
	*value = x_strndup(p, q - p);

	return true;

#undef SKIP_WS
}
Beispiel #6
0
int getvalue(FILE* html,char* value)
{
    int c=0;

    int quote_non=0;
    int quote_sng=0;
    int quote_dbl=0;
    int lim=MAX_VALUE-2;

    SKIP_WS(html,c);
    switch (c)
    {
        case '\"' :
            quote_dbl=1;
            break;
        case '\'' :
            quote_sng=1;
            break;
        default :
            quote_non=1;
            *value++=c;
            if (lim--<0) break;
            break;
    }


    do
    {
        c=NEXT_CHAR(html);

        if (END_OF_VALUE(c))
            break;
        else {
            if (lim--<0) break;
            *value++=c;
            }

    } while (c!=EOF) ;
   
   if (c==EOF) goto END_OF_FILE;

    *value='\0';

   // printf("%s\n",value);
    return 1;

    END_OF_FILE:
    *value='\0';
   // printf("%s\n",value);
    return 0;
}
Beispiel #7
0
static int
skip_tag_name(const char **text_p, const char *token)
{
	size_t len = strlen(token);
	const char *text = *text_p;

	SKIP_WS(text);
	if (strncmp(text, token, len) == 0) {
		text += len;
		goto delimiter;
	}
	if (*text == *token) {
		text++;
		goto delimiter;
	}
	return 0;

delimiter:
	SKIP_WS(text);
	if (*text == ':')
		text++;
	*text_p = text;
	return 1;
}
Beispiel #8
0
TCHAR* LabelCheck(TCHAR* s, TCHAR* pszLabel)
{
	int nLen;
	if (s == NULL) {
		return NULL;
	}
	if (pszLabel == NULL) {
		return NULL;
	}
	nLen = _tcslen(pszLabel);

	SKIP_WS(s);													// Skip whitespace

	if (_tcsncmp(s, pszLabel, nLen)){							// Doesn't match
		return NULL;
	}
	return s + nLen;
}
Beispiel #9
0
int QuoteRead(TCHAR** ppszQuote, TCHAR** ppszEnd, TCHAR* pszSrc)	// Read a (quoted) string from szSrc and point to the end
{
	static TCHAR szQuote[QUOTE_MAX];
	TCHAR* s = pszSrc;
	TCHAR* e;

	// Skip whitespace
	SKIP_WS(s);

	e = s;

	if (*s == _T('\"')) {										// Quoted string
		s++;
		e++;
		// Find end quote
		FIND_QT(e);
		_tcsncpy(szQuote, s, e - s);
		// Zero-terminate
		szQuote[e - s] = _T('\0');
		e++;
	} else {													// Non-quoted string
		// Find whitespace
		FIND_WS(e);
		_tcsncpy(szQuote, s, e - s);
		// Zero-terminate
		szQuote[e - s] = _T('\0');
	}

	if (ppszQuote) {
		*ppszQuote = szQuote;
	}
	if (ppszEnd)	{
		*ppszEnd = e;
	}

	return 0;
}
Beispiel #10
0
static char *
get_token(const char **text_p)
{
	char *token = NULL;
	const char *ep;

	ep = *text_p;
	SKIP_WS(ep);

	while (*ep!='\0' && *ep!='\r' && *ep!='\n' && *ep!=':' && *ep!=',')
		ep++;
	if (ep == *text_p)
		goto after_token;
	token = (char*)malloc(ep - *text_p + 1);
	if (token == 0)
		goto after_token;
	memcpy(token, *text_p, (ep - *text_p));
	token[ep - *text_p] = '\0';
after_token:
	if (*ep == ':')
		ep++;
	*text_p = ep;
	return token;
}
Beispiel #11
0
static int StringToInp(struct GameInp* pgi, TCHAR* s)
{
	TCHAR* szRet = NULL;

	SKIP_WS(s);											// skip whitespace
	szRet = LabelCheck(s, _T("undefined"));
	if (szRet) {
		pgi->nInput = 0;
		return 0;
	}

	szRet = LabelCheck(s, _T("constant"));
	if (szRet) {
		pgi->nInput = GIT_CONSTANT;
		s = szRet;
		pgi->Input.Constant.nConst=(unsigned char)_tcstol(s, &szRet, 0);
		*(pgi->Input.pVal) = pgi->Input.Constant.nConst;
		return 0;
	}

	szRet = LabelCheck(s, _T("switch"));
	if (szRet) {
		pgi->nInput = GIT_SWITCH;
		s = szRet;
		pgi->Input.Switch.nCode = (unsigned short)_tcstol(s, &szRet, 0);
		return 0;
	}

	// Analog using mouse axis:
	szRet = LabelCheck(s, _T("mouseaxis"));
	if (szRet) {
		pgi->nInput = GIT_MOUSEAXIS;
		return StringToMouseAxis(pgi, szRet);
	}
	// Analog using joystick axis:
	szRet = LabelCheck(s, _T("joyaxis-neg"));
	if (szRet) {
		pgi->nInput = GIT_JOYAXIS_NEG;
		return StringToJoyAxis(pgi, szRet);
	}
	szRet = LabelCheck(s, _T("joyaxis-pos"));
	if (szRet) {
		pgi->nInput = GIT_JOYAXIS_POS;
		return StringToJoyAxis(pgi, szRet);
	}
	szRet = LabelCheck(s, _T("joyaxis"));
	if (szRet) {
		pgi->nInput = GIT_JOYAXIS_FULL;
		return StringToJoyAxis(pgi, szRet);
	}

	// Analog using keyboard slider
	szRet = LabelCheck(s, _T("slider"));
	if (szRet) {
		s = szRet;
		pgi->nInput = GIT_KEYSLIDER;
		pgi->Input.Slider.SliderAxis.nSlider[0] = 0;	// defaults
		pgi->Input.Slider.SliderAxis.nSlider[1] = 0;	//

		pgi->Input.Slider.SliderAxis.nSlider[0] = (unsigned short)_tcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		pgi->Input.Slider.SliderAxis.nSlider[1] = (unsigned short)_tcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		szRet = SliderInfo(pgi, s);
		s = szRet;
		if (s == NULL) {								// Get remaining slider info
			return 1;
		}
		return 0;
	}

	// Analog using joystick slider
	szRet = LabelCheck(s, _T("joyslider"));
	if (szRet) {
		s = szRet;
		pgi->nInput = GIT_JOYSLIDER;
		pgi->Input.Slider.JoyAxis.nJoy = 0;				// defaults
		pgi->Input.Slider.JoyAxis.nAxis = 0;			//

		pgi->Input.Slider.JoyAxis.nJoy = (unsigned char)_tcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		pgi->Input.Slider.JoyAxis.nAxis = (unsigned char)_tcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		szRet = SliderInfo(pgi, s);						// Get remaining slider info
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		return 0;
	}

	return 1;
}
Beispiel #12
0
void
load_cookies (const char *file)
{
  char *line;
  FILE *fp = fopen (file, "r");
  if (!fp)
    {
      logprintf (LOG_NOTQUIET, "Cannot open cookies file `%s': %s\n",
		 file, strerror (errno));
      return;
    }
  cookies_now = time (NULL);

  for (; ((line = read_whole_line (fp)) != NULL); xfree (line))
    {
      struct cookie *cookie;
      char *p = line;

      int port;

      char *domain_b  = NULL, *domain_e  = NULL;
      char *ignore_b  = NULL, *ignore_e  = NULL;
      char *path_b    = NULL, *path_e    = NULL;
      char *secure_b  = NULL, *secure_e  = NULL;
      char *expires_b = NULL, *expires_e = NULL;
      char *name_b    = NULL, *name_e    = NULL;
      char *value_b   = NULL, *value_e   = NULL;

      SKIP_WS (p);

      if (!*p || *p == '#')
	/* empty line */
	continue;

      SET_WORD_BOUNDARIES (p, domain_b,  domain_e);
      SET_WORD_BOUNDARIES (p, ignore_b,  ignore_e);
      SET_WORD_BOUNDARIES (p, path_b,    path_e);
      SET_WORD_BOUNDARIES (p, secure_b,  secure_e);
      SET_WORD_BOUNDARIES (p, expires_b, expires_e);
      SET_WORD_BOUNDARIES (p, name_b,    name_e);

      /* Don't use SET_WORD_BOUNDARIES for value because it may
	 contain whitespace.  Instead, set value_e to the end of line,
	 modulo trailing space (this will skip the line separator.) */
      SKIP_WS (p);
      value_b = p;
      value_e = p + strlen (p);
      while (value_e > value_b && ISSPACE (*(value_e - 1)))
	--value_e;
      if (value_b == value_e)
	/* Hmm, should we check for empty value?  I guess that's
	   legal, so I leave it.  */
	;

      cookie = cookie_new ();

      cookie->attr    = strdupdelim (name_b, name_e);
      cookie->value   = strdupdelim (value_b, value_e);
      cookie->path    = strdupdelim (path_b, path_e);

      if (BOUNDED_EQUAL (secure_b, secure_e, "TRUE"))
	cookie->secure = 1;

      /* DOMAIN needs special treatment because we might need to
	 extract the port.  */
      port = domain_port (domain_b, domain_e, (const char **)&domain_e);
      if (port)
	cookie->port = port;
      else
	cookie->port = cookie->secure ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT;

      cookie->domain  = strdupdelim (domain_b, domain_e);

      /* safe default in case EXPIRES field is garbled. */
      cookie->expiry_time = cookies_now - 1;

      /* I don't like changing the line, but it's completely safe.
	 (line is malloced.)  */
      *expires_e = '\0';
      sscanf (expires_b, "%lu", &cookie->expiry_time);
      if (cookie->expiry_time < cookies_now)
	/* ignore stale cookie. */
	goto abort;
      cookie->permanent = 1;

      store_cookie (cookie);

    next:
      continue;

    abort:
      delete_cookie (cookie);
    }
  fclose (fp);
}
Beispiel #13
0
static int
parse_acl_entry(const char **text_p, acl_t *acl_p)
{
	acl_entry_obj entry_obj;
	acl_entry_t entry_d;
	char *str;
	const char *backup;
	int error, perm_chars;

	new_obj_p_here(acl_entry, &entry_obj);
	init_acl_entry_obj(entry_obj);

	/* parse acl entry type */
	SKIP_WS(*text_p);
	switch (**text_p) {
		case 'u':  /* user */
			if (!skip_tag_name(text_p, "user"))
				goto fail;
			backup = *text_p;
			str = get_token(text_p);
			if (str) {
				entry_obj.etag = ACL_USER;
				error = get_uid(unquote(str),
						&entry_obj.eid.qid);
				free(str);
				if (error) {
					*text_p = backup;
					return -1;
				}
			} else {
				entry_obj.etag = ACL_USER_OBJ;
			}
			break;

		case 'g':  /* group */
			if (!skip_tag_name(text_p, "group"))
				goto fail;
			backup = *text_p;
			str = get_token(text_p);
			if (str) {
				entry_obj.etag = ACL_GROUP;
				error = get_gid(unquote(str),
						&entry_obj.eid.qid);
				free(str);
				if (error) {
					*text_p = backup;
					return -1;
				}
			} else {
				entry_obj.etag = ACL_GROUP_OBJ;
			}
			break;

		case 'm':  /* mask */
			if (!skip_tag_name(text_p, "mask"))
				goto fail;
			/* skip empty entry qualifier field (this field may
			   be missing for compatibility with Solaris.) */
			SKIP_WS(*text_p);
			if (**text_p == ':')
				(*text_p)++;
			entry_obj.etag = ACL_MASK;
			break;

		case 'o':  /* other */
			if (!skip_tag_name(text_p, "other"))
				goto fail;
			/* skip empty entry qualifier field (this field may
			   be missing for compatibility with Solaris.) */
			SKIP_WS(*text_p);
			if (**text_p == ':')
				(*text_p)++;
			entry_obj.etag = ACL_OTHER;
			break;

		default:
			goto fail;
	}

	for (perm_chars=0; perm_chars<3; perm_chars++, (*text_p)++) {
		switch(**text_p) {
			case 'r':
				if (entry_obj.eperm.sperm & ACL_READ)
					goto fail;
				entry_obj.eperm.sperm |= ACL_READ;
				break;

			case 'w':
				if (entry_obj.eperm.sperm  & ACL_WRITE)
					goto fail;
				entry_obj.eperm.sperm  |= ACL_WRITE;
				break;

			case 'x':
				if (entry_obj.eperm.sperm  & ACL_EXECUTE)
					goto fail;
				entry_obj.eperm.sperm  |= ACL_EXECUTE;
				break;

			case '-':
				/* ignore */
				break;

			default:
				if (perm_chars == 0)
					goto fail;
				goto create_entry;
		}
	}

create_entry:
	if (acl_create_entry(acl_p, &entry_d) != 0)
		return -1;
	if (acl_copy_entry(entry_d, int2ext(&entry_obj)) != 0)
		return -1;
	return 0;

fail:
	errno = EINVAL;
	return -1;
}
Beispiel #14
0
int
parse_response (struct content *resp_content, struct response *resp)
{
	char *p = resp_content->body;
	char *body = resp_content->body;

	/* The response should begin with HTTP/\d.\d \d\d\d, that is, HTTP version and status code */
	if (strncmp (p, "HTTP", 4) != 0) 
	{
		fprintf (stderr, "Protocol does not appear to be HTTP\n");
		return -1;
	}
	resp->protocol = xstrdup ("HTTP");
	p += 4;
	if (p[0] != '/' || !IS_DIGIT(p[1]) || p[2] != '.' || !IS_DIGIT(p[3])) 
	{
		fprintf (stderr, "HTTP Version not found\n");
		return -1;	
	}
	p += 4;
	SKIP_WS(p);

	/* Get the status code */	
	
	if (!IS_DIGIT(p[0]) || !IS_DIGIT(p[1]) || !IS_DIGIT(p[2])) 
	{
		fprintf (stderr, "Couldn't find the status code in the response body\n");	
		return -1;
	}
	resp->status = 100 * (p[0] - '0') + 10 * (p[1] - '0') + p[2] - '0';
	fprintf (stderr, "Server returned status %d\n", resp->status);
	
	/* Find the next line. This is where the headers should begin */

	resp->headers = hash_table_new (31);

	/* Make sure we are not at the end of the header */

	while ((p = strstr (p, "\r\n")))
	{
		char *name, *name_end;
		char *value, *value_end;
		if (strstr (p + 2, "\r\n") == p + 2)
		{
			p += 4;	
			break;
		}
	
		p = p + 2;
	
		name_end = strchr (p, ':');	
		name = malloc (name_end - p + 1);
		strncpy (name, p, name_end - p);
	
		p = name_end + 2;
		value_end = strstr (p, "\r\n");
		value = malloc (value_end - p + 1);
		strncpy (value, p, value_end - p);
	
		hash_table_put (resp->headers, name, value);
	}	

	resp->header_body = calloc (1, p - body + 1);
	strncpy (resp->header_body, body, p - body);
		
	destroy_content (resp_content);

	return 0;
}
Beispiel #15
0
static int ConfigParseFile(TCHAR* pszFilename)
{
#define INSIDE_NOTHING (0xFFFF & (1 << ((sizeof(TCHAR) * 8) - 1)))

	TCHAR szLine[1024];
	TCHAR* s;
	TCHAR* t;
	int nLen;

	int nLine = 0;
	TCHAR nInside = INSIDE_NOTHING;

	CheatInfo* pCurrentCheat = NULL;

	FILE* h = _tfopen(pszFilename, _T("rt"));
	if (h == NULL) {
		return 1;
	}

	while (1) {
		if (_fgetts(szLine, sizeof(szLine), h) == NULL) {
			break;
		}

		nLine++;

		nLen = _tcslen(szLine);
		// Get rid of the linefeed at the end
		while (szLine[nLen - 1] == 0x0A || szLine[nLen - 1] == 0x0D) {
			szLine[nLen - 1] = 0;
			nLen--;
		}

		s = szLine;													// Start parsing

		if (s[0] == _T('/') && s[1] == _T('/')) {					// Comment
			continue;
		}

		if ((t = LabelCheck(s, _T("include"))) != 0) {				// Include a file
			s = t;

			TCHAR szFilename[MAX_PATH] = _T("");

			// Read name of the cheat file
			TCHAR* szQuote = NULL;
			QuoteRead(&szQuote, NULL, s);

			_stprintf(szFilename, _T("%s%s.dat"), szAppCheatsPath, szQuote);

			if (ConfigParseFile(szFilename)) {
				_stprintf(szFilename, _T("%s%s.ini"), szAppCheatsPath, szQuote);
				if (ConfigParseFile(szFilename)) {
					CheatError(pszFilename, nLine, NULL, _T("included file doesn't exist"), szLine);
				}
			}

			continue;
		}

		if ((t = LabelCheck(s, _T("cheat"))) != 0) {				// Add new cheat
			s = t;

			// Read cheat name
			TCHAR* szQuote = NULL;
			TCHAR* szEnd = NULL;

			QuoteRead(&szQuote, &szEnd, s);

			s = szEnd;

			if ((t = LabelCheck(s, _T("advanced"))) != 0) {			// Advanced cheat
				s = t;
			}

			SKIP_WS(s);

			if (nInside == _T('{')) {
				CheatError(pszFilename, nLine, pCurrentCheat, _T("missing closing bracket"), NULL);
				break;
			}
#if 0
			if (*s != _T('\0') && *s != _T('{')) {
				CheatError(pszFilename, nLine, NULL, _T("malformed cheat declaration"), szLine);
				break;
			}
#endif
			nInside = *s;

			// Link new node into the list
			CheatInfo* pPreviousCheat = pCurrentCheat;
			pCurrentCheat = (CheatInfo*)malloc(sizeof(CheatInfo));
			if (pCheatInfo == NULL) {
				pCheatInfo = pCurrentCheat;
			}

			memset(pCurrentCheat, 0, sizeof(CheatInfo));
			pCurrentCheat->pPrevious = pPreviousCheat;
			if (pPreviousCheat) {
				pPreviousCheat->pNext = pCurrentCheat;
			}

			// Fill in defaults
			pCurrentCheat->nType = 0;								// Default to cheat type 0 (apply each frame)
			pCurrentCheat->nStatus = -1;							// Disable cheat

			memcpy(pCurrentCheat->szCheatName, szQuote, QUOTE_MAX);

			continue;
		}

		if ((t = LabelCheck(s, _T("type"))) != 0) {					// Cheat type
			if (nInside == INSIDE_NOTHING || pCurrentCheat == NULL) {
				CheatError(pszFilename, nLine, pCurrentCheat, _T("rogue cheat type"), szLine);
				break;
			}
			s = t;

			// Set type
			pCurrentCheat->nType = _tcstol(s, NULL, 0);

			continue;
		}

		if ((t = LabelCheck(s, _T("default"))) != 0) {				// Default option
			if (nInside == INSIDE_NOTHING || pCurrentCheat == NULL) {
				CheatError(pszFilename, nLine, pCurrentCheat, _T("rogue default"), szLine);
				break;
			}
			s = t;

			// Set default option
			pCurrentCheat->nDefault = _tcstol(s, NULL, 0);

			continue;
		}

		int n = _tcstol(s, &t, 0);
		if (t != s) {				   								// New option

			if (nInside == INSIDE_NOTHING || pCurrentCheat == NULL) {
				CheatError(pszFilename, nLine, pCurrentCheat, _T("rogue option"), szLine);
				break;
			}

			// Link a new Option structure to the cheat
			if (n < CHEAT_MAX_OPTIONS) {
				s = t;

				// Read option name
				TCHAR* szQuote = NULL;
				TCHAR* szEnd = NULL;
				if (QuoteRead(&szQuote, &szEnd, s)) {
					CheatError(pszFilename, nLine, pCurrentCheat, _T("option name omitted"), szLine);
					break;
				}
				s = szEnd;

				if (pCurrentCheat->pOption[n] == NULL) {
					pCurrentCheat->pOption[n] = (CheatOption*)malloc(sizeof(CheatOption));
				}
				memset(pCurrentCheat->pOption[n], 0, sizeof(CheatOption));

				memcpy(pCurrentCheat->pOption[n]->szOptionName, szQuote, QUOTE_MAX * sizeof(TCHAR));

				int nCurrentAddress = 0;
				bool bOK = true;
				while (nCurrentAddress < CHEAT_MAX_ADDRESS) {
					int nCPU = 0, nAddress = 0, nValue = 0;

					if (SkipComma(&s)) {
						nCPU = _tcstol(s, &t, 0);		// CPU number
						if (t == s) {
							CheatError(pszFilename, nLine, pCurrentCheat, _T("CPU number omitted"), szLine);
							bOK = false;
							break;
						}
						s = t;

						SkipComma(&s);
						nAddress = _tcstol(s, &t, 0);	// Address
						if (t == s) {
							bOK = false;
							CheatError(pszFilename, nLine, pCurrentCheat, _T("address omitted"), szLine);
							break;
						}
						s = t;

						SkipComma(&s);
						nValue = _tcstol(s, &t, 0);		// Value
						if (t == s) {
							bOK = false;
							CheatError(pszFilename, nLine, pCurrentCheat, _T("value omitted"), szLine);
							break;
						}
					} else {
						if (nCurrentAddress) {			// Only the first option is allowed no address
							break;
						}
						if (n) {
							bOK = false;
							CheatError(pszFilename, nLine, pCurrentCheat, _T("CPU / address / value omitted"), szLine);
							break;
						}
					}

					pCurrentCheat->pOption[n]->AddressInfo[nCurrentAddress].nCPU = nCPU;
					pCurrentCheat->pOption[n]->AddressInfo[nCurrentAddress].nAddress = nAddress;
					pCurrentCheat->pOption[n]->AddressInfo[nCurrentAddress].nValue = nValue;
					nCurrentAddress++;
				}

				if (!bOK) {
					break;
				}

			}

			continue;
		}

		SKIP_WS(s);
		if (*s == _T('}')) {
			if (nInside != _T('{')) {
				CheatError(pszFilename, nLine, pCurrentCheat, _T("missing opening bracket"), NULL);
				break;
			}

			nInside = INSIDE_NOTHING;
		}

		// Line isn't (part of) a valid cheat
#if 0
		if (*s) {
			CheatError(pszFilename, nLine, NULL, _T("rogue line"), szLine);
			break;
		}
#endif

	}

	if (h) {
		fclose(h);
	}

	return 0;
}