X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgerror.c;h=db31692b0038ea71f379a544e142484ca5a8eb33;hb=40650e33239994237917322d4cfecc34e4cc4394;hp=b2834b289d365cc857f5899fd3692c8b2c2d2ec7;hpb=b420fa84187d9cb2f89ab8b3dd2040eab24742af;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gerror.c b/glib/gerror.c index b2834b2..db31692 100644 --- a/glib/gerror.c +++ b/glib/gerror.c @@ -2,251 +2,724 @@ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public + * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. + * Lesser General Public License for more details. * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "glib.h" - -#ifdef HAVE_SYS_SELECT_H -#include -#endif /* HAVE_SYS_SELECT_H */ - -#ifdef STDC_HEADERS -#include /* for bzero on BSD systems */ -#endif - - -#ifndef NO_FD_SET -# define SELECT_MASK fd_set -#else -# ifndef _AIX - typedef long fd_mask; -# endif -# if defined(_IBMR2) -# define SELECT_MASK void -# else -# define SELECT_MASK int -# endif -#endif - - -static void stack_trace (char **args); - -extern volatile gboolean glib_on_error_halt; -volatile gboolean glib_on_error_halt = TRUE; +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +/** + * SECTION:error_reporting + * @Title: Error Reporting + * @Short_description: a system for reporting errors + * + * GLib provides a standard method of reporting errors from a called + * function to the calling code. (This is the same problem solved by + * exceptions in other languages.) It's important to understand that + * this method is both a data type (the #GError struct) and a set of + * rules. If you use #GError incorrectly, then your code will not + * properly interoperate with other code that uses #GError, and users + * of your API will probably get confused. + * + * First and foremost: #GError should only be used to report recoverable + * runtime errors, never to report programming errors. If the programmer + * has screwed up, then you should use g_warning(), g_return_if_fail(), + * g_assert(), g_error(), or some similar facility. (Incidentally, + * remember that the g_error() function should only be used for + * programming errors, it should not be used to print any error + * reportable via #GError.) + * + * Examples of recoverable runtime errors are "file not found" or + * "failed to parse input." Examples of programming errors are "NULL + * passed to strcmp()" or "attempted to free the same pointer twice." + * These two kinds of errors are fundamentally different: runtime errors + * should be handled or reported to the user, programming errors should + * be eliminated by fixing the bug in the program. This is why most + * functions in GLib and GTK+ do not use the #GError facility. + * + * Functions that can fail take a return location for a #GError as their + * last argument. For example: + * |[ + * gboolean g_file_get_contents (const gchar *filename, + * gchar **contents, + * gsize *length, + * GError **error); + * ]| + * If you pass a non-%NULL value for the `error` argument, it should + * point to a location where an error can be placed. For example: + * |[ + * gchar *contents; + * GError *err = NULL; + * + * g_file_get_contents ("foo.txt", &contents, NULL, &err); + * g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL)); + * if (err != NULL) + * { + * // Report error to user, and free error + * g_assert (contents == NULL); + * fprintf (stderr, "Unable to read file: %s\n", err->message); + * g_error_free (err); + * } + * else + * { + * // Use file contents + * g_assert (contents != NULL); + * } + * ]| + * Note that `err != NULL` in this example is a reliable indicator + * of whether g_file_get_contents() failed. Additionally, + * g_file_get_contents() returns a boolean which + * indicates whether it was successful. + * + * Because g_file_get_contents() returns %FALSE on failure, if you + * are only interested in whether it failed and don't need to display + * an error message, you can pass %NULL for the @error argument: + * |[ + * if (g_file_get_contents ("foo.txt", &contents, NULL, NULL)) // ignore errors + * // no error occurred + * ; + * else + * // error + * ; + * ]| + * + * The #GError object contains three fields: @domain indicates the module + * the error-reporting function is located in, @code indicates the specific + * error that occurred, and @message is a user-readable error message with + * as many details as possible. Several functions are provided to deal + * with an error received from a called function: g_error_matches() + * returns %TRUE if the error matches a given domain and code, + * g_propagate_error() copies an error into an error location (so the + * calling function will receive it), and g_clear_error() clears an + * error location by freeing the error and resetting the location to + * %NULL. To display an error to the user, simply display the @message, + * perhaps along with additional context known only to the calling + * function (the file being opened, or whatever - though in the + * g_file_get_contents() case, the @message already contains a filename). + * + * When implementing a function that can report errors, the basic + * tool is g_set_error(). Typically, if a fatal error occurs you + * want to g_set_error(), then return immediately. g_set_error() + * does nothing if the error location passed to it is %NULL. + * Here's an example: + * |[ + * gint + * foo_open_file (GError **error) + * { + * gint fd; + * + * fd = open ("file.txt", O_RDONLY); + * + * if (fd < 0) + * { + * g_set_error (error, + * FOO_ERROR, // error domain + * FOO_ERROR_BLAH, // error code + * "Failed to open file: %s", // error message format string + * g_strerror (errno)); + * return -1; + * } + * else + * return fd; + * } + * ]| + * + * Things are somewhat more complicated if you yourself call another + * function that can report a #GError. If the sub-function indicates + * fatal errors in some way other than reporting a #GError, such as + * by returning %TRUE on success, you can simply do the following: + * |[ + * gboolean + * my_function_that_can_fail (GError **err) + * { + * g_return_val_if_fail (err == NULL || *err == NULL, FALSE); + * + * if (!sub_function_that_can_fail (err)) + * { + * // assert that error was set by the sub-function + * g_assert (err == NULL || *err != NULL); + * return FALSE; + * } + * + * // otherwise continue, no error occurred + * g_assert (err == NULL || *err == NULL); + * } + * ]| + * + * If the sub-function does not indicate errors other than by + * reporting a #GError, you need to create a temporary #GError + * since the passed-in one may be %NULL. g_propagate_error() is + * intended for use in this case. + * |[ + * gboolean + * my_function_that_can_fail (GError **err) + * { + * GError *tmp_error; + * + * g_return_val_if_fail (err == NULL || *err == NULL, FALSE); + * + * tmp_error = NULL; + * sub_function_that_can_fail (&tmp_error); + * + * if (tmp_error != NULL) + * { + * // store tmp_error in err, if err != NULL, + * // otherwise call g_error_free() on tmp_error + * g_propagate_error (err, tmp_error); + * return FALSE; + * } + * + * // otherwise continue, no error occurred + * } + * ]| + * + * Error pileups are always a bug. For example, this code is incorrect: + * |[ + * gboolean + * my_function_that_can_fail (GError **err) + * { + * GError *tmp_error; + * + * g_return_val_if_fail (err == NULL || *err == NULL, FALSE); + * + * tmp_error = NULL; + * sub_function_that_can_fail (&tmp_error); + * other_function_that_can_fail (&tmp_error); + * + * if (tmp_error != NULL) + * { + * g_propagate_error (err, tmp_error); + * return FALSE; + * } + * } + * ]| + * @tmp_error should be checked immediately after sub_function_that_can_fail(), + * and either cleared or propagated upward. The rule is: after each error, + * you must either handle the error, or return it to the calling function. + * + * Note that passing %NULL for the error location is the equivalent + * of handling an error by always doing nothing about it. So the + * following code is fine, assuming errors in sub_function_that_can_fail() + * are not fatal to my_function_that_can_fail(): + * |[ + * gboolean + * my_function_that_can_fail (GError **err) + * { + * GError *tmp_error; + * + * g_return_val_if_fail (err == NULL || *err == NULL, FALSE); + * + * sub_function_that_can_fail (NULL); // ignore errors + * + * tmp_error = NULL; + * other_function_that_can_fail (&tmp_error); + * + * if (tmp_error != NULL) + * { + * g_propagate_error (err, tmp_error); + * return FALSE; + * } + * } + * ]| + * + * Note that passing %NULL for the error location ignores errors; + * it's equivalent to + * `try { sub_function_that_can_fail (); } catch (...) {}` + * in C++. It does not mean to leave errors unhandled; it means + * to handle them by doing nothing. + * + * Error domains and codes are conventionally named as follows: + * + * - The error domain is called __ERROR, + * for example %G_SPAWN_ERROR or %G_THREAD_ERROR: + * |[ + * #define G_SPAWN_ERROR g_spawn_error_quark () + * + * GQuark + * g_spawn_error_quark (void) + * { + * return g_quark_from_static_string ("g-spawn-error-quark"); + * } + * ]| + * + * - The quark function for the error domain is called + * __error_quark, + * for example g_spawn_error_quark() or g_thread_error_quark(). + * + * - The error codes are in an enumeration called + * Error; + * for example, #GThreadError or #GSpawnError. + * + * - Members of the error code enumeration are called + * __ERROR_, + * for example %G_SPAWN_ERROR_FORK or %G_THREAD_ERROR_AGAIN. + * + * - If there's a "generic" or "unknown" error code for unrecoverable + * errors it doesn't make sense to distinguish with specific codes, + * it should be called __ERROR_FAILED, + * for example %G_SPAWN_ERROR_FAILED. In the case of error code + * enumerations that may be extended in future releases, you should + * generally not handle this error code explicitly, but should + * instead treat any unrecognized error code as equivalent to + * FAILED. + * + * Summary of rules for use of #GError: + * + * - Do not report programming errors via #GError. + * + * - The last argument of a function that returns an error should + * be a location where a #GError can be placed (i.e. "#GError** error"). + * If #GError is used with varargs, the #GError** should be the last + * argument before the "...". + * + * - The caller may pass %NULL for the #GError** if they are not interested + * in details of the exact error that occurred. + * + * - If %NULL is passed for the #GError** argument, then errors should + * not be returned to the caller, but your function should still + * abort and return if an error occurs. That is, control flow should + * not be affected by whether the caller wants to get a #GError. + * + * - If a #GError is reported, then your function by definition had a + * fatal failure and did not complete whatever it was supposed to do. + * If the failure was not fatal, then you handled it and you should not + * report it. If it was fatal, then you must report it and discontinue + * whatever you were doing immediately. + * + * - If a #GError is reported, out parameters are not guaranteed to + * be set to any defined value. + * + * - A #GError* must be initialized to %NULL before passing its address + * to a function that can report errors. + * + * - "Piling up" errors is always a bug. That is, if you assign a + * new #GError to a #GError* that is non-%NULL, thus overwriting + * the previous error, it indicates that you should have aborted + * the operation instead of continuing. If you were able to continue, + * you should have cleared the previous error with g_clear_error(). + * g_set_error() will complain if you pile up errors. + * + * - By convention, if you return a boolean value indicating success + * then %TRUE means success and %FALSE means failure. + * Avoid creating functions which have a boolean + * return value and a GError parameter, but where the boolean does + * something other than signal whether the GError is set. Among other + * problems, it requires C callers to allocate a temporary error. Instead, + * provide a "gboolean *" out parameter. There are functions in GLib + * itself such as g_key_file_has_key() that are deprecated because of this. + * + * If %FALSE is + * returned, the error must be set to a non-%NULL value. + * One exception to this is that in situations that are + * already considered to be undefined behaviour (such as when a + * g_return_val_if_fail() check fails), the error need not be set. + * Instead of checking separately whether the error is set, callers + * should ensure that they do not provoke undefined behaviour, then + * assume that the error will be set on failure. + * + * - A %NULL return value is also frequently used to mean that an error + * occurred. You should make clear in your documentation whether %NULL + * is a valid return value in non-error cases; if %NULL is a valid value, + * then users must check whether an error was returned to see if the + * function succeeded. + * + * - When implementing a function that can report errors, you may want + * to add a check at the top of your function that the error return + * location is either %NULL or contains a %NULL error (e.g. + * `g_return_if_fail (error == NULL || *error == NULL);`). + */ + +#include "config.h" + +#include "gerror.h" + +#include "gslice.h" +#include "gstrfuncs.h" +#include "gtestutils.h" + +/** + * g_error_new_valist: + * @domain: error domain + * @code: error code + * @format: printf()-style format for error message + * @args: #va_list of parameters for the message format + * + * Creates a new #GError with the given @domain and @code, + * and a message formatted with @format. + * + * Returns: a new #GError + * + * Since: 2.22 + */ +GError* +g_error_new_valist (GQuark domain, + gint code, + const gchar *format, + va_list args) +{ + GError *error; + + /* Historically, GError allowed this (although it was never meant to work), + * and it has significant use in the wild, which g_return_val_if_fail + * would break. It should maybe g_return_val_if_fail in GLib 4. + * (GNOME#660371, GNOME#560482) + */ + g_warn_if_fail (domain != 0); + g_warn_if_fail (format != NULL); + + error = g_slice_new (GError); + + error->domain = domain; + error->code = code; + error->message = g_strdup_vprintf (format, args); + + return error; +} + +/** + * g_error_new: + * @domain: error domain + * @code: error code + * @format: printf()-style format for error message + * @...: parameters for message format + * + * Creates a new #GError with the given @domain and @code, + * and a message formatted with @format. + * + * Returns: a new #GError + */ +GError* +g_error_new (GQuark domain, + gint code, + const gchar *format, + ...) +{ + GError* error; + va_list args; + + g_return_val_if_fail (format != NULL, NULL); + g_return_val_if_fail (domain != 0, NULL); + + va_start (args, format); + error = g_error_new_valist (domain, code, format, args); + va_end (args); + + return error; +} + +/** + * g_error_new_literal: + * @domain: error domain + * @code: error code + * @message: error message + * + * Creates a new #GError; unlike g_error_new(), @message is + * not a printf()-style format string. Use this function if + * @message contains text you don't have control over, + * that could include printf() escape sequences. + * + * Returns: a new #GError + **/ +GError* +g_error_new_literal (GQuark domain, + gint code, + const gchar *message) +{ + GError* err; + + g_return_val_if_fail (message != NULL, NULL); + g_return_val_if_fail (domain != 0, NULL); + + err = g_slice_new (GError); + + err->domain = domain; + err->code = code; + err->message = g_strdup (message); + + return err; +} + +/** + * g_error_free: + * @error: a #GError + * + * Frees a #GError and associated resources. + */ void -g_on_error_query (const gchar *prg_name) +g_error_free (GError *error) { - static const gchar *query1 = "[E]xit, [H]alt"; - static const gchar *query2 = ", show [S]tack trace"; - static const gchar *query3 = " or [P]roceed"; - gchar buf[16]; - - if (!prg_name) - prg_name = g_get_prgname (); - - retry: - - if (prg_name) - fprintf (stdout, - "%s (pid:%u): %s%s%s: ", - prg_name, - (guint) getpid (), - query1, - query2, - query3); - else - fprintf (stdout, - "(process:%u): %s%s: ", - (guint) getpid (), - query1, - query3); - fflush (stdout); - - fgets (buf, 8, stdin); - - if ((buf[0] == 'E' || buf[0] == 'e') - && buf[1] == '\n') - _exit (0); - else if ((buf[0] == 'P' || buf[0] == 'p') - && buf[1] == '\n') + g_return_if_fail (error != NULL); + + g_free (error->message); + + g_slice_free (GError, error); +} + +/** + * g_error_copy: + * @error: a #GError + * + * Makes a copy of @error. + * + * Returns: a new #GError + */ +GError* +g_error_copy (const GError *error) +{ + GError *copy; + + g_return_val_if_fail (error != NULL, NULL); + /* See g_error_new_valist for why these don't return */ + g_warn_if_fail (error->domain != 0); + g_warn_if_fail (error->message != NULL); + + copy = g_slice_new (GError); + + *copy = *error; + + copy->message = g_strdup (error->message); + + return copy; +} + +/** + * g_error_matches: + * @error: (allow-none): a #GError or %NULL + * @domain: an error domain + * @code: an error code + * + * Returns %TRUE if @error matches @domain and @code, %FALSE + * otherwise. In particular, when @error is %NULL, %FALSE will + * be returned. + * + * If @domain contains a `FAILED` (or otherwise generic) error code, + * you should generally not check for it explicitly, but should + * instead treat any not-explicitly-recognized error code as being + * equilalent to the `FAILED` code. This way, if the domain is + * extended in the future to provide a more specific error code for + * a certain case, your code will still work. + * + * Returns: whether @error has @domain and @code + */ +gboolean +g_error_matches (const GError *error, + GQuark domain, + gint code) +{ + return error && + error->domain == domain && + error->code == code; +} + +#define ERROR_OVERWRITTEN_WARNING "GError set over the top of a previous GError or uninitialized memory.\n" \ + "This indicates a bug in someone's code. You must ensure an error is NULL before it's set.\n" \ + "The overwriting error message was: %s" + +/** + * g_set_error: + * @err: (allow-none): a return location for a #GError, or %NULL + * @domain: error domain + * @code: error code + * @format: printf()-style format + * @...: args for @format + * + * Does nothing if @err is %NULL; if @err is non-%NULL, then *@err + * must be %NULL. A new #GError is created and assigned to *@err. + */ +void +g_set_error (GError **err, + GQuark domain, + gint code, + const gchar *format, + ...) +{ + GError *new; + + va_list args; + + if (err == NULL) return; - else if (prg_name - && (buf[0] == 'S' || buf[0] == 's') - && buf[1] == '\n') - { - g_on_error_stack_trace (prg_name); - goto retry; - } - else if ((buf[0] == 'H' || buf[0] == 'h') - && buf[1] == '\n') + + va_start (args, format); + new = g_error_new_valist (domain, code, format, args); + va_end (args); + + if (*err == NULL) + *err = new; + else { - while (glib_on_error_halt) - ; - glib_on_error_halt = TRUE; - return; + g_warning (ERROR_OVERWRITTEN_WARNING, new->message); + g_error_free (new); } - else - goto retry; } +/** + * g_set_error_literal: + * @err: (allow-none): a return location for a #GError, or %NULL + * @domain: error domain + * @code: error code + * @message: error message + * + * Does nothing if @err is %NULL; if @err is non-%NULL, then *@err + * must be %NULL. A new #GError is created and assigned to *@err. + * Unlike g_set_error(), @message is not a printf()-style format string. + * Use this function if @message contains text you don't have control over, + * that could include printf() escape sequences. + * + * Since: 2.18 + */ void -g_on_error_stack_trace (const gchar *prg_name) +g_set_error_literal (GError **err, + GQuark domain, + gint code, + const gchar *message) { - pid_t pid; - gchar buf[16]; - gchar *args[4] = { "gdb", NULL, NULL, NULL }; - - if (!prg_name) + if (err == NULL) return; - sprintf (buf, "%u", (guint) getpid ()); - - args[1] = (gchar*) prg_name; - args[2] = buf; + if (*err == NULL) + *err = g_error_new_literal (domain, code, message); + else + g_warning (ERROR_OVERWRITTEN_WARNING, message); +} - pid = fork (); - if (pid == 0) +/** + * g_propagate_error: + * @dest: error return location + * @src: error to move into the return location + * + * If @dest is %NULL, free @src; otherwise, moves @src into *@dest. + * The error variable @dest points to must be %NULL. + */ +void +g_propagate_error (GError **dest, + GError *src) +{ + g_return_if_fail (src != NULL); + + if (dest == NULL) { - stack_trace (args); - _exit (0); + if (src) + g_error_free (src); + return; } - else if (pid == (pid_t) -1) + else { - perror ("unable to fork gdb"); - return; + if (*dest != NULL) + { + g_warning (ERROR_OVERWRITTEN_WARNING, src->message); + g_error_free (src); + } + else + *dest = src; } - - while (glib_on_error_halt) - ; - glib_on_error_halt = TRUE; } -static gboolean stack_trace_done = FALSE; - -static void -stack_trace_sigchld (int signum) +/** + * g_clear_error: + * @err: a #GError return location + * + * If @err is %NULL, does nothing. If @err is non-%NULL, + * calls g_error_free() on *@err and sets *@err to %NULL. + */ +void +g_clear_error (GError **err) { - stack_trace_done = TRUE; + if (err && *err) + { + g_error_free (*err); + *err = NULL; + } } +G_GNUC_PRINTF(2, 0) static void -stack_trace (char **args) +g_error_add_prefix (gchar **string, + const gchar *format, + va_list ap) { - pid_t pid; - int in_fd[2]; - int out_fd[2]; - SELECT_MASK fdset; - SELECT_MASK readset; - struct timeval tv; - int sel, index, state; - char buffer[256]; - char c; - - stack_trace_done = FALSE; - signal (SIGCHLD, stack_trace_sigchld); - - if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1)) - { - perror ("unable to open pipe"); - _exit (0); - } + gchar *oldstring; + gchar *prefix; - pid = fork (); - if (pid == 0) - { - close (0); dup (in_fd[0]); /* set the stdin to the in pipe */ - close (1); dup (out_fd[1]); /* set the stdout to the out pipe */ - close (2); dup (out_fd[1]); /* set the stderr to the out pipe */ + prefix = g_strdup_vprintf (format, ap); + oldstring = *string; + *string = g_strconcat (prefix, oldstring, NULL); + g_free (oldstring); + g_free (prefix); +} - execvp (args[0], args); /* exec gdb */ - perror ("exec failed"); - _exit (0); - } - else if (pid == (pid_t) -1) +/** + * g_prefix_error: + * @err: (allow-none): a return location for a #GError, or %NULL + * @format: printf()-style format string + * @...: arguments to @format + * + * Formats a string according to @format and prefix it to an existing + * error message. If @err is %NULL (ie: no error variable) then do + * nothing. + * + * If *@err is %NULL (ie: an error variable is present but there is no + * error condition) then also do nothing. Whether or not it makes sense + * to take advantage of this feature is up to you. + * + * Since: 2.16 + */ +void +g_prefix_error (GError **err, + const gchar *format, + ...) +{ + if (err && *err) { - perror ("unable to fork"); - _exit (0); - } - - FD_ZERO (&fdset); - FD_SET (out_fd[0], &fdset); + va_list ap; - write (in_fd[1], "backtrace\n", 10); - write (in_fd[1], "p x = 0\n", 8); - write (in_fd[1], "quit\n", 5); + va_start (ap, format); + g_error_add_prefix (&(*err)->message, format, ap); + va_end (ap); + } +} - index = 0; - state = 0; +/** + * g_propagate_prefixed_error: + * @dest: error return location + * @src: error to move into the return location + * @format: printf()-style format string + * @...: arguments to @format + * + * If @dest is %NULL, free @src; otherwise, moves @src into *@dest. + * *@dest must be %NULL. After the move, add a prefix as with + * g_prefix_error(). + * + * Since: 2.16 + **/ +void +g_propagate_prefixed_error (GError **dest, + GError *src, + const gchar *format, + ...) +{ + g_propagate_error (dest, src); - while (1) + if (dest && *dest) { - readset = fdset; - tv.tv_sec = 1; - tv.tv_usec = 0; - - sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv); - if (sel == -1) - break; + va_list ap; - if ((sel > 0) && (FD_ISSET (out_fd[0], &readset))) - { - if (read (out_fd[0], &c, 1)) - { - switch (state) - { - case 0: - if (c == '#') - { - state = 1; - index = 0; - buffer[index++] = c; - } - break; - case 1: - buffer[index++] = c; - if ((c == '\n') || (c == '\r')) - { - buffer[index] = 0; - fprintf (stdout, "%s", buffer); - state = 0; - index = 0; - } - break; - default: - break; - } - } - } - else if (stack_trace_done) - break; + va_start (ap, format); + g_error_add_prefix (&(*dest)->message, format, ap); + va_end (ap); } - - close (in_fd[0]); - close (in_fd[1]); - close (out_fd[0]); - close (out_fd[1]); - _exit (0); }