blob: 3d2e75315be0f3ac1c247549a972dd468747df9f [file] [log] [blame]
/*
* \file optimus_ini_parser.c
* \brief ini parsing utilities for sdc burnning
*
* \version 1.0.0
* \date 2013-7-11
* \author Sam.Wu <yihui.wu@amlgic.com>
*
* Copyright (c) 2013 Amlogic. All Rights Reserved.
*
*/
#include "optimus_sdc_burn_i.h"
#define dbg(fmt ...) //printf("[INI]"fmt)
#define msg DWN_MSG
#define err DWN_ERR
#define MAX_ARGS 4
#define is_space_char(c) ('\t' == c || ' ' == c)
#define is_delimeter(c) ('[' == c || ']' == c || '=' == c)
#define is_valid_char(c) ( ('0' <= c && '9' >= c) || ('_' == c)\
|| ('a' <= c && 'z' >= c) || ('A' <= c && 'Z' >= c) ) \
|| ('.' == c) || ('\\' == c) || ('/' == c) || ('-' == c) \
|| (':' == c)
static int line_is_valid(const char* line)
{
char c = 0;
while (c = *line++, c)
{
int ret = is_delimeter(c) || is_valid_char(c) || is_space_char(c);
if (!ret) {
err("invalid chars! ascii val(0x%x)\n", c);
return 0;
}
}
return 1;//line is valid
}
//valid lines type: set or key/value pair
enum _INI_LINE_TYPE{
INI_LINE_TYPE_ERR = 0,
INI_LINE_TYPE_SET ,
INI_LINE_TYPE_KE_VALUE ,
};
//this func is used after line_is_valid
static int line_2_words(char* line, char* argv[], const int maxWords)
{
int nargs = 0;
char cur = 0;
for (cur = *line; is_space_char(cur); cur = *++line) {}
argv[nargs++] = line;
for (;cur = *line, cur; ++line)
{
if (!is_space_char(cur)) continue;
//following do with space character
*line = 0;
for (cur = *++line; is_space_char(cur) && cur; cur = *++line) {}//ignore all space between words
if (!cur) break;//line ended
argv[nargs++] = line;
if (maxWords <= nargs) {
err("too many words num %d, max is %d\n", nargs, maxWords);
return 0;
}
}
return nargs;
}
//step1:first loop to seprate buffer to lines
int _optimus_parse_buf_2_lines(char* pTextBuf, const unsigned textSz,
const char* lines[], unsigned* totalLineNum, const unsigned MaxLines)
{
const char* curLine = pTextBuf;
char* pTemp = pTextBuf;
unsigned i = 0;
unsigned lineNum = 0;
pTextBuf[textSz] = '\0';
//loop to seprate buffer to lines
for (i = 0; i < textSz ; i++, ++pTemp)
{
char c = *pTemp;
const int isFileEnd = i + 1 >= textSz;
if (MaxLines <= lineNum) {
DWN_ERR("total line number %d too many, at most %d lines!\n", lineNum, MaxLines);
break;
}
if ('\r' != c && '\n' != c) {
continue;
}
*pTemp = 0;///
if (isFileEnd) {
dbg("fileend:curLine=[%s]\n", curLine);
lines[lineNum++] = curLine;
break;//End to read file if file ended
}
if ('\r' == c) //for DOS \r\n mode
{
if ('\n' == pTemp[1])
{
lines[lineNum++] = curLine;
++pTemp;
curLine = pTemp + 1;
++i;//skip '\n' which follows '\r'
}
else
{
DWN_ERR("Syntax error at line %d, DOS end \\r\\n, but \\r%x\n", lineNum + 1, pTemp[1]);
return __LINE__;
}
}
else if('\n' == c)//for UNIX '\n' mode
{
lines[lineNum++] = curLine;
curLine = pTemp + 1;
}
dbg("Get Line[%03d]: %s\n", lineNum, lines[lineNum - 1]);
}
*totalLineNum = lineNum;
return 0;
}
//abandon comments lines and space lines,
//but not decrease thie line numbers
int _optimus_abandon_ini_comment_lines(char* lines[], const unsigned lineNum)
{
unsigned lineIndex = 0;
for (lineIndex = 0; lineIndex < lineNum ; lineIndex++)
{
int isSpaceLine = 1;
char c = 0;
char* thisLine = lines[lineIndex];
while (c = *thisLine++, c)
{
//escape space and tab
if (is_space_char(c))
{
continue;
}
isSpaceLine = 0;//no space line
//test if frist char is comment delimeter
if (';' == c)
{
lines[lineIndex] = NULL;//invalid comment lines
}
}
//if all character is space or tab, also invlalid it
if (isSpaceLine)
{
lines[lineIndex] = NULL;
}
}
return 0;
}
//Return value is the valid line numbers
//1, Read the whole file content to buffer
//2, parse file content to lines
//3, parse each valid line
int parse_ini_file_2_valid_lines(const char* filePath, char* iniBuf, const unsigned bufSz, char* lines[])
{
const int MaxLines = 1024;//
int ret = 0;
unsigned fileSz = bufSz;
unsigned lineNum = 0;
int hFile = -1;
unsigned readLen = 0;
fileSz = (unsigned)do_fat_get_fileSz(filePath);
if (!fileSz) {
err("File %s not exist in sdcard??\n", filePath);
return 0;
}
if (fileSz >= bufSz) {
err("file size 0x%x illegal, > bufSz 0x%x\n", fileSz, bufSz);
return 0;
}
DWN_MSG("ini sz 0x%xB\n", fileSz);
hFile = do_fat_fopen(filePath);
if (hFile < 0) {
err("Fail to open file %s\n", filePath);
return 0;
}
readLen = do_fat_fread(hFile, (u8*)iniBuf, fileSz);
if (readLen != fileSz) {
err("failed to load cfg file, want size 0x%x, but 0x%x\n", fileSz, readLen);
do_fat_fclose(hFile);
return 0;
}
iniBuf[fileSz] = 0;
do_fat_fclose(hFile);
dbg("\\r is 0x%x\t, \\n is 0x%x\n", '\r', '\n');
//step1:first loop to seprate buffer to lines
ret = _optimus_parse_buf_2_lines(iniBuf, fileSz, (const char**)lines, &lineNum, MaxLines);
if (ret) {
err("Fail to parse buf to lines.ret=%d\n", ret);
return 0;
}
//step 2: abandon comment or space lines
ret = _optimus_abandon_ini_comment_lines(lines, lineNum);
return lineNum;
}
int optimus_ini_trans_lines_2_usr_params(const char* const lines[], const unsigned lineNum,
int (*pCheckSetUseFul)(const char* setName),
int (*pParseCfgVal)(const char* setName, const char* keyName, const char* keyVal))
{
const int MaxWordsALine = 32;
char* wordsALine[MaxWordsALine];
int ret = 0;
int nwords = 0;
unsigned i = 0;
unsigned lineIndex = 0;
const int MaxUsefulSets = 8;
const char* cacheSetNames[MaxUsefulSets];
const char* CurrentSetName = NULL;
while (i < MaxUsefulSets) cacheSetNames[i++] = NULL;
dbg("\nvalid lines:\n");
for (lineIndex = 0; lineIndex < lineNum ; lineIndex++)
{
int lineType = INI_LINE_TYPE_ERR;
const char* iniKey = NULL;
const char* iniVal = NULL;
const char* iniSet = NULL;
const char* const curLine = lines[lineIndex];
if (!curLine) continue;//comment or space lines
if (!line_is_valid(curLine)) //only comment lines can contain non-ASCII letters
{
err("line %d contain invalid chars\n", lineIndex + 1);
ret = __LINE__;
break;
}
dbg("%3d: %s\n",lineIndex, curLine);
nwords = line_2_words((char*)curLine, wordsALine, MaxWordsALine);
if (nwords <= 0) {
ret = __LINE__;
break;
}
if (nwords > 3) {
err("line %d error: ini support at most 3 words, but %d\n", lineIndex + 1, nwords);
ret = __LINE__;
break;
}
switch (nwords)
{
case 3:
{
if (!strcmp("=", wordsALine[1]))//k/v pair
{
lineType = INI_LINE_TYPE_KE_VALUE;
iniKey = wordsALine[0]; iniVal = wordsALine[2];
break;
}
else if(!strcmp("[" , wordsALine[0]) && !strcmp("]" , wordsALine[2]))//set line
{
lineType = INI_LINE_TYPE_SET;
iniSet = wordsALine[1];
break;
}
else
{
lineType = INI_LINE_TYPE_ERR;
err("Ini syntax error when parse line %d\n", lineIndex + 1);
ret = __LINE__; break;
}
}
break;
case 2:
{
if ('[' == wordsALine[0][0]) //set like "[set ]" or "[ set]"
{
if (!strcmp("]", wordsALine[1]))
{
lineType = INI_LINE_TYPE_SET;
iniSet = wordsALine[0] + 1;
break;
}
else if (']' == wordsALine[1][strlen(wordsALine[1]) - 1] && !strcmp("[", wordsALine[0]))
{
lineType = INI_LINE_TYPE_SET;
iniSet = wordsALine[1];
wordsALine[1][strlen(wordsALine[1]) - 1] = 0;
break;
}
}
else if(!strcmp("=", wordsALine[1]))//k/v pair like "key = "
{
lineType = INI_LINE_TYPE_KE_VALUE;
iniKey = wordsALine[0];
break;
}
else if('=' == wordsALine[1][0])//k/v pair like "key =v" or "key= v"
{
lineType = INI_LINE_TYPE_KE_VALUE;
iniKey = wordsALine[0];
iniVal = wordsALine[1] + 1;
break;
}
else if ('=' == wordsALine[0][strlen(wordsALine[0]) - 1])//k/v pair like "key= v"
{
wordsALine[0][strlen(wordsALine[0]) - 1] = 0;
lineType = INI_LINE_TYPE_KE_VALUE;
iniKey = wordsALine[0];
iniVal = wordsALine[1];
}
}
break;
case 1:
{
char* word = wordsALine[0];
char firstChar = word[0];
char lastChar = word[strlen(word) - 1];
if ('[' == firstChar && ']' == lastChar)
{
lineType = INI_LINE_TYPE_SET;
iniSet = word + 1;
word[strlen(word) - 1] = 0;
break;
}
else
{
char c = 0;
iniKey = word;
while (c = *word++, c)
{
if ('=' == c)//TODO: not assert only delimeter in a line yet
{
lineType = INI_LINE_TYPE_KE_VALUE;
*--word = 0;
iniVal = ++word;
iniVal = *iniVal ? iniVal : NULL;
break;
}
}
}
}
break;
default:
break;
}
if (INI_LINE_TYPE_SET == lineType)
{
int setIndex = 0;
dbg("set line, set is %s\n", iniSet);
CurrentSetName = NULL;
if ( !pCheckSetUseFul(iniSet) ) {//the set don't care
continue;
}
//Check the useful set name is not duplicated!
for (setIndex = 0; setIndex < MaxUsefulSets; ++setIndex) {
const char* pset = cacheSetNames[setIndex];
if (!pset) {
CurrentSetName = cacheSetNames[setIndex] = iniSet;
break;
}
if (!strcmp(pset, iniSet)) {
ret = __LINE__;
goto _set_duplicated;
}
}
}
else if(INI_LINE_TYPE_KE_VALUE == lineType && CurrentSetName)
{
dbg("k/v line, key (%s), val (%s)\n", iniKey, iniVal);
ret = pParseCfgVal(CurrentSetName, iniKey, iniVal);
if (ret) {
goto _line_err;
}
}
}
return ret;
_line_err:
err("Fail to parse line %d\n", lineIndex + 1);
return ret;
_set_duplicated:
err("line %d err:set is duplicated!!\n", lineIndex + 1);
return ret;
}