1 /*$
2  Copyright (C) 2016-2020 Azel.
3 
4  This file is part of AzPainterB.
5 
6  AzPainterB is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  AzPainterB is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 $*/
19 
20 /*****************************************
21  * mTextParam
22  *****************************************/
23 
24 #include <string.h>
25 #include <stdlib.h>
26 #include <math.h>
27 
28 #include "mDef.h"
29 
30 #include "mTextParam.h"
31 
32 #include "mStr.h"
33 #include "mList.h"
34 #include "mUtilStr.h"
35 
36 
37 //----------------------
38 
39 struct _mTextParam
40 {
41 	char *buf;   //文字列バッファ
42 	mList list;  //キーと値のリスト
43 };
44 
45 typedef struct
46 {
47 	mListItem i;
48 
49 	char *key,*param;  //バッファの位置
50 	uint32_t hash;     //キーのハッシュ値
51 }mTextParamItem;
52 
53 //----------------------
54 
55 
56 /**
57 @defgroup textparam mTextParam
58 @brief 文字列から各パラメータを取得
59 
60 \c "key1=param1;key2=param2;..." などのように指定文字で区切られた形式の文字列から、各値を取得する。\n
61 キー名は、大文字小文字を区別しない。
62 
63 @ingroup group_etc
64 @{
65 
66 @file mTextParam.h
67 */
68 
69 
70 //==============================
71 // sub
72 //==============================
73 
74 
75 /** ハッシュ値取得 */
76 
_get_hash(const char * pc)77 static uint32_t _get_hash(const char *pc)
78 {
79 	uint32_t h = 0;
80 
81 	for(; *pc; pc++)
82 		h = h * 37 + (*pc);
83 
84 	return h;
85 }
86 
87 /** 文字列からリスト作成 */
88 
_create_list(mTextParam * p,char split,char splitparam)89 static void _create_list(mTextParam *p,char split,char splitparam)
90 {
91 	char *pc,*end,*keyend;
92 	mTextParamItem *pi;
93 	mBool loop = TRUE;
94 
95 	for(pc = p->buf; loop && *pc; pc = end + 1)
96 	{
97 		//終端位置
98 
99 		end = strchr(pc, split);
100 		if(!end)
101 		{
102 			end = strchr(pc, 0);
103 			loop = FALSE;
104 		}
105 
106 		*end = 0;
107 
108 		//空の場合は処理しない
109 
110 		if(!(*pc)) continue;
111 
112 		//キーと値の区切り文字位置
113 
114 		keyend = strchr(pc, splitparam);
115 
116 		if(keyend) *keyend = 0;
117 
118 		//キーを小文字に
119 
120 		mToLower(pc);
121 
122 		//項目追加
123 
124 		pi = (mTextParamItem *)mListAppendNew(&p->list, sizeof(mTextParamItem), NULL);
125 		if(!pi) return;
126 
127 		pi->key = pc;
128 		pi->param = (keyend)? keyend + 1: end;
129 		pi->hash = _get_hash(pc);
130 	}
131 }
132 
133 /** キーから検索 */
134 
_find(mTextParam * p,const char * key)135 static char *_find(mTextParam *p,const char *key)
136 {
137 	mTextParamItem *pi;
138 	uint32_t hash;
139 	char *keycmp,*param = NULL;
140 
141 	//比較用に、キーを小文字に
142 
143 	keycmp = mStrdup(key);
144 	if(!keycmp) return NULL;
145 
146 	mToLower(keycmp);
147 
148 	hash = _get_hash(keycmp);
149 
150 	//キー検索
151 
152 	for(pi = (mTextParamItem *)p->list.top; pi; pi = (mTextParamItem *)pi->i.next)
153 	{
154 		if(hash == pi->hash && strcmp(keycmp, pi->key) == 0)
155 		{
156 			param = pi->param;
157 			break;
158 		}
159 	}
160 
161 	mFree(keycmp);
162 
163 	return param;
164 }
165 
166 
167 //==============================
168 
169 
170 /** 解放 */
171 
mTextParamFree(mTextParam * p)172 void mTextParamFree(mTextParam *p)
173 {
174 	if(p)
175 	{
176 		mFree(p->buf);
177 		mListDeleteAll(&p->list);
178 
179 		mFree(p);
180 	}
181 }
182 
183 /** 文字列から作成
184  *
185  * @param split ';' など、各値を区切る文字
186  * @param splitparam '=' など、キーと値を区切る文字 (-1 で '=') */
187 
mTextParamCreate(const char * text,int split,int splitparam)188 mTextParam *mTextParamCreate(const char *text,int split,int splitparam)
189 {
190 	mTextParam *p;
191 
192 	if(splitparam < 0) splitparam = '=';
193 
194 	p = (mTextParam *)mMalloc(sizeof(mTextParam), TRUE);
195 	if(!p) return NULL;
196 
197 	//文字列をコピー
198 
199 	p->buf = mStrdup(text);
200 	if(!p->buf)
201 	{
202 		mFree(p);
203 		return NULL;
204 	}
205 
206 	//リストを作成
207 
208 	_create_list(p, split, splitparam);
209 
210 	return p;
211 }
212 
213 
214 /** int 値を取得
215  *
216  * 16進数 (0x...)、8進数 (0...) にも対応 */
217 
mTextParamGetInt(mTextParam * p,const char * key,int * dst)218 mBool mTextParamGetInt(mTextParam *p,const char *key,int *dst)
219 {
220 	char *pc = _find(p, key);
221 
222 	if(pc)
223 	{
224 		*dst = strtol(pc, NULL, 0);
225 		return TRUE;
226 	}
227 	else
228 		return FALSE;
229 }
230 
231 /** int 値を取得 (範囲指定) */
232 
mTextParamGetInt_range(mTextParam * p,const char * key,int * dst,int min,int max)233 mBool mTextParamGetInt_range(mTextParam *p,const char *key,int *dst,int min,int max)
234 {
235 	int n;
236 
237 	if(!mTextParamGetInt(p, key, &n))
238 		return FALSE;
239 	else
240 	{
241 		if(n < min) n = min;
242 		else if(n > max) n = max;
243 
244 		*dst = n;
245 
246 		return TRUE;
247 	}
248 }
249 
250 /** double 値を取得 */
251 
mTextParamGetDouble(mTextParam * p,const char * key,double * dst)252 mBool mTextParamGetDouble(mTextParam *p,const char *key,double *dst)
253 {
254 	char *pc = _find(p, key);
255 
256 	if(pc)
257 	{
258 		*dst = strtod(pc, NULL);
259 		return TRUE;
260 	}
261 	else
262 		return FALSE;
263 }
264 
265 /** double 値を指定桁数の int にして取得 */
266 
mTextParamGetDoubleInt(mTextParam * p,const char * key,int * dst,int dig)267 mBool mTextParamGetDoubleInt(mTextParam *p,const char *key,int *dst,int dig)
268 {
269 	double d;
270 	int n;
271 
272 	if(!mTextParamGetDouble(p, key, &d))
273 		return FALSE;
274 	else
275 	{
276 		for(n = 1; dig > 0; dig--, n *= 10);
277 
278 		*dst = (int)round(d * n);
279 
280 		return TRUE;
281 	}
282 }
283 
284 /** double 値を指定桁数の int にして取得 (範囲指定) */
285 
mTextParamGetDoubleInt_range(mTextParam * p,const char * key,int * dst,int dig,int min,int max)286 mBool mTextParamGetDoubleInt_range(mTextParam *p,const char *key,int *dst,int dig,int min,int max)
287 {
288 	int n;
289 
290 	if(!mTextParamGetDoubleInt(p, key, &n, dig))
291 		return FALSE;
292 	else
293 	{
294 		if(n < min) n = min;
295 		else if(n > max) n = max;
296 
297 		*dst = n;
298 
299 		return TRUE;
300 	}
301 }
302 
303 /** 文字列をポインタで直接取得
304  *
305  * ポインタは解放したりしないこと。 */
306 
mTextParamGetText_raw(mTextParam * p,const char * key,char ** dst)307 mBool mTextParamGetText_raw(mTextParam *p,const char *key,char **dst)
308 {
309 	char *pc = _find(p, key);
310 
311 	if(pc)
312 	{
313 		*dst = pc;
314 		return TRUE;
315 	}
316 	else
317 		return FALSE;
318 }
319 
320 /** 文字列を複製して取得 */
321 
mTextParamGetText_dup(mTextParam * p,const char * key,char ** buf)322 mBool mTextParamGetText_dup(mTextParam *p,const char *key,char **buf)
323 {
324 	char *pc = _find(p, key);
325 
326 	if(pc)
327 	{
328 		*buf = mStrdup(pc);
329 		return TRUE;
330 	}
331 	else
332 		return FALSE;
333 }
334 
335 /** mStr に文字列を取得 */
336 
mTextParamGetStr(mTextParam * p,const char * key,mStr * str)337 mBool mTextParamGetStr(mTextParam *p,const char *key,mStr *str)
338 {
339 	char *pc = _find(p, key);
340 
341 	if(pc)
342 	{
343 		mStrSetText(str, pc);
344 		return TRUE;
345 	}
346 	else
347 		return FALSE;
348 }
349 
350 /** key の値の文字列を単語リストから探し、そのインデックス番号を取得
351  *
352  * @param words '\0' で区切った単語リスト。最後は \0\0 で終わること。
353  * @return 見つからなかった場合、-1 */
354 
mTextParamFindText(mTextParam * p,const char * key,const char * words,mBool bNoCase)355 int mTextParamFindText(mTextParam *p,const char *key,const char *words,mBool bNoCase)
356 {
357 	char *param = _find(p, key);
358 	const char *pc;
359 	int ret,no;
360 
361 	if(param)
362 	{
363 		for(pc = words, no = 0; *pc; pc += strlen(pc) + 1, no++)
364 		{
365 			ret = (bNoCase)? strcasecmp(pc, param): strcmp(pc, param);
366 
367 			if(ret == 0) return no;
368 		}
369 	}
370 
371 	return -1;
372 }
373 
374 /** @} */
375