aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Levitte <levitte@openssl.org>2001-06-23 16:22:48 +0000
committerRichard Levitte <levitte@openssl.org>2001-06-23 16:22:48 +0000
commit2d2ed9dffd6c2f3bb1c591bdc282f0bb3c19132e (patch)
treea6ea9ca5df9f5b4ba80ad00cb81eaca08d915243
parent8ada6e7705685be0d97987d31f23e6cf543ef544 (diff)
downloadopenssl-2d2ed9dffd6c2f3bb1c591bdc282f0bb3c19132e.tar.gz
Implement boolean (yes/no or OK/Cancel, ...) input.
Implement UI controls. Current controls are the possibility to output the OpenSSL error stack on the same channel from within UI_process() and to check if the same user interface can be redone without being rebuilt (this is often more a question of philosophy than technicalities).
-rw-r--r--crypto/ui/ui.h66
-rw-r--r--crypto/ui/ui_lib.c350
-rw-r--r--crypto/ui/ui_locl.h35
3 files changed, 398 insertions, 53 deletions
diff --git a/crypto/ui/ui.h b/crypto/ui/ui.h
index 580b7d7fc9..07128953be 100644
--- a/crypto/ui/ui.h
+++ b/crypto/ui/ui.h
@@ -91,8 +91,10 @@ UI *UI_new_method(const UI_METHOD *method);
void UI_free(UI *ui);
/* The following functions are used to add strings to be printed and prompt
- strings to prompt for data. The names are UI_{add,dup}_<function>_string,
- with the following meanings:
+ strings to prompt for data. The names are UI_{add,dup}_<function>_string
+ and UI_{add,dup}_input_boolean.
+
+ UI_{add,dup}_<function>_string have the following meanings:
add add a text or prompt string. The pointers given to these
functions are used verbatim, no copying is done.
dup make a copy of the text or prompt string, then add the copy
@@ -108,12 +110,26 @@ void UI_free(UI *ui);
Honestly, there's currently no difference between info and error for the
moment.
- All of the functions in this group take a UI and a string. The input and
- verify addition functions also take a flag argument, a buffer for the result
- to end up with, a minimum input size and a maximum input size (the result
- buffer MUST be large enough to be able to contain the maximum number of
- characters). Additionally, the verify addition functions takes another
- buffer to compare the result against.
+ UI_{add,dup}_input_boolean have the same semantics for "add" and "dup",
+ and are typically used when one wants to prompt for a yes/no response.
+
+
+ All of the functions in this group take a UI and a prompt string.
+ The string input and verify addition functions also take a flag argument,
+ a buffer for the result to end up with, a minimum input size and a maximum
+ input size (the result buffer MUST be large enough to be able to contain
+ the maximum number of characters). Additionally, the verify addition
+ functions takes another buffer to compare the result against.
+ The boolean input functions take an action description string (which should
+ be safe to ignore if the expected user action is obvious, for example with
+ a dialog box with an OK button and a Cancel button), a string of acceptable
+ characters to mean OK and to mean Cancel. The two last strings are checked
+ to make sure they don't have common characters. Additionally, the same
+ flag argument as for the string input is taken, as well as a result buffer.
+ The result buffer is required to be at least one byte long. Depending on
+ the answer, the first character from the OK or the Cancel character strings
+ will be stored in the first byte of the result buffer. No NUL will be
+ added, so the result is *not* a string.
On success, the all return an index of the added information. That index
is usefull when retrieving results with UI_get0_result(). */
@@ -125,6 +141,12 @@ int UI_add_verify_string(UI *ui, const char *prompt, int flags,
char *result_buf, int minsize, int maxsize, const char *test_buf);
int UI_dup_verify_string(UI *ui, const char *prompt, int flags,
char *result_buf, int minsize, int maxsize, const char *test_buf);
+int UI_add_input_boolean(UI *ui, const char *prompt, const char *action_desc,
+ const char *ok_chars, const char *cancel_chars,
+ int flags, char *result_buf);
+int UI_dup_input_boolean(UI *ui, const char *prompt, const char *action_desc,
+ const char *ok_chars, const char *cancel_chars,
+ int flags, char *result_buf);
int UI_add_info_string(UI *ui, const char *text);
int UI_dup_info_string(UI *ui, const char *text);
int UI_add_error_string(UI *ui, const char *text);
@@ -192,6 +214,22 @@ const char *UI_get0_result(UI *ui, int i);
/* When all strings have been added, process the whole thing. */
int UI_process(UI *ui);
+/* Give a user interface parametrised control commands. This can be used to
+ send down an integer, a data pointer or a function pointer, as well as
+ be used to get information from a UI. */
+int UI_ctrl(UI *ui, int cmd, long i, void *p, void (*f)());
+
+/* The commands */
+/* Use UI_CONTROL_PRINT_ERRORS with the value 1 to have UI_process print the
+ OpenSSL error stack before printing any info or added error messages and
+ before any prompting. */
+#define UI_CTRL_PRINT_ERRORS 1
+/* Check if a UI_process() is possible to do again with the same instance of
+ a user interface. This makes UI_ctrl() return 1 if it is redoable, and 0
+ if not. */
+#define UI_CTRL_IS_REDOABLE 2
+
+
/* Some methods may use extra data */
#define UI_set_app_data(s,arg) UI_set_ex_data(s,0,arg)
#define UI_get_app_data(s) UI_get_ex_data(s,0)
@@ -265,6 +303,7 @@ enum UI_string_types
UIT_NONE=0,
UIT_PROMPT, /* Prompt for a string */
UIT_VERIFY, /* Prompt for a string and verify */
+ UIT_BOOLEAN, /* Prompt for a yes/no response */
UIT_INFO, /* Send info to the user */
UIT_ERROR /* Send an error message to the user */
};
@@ -292,6 +331,8 @@ enum UI_string_types UI_get_string_type(UI_STRING *uis);
int UI_get_input_flags(UI_STRING *uis);
/* Return the actual string to output (the prompt, info or error) */
const char *UI_get0_output_string(UI_STRING *uis);
+/* Return the optional action string to output (the boolean promtp instruction) */
+const char *UI_get0_action_string(UI_STRING *uis);
/* Return the result of a prompt */
const char *UI_get0_result_string(UI_STRING *uis);
/* Return the string to test the result against. Only useful with verifies. */
@@ -301,7 +342,7 @@ int UI_get_result_minsize(UI_STRING *uis);
/* Return the required maximum size of the result */
int UI_get_result_maxsize(UI_STRING *uis);
/* Set the result of a UI_STRING. */
-int UI_set_result(UI_STRING *uis, const char *result);
+int UI_set_result(UI *ui, UI_STRING *uis, const char *result);
/* BEGIN ERROR CODES */
@@ -313,9 +354,13 @@ void ERR_load_UI_strings(void);
/* Error codes for the UI functions. */
/* Function codes. */
+#define UI_F_GENERAL_ALLOCATE_BOOLEAN 108
+#define UI_F_GENERAL_ALLOCATE_PROMPT 109
#define UI_F_GENERAL_ALLOCATE_STRING 100
+#define UI_F_UI_CTRL 111
#define UI_F_UI_DUP_ERROR_STRING 101
#define UI_F_UI_DUP_INFO_STRING 102
+#define UI_F_UI_DUP_INPUT_BOOLEAN 110
#define UI_F_UI_DUP_INPUT_STRING 103
#define UI_F_UI_DUP_VERIFY_STRING 106
#define UI_F_UI_GET0_RESULT 107
@@ -323,10 +368,13 @@ void ERR_load_UI_strings(void);
#define UI_F_UI_SET_RESULT 105
/* Reason codes. */
+#define UI_R_COMMON_OK_AND_CANCEL_CHARACTERS 104
#define UI_R_INDEX_TOO_LARGE 102
#define UI_R_INDEX_TOO_SMALL 103
+#define UI_R_NO_RESULT_BUFFER 105
#define UI_R_RESULT_TOO_LARGE 100
#define UI_R_RESULT_TOO_SMALL 101
+#define UI_R_UNKNOWN_CONTROL_COMMAND 106
#ifdef __cplusplus
}
diff --git a/crypto/ui/ui_lib.c b/crypto/ui/ui_lib.c
index 395f700ffd..2559ea54b6 100644
--- a/crypto/ui/ui_lib.c
+++ b/crypto/ui/ui_lib.c
@@ -98,7 +98,19 @@ UI *UI_new_method(const UI_METHOD *method)
static void free_string(UI_STRING *uis)
{
if (uis->flags & OUT_STRING_FREEABLE)
+ {
OPENSSL_free((char *)uis->out_string);
+ switch(uis->type)
+ {
+ case UIT_BOOLEAN:
+ OPENSSL_free((char *)uis->_.boolean_data.action_desc);
+ OPENSSL_free((char *)uis->_.boolean_data.ok_chars);
+ OPENSSL_free((char *)uis->_.boolean_data.cancel_chars);
+ break;
+ default:
+ break;
+ }
+ }
OPENSSL_free(uis);
}
@@ -123,28 +135,98 @@ static int allocate_string_stack(UI *ui)
return 0;
}
+static UI_STRING *general_allocate_prompt(UI *ui, const char *prompt,
+ int prompt_freeable, enum UI_string_types type, int input_flags,
+ char *result_buf)
+ {
+ UI_STRING *ret = NULL;
+
+ if (prompt == NULL)
+ {
+ UIerr(UI_F_GENERAL_ALLOCATE_PROMPT,ERR_R_PASSED_NULL_PARAMETER);
+ }
+ else if (result_buf == NULL)
+ {
+ UIerr(UI_F_GENERAL_ALLOCATE_PROMPT,UI_R_NO_RESULT_BUFFER);
+ }
+ else if ((ret = (UI_STRING *)OPENSSL_malloc(sizeof(UI_STRING))))
+ {
+ ret->out_string=prompt;
+ ret->flags=prompt_freeable ? OUT_STRING_FREEABLE : 0;
+ ret->input_flags=input_flags;
+ ret->type=type;
+ ret->result_buf=result_buf;
+ }
+ return ret;
+ }
+
static int general_allocate_string(UI *ui, const char *prompt,
int prompt_freeable, enum UI_string_types type, int input_flags,
char *result_buf, int minsize, int maxsize, const char *test_buf)
{
- int ret=-1;
+ int ret = -1;
+ UI_STRING *s = general_allocate_prompt(ui, prompt, prompt_freeable,
+ type, input_flags, result_buf);
- if (prompt == NULL)
+ if (s)
+ {
+ if (allocate_string_stack(ui) >= 0)
+ {
+ s->_.string_data.result_minsize=minsize;
+ s->_.string_data.result_maxsize=maxsize;
+ s->_.string_data.test_buf=test_buf;
+ ret=sk_UI_STRING_push(ui->strings, s);
+ }
+ else
+ free_string(s);
+ }
+ return ret;
+ }
+
+static int general_allocate_boolean(UI *ui,
+ const char *prompt, const char *action_desc,
+ const char *ok_chars, const char *cancel_chars,
+ int prompt_freeable, enum UI_string_types type, int input_flags,
+ char *result_buf)
+ {
+ int ret = -1;
+ UI_STRING *s;
+ const char *p;
+
+ if (ok_chars == NULL)
{
- UIerr(UI_F_GENERAL_ALLOCATE_STRING,ERR_R_PASSED_NULL_PARAMETER);
+ UIerr(UI_F_GENERAL_ALLOCATE_BOOLEAN,ERR_R_PASSED_NULL_PARAMETER);
}
- else if (allocate_string_stack(ui) >= 0)
+ else if (cancel_chars == NULL)
{
- UI_STRING *s=(UI_STRING *)OPENSSL_malloc(sizeof(UI_STRING));
- s->out_string=prompt;
- s->flags=prompt_freeable ? OUT_STRING_FREEABLE : 0;
- s->input_flags=input_flags;
- s->type=type;
- s->result_buf=result_buf;
- s->result_minsize=minsize;
- s->result_maxsize=maxsize;
- s->test_buf=test_buf;
- ret=sk_UI_STRING_push(ui->strings, s);
+ UIerr(UI_F_GENERAL_ALLOCATE_BOOLEAN,ERR_R_PASSED_NULL_PARAMETER);
+ }
+ else
+ {
+ for(p = ok_chars; *p; p++)
+ {
+ if (strchr(cancel_chars, *p))
+ {
+ UIerr(UI_F_GENERAL_ALLOCATE_BOOLEAN,
+ UI_R_COMMON_OK_AND_CANCEL_CHARACTERS);
+ }
+ }
+
+ s = general_allocate_prompt(ui, prompt, prompt_freeable,
+ type, input_flags, result_buf);
+
+ if (s)
+ {
+ if (allocate_string_stack(ui) >= 0)
+ {
+ s->_.boolean_data.action_desc = action_desc;
+ s->_.boolean_data.ok_chars = ok_chars;
+ s->_.boolean_data.cancel_chars = cancel_chars;
+ ret=sk_UI_STRING_push(ui->strings, s);
+ }
+ else
+ free_string(s);
+ }
}
return ret;
}
@@ -204,6 +286,74 @@ int UI_dup_verify_string(UI *ui, const char *prompt, int flags,
UIT_VERIFY, flags, result_buf, minsize, maxsize, test_buf);
}
+int UI_add_input_boolean(UI *ui, const char *prompt, const char *action_desc,
+ const char *ok_chars, const char *cancel_chars,
+ int flags, char *result_buf)
+ {
+ return general_allocate_boolean(ui, prompt, action_desc,
+ ok_chars, cancel_chars, 0, UIT_BOOLEAN, flags, result_buf);
+ }
+
+int UI_dup_input_boolean(UI *ui, const char *prompt, const char *action_desc,
+ const char *ok_chars, const char *cancel_chars,
+ int flags, char *result_buf)
+ {
+ char *prompt_copy = NULL;
+ char *action_desc_copy = NULL;
+ char *ok_chars_copy = NULL;
+ char *cancel_chars_copy = NULL;
+
+ if (prompt)
+ {
+ prompt_copy=BUF_strdup(prompt);
+ if (prompt_copy == NULL)
+ {
+ UIerr(UI_F_UI_DUP_INPUT_BOOLEAN,ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ }
+
+ if (action_desc)
+ {
+ action_desc_copy=BUF_strdup(action_desc);
+ if (action_desc_copy == NULL)
+ {
+ UIerr(UI_F_UI_DUP_INPUT_BOOLEAN,ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ }
+
+ if (ok_chars)
+ {
+ ok_chars_copy=BUF_strdup(ok_chars);
+ if (ok_chars_copy == NULL)
+ {
+ UIerr(UI_F_UI_DUP_INPUT_BOOLEAN,ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ }
+
+ if (cancel_chars)
+ {
+ cancel_chars_copy=BUF_strdup(cancel_chars);
+ if (cancel_chars_copy == NULL)
+ {
+ UIerr(UI_F_UI_DUP_INPUT_BOOLEAN,ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ }
+
+ return general_allocate_boolean(ui, prompt_copy, action_desc_copy,
+ ok_chars_copy, cancel_chars_copy, 1, UIT_BOOLEAN, flags,
+ result_buf);
+ err:
+ if (prompt_copy) OPENSSL_free(prompt_copy);
+ if (action_desc_copy) OPENSSL_free(action_desc_copy);
+ if (ok_chars_copy) OPENSSL_free(ok_chars_copy);
+ if (cancel_chars_copy) OPENSSL_free(cancel_chars_copy);
+ return -1;
+ }
+
int UI_add_info_string(UI *ui, const char *text)
{
return general_allocate_string(ui, text, 0, UIT_INFO, 0, NULL, 0, 0,
@@ -224,8 +374,8 @@ int UI_dup_info_string(UI *ui, const char *text)
}
}
- return general_allocate_string(ui, text_copy, 1, UIT_INFO, 0, NULL, 0, 0,
- NULL);
+ return general_allocate_string(ui, text_copy, 1, UIT_INFO, 0, NULL,
+ 0, 0, NULL);
}
int UI_add_error_string(UI *ui, const char *text)
@@ -313,6 +463,20 @@ const char *UI_get0_result(UI *ui, int i)
return UI_get0_result_string(sk_UI_STRING_value(ui->strings, i));
}
+static int print_error(const char *str, size_t len, UI *ui)
+ {
+ UI_STRING uis;
+
+ memset(&uis, 0, sizeof(uis));
+ uis.type = UIT_ERROR;
+ uis.out_string = str;
+
+ if (ui->meth->ui_write_string
+ && !ui->meth->ui_write_string(ui, &uis))
+ return -1;
+ return 0;
+ }
+
int UI_process(UI *ui)
{
int i, ok=0;
@@ -320,6 +484,11 @@ int UI_process(UI *ui)
if (ui->meth->ui_open_session && !ui->meth->ui_open_session(ui))
return -1;
+ if (ui->flags & UI_FLAG_PRINT_ERRORS)
+ ERR_print_errors_cb(
+ (int (*)(const char *, size_t, void *))print_error,
+ (void *)ui);
+
for(i=0; i<sk_UI_STRING_num(ui->strings); i++)
{
if (ui->meth->ui_write_string
@@ -370,6 +539,33 @@ int UI_process(UI *ui)
return ok;
}
+int UI_ctrl(UI *ui, int cmd, long i, void *p, void (*f)())
+ {
+ if (ui == NULL)
+ {
+ UIerr(UI_F_UI_CTRL,ERR_R_PASSED_NULL_PARAMETER);
+ return -1;
+ }
+ switch(cmd)
+ {
+ case UI_CTRL_PRINT_ERRORS:
+ {
+ int save_flag = !!(ui->flags & UI_FLAG_PRINT_ERRORS);
+ if (i)
+ ui->flags |= UI_FLAG_PRINT_ERRORS;
+ else
+ ui->flags &= ~UI_FLAG_PRINT_ERRORS;
+ return save_flag;
+ }
+ case UI_CTRL_IS_REDOABLE:
+ return !!(ui->flags & UI_FLAG_REDOABLE);
+ default:
+ break;
+ }
+ UIerr(UI_F_UI_CTRL,UI_R_UNKNOWN_CONTROL_COMMAND);
+ return -1;
+ }
+
int UI_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
{
@@ -542,6 +738,20 @@ const char *UI_get0_output_string(UI_STRING *uis)
return uis->out_string;
}
+const char *UI_get0_action_string(UI_STRING *uis)
+ {
+ if (!uis)
+ return NULL;
+ switch(uis->type)
+ {
+ case UIT_PROMPT:
+ case UIT_BOOLEAN:
+ return uis->_.boolean_data.action_desc;
+ default:
+ return NULL;
+ }
+ }
+
const char *UI_get0_result_string(UI_STRING *uis)
{
if (!uis)
@@ -560,51 +770,119 @@ const char *UI_get0_test_string(UI_STRING *uis)
{
if (!uis)
return NULL;
- return uis->test_buf;
+ switch(uis->type)
+ {
+ case UIT_VERIFY:
+ return uis->_.string_data.test_buf;
+ default:
+ return NULL;
+ }
}
int UI_get_result_minsize(UI_STRING *uis)
{
if (!uis)
return -1;
- return uis->result_minsize;
+ switch(uis->type)
+ {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ return uis->_.string_data.result_minsize;
+ default:
+ return -1;
+ }
}
int UI_get_result_maxsize(UI_STRING *uis)
{
if (!uis)
return -1;
- return uis->result_maxsize;
+ switch(uis->type)
+ {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ return uis->_.string_data.result_maxsize;
+ default:
+ return -1;
+ }
}
-int UI_set_result(UI_STRING *uis, const char *result)
+int UI_set_result(UI *ui, UI_STRING *uis, const char *result)
{
int l = strlen(result);
+ ui->flags &= ~UI_FLAG_REDOABLE;
+
if (!uis)
return -1;
- if (l < uis->result_minsize)
+ switch (uis->type)
{
- UIerr(UI_F_UI_SET_RESULT,UI_R_RESULT_TOO_SMALL);
- return -1;
- }
- if (l > uis->result_maxsize)
+ case UIT_PROMPT:
+ case UIT_VERIFY:
{
- UIerr(UI_F_UI_SET_RESULT,UI_R_RESULT_TOO_LARGE);
- return -1;
- }
+ char number1[20];
+ char number2[20];
- if (!uis->result_buf)
- {
- uis->result_buf = OPENSSL_malloc(uis->result_maxsize+1);
+ BIO_snprintf(number1, sizeof(number1), "%d",
+ uis->_.string_data.result_minsize);
+ BIO_snprintf(number2, sizeof(number2), "%d",
+ uis->_.string_data.result_maxsize);
+
+ if (l < uis->_.string_data.result_minsize)
+ {
+ ui->flags |= UI_FLAG_REDOABLE;
+ UIerr(UI_F_UI_SET_RESULT,UI_R_RESULT_TOO_SMALL);
+ ERR_add_error_data(5,"You must type in ",
+ number1," to ",number2," characters");
+ return -1;
+ }
+ if (l > uis->_.string_data.result_maxsize)
+ {
+ ui->flags |= UI_FLAG_REDOABLE;
+ UIerr(UI_F_UI_SET_RESULT,UI_R_RESULT_TOO_LARGE);
+ ERR_add_error_data(5,"You must type in ",
+ number1," to ",number2," characters");
+ return -1;
+ }
}
- if (!uis->result_buf)
+ if (!uis->result_buf)
+ {
+ UIerr(UI_F_UI_SET_RESULT,UI_R_NO_RESULT_BUFFER);
+ return -1;
+ }
+
+ strcpy(uis->result_buf, result);
+ break;
+ case UIT_BOOLEAN:
{
- UIerr(UI_F_UI_NEW_METHOD,ERR_R_MALLOC_FAILURE);
- return -1;
- }
+ const char *p;
- strcpy(uis->result_buf, result);
+ if (!uis->result_buf)
+ {
+ UIerr(UI_F_UI_SET_RESULT,UI_R_NO_RESULT_BUFFER);
+ return -1;
+ }
+
+ uis->result_buf[0] = '\0';
+ for(p = result; *p; p++)
+ {
+ if (strchr(uis->_.boolean_data.ok_chars, *p))
+ {
+ uis->result_buf[0] =
+ uis->_.boolean_data.ok_chars[0];
+ break;
+ }
+ if (strchr(uis->_.boolean_data.cancel_chars, *p))
+ {
+ uis->result_buf[0] =
+ uis->_.boolean_data.cancel_chars[0];
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ }
return 0;
}
diff --git a/crypto/ui/ui_locl.h b/crypto/ui/ui_locl.h
index 89cdc2fe6a..6499ccac93 100644
--- a/crypto/ui/ui_locl.h
+++ b/crypto/ui/ui_locl.h
@@ -96,21 +96,36 @@ struct ui_method_st
struct ui_string_st
{
- const char *out_string; /* Input */
enum UI_string_types type; /* Input */
+ const char *out_string; /* Input */
int input_flags; /* Flags from the user */
- /* The following parameters are completely irrelevant for UI_INFO,
- and can therefore be set to 0 ro NULL */
+ /* The following parameters are completely irrelevant for UIT_INFO,
+ and can therefore be set to 0 or NULL */
char *result_buf; /* Input and Output: If not NULL, user-defined
with size in result_maxsize. Otherwise, it
may be allocated by the UI routine, meaning
result_minsize is going to be overwritten.*/
- int result_minsize; /* Input: minimum required size of the result*/
- int result_maxsize; /* Input: maximum permitted size of the
- result */
-
- const char *test_buf; /* Input: test string to verify against */
+ union
+ {
+ struct
+ {
+ int result_minsize; /* Input: minimum required
+ size of the result.
+ */
+ int result_maxsize; /* Input: maximum permitted
+ size of the result */
+
+ const char *test_buf; /* Input: test string to verify
+ against */
+ } string_data;
+ struct
+ {
+ const char *action_desc; /* Input */
+ const char *ok_chars; /* Input */
+ const char *cancel_chars; /* Input */
+ } boolean_data;
+ } _;
#define OUT_STRING_FREEABLE 0x01
int flags; /* flags for internal use */
@@ -124,6 +139,10 @@ struct ui_st
with different echoing status. */
void *user_data;
CRYPTO_EX_DATA ex_data;
+
+#define UI_FLAG_REDOABLE 0x0001
+#define UI_FLAG_PRINT_ERRORS 0x0100
+ int flags;
};
#endif