gwenhywfar 5.10.1
cttest.c
Go to the documentation of this file.
1/***************************************************************************
2 $RCSfile$
3 -------------------
4 cvs : $Id: main.c 1107 2007-01-07 21:17:05Z martin $
5 begin : Tue May 03 2005
6 copyright : (C) 2005 by Martin Preuss
7 email : martin@libchipcard.de
8
9 ***************************************************************************
10 * Please see toplevel file COPYING for license details *
11 ***************************************************************************/
12
13#ifdef HAVE_CONFIG_H
14# include <config.h>
15#endif
16
17#include <gwenhywfar/debug.h>
18#include <gwenhywfar/pathmanager.h>
19#include <gwenhywfar/cgui.h>
20
21#include <gwenhywfar/gwenhywfar.h>
22#include <gwenhywfar/args.h>
23#include <gwenhywfar/logger.h>
24#include <gwenhywfar/db.h>
25#include <gwenhywfar/misc.h>
26#include <gwenhywfar/misc2.h>
27#include <gwenhywfar/inherit.h>
28#include <gwenhywfar/ct.h>
29#include <gwenhywfar/ctplugin.h>
30#include <gwenhywfar/text.h>
31#include <gwenhywfar/mdigest.h>
32
33#include "src/base/i18n_l.h"
34
35#ifdef OS_WIN32
36# define DIRSEP "\\"
37#else
38# define DIRSEP "/"
39#endif
40
41#ifdef HAVE_I18N
42# include <libintl.h>
43# include <locale.h>
44#endif
45
46
47#define GCT_LOGDOMAIN "GCT"
48
49//#define DEBUG_GCT_TOOL
50
51
52
53int signWithNew(GWEN_DB_NODE *dbArgs, int argc, char **argv)
54{
55 GWEN_DB_NODE *db;
56 const char *ttype;
57 const char *tname;
59 GWEN_PLUGIN *pl;
61 /* unsigned int cid; */
62 int rv;
63 const GWEN_ARGS args[]= {
64 {
66 GWEN_ArgsType_Int, /* type */
67 "contextId", /* name */
68 0, /* minnum */
69 1, /* maxnum */
70 "i", /* short option */
71 "id", /* long option */
72 "Context id (0 for any)", /* short description */
73 "Context id (0 for any)" /* long description */
74 },
75 {
77 GWEN_ArgsType_Char, /* type */
78 "tokenType", /* name */
79 1, /* minnum */
80 1, /* maxnum */
81 "t", /* short option */
82 "ttype", /* long option */
83 "Specify the crypt token type", /* short description */
84 "Specify the crypt token type" /* long description */
85 },
86 {
88 GWEN_ArgsType_Char, /* type */
89 "tokenName", /* name */
90 0, /* minnum */
91 1, /* maxnum */
92 "n", /* short option */
93 "tname", /* long option */
94 "Specify the crypt token name", /* short description */
95 "Specify the crypt token name" /* long description */
96 },
97 {
99 GWEN_ArgsType_Int, /* type */
100 "help", /* name */
101 0, /* minnum */
102 0, /* maxnum */
103 "h", /* short option */
104 "help", /* long option */
105 "Show this help screen", /* short description */
106 "Show this help screen" /* long description */
107 }
108 };
109
110 db=GWEN_DB_GetGroup(dbArgs, GWEN_DB_FLAGS_DEFAULT, "local");
111 rv=GWEN_Args_Check(argc, argv, 1,
113 args,
114 db);
115 if (rv==GWEN_ARGS_RESULT_ERROR) {
116 fprintf(stderr, "ERROR: Could not parse arguments\n");
117 return 1;
118 }
119 else if (rv==GWEN_ARGS_RESULT_HELP) {
120 GWEN_BUFFER *ubuf;
121
122 ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
123 if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
124 fprintf(stderr, "ERROR: Could not create help string\n");
125 return 1;
126 }
127 fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf));
128 GWEN_Buffer_free(ubuf);
129 return 0;
130 }
131
132 /* cid=GWEN_DB_GetIntValue(db, "contextId", 0, 0); */
133
134 ttype=GWEN_DB_GetCharValue(db, "tokenType", 0, 0);
135 assert(ttype);
136
137 tname=GWEN_DB_GetCharValue(db, "tokenName", 0, 0);
138
139 /* get crypt token */
141 if (pm==0) {
142 DBG_ERROR(0, "Plugin manager not found");
143 return 3;
144 }
145
146 pl=GWEN_PluginManager_GetPlugin(pm, ttype);
147 if (pl==0) {
148 DBG_ERROR(0, "Plugin not found");
149 return 3;
150 }
151 DBG_INFO(0, "Plugin found");
152
154 if (ct==0) {
155 DBG_ERROR(0, "Could not create crypt token");
156 return 3;
157 }
158
159 if (GWEN_DB_GetIntValue(dbArgs, "forcePin", 0, 0))
161
162 /* open crypt token for use */
163 rv=GWEN_Crypt_Token_Open(ct, 0, 0);
164 if (rv) {
165 DBG_ERROR(0, "Could not open token");
166 return 3;
167 }
168 else {
169 uint8_t clearText[20]= {
170 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
171 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
172 0x11, 0x12, 0x13, 0x14
173 };
174 uint8_t signature[256];
175 uint32_t signLen;
177
178 algo=GWEN_Crypt_PaddAlgo_new(GWEN_Crypt_PaddAlgoId_Iso9796_1A4);
179 signLen=sizeof(signature);
181 0x81, /* local sign key */
182 algo,
183 clearText,
184 20,
185 signature,
186 &signLen,
187 NULL,
188 0);
189 if (rv) {
190 DBG_ERROR(0, "Could not sign data (%d)", rv);
191 return 3;
192 }
193
194 fprintf(stderr, "Signature is:\n");
195 GWEN_Text_DumpString((const char *) signature, signLen, 2);
196
197 rv=GWEN_Crypt_Token_Verify(ct, 0x81,
198 algo,
199 clearText,
200 20,
201 signature,
202 signLen,
203 0,
204 0);
205 if (rv) {
206 DBG_ERROR(0, "Could not verify data (%d)", rv);
207 return 3;
208 }
209 fprintf(stderr, "Signature is ok.\n");
210
211 }
212
213 /* close crypt token */
214 rv=GWEN_Crypt_Token_Close(ct, 0, 0);
215 if (rv) {
216 DBG_ERROR(0, "Could not close token");
217 return 3;
218 }
219
220 return 0;
221}
222
223
224
225int cryptWithNew(GWEN_DB_NODE *dbArgs, int argc, char **argv)
226{
227 GWEN_DB_NODE *db;
228 const char *ttype;
229 const char *tname;
231 GWEN_PLUGIN *pl;
233 /* unsigned int cid; */
234 int rv;
235 const GWEN_ARGS args[]= {
236 {
238 GWEN_ArgsType_Int, /* type */
239 "contextId", /* name */
240 0, /* minnum */
241 1, /* maxnum */
242 "i", /* short option */
243 "id", /* long option */
244 "Context id (0 for any)", /* short description */
245 "Context id (0 for any)" /* long description */
246 },
247 {
249 GWEN_ArgsType_Char, /* type */
250 "tokenType", /* name */
251 1, /* minnum */
252 1, /* maxnum */
253 "t", /* short option */
254 "ttype", /* long option */
255 "Specify the crypt token type", /* short description */
256 "Specify the crypt token type" /* long description */
257 },
258 {
260 GWEN_ArgsType_Char, /* type */
261 "tokenName", /* name */
262 0, /* minnum */
263 1, /* maxnum */
264 "n", /* short option */
265 "tname", /* long option */
266 "Specify the crypt token name", /* short description */
267 "Specify the crypt token name" /* long description */
268 },
269 {
271 GWEN_ArgsType_Int, /* type */
272 "help", /* name */
273 0, /* minnum */
274 0, /* maxnum */
275 "h", /* short option */
276 "help", /* long option */
277 "Show this help screen", /* short description */
278 "Show this help screen" /* long description */
279 }
280 };
281
282 db=GWEN_DB_GetGroup(dbArgs, GWEN_DB_FLAGS_DEFAULT, "local");
283 rv=GWEN_Args_Check(argc, argv, 1,
285 args,
286 db);
287 if (rv==GWEN_ARGS_RESULT_ERROR) {
288 fprintf(stderr, "ERROR: Could not parse arguments\n");
289 return 1;
290 }
291 else if (rv==GWEN_ARGS_RESULT_HELP) {
292 GWEN_BUFFER *ubuf;
293
294 ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
295 if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
296 fprintf(stderr, "ERROR: Could not create help string\n");
297 return 1;
298 }
299 fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf));
300 GWEN_Buffer_free(ubuf);
301 return 0;
302 }
303
304 /* cid=GWEN_DB_GetIntValue(db, "contextId", 0, 0); */
305
306 ttype=GWEN_DB_GetCharValue(db, "tokenType", 0, 0);
307 assert(ttype);
308
309 tname=GWEN_DB_GetCharValue(db, "tokenName", 0, 0);
310
311 /* get crypt token */
313 if (pm==0) {
314 DBG_ERROR(0, "Plugin manager not found");
315 return 3;
316 }
317
318 pl=GWEN_PluginManager_GetPlugin(pm, ttype);
319 if (pl==0) {
320 DBG_ERROR(0, "Plugin not found");
321 return 3;
322 }
323 DBG_INFO(0, "Plugin found");
324
326 if (ct==0) {
327 DBG_ERROR(0, "Could not create crypt token");
328 return 3;
329 }
330
331 if (GWEN_DB_GetIntValue(dbArgs, "forcePin", 0, 0))
333
334 /* open crypt token for use */
335 rv=GWEN_Crypt_Token_Open(ct, 0, 0);
336 if (rv) {
337 DBG_ERROR(0, "Could not open token");
338 return 3;
339 }
340 else {
341 uint8_t clearText[16]= {
342 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
343 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10
344 };
345 uint8_t encrypted[128];
346 uint32_t elen;
347 uint8_t decrypted[128];
348 uint32_t dlen;
350
351 algo=GWEN_Crypt_PaddAlgo_new(GWEN_Crypt_PaddAlgoId_LeftZero);
352 elen=sizeof(encrypted);
354 0x86,
355 algo,
356 clearText,
357 16,
358 encrypted,
359 &elen,
360 0);
361 if (rv) {
362 DBG_ERROR(0, "Could not encipher data (%d)", rv);
363 return 3;
364 }
365
366 fprintf(stderr, "Encrypted data is:\n");
367 GWEN_Text_DumpString((const char *) encrypted, elen, 2);
368
369 dlen=sizeof(decrypted);
371 0x86,
372 algo,
373 encrypted,
374 elen,
375 decrypted,
376 &dlen,
377 0);
378 if (rv) {
379 DBG_ERROR(0, "Could not decipher data (%d)", rv);
380 return 3;
381 }
382
383 if (memcmp(clearText, decrypted, dlen)) {
384 DBG_ERROR(0, "Deciphered data does not equal clear text(%d)", rv);
385 return 3;
386 }
387 fprintf(stderr, "Deciphered data is ok.\n");
388
389 }
390
391 /* close crypt token */
392 rv=GWEN_Crypt_Token_Close(ct, 0, 0);
393 if (rv) {
394 DBG_ERROR(0, "Could not close token");
395 return 3;
396 }
397
398 return 0;
399}
400
401
402
403
404
405
406
407
408int main(int argc, char **argv)
409{
410 GWEN_DB_NODE *db;
411 const char *cmd;
412 int rv;
413 int err;
414 GWEN_GUI *gui;
415 GWEN_STRINGLIST *slist;
416 const GWEN_ARGS args[]= {
417 {
419 GWEN_ArgsType_Char, /* type */
420 "cfgfile", /* name */
421 0, /* minnum */
422 1, /* maxnum */
423 "C", /* short option */
424 "cfgfile", /* long option */
425 "Specify the configuration file", /* short description */
426 "Specify the configuration file" /* long description */
427 },
428 {
429 0, /* flags */
430 GWEN_ArgsType_Int, /* type */
431 "forcePin", /* name */
432 0, /* minnum */
433 1, /* maxnum */
434 0, /* short option */
435 "forcepin", /* long option */
436 "force pin entry", /* short description */
437 "force pin entry even if the error counter is not zero"
438 },
439 {
441 GWEN_ArgsType_Int, /* type */
442 "help", /* name */
443 0, /* minnum */
444 0, /* maxnum */
445 "h", /* short option */
446 "help", /* long option */
447 "Show this help screen", /* short description */
448 "Show this help screen" /* long description */
449 }
450 };
451
452 err=GWEN_Init();
453 if (err) {
454 fprintf(stderr, "Could not initialize Gwenhywfar.\n");
455 return 2;
456 }
457
458 gui=GWEN_Gui_CGui_new();
459 GWEN_Gui_SetGui(gui);
460
461 slist =
463
464 assert(GWEN_StringList_Count(slist) > 0);
465#ifdef HAVE_I18N
466 {
467 const char *localedir = GWEN_StringList_FirstString(slist);
468 setlocale(LC_ALL, "");
469 if (bindtextdomain(PACKAGE, localedir)==0)
470 fprintf(stderr, "Error binding locale\n");
471 }
472#endif
474
475 GWEN_Logger_Open(GCT_LOGDOMAIN, "gct-tool", 0,
478
479#ifdef DEBUG_GCT_TOOL
483#else
487#endif
488
489#ifdef GCT_IS_EXPERIMENTAL
490 fprintf(stderr, "\n");
491 fprintf(stderr, "\n");
492 fprintf(stderr, "=================== WARNING ===================\n");
493 fprintf(stderr, "This tool is still EXPERIMENTAL !!!\n");
494 fprintf(stderr, "Please DON'T USE it with your daily key files !\n");
495 fprintf(stderr, "===============================================\n");
496 fprintf(stderr, "\n");
497 fprintf(stderr, "\n");
498#endif
499
500 db=GWEN_DB_Group_new("arguments");
501 rv=GWEN_Args_Check(argc, argv, 1,
504 args,
505 db);
506 if (rv==GWEN_ARGS_RESULT_ERROR) {
507 fprintf(stderr, "ERROR: Could not parse arguments main\n");
508 return -1;
509 }
510 else if (rv==GWEN_ARGS_RESULT_HELP) {
511 GWEN_BUFFER *ubuf;
512
513 ubuf=GWEN_Buffer_new(0, 1024, 0, 1);
515 I18N("Usage: "));
516 GWEN_Buffer_AppendString(ubuf, argv[0]);
518 I18N(" [GLOBAL OPTIONS] COMMAND "
519 "[LOCAL OPTIONS]\n"));
521 I18N("\nGlobal Options:\n"));
522 if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) {
523 fprintf(stderr, "ERROR: Could not create help string\n");
524 return 1;
525 }
527 I18N("\nCommands:\n\n"));
529 I18N(" create:\n"
530 " This command creates a crypt token"
531 "\n\n"));
533 I18N(" showuser:\n"
534 " Display user data stored on the "
535 "token\n\n"));
536
537 fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf));
538 GWEN_Buffer_free(ubuf);
539 return 0;
540 }
541 if (rv) {
542 argc-=rv-1;
543 argv+=rv-1;
544 }
545
546 cmd=GWEN_DB_GetCharValue(db, "params", 0, 0);
547 if (!cmd) {
548 fprintf(stderr, "ERROR: Command needed.\n");
549 return 1;
550 }
551
552 if (strcasecmp(cmd, "sign")==0) {
553 rv=signWithNew(db, argc, argv);
554 }
555 else if (strcasecmp(cmd, "crypt")==0) {
556 rv=cryptWithNew(db, argc, argv);
557 }
558 else {
559 fprintf(stderr, "ERROR: Unknown command \"%s\".\n", cmd);
560 rv=1;
561 }
562
563 err=GWEN_Fini();
564 if (err) {
565 fprintf(stderr,
566 "WARNING: Could not deinitialize Gwenhywfar.\n");
567 }
568
569 return rv;
570}
571
572
573
574
575
576
577
578
579
#define NULL
Definition: binreloc.c:300
GWEN_BUFFER * GWEN_Buffer_new(char *buffer, uint32_t size, uint32_t used, int take)
Definition: buffer.c:42
void GWEN_Buffer_free(GWEN_BUFFER *bf)
Definition: buffer.c:89
int GWEN_Buffer_AppendString(GWEN_BUFFER *bf, const char *buffer)
Definition: buffer.c:989
char * GWEN_Buffer_GetStart(const GWEN_BUFFER *bf)
Definition: buffer.c:235
GWEN_GUI * GWEN_Gui_CGui_new(void)
Definition: cgui.c:74
int GWEN_Crypt_Token_Sign(GWEN_CRYPT_TOKEN *ct, uint32_t keyId, GWEN_CRYPT_PADDALGO *a, const uint8_t *pInData, uint32_t inLen, uint8_t *pSignatureData, uint32_t *pSignatureLen, uint32_t *pSeqCounter, uint32_t gid)
Definition: ct.c:418
int GWEN_Crypt_Token_Open(GWEN_CRYPT_TOKEN *ct, int admin, uint32_t gid)
Definition: ct.c:222
int GWEN_Crypt_Token_Verify(GWEN_CRYPT_TOKEN *ct, uint32_t keyId, GWEN_CRYPT_PADDALGO *a, const uint8_t *pInData, uint32_t inLen, const uint8_t *pSignatureData, uint32_t signatureLen, uint32_t seqCounter, uint32_t gid)
Definition: ct.c:443
int GWEN_Crypt_Token_Close(GWEN_CRYPT_TOKEN *ct, int abandon, uint32_t gid)
Definition: ct.c:265
int GWEN_Crypt_Token_Encipher(GWEN_CRYPT_TOKEN *ct, uint32_t keyId, GWEN_CRYPT_PADDALGO *a, const uint8_t *pInData, uint32_t inLen, uint8_t *pOutData, uint32_t *pOutLen, uint32_t gid)
Definition: ct.c:468
int GWEN_Crypt_Token_Decipher(GWEN_CRYPT_TOKEN *ct, uint32_t keyId, GWEN_CRYPT_PADDALGO *a, const uint8_t *pInData, uint32_t inLen, uint8_t *pOutData, uint32_t *pOutLen, uint32_t gid)
Definition: ct.c:491
void GWEN_Crypt_Token_AddModes(GWEN_CRYPT_TOKEN *ct, uint32_t f)
Definition: ct.c:202
#define GWEN_CRYPT_TOKEN_MODE_FORCE_PIN_ENTRY
Definition: ct.h:59
struct GWEN_CRYPT_TOKEN GWEN_CRYPT_TOKEN
Definition: ct.h:19
GWEN_CRYPT_TOKEN * GWEN_Crypt_Token_Plugin_CreateToken(GWEN_PLUGIN *pl, const char *name)
Definition: ctplugin.c:146
int main(int argc, char **argv)
Definition: cttest.c:408
#define GCT_LOGDOMAIN
Definition: cttest.c:47
int signWithNew(GWEN_DB_NODE *dbArgs, int argc, char **argv)
Definition: cttest.c:53
int cryptWithNew(GWEN_DB_NODE *dbArgs, int argc, char **argv)
Definition: cttest.c:225
const char * GWEN_DB_GetCharValue(GWEN_DB_NODE *n, const char *path, int idx, const char *defVal)
Definition: db.c:971
GWEN_DB_NODE * GWEN_DB_Group_new(const char *name)
Definition: db.c:173
GWEN_DB_NODE * GWEN_DB_GetGroup(GWEN_DB_NODE *n, uint32_t flags, const char *path)
Definition: db.c:1381
int GWEN_DB_GetIntValue(GWEN_DB_NODE *n, const char *path, int idx, int defVal)
Definition: db.c:1163
#define GWEN_DB_FLAGS_DEFAULT
Definition: db.h:168
struct GWEN_DB_NODE GWEN_DB_NODE
Definition: db.h:228
#define DBG_INFO(dbg_logger, format, args...)
Definition: debug.h:181
#define DBG_ERROR(dbg_logger, format, args...)
Definition: debug.h:97
#define I18N(m)
Definition: error.c:42
struct GWEN_BUFFER GWEN_BUFFER
A dynamically resizeable text buffer.
Definition: buffer.h:38
void GWEN_Gui_SetGui(GWEN_GUI *gui)
Definition: gui.c:170
struct GWEN_GUI GWEN_GUI
Definition: gui.h:176
int GWEN_Init(void)
Definition: gwenhywfar.c:92
int GWEN_Fini(void)
Definition: gwenhywfar.c:303
#define GWEN_PM_LIBNAME
Definition: gwenhywfar.h:42
#define GWEN_PM_LOCALEDIR
Definition: gwenhywfar.h:49
int GWEN_Logger_Open(const char *logDomain, const char *ident, const char *file, GWEN_LOGGER_LOGTYPE logtype, GWEN_LOGGER_FACILITY facility)
Definition: logger.c:225
void GWEN_Logger_SetLevel(const char *logDomain, GWEN_LOGGER_LEVEL l)
Definition: logger.c:627
#define GWEN_LOGDOMAIN
Definition: logger.h:35
@ GWEN_LoggerType_Console
Definition: logger.h:44
@ GWEN_LoggerFacility_User
Definition: logger.h:58
@ GWEN_LoggerLevel_Warning
Definition: logger.h:69
@ GWEN_LoggerLevel_Debug
Definition: logger.h:72
@ GWEN_LoggerLevel_Info
Definition: logger.h:71
GWEN_CRYPT_PADDALGO * GWEN_Crypt_PaddAlgo_new(GWEN_CRYPT_PADDALGOID id)
Definition: paddalgo.c:90
struct GWEN_CRYPT_PADDALGO GWEN_CRYPT_PADDALGO
Definition: paddalgo.h:21
GWEN_STRINGLIST * GWEN_PathManager_GetPaths(const char *destLib, const char *pathName)
Definition: pathmanager.c:494
GWEN_PLUGIN_MANAGER * GWEN_PluginManager_FindPluginManager(const char *s)
Definition: plugin.c:548
GWEN_PLUGIN * GWEN_PluginManager_GetPlugin(GWEN_PLUGIN_MANAGER *pm, const char *s)
Definition: plugin.c:529
struct GWEN_PLUGIN_MANAGER GWEN_PLUGIN_MANAGER
Definition: plugin.h:40
struct GWEN_PLUGIN GWEN_PLUGIN
Definition: plugin.h:39
int GWEN_Args_Check(int argc, char **argv, int startAt, uint32_t mode, const GWEN_ARGS *args, GWEN_DB_NODE *db)
Definition: src/base/args.c:45
int GWEN_Args_Usage(const GWEN_ARGS *args, GWEN_BUFFER *ubuf, GWEN_ARGS_OUTTYPE ot)
#define GWEN_ARGS_FLAGS_HAS_ARGUMENT
Definition: src/base/args.h:50
#define GWEN_ARGS_RESULT_ERROR
Definition: src/base/args.h:57
#define GWEN_ARGS_MODE_STOP_AT_FREEPARAM
Definition: src/base/args.h:55
#define GWEN_ARGS_MODE_ALLOW_FREEPARAM
Definition: src/base/args.h:54
#define GWEN_ARGS_FLAGS_HELP
Definition: src/base/args.h:52
#define GWEN_ARGS_FLAGS_LAST
Definition: src/base/args.h:51
@ GWEN_ArgsOutType_Txt
Definition: src/base/args.h:68
@ GWEN_ArgsType_Int
Definition: src/base/args.h:63
@ GWEN_ArgsType_Char
Definition: src/base/args.h:62
#define GWEN_ARGS_RESULT_HELP
Definition: src/base/args.h:58
void GWEN_StringList_free(GWEN_STRINGLIST *sl)
Definition: stringlist.c:62
unsigned int GWEN_StringList_Count(const GWEN_STRINGLIST *sl)
Definition: stringlist.c:427
const char * GWEN_StringList_FirstString(const GWEN_STRINGLIST *l)
Definition: stringlist.c:576
struct GWEN_STRINGLISTSTRUCT GWEN_STRINGLIST
Definition: stringlist.h:56
void GWEN_Text_DumpString(const char *s, unsigned int l, unsigned int insert)
Definition: text.c:1283