campo-sirio/include/config.cpp
guy 32f27bfb34 Patch level : 10.0 376
Files correlati     : ba8.exe ve1.exe
Ricompilazione Demo : [ ]
Commento            :
Uniformato spessore linee in sede di stampa report su carta e/o pdf


git-svn-id: svn://10.65.10.50/trunk@19115 c028cbd2-c16b-5b4b-a496-9718f37d4682
2009-07-15 15:10:28 +00:00

902 lines
24 KiB
C++
Executable File

#include <applicat.h>
#include <colors.h>
#include <config.h>
#include <dongle.h>
#include <scanner.h>
#include <prefix.h>
#include <utility.h>
#include <extcdecl.h> // CGetPrawinName
bool TConfig::add_line(const TString& l)
{
const int ind = l.find('=');
if (ind < 0)
return FALSE;
TString256 key = l.left(ind); key.trim();
TString val = l.mid(ind+1); val.trim();
if (val[0] == '%')
{
if (val == "%yr%")
{
struct tm * oggi = xvt_time_now();
if (oggi != NULL)
val.format("%04d", 1900 + oggi->tm_year);
else
NFCHECK("Impossibile reperire la data corrente del sistema.");
}
else
{
if (val == "%frm%")
val.format("%05ld", prefix().get_codditta());
}
}
// sostituzione abilitata
return _data.add(key,val,TRUE);
}
// @doc EXTERNAL
// @mfunc Legge i dati del paragrafo
//
// @rdesc Ritorna i seguenti valori:
//
// @flag TRUE | Se il paragrafo c'ere
// @flag FALSE | Se il pragarafo non e' esitente
bool TConfig::_read_paragraph()
// @comm Legge il contenuto di tutte le variabili del paragrafo attivo
{
bool itwas = FALSE;
_data.destroy();
TScanner scan(_file);
if (scan.paragraph(_paragraph))
{
itwas = TRUE;
// populate array
while(TRUE)
{
const TString& l = scan.line();
if (l.empty() || l[0] == '[') break; // Fine paragrafo
if (l[0] == '#' || l[0] == '/') continue; // Riga di commento
add_line(l);
}
}
return itwas;
}
// @doc EXTERNAL
// @mfunc Scrive i dati del paragrafo
void TConfig::_write_paragraph(
ofstream& out) // @parm Indirizzo dell'utput sul quale scrivere il paragrafo
// @comm Scrive sullo stream <p>out le variabili del paragrafo attivo.
{
if (_data.items() > 0) // Inutile scrivere paragrafi vuoti!
{
out << '[' << _paragraph << ']' << endl;
/*
_data.restart();
for (THash_object* o = _data.get_hashobj(); o; o = _data.get_hashobj())
out << o->key() << " = " << (TString&)(o->obj()) << '\n';
*/
TString_array a; list_variables(a, FALSE,_paragraph,TRUE); // get array sorted by varname
for (int i = 0; i < a.items(); i++)
{
TToken_string& name = a.row(i);
out << name << " = ";
out << get(name) << endl;
}
out << endl;
}
}
void TConfig::_write_file()
{
if (_write_protected)
return;
TFilename temp;
temp.temp("cnf");
ofstream out(temp);
if (!out.good())
{
NFCHECK("Impossibile scrivere %s per aggiornare %s",
(const char*)temp, (const char*)_file);
return;
}
bool skip = FALSE, done = FALSE, skip_empty = TRUE;
if (_file.exist())
{
ifstream in(_file, ios::in);
if (in.good())
{
TString l(1024);
TString cnf; cnf << '[' << _paragraph << ']';
while (!in.eof())
{
in.getline(l.get_buffer(), l.size());
l.trim();
if (cnf == l)
{
// write paragraph and all variables
_write_paragraph(out);
skip = skip_empty = done = TRUE;
}
else
{
if (skip)
skip = l[0] != '[';
if (!skip)
{
const bool empty = l.empty();
if (!empty || !skip_empty)
out << l << endl;
skip_empty = empty;
}
}
}
}
else
{
NFCHECK("Impossibile aggiornare il file %s", (const char*)_file);
return;
}
}
// new paragraph
if (!done)
_write_paragraph(out);
out.close();
if (fexist(_file))
{
while (xvt_fsys_access(_file, 02) != 0)
message_box("Il file %s e' gia' in uso", (const char*)_file);
}
fcopy(temp, _file); // Copia dalla tempdir al nuovo .ini
::remove(temp); // Cancella file temporaneo
}
bool TConfig::set_paragraph(const char* section)
{
bool ok = TRUE;
if (section != NULL && _paragraph != section)
{
if (_dirty)
_write_file();
_paragraph = section;
_dirty = FALSE;
_ispresent = _read_paragraph();
ok = _ispresent;
}
return ok;
}
const char* TConfig::get_varkey(const char* var, int index) const
{
if (index >= 0)
{
TString& tmp = get_tmp_string();
tmp << var << '(' << index << ')';
return tmp;
}
return var;
}
// @doc EXTERNAL
// @mfunc Controlla se esite una variabile nel paragrafo attivo
//
// @rdesc Ritorna i seguenti valori:
//
// @flag TRUE | Se la variabile esite
// @flag FALSE | Se la variabile non esite
bool TConfig::exist(
const char* var, // @parm Nome della variabile
int index) // @parm Indice dell'elemento dell'array (default -1)
// @comm Se <p index> e' <gt>= 0 viene costruito il nome dell'elemento
// dell'array da cercare, diversamente viene cercata la variabile
// normale passata in <p var>.
{
const char* key = get_varkey(var, index);
return _data.is_key(key);
}
// @doc EXTERNAL
// @mfunc Elimina una variabile dal paragrafo corrente
//
// @rdesc Ritorna i seguenti valori:
//
// @flag TRUE | Se la variabile esiteva
// @flag FALSE | Se la variabile non esiteva
bool TConfig::remove(
const char* var, // @parm Nome della variabile
int index) // @parm Indice dell'elemento dell'array (default -1)
// @comm Se <p index> e' <gt>= 0 viene costruito il nome dell'elemento
// dell'array da cercare, diversamente viene cercata la variabile
// normale passata in <p var>.
{
const char* key = get_varkey(var, index);
const bool ok = _data.remove(key);
if (ok) _dirty = TRUE;
return ok;
}
void TConfig::remove_all()
{
if (_data.items() > 0)
{
_data.destroy();
_dirty = TRUE;
}
}
// @doc EXTERNAL
// @mfunc Ritorna il valore della variabile nella sezione corrente o in
// quella specificata
//
// @rdesc Ritorna la stringa contenuta nella variabile, se questa esiste, altrimenti
// il valore di default che dovrebbe assumere determinato dal parametro
// <p def>
const TString& TConfig::get(
const char* var, // @parm Variabile della quale ritornare il valore
const char* section, // @parm Sezione della variabile (default NULL)
int index, // @parm Eventuale indice della varaibailie (default -1)
const char* def) // @parm Valore default della varaibile (default "")
// @comm Passando <p index> <gt>= 0 viene appeso al nome della variabile per
// implementare un array.
// <nl>Il paragrafo passato in <p section> diventa quello attivo.
//
// @xref <mf TConfig::get_long> <mf TConfig::get_int> <mf TConfig::get_bool>
// <mf TConfig::get_color>
{
if (section && *section) // Cambia paragrafo se necessario
set_paragraph(section);
const char* key = get_varkey(var, index);
const TString* val = (TString*)_data.objptr(key);
if (val == NULL) // Se non la trova inserisci il default
{
if (def && *def)
{
set(var, def, section, TRUE, index);
val = &get(var, NULL, index);
}
else
val = &EMPTY_STRING;
}
return *val;
}
// @doc EXTERNAL
// @mfunc Ritorna il valore della variabile nella sezione corrente o in
// quella specificata
//
// @rdesc Ritorna il numero contenuto nella variabile, se questa esiste, altrimenti
// il valore di default che dovrebbe assumere determinato dal parametro
// <p def>
long TConfig::get_long(
const char* var, // @parm Variabile della quale ritornare il valore
const char* section, // @parm Sezione della varaibile (default NULL)
int index, // @parm Eventuale indice della varaibailie (default -1)
long def) // @parm Valore default della varaibile (default 0L)
// @comm Passando <p index> <gt>= 0 viene appeso al nome variabile per
// implementare un array.
// <nl>Il paragrafo passato in <p section> diventa quello attivo.
//
// @xref <mf TConfig::get> <mf TConfig::get_int> <mf TConfig::get_bool>
// <mf TConfig::get_color>
{
const char* n = get(var,section,index);
if (*n)
def = atol(n);
else if (def != 0)
{
TString16 d; d << def;
set(var, d, section, TRUE, index);
}
return def;
}
// @doc EXTERNAL
// @mfunc Ritorna il valore della variabile nella sezione corrente o in
// quella specificata
//
// @rdesc Ritorna il primo carattere della variabile, se questa esiste, altrimenti
// il valore di default che dovrebbe assumere determinato dal parametro
// <p def>
char TConfig::get_char(
const char* var, // @parm Variabile della quale ritornare il valore
const char* section, // @parm Sezione della varaibile (default NULL)
int index, // @parm Eventuale indice della varaibailie (default -1)
char def) // @parm Valore default della variabile (default ' ')
// @comm Passando <p index> <gt>= 0 viene appeso al nome variabile per
// implementare un array.
// <nl>Il paragrafo passato in <p section> diventa quello attivo.
//
// @xref <mf TConfig::get> <mf TConfig::get_int> <mf TConfig::get_bool>
// <mf TConfig::get_color>
{
const char* n = get(var,section,index);
if (*n)
def = *n;
else if (!isspace(def))
{
const char d[2] = { def, '\0' };
set(var, d, section, TRUE, index);
}
return def;
}
// @doc EXTERNAL
// @mfunc Ritorna il valore della variabile nella sezione corrente o in
// quella specificata
//
// @rdesc Ritorna l'intero contenuto nella variabile, se questa esiste, altrimenti
// il valore di default che dovrebbe assumere determinato dal parametro
// <p def>
int TConfig::get_int(
const char* var, // @parm Variabile della quale ritornare il valore
const char* section, // @parm Sezione della varaibile (default NULL)
int index, // @parm Eventuale indice della varaibailie (default -1)
int def) // @parm Valore default della varaibile (default 0)
// @comm Chiama la funzione <mf TConfig::get_long> e ne ritorna un intero.
// <nl>Passando <p index> <gt>= 0 viene appeso al nome variabile per
// implementare un array.
// <nl>Il paragrafo passato in <p section> diventa quello attivo.
//
// @xref <mf TConfig::get> <mf TConfig::get_long> <mf TConfig::get_bool>
// <mf TConfig::get_color>
{
return (int)get_long(var, section, index, def);
}
// @doc EXTERNAL
// @mfunc Ritorna il valore della variabile nella sezione corrente o in
// quella specificata
//
// @rdesc Ritorna i seguenti valori
//
// @flag TRUE | Se la varabile e' settata con X
// @flag FALSE | Se la varabile nen e' settata con X
// @flag <p def> | Se la varabile non esiste
bool TConfig::get_bool(
const char* var, // @parm Variabile della quale ritornare il valore
const char* section, // @parm Sezione della varaibile (default NULL)
int index, // @parm Eventuale indice della varaibailie (default -1)
bool def) // @parm Valore default della varaibile (default FALSE)
// @comm Viene chiamata la funzione <mf TConfig::get>.
// <nl>Passando <p index> <gt>= 0 viene appeso al nome variabile per
// implementare un array.
// <nl>Il paragrafo passato in <p section> diventa quello attivo.
//
// @xref <mf TConfig::get> <mf TConfig::get_long> <mf TConfig::get_int>
// <mf TConfig::get_color>
{
const char* d = def ? "X" : "";
TString& s = (TString&)get(var, section, index, d);
s.upper();
return s != "" && (s == "X" || s == "Y" || s == "1" || s == "ON" || s == "YES" || s == "OK" || s == "TRUE");
}
// @doc EXTERNAL
// @mfunc Ritorna il valore del colore settato nella variabile nella
// sezione corrente o in quella specificata
COLOR TConfig::get_color(
const char* var, // @parm Variabile della quale ritornare il valore
const char* section, // @parm Sezione della varaibile (default NULL)
int index, // @parm Eventuale indice della varaibailie (default -1)
COLOR def) // @parm Valore default della varaibile (default 0)
// @comm Passando <p index> <gt>= 0 viene appeso al nome variabile per
// implementare un array.
// <nl>Il paragrafo passato in <p section> diventa quello attivo.
//
// @xref <mf TConfig::get> <mf TConfig::get_long> <mf TConfig::get_int>
// <mf TConfig::get_bool>
{
TToken_string s(get(var, section, index), ',');
if (s.full())
{
if (s.find(',') > 0)
{
const byte r = (byte)s.get_int();
const byte g = (byte)s.get_int();
const byte b = (byte)s.get_int();
def = RGB2COLOR(r, g, b);
}
else
{
def = atol(s);
if (def == 0L)
def = COLOR_BLACK;
}
}
else
{
TString16 d;
d.format("%d,%d,%d",
XVT_COLOR_GET_RED(def), XVT_COLOR_GET_GREEN(def), XVT_COLOR_GET_BLUE(def));
set(var, d, section, TRUE, index);
}
return def;
}
// @doc EXTERNAL
// @mfunc Setta la variabile nella sezione corrente o specificata
//
// @rdesc Ritorna i seguenti valori:
//
// @flag TRUE | Se la variabile era gia' esistente
// @flag FALSE | Se la variabile non era gia' esistente
bool TConfig::set(
const char* var, // @parm Nome della variabile da settare
const char* value, // @parm Stringa da assegnare alla variabile
const char* section, // @parm Nome del paragrafo a cui appartiene la variabile
bool force, // @parm Per la creazione di una variabile inesistente
int index) // @parm Eventuale indice della variabile
// @parm long | value | Valore da assegnare alla variabile
{
// @syntax set(const char* var, const char* value, const char* section, bool force, int index);
// @syntax set(const char* var, long value, const char* section, bool force, int index);
//
// @comm Se <p force> == TRUE crea il paragrafo e la variabile se non esistono;
// altrimenti da' errore.
// <nl>Passando <p index> <gt>= 0 viene appeso al nome variabile per
// implementare un array.
// <nl>Il paragrafo passato in <p section> diventa quello attivo.
if (section && *section)
set_paragraph(section);
const char* key = get_varkey(var, index);
TString* val = (TString*)_data.objptr(key);
const bool itwas = val != NULL;
if (itwas && !force)
error_box("Tentativo di ridefinizione simbolo: %s", (const char*)key);
else
{
if (itwas)
{
const TFixed_string str(value);
// Se la variabile esisteva ed aveva un valore diverso ...
if (*val != str && !(str.blank() && val->empty()))
{
*val = str; // ... allora la sostituisco ...
val->trim();
_dirty = TRUE; // ... e metto a dirty.
}
}
else
{
// Se la variabile non esisteva allora la aggiungo e metto a dirty.
val = new TString(value);
val->trim();
_data.add(key, val, TRUE);
_dirty = TRUE;
}
}
return itwas;
}
bool TConfig::set(const char* var, long value, const char* section,
bool force, int index)
{
TString16 t; t << value;
return set(var,t,section,force,index);
}
bool TConfig::set_color(const char* var, COLOR col, const char* section,
bool force, int index)
{
TString16 t;
t.format("%d,%d,%d", XVT_COLOR_GET_RED(col), XVT_COLOR_GET_GREEN(col), XVT_COLOR_GET_BLUE(col));
return set(var,t,section,force,index);
}
// @doc EXTERNAL
// @mfunc Ritorna quanti elementi dell'array nominato sono presenti nella
// sezione indicata.
word TConfig::items(
const char* var, // @parm Nome dell'array
const char* section) // @parm Sezione indicata
// @comm Il paragrafo passato in <p section> diventa quello attivo.
// <nl>Possono esserci dei "buchi" causati da set() errate
{
int cnt;
if (section && *section)
set_paragraph(section);
for (cnt = 0; exist(var, cnt); cnt++);
return cnt;
}
// @doc EXTERNAL
// @mfunc Inizializza il paragrafo leggendo dal file i dati
void TConfig::init(
const char *fn, // @parm Nome del file da leggere
const char* pa) // @parm Nome del paragrafo da utilizzare
// @comm Apre il file <p fn> e cerca il paragrafo <p pa>. Se il file non esiste
// viene creato con il paragrafo passato.
{
_file = fn;
_paragraph = pa;
_dirty = FALSE;
_write_protected = FALSE;
if (!_file.exist())
{
ofstream c(fn);
c.close();
}
if (_paragraph.blank())
{
_paragraph = main_app().name();
_paragraph.cut(2);
_paragraph.lower();
}
_ispresent = _read_paragraph();
}
int TConfig::list_paragraphs(TString_array& pl)
{
TScanner s(_file);
pl.destroy();
while (s.line().not_empty())
{
if (s.token()[0] == '[')
{
TToken_string* p = new TToken_string(s.token());
p->strip("[]");
pl.add(p);
}
}
return pl.items();
}
HIDDEN int compare_ini_variables(const TObject**o1, const TObject**o2)
{
const TString* s1 = (const TString*)*o1;
const TString* s2 = (const TString*)*o2;
const int p1 = s1->find('(');
if (p1 < 0)
return strcmp(*s1, *s2);
else
{
// variabile tipo XX(i)
int result= strncmp(*s1, *s2,p1);
if (result==0)
{
if (s2->find('(')<0)
result = 1;
else
{
CHECKS(s1->find(')')>=0,"Errore: parentesi non chiusa in ",(const char *)(*s1));
CHECKS(s2->find(')')>=0,"Errore: parentesi non chiusa in ",(const char *)(*s2));
const int i1=atoi(s1->sub(p1+1,s1->len()-1));
const int i2=atoi(s2->sub(p1+1,s2->len()-1));
result = i1-i2;
}
}
return result;
}
}
int TConfig::list_variables(TString_array& vl, bool value, const char* section, const bool sort)
{
set_paragraph(section);
vl.destroy();
_data.restart();
for (int i = 0; i < _data.items(); i++)
{
THash_object* o = _data.get_hashobj();
TToken_string* t = new TToken_string(o->key());
if (value) t->add((TString&)(o->obj()));
vl.add(t);
}
if (sort)
vl.TArray::sort(compare_ini_variables);
return vl.items();
}
TAssoc_array& TConfig::list_variables(const char* section)
{
set_paragraph(section);
return _data;
}
int TConfig::for_each_paragraph(CONFIG_CALLBACK cfgcb, void* jolly)
{
int count = 0;
TScanner scanner(_file);
_paragraph.cut(0);
bool needs_call = FALSE;
while (scanner.line().not_empty())
{
const TString& riga = scanner.token();
if (riga[0] == '[')
{
count++;
if (needs_call)
{
needs_call = FALSE;
if (cfgcb && cfgcb(*this, jolly))
break;
}
_paragraph = riga;
_paragraph.strip("[]");
_data.destroy();
needs_call = TRUE;
}
else
add_line(riga);
}
if (needs_call && cfgcb)
cfgcb(*this, jolly);
return count;
}
static void cfg2file(int which_config, TFilename& file)
{
switch (which_config)
{
case CONFIG_GENERAL:
file = "./install.ini";
break;
case CONFIG_DITTA:
file = firm2dir(prefix().get_codditta());
file.add("ditta.ini");
if (!file.exist())
{
TFilename oldfile = file;
const int pos = oldfile.find("ditta.ini");
oldfile.cut(pos);
oldfile.add("prassid.ini"); // Old config file!
if (oldfile.exist())
fcopy(oldfile, file);
}
break;
case CONFIG_STUDIO:
case CONFIG_STAMPE:
case CONFIG_GUI:
case CONFIG_USER:
case CONFIG_WST:
file = firm2dir(-1); // Directory dati
file.add("config"); // + Directory config
if (!file.exist()) // Creala se necessario
make_dir(file);
switch (which_config)
{
case CONFIG_STUDIO:
file.add("studio.ini");
if (!file.exist())
{
TFilename oldfile = file;
const int pos = oldfile.find("studio.ini");
oldfile.cut(pos);
oldfile.add("prassis.ini"); // Old config file!
if (oldfile.exist())
fcopy(oldfile, file);
}
break;
case CONFIG_STAMPE:
file.add("print.ini");
break;
case CONFIG_GUI:
{
TFilename gui = "gui.ini";
if (gui.custom_path())
{
file = gui; // I colori sono qui ...
break; // ... scavalca utente
}
}
case CONFIG_USER:
{
TString80 u = user();
if (u.blank())
u = ::dongle().administrator();
u.lower();
file.add(u);
file.ext("ini");
if (u != "admin" && !file.exist())
{
TFilename prassi = file.path();
prassi.add("admin.ini");
fcopy(prassi, file);
}
}
break;
case CONFIG_WST:
{
TString80 u = get_hostname();
if (u.blank())
u = "localhost";
u.lower();
file.add(u);
file.ext("ini");
}
break;
default:
break;
}
break;
case CONFIG_FCONV:
file = "fconv.ini";
break;
case CONFIG_INSTALL:
file = CGetCampoIni();
break;
case CONFIG_OEM:
file = "setup/oem.ini";
break;
default:
NFCHECK("Chi usa questo strano .ini?");
break;
}
}
TConfig::TConfig(int which_config, const char* paragraph)
{
cfg2file(which_config, _file);
init(_file, paragraph);
}
TConfig::TConfig(const char *fn, const char* pa)
{
TFilename f(fn);
f.custom_path();
init(f, pa);
}
TConfig::~TConfig()
{
// il distruttore riscrive il file con le modifiche se necessario
if (_dirty && !_write_protected)
_write_file();
}
///////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////
#define DECLARE_VARNAME(name, idx) const char* varname = idx >= 0 ? (const char*)(get_tmp_string() << name << '(' << idx << ')') : name
#define DECLARE_FILENAME(cfg) TFilename filename; cfg2file(cfg, filename)
const TString& ini_get_string(const char* file, const char* paragraph, const char* name, const char* defval, int idx)
{
DECLARE_VARNAME(name, idx);
TString& tmp = get_tmp_string();
const int len = xvt_sys_get_profile_string(file, paragraph, varname, defval, tmp.get_buffer(), tmp.size());
if (len > tmp.size())
{
tmp.get_buffer(len);
xvt_sys_get_profile_string(file, paragraph, varname, defval, tmp.get_buffer(), tmp.size());
}
if (tmp[0] == '"')
{
tmp.rtrim(1);
tmp.ltrim(1);
}
return tmp;
}
bool ini_set_string(const char* file, const char* paragraph, const char* name, const char* val, int idx)
{
DECLARE_VARNAME(name, idx);
return xvt_sys_set_profile_string(file, paragraph, varname, val) != 0;
}
bool ini_get_bool(const char* file, const char* para, const char* name, bool defval, int idx)
{
const char b = ini_get_string(file, para, name, defval ? "1" : "0", idx)[0];
return b ? (strchr("XY1", b) != NULL) : defval;
}
bool ini_get_bool(int cfg, const char* para, const char* name, bool defval, int idx)
{
DECLARE_FILENAME(cfg);
return ini_get_bool(filename, para, name, defval, idx);
}
int ini_get_int(const char* file, const char* para, const char* name, int defval, int idx)
{
DECLARE_VARNAME(name, idx);
return xvt_sys_get_profile_int(file, para, name, defval);
}
bool ini_set_int(const char* file, const char* paragraph, const char* name, int val, int idx)
{
TString16 value; value << val;
return ini_set_string(file, paragraph, name, value, idx);
}
bool ini_set_bool(const char* file, const char* paragraph, const char* name, bool val, int idx)
{
return ini_set_string(file, paragraph, name, val ? "1" : "0", idx);
}
const TString& ini_get_string(int cfg, const char* paragraph, const char* name, const char* defval, int idx)
{
DECLARE_FILENAME(cfg);
return ini_get_string(filename, paragraph, name, defval, idx);
}
bool ini_set_string(int cfg, const char* paragraph, const char* name, const char* val, int idx)
{
DECLARE_FILENAME(cfg);
return ini_set_string(filename, paragraph, name, val);
}
int ini_get_int(int cfg, const char* paragraph, const char* name, int defval, int idx)
{
DECLARE_FILENAME(cfg);
return ini_get_int(filename, paragraph, name, defval, idx);
}
bool ini_set_int(int cfg, const char* paragraph, const char* name, int val, int idx)
{
DECLARE_FILENAME(cfg);
return ini_set_int(filename, paragraph, name, val, idx);
}
bool ini_set_bool(int cfg, const char* paragraph, const char* name, bool val, int idx)
{
DECLARE_FILENAME(cfg);
return ini_set_string(filename, paragraph, name, val ? "1" : "0", idx);
}
const TString& get_oem_info(const char* varname)
{
static int oem = ini_get_int(CONFIG_OEM, "MAIN", "OEM");
TString8 para; para.format("OEM_%d", oem);
return ini_get_string(CONFIG_OEM, para, varname);
}