1 // Copyright 2012 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "atf_result.h"
31 #include <sys/types.h>
46 // Enumeration of the different result types returned by an ATF test case.
48 ATF_STATUS_EXPECTED_DEATH
,
49 ATF_STATUS_EXPECTED_EXIT
,
50 ATF_STATUS_EXPECTED_FAILURE
,
51 ATF_STATUS_EXPECTED_SIGNAL
,
52 ATF_STATUS_EXPECTED_TIMEOUT
,
57 // The broken status below is never returned by the test cases themselves.
58 // We use it internally to pass around problems detected while dealing with
59 // the test case itself (like an invalid result file).
64 /// Magic number representing a missing argument to the test result status.
66 /// Use this to specify that an expected_exit or expected_signal result accepts
67 /// any exit code or signal, respectively.
68 #define NO_STATUS_ARG -1
71 /// Removes a trailing newline from a string (supposedly read by fgets(3)).
73 /// \param [in,out] str The string to remove the trailing newline from.
75 /// \return True if there was a newline character; false otherwise.
77 trim_newline(char* str
)
79 const size_t length
= strlen(str
);
83 if (str
[length
- 1] == '\n') {
84 str
[length
- 1] = '\0';
93 /// Force read on stream to see if we are really at EOF.
95 /// A call to fgets(3) will not return EOF when it returns valid data. But
96 /// because of our semantics below, we need to be able to tell if more lines are
97 /// available before actually reading them.
99 /// \param input The stream to check for EOF.
101 /// \return True if the stream is not at EOF yet; false otherwise.
103 is_really_eof(FILE* input
)
105 const int ch
= getc(input
);
106 const bool real_eof
= feof(input
);
107 (void)ungetc(ch
, input
);
112 /// Parses the optional argument to a result status.
114 /// \param str Pointer to the argument. May be \0 in those cases where the
115 /// status does not have any argument.
116 /// \param [out] status_arg Value of the parsed argument.
118 /// \return OK if the argument exists and is valid, or if it does not exist; an
121 parse_status_arg(const char* str
, int* status_arg
)
124 *status_arg
= NO_STATUS_ARG
;
125 return kyua_error_ok();
128 const size_t length
= strlen(str
);
129 if (*str
!= '(' || *(str
+ length
- 1) != ')')
130 return kyua_generic_error_new("Invalid status argument %s", str
);
131 const char* const arg
= str
+ 1;
134 const long value
= strtol(arg
, &endptr
, 10);
135 if (arg
[0] == '\0' || endptr
!= str
+ length
- 1)
136 return kyua_generic_error_new("Invalid status argument %s: not a "
138 if (errno
== ERANGE
&& (value
== LONG_MAX
|| value
== LONG_MIN
))
139 return kyua_generic_error_new("Invalid status argument %s: out of "
141 if (value
< INT_MIN
|| value
> INT_MAX
)
142 return kyua_generic_error_new("Invalid status argument %s: out of "
145 *status_arg
= (int)value
;
146 return kyua_error_ok();
150 /// Parses a textual result status.
152 /// \param str The text to parse.
153 /// \param [out] status Status type if the input is valid.
154 /// \param [out] status_arg Optional integral argument to the status.
155 /// \param [out] need_reason Whether the detected status requires a reason.
157 /// \return An error if the status is not valid.
159 parse_status(const char* str
, enum atf_status
* status
, int* status_arg
,
162 if (strcmp(str
, "passed") == 0) {
163 *status
= ATF_STATUS_PASSED
;
164 *need_reason
= false;
165 return kyua_error_ok();
166 } else if (strcmp(str
, "failed") == 0) {
167 *status
= ATF_STATUS_FAILED
;
169 return kyua_error_ok();
170 } else if (strcmp(str
, "skipped") == 0) {
171 *status
= ATF_STATUS_SKIPPED
;
173 return kyua_error_ok();
174 } else if (strcmp(str
, "expected_death") == 0) {
175 *status
= ATF_STATUS_EXPECTED_DEATH
;
177 return kyua_error_ok();
178 } else if (strncmp(str
, "expected_exit", 13) == 0) {
179 *status
= ATF_STATUS_EXPECTED_EXIT
;
181 return parse_status_arg(str
+ 13, status_arg
);
182 } else if (strcmp(str
, "expected_failure") == 0) {
183 *status
= ATF_STATUS_EXPECTED_FAILURE
;
185 return kyua_error_ok();
186 } else if (strncmp(str
, "expected_signal", 15) == 0){
187 *status
= ATF_STATUS_EXPECTED_SIGNAL
;
189 return parse_status_arg(str
+ 15, status_arg
);
190 } else if (strcmp(str
, "expected_timeout") == 0) {
191 *status
= ATF_STATUS_EXPECTED_TIMEOUT
;
193 return kyua_error_ok();
195 return kyua_generic_error_new("Unknown test case result status %s",
201 /// Advances a pointer to a buffer to its end.
203 /// \param [in,out] buffer Current buffer contents; updated on exit to point to
204 /// the termination character.
205 /// \param [in,out] buffer_size Current buffer size; updated on exit to account
206 /// for the decreased capacity due to the pointer increase.
208 advance(char** buffer
, size_t* buffer_size
)
210 const size_t increment
= strlen(*buffer
);
211 *buffer
+= increment
;
212 *buffer_size
-= increment
;
216 /// Extracts the result reason from the input file.
218 /// \pre This can only be called for those result types that require a reason.
220 /// \param [in,out] input The file from which to read.
221 /// \param first_line The first line of the reason. Because this is part of the
222 /// same line in which the result status is printed, this line has already
223 /// been read by the caller and thus must be provided here.
224 /// \param [out] output Buffer to which to write the full reason.
225 /// \param output_size Size of the output buffer.
227 /// \return An error if there was no reason in the input or if there is a
228 /// problem reading it.
230 read_reason(FILE* input
, const char* first_line
, char* output
,
233 if (first_line
== NULL
|| *first_line
== '\0')
234 return kyua_generic_error_new("Test case should have reported a "
235 "failure reason but didn't");
237 snprintf(output
, output_size
, "%s", first_line
);
238 advance(&output
, &output_size
);
240 bool had_newline
= true;
241 while (!is_really_eof(input
)) {
243 snprintf(output
, output_size
, "<<NEWLINE>>");
244 advance(&output
, &output_size
);
247 if (fgets(output
, output_size
, input
) == NULL
) {
248 assert(ferror(input
));
249 return kyua_libc_error_new(errno
, "Failed to read reason from "
252 had_newline
= trim_newline(output
);
253 advance(&output
, &output_size
);
256 return kyua_error_ok();
260 /// Parses a results file written by an ATF test case.
262 /// \param input_name Path to the result file to parse.
263 /// \param [out] status Type of result.
264 /// \param [out] status_arg Optional integral argument to the status.
265 /// \param [out] reason Textual explanation of the result, if any.
266 /// \param reason_size Length of the reason output buffer.
268 /// \return An error if the input_name file has an invalid syntax; OK otherwise.
270 read_atf_result(const char* input_name
, enum atf_status
* status
,
271 int* status_arg
, char* const reason
, const size_t reason_size
)
273 kyua_error_t error
= kyua_error_ok();
275 FILE* input
= fopen(input_name
, "r");
277 error
= kyua_generic_error_new("Premature exit");
282 if (fgets(line
, sizeof(line
), input
) == NULL
) {
284 error
= kyua_libc_error_new(errno
, "Failed to read result from "
285 "file %s", input_name
);
289 error
= kyua_generic_error_new("Empty result file %s", input_name
);
294 if (!trim_newline(line
)) {
295 error
= kyua_generic_error_new("Missing newline in result file");
299 char* reason_start
= strstr(line
, ": ");
300 if (reason_start
!= NULL
) {
301 *reason_start
= '\0';
302 *(reason_start
+ 1) = '\0';
306 bool need_reason
= false; // Initialize to shut up gcc warning.
307 error
= parse_status(line
, status
, status_arg
, &need_reason
);
308 if (kyua_error_is_set(error
))
312 error
= read_reason(input
, reason_start
, reason
, reason_size
);
314 if (reason_start
!= NULL
|| !is_really_eof(input
)) {
315 error
= kyua_generic_error_new("Found unexpected reason in passed "
329 /// Writes a generic result file for an ATF broken result.
331 /// \param reason Textual explanation of the result.
332 /// \param status Exit code of the test program as returned by wait().
333 /// \param output Path to the generic result file to create.
334 /// \param [out] success Whether the result should be considered a success or
335 /// not; e.g. passed and skipped are successful, but failed is not.
337 /// \return An error if the conversion fails; OK otherwise.
339 convert_broken(const char* reason
, int status
, const char* output
,
342 if (WIFEXITED(status
)) {
344 return kyua_result_write(
345 output
, KYUA_RESULT_BROKEN
, "%s; test case exited with code %d",
346 reason
, WEXITSTATUS(status
));
348 assert(WIFSIGNALED(status
));
350 return kyua_result_write(
351 output
, KYUA_RESULT_BROKEN
, "%s; test case received signal %d%s",
352 reason
, WTERMSIG(status
),
353 WCOREDUMP(status
) ? " (core dumped)" : "");
358 /// Writes a generic result file for an ATF expected_death result.
360 /// \param reason Textual explanation of the result.
361 /// \param status Exit code of the test program as returned by wait().
362 /// \param output Path to the generic result file to create.
363 /// \param [out] success Whether the result should be considered a success or
364 /// not; e.g. passed and skipped are successful, but failed is not.
366 /// \return An error if the conversion fails; OK otherwise.
368 convert_expected_death(const char* reason
, int status
, const char* output
,
371 if (WIFEXITED(status
) && WEXITSTATUS(status
) == EXIT_SUCCESS
) {
373 return kyua_result_write(
374 output
, KYUA_RESULT_FAILED
, "Test case expected to die but exited "
378 return kyua_result_write(
379 output
, KYUA_RESULT_EXPECTED_FAILURE
, "%s", reason
);
384 /// Writes a generic result file for an ATF expected_exit result
386 /// \param status_arg Optional integral argument to the status.
387 /// \param reason Textual explanation of the result.
388 /// \param status Exit code of the test program as returned by wait().
389 /// \param output Path to the generic result file to create.
390 /// \param [out] success Whether the result should be considered a success or
391 /// not; e.g. passed and skipped are successful, but failed is not.
393 /// \return An error if the conversion fails; OK otherwise.
395 convert_expected_exit(const int status_arg
, const char* reason
, int status
,
396 const char* output
, bool* success
)
398 if (WIFEXITED(status
)) {
399 if (status_arg
== NO_STATUS_ARG
|| status_arg
== WEXITSTATUS(status
)) {
401 return kyua_result_write(
402 output
, KYUA_RESULT_EXPECTED_FAILURE
, "%s", reason
);
405 return kyua_result_write(
406 output
, KYUA_RESULT_FAILED
, "Test case expected to exit with "
407 "code %d but got code %d", status_arg
, WEXITSTATUS(status
));
410 assert(WIFSIGNALED(status
));
412 return kyua_result_write(
413 output
, KYUA_RESULT_FAILED
, "Test case expected to exit normally "
414 "but received signal %d%s", WTERMSIG(status
),
415 WCOREDUMP(status
) ? " (core dumped)" : "");
420 /// Writes a generic result file for an ATF expected_failure result.
422 /// \param reason Textual explanation of the result.
423 /// \param status Exit code of the test program as returned by wait().
424 /// \param output Path to the generic result file to create.
425 /// \param [out] success Whether the result should be considered a success or
426 /// not; e.g. passed and skipped are successful, but failed is not.
428 /// \return An error if the conversion fails; OK otherwise.
430 convert_expected_failure(const char* reason
, int status
, const char* output
,
433 if (WIFEXITED(status
)) {
434 if (WEXITSTATUS(status
) == EXIT_SUCCESS
) {
436 return kyua_result_write(
437 output
, KYUA_RESULT_EXPECTED_FAILURE
, "%s", reason
);
440 return kyua_result_write(
441 output
, KYUA_RESULT_FAILED
, "Test case expected a failure but "
442 "exited with error code %d", WEXITSTATUS(status
));
445 assert(WIFSIGNALED(status
));
447 return kyua_result_write(
448 output
, KYUA_RESULT_FAILED
, "Test case expected a failure but "
449 "received signal %d%s", WTERMSIG(status
),
450 WCOREDUMP(status
) ? " (core dumped)" : "");
455 /// Writes a generic result file for an ATF expected_signal result.
457 /// \param status_arg Optional integral argument to the status.
458 /// \param reason Textual explanation of the result.
459 /// \param status Exit code of the test program as returned by wait().
460 /// \param output Path to the generic result file to create.
461 /// \param [out] success Whether the result should be considered a success or
462 /// not; e.g. passed and skipped are successful, but failed is not.
464 /// \return An error if the conversion fails; OK otherwise.
466 convert_expected_signal(const int status_arg
, const char* reason
, int status
,
467 const char* output
, bool* success
)
469 if (WIFSIGNALED(status
)) {
470 if (status_arg
== NO_STATUS_ARG
|| status_arg
== WTERMSIG(status
)) {
472 return kyua_result_write(
473 output
, KYUA_RESULT_EXPECTED_FAILURE
, "%s", reason
);
476 return kyua_result_write(
477 output
, KYUA_RESULT_FAILED
, "Test case expected to receive "
478 "signal %d but got %d", status_arg
, WTERMSIG(status
));
481 assert(WIFEXITED(status
));
483 return kyua_result_write(
484 output
, KYUA_RESULT_FAILED
, "Test case expected to receive a "
485 "signal but exited with code %d", WEXITSTATUS(status
));
490 /// Writes a generic result file for an ATF expected_timeout result.
492 /// \param status Exit code of the test program as returned by wait().
493 /// \param output Path to the generic result file to create.
494 /// \param [out] success Whether the result should be considered a success or
495 /// not; e.g. passed and skipped are successful, but failed is not.
497 /// \return An error if the conversion fails; OK otherwise.
499 convert_expected_timeout(int status
, const char* output
, bool* success
)
501 if (WIFEXITED(status
)) {
503 return kyua_result_write(
504 output
, KYUA_RESULT_FAILED
, "Test case expected to time out but "
505 "exited with code %d", WEXITSTATUS(status
));
507 assert(WIFSIGNALED(status
));
509 return kyua_result_write(
510 output
, KYUA_RESULT_FAILED
, "Test case expected to time out but "
511 "received signal %d%s", WTERMSIG(status
),
512 WCOREDUMP(status
) ? " (core dumped)" : "");
517 /// Writes a generic result file for an ATF failed result.
519 /// \param reason Textual explanation of the result.
520 /// \param status Exit code of the test program as returned by wait().
521 /// \param output Path to the generic result file to create.
522 /// \param [out] success Whether the result should be considered a success or
523 /// not; e.g. passed and skipped are successful, but failed is not.
525 /// \return An error if the conversion fails; OK otherwise.
527 convert_failed(const char* reason
, int status
, const char* output
,
530 if (WIFEXITED(status
)) {
531 if (WEXITSTATUS(status
) == EXIT_SUCCESS
) {
533 return kyua_result_write(
534 output
, KYUA_RESULT_BROKEN
, "Test case reported a failed "
535 "result but exited with a successful exit code");
538 return kyua_result_write(
539 output
, KYUA_RESULT_FAILED
, "%s", reason
);
542 assert(WIFSIGNALED(status
));
544 return kyua_result_write(
545 output
, KYUA_RESULT_BROKEN
, "Test case reported a failed result "
546 "but received signal %d%s", WTERMSIG(status
),
547 WCOREDUMP(status
) ? " (core dumped)" : "");
552 /// Writes a generic result file for an ATF passed result.
554 /// \param status Exit code of the test program as returned by wait().
555 /// \param output Path to the generic result file to create.
556 /// \param [out] success Whether the result should be considered a success or
557 /// not; e.g. passed and skipped are successful, but failed is not.
559 /// \return An error if the conversion fails; OK otherwise.
561 convert_passed(int status
, const char* output
, bool* success
)
563 if (WIFEXITED(status
)) {
564 if (WEXITSTATUS(status
) == EXIT_SUCCESS
) {
566 return kyua_result_write(output
, KYUA_RESULT_PASSED
, NULL
);
569 return kyua_result_write(
570 output
, KYUA_RESULT_BROKEN
, "Test case reported a passed "
571 "result but returned a non-zero exit code %d",
572 WEXITSTATUS(status
));
575 assert(WIFSIGNALED(status
));
577 return kyua_result_write(
578 output
, KYUA_RESULT_BROKEN
, "Test case reported a passed result "
579 "but received signal %d%s", WTERMSIG(status
),
580 WCOREDUMP(status
) ? " (core dumped)" : "");
585 /// Writes a generic result file for an ATF skipped result.
587 /// \param reason Textual explanation of the result.
588 /// \param status Exit code of the test program as returned by wait().
589 /// \param output Path to the generic result file to create.
590 /// \param [out] success Whether the result should be considered a success or
591 /// not; e.g. passed and skipped are successful, but failed is not.
593 /// \return An error if the conversion fails; OK otherwise.
595 convert_skipped(const char* reason
, int status
, const char* output
,
598 if (WIFEXITED(status
)) {
599 if (WEXITSTATUS(status
) == EXIT_SUCCESS
) {
601 return kyua_result_write(output
, KYUA_RESULT_SKIPPED
, "%s", reason
);
604 return kyua_result_write(
605 output
, KYUA_RESULT_BROKEN
, "Test case reported a skipped "
606 "result but returned a non-zero exit code %d",
607 WEXITSTATUS(status
));
611 assert(WIFSIGNALED(status
));
612 return kyua_result_write(
613 output
, KYUA_RESULT_BROKEN
, "Test case reported a skipped result "
614 "but received signal %d%s", WTERMSIG(status
),
615 WCOREDUMP(status
) ? " (core dumped)" : "");
620 /// Writes a generic result file based on an ATF result and an exit code.
622 /// \param status Type of the ATF result.
623 /// \param status_arg Optional integral argument to the status.
624 /// \param reason Textual explanation of the result.
625 /// \param wait_status Exit code of the test program as returned by wait().
626 /// \param timed_out Whether the test program timed out or not.
627 /// \param output Path to the generic result file to create.
628 /// \param [out] success Whether the result should be considered a success or
629 /// not; e.g. passed and skipped are successful, but failed is not.
631 /// \return An error if the conversion fails; OK otherwise.
633 convert_result(const enum atf_status status
, const int status_arg
,
634 const char* reason
, const int wait_status
, const bool timed_out
,
635 const char* output
, bool* success
)
638 if (status
== ATF_STATUS_EXPECTED_TIMEOUT
) {
640 return kyua_result_write(
641 output
, KYUA_RESULT_EXPECTED_FAILURE
, "%s", reason
);
643 assert(status
== ATF_STATUS_BROKEN
);
645 return kyua_result_write(
646 output
, KYUA_RESULT_BROKEN
, "Test case body timed out");
651 case ATF_STATUS_BROKEN
:
652 return convert_broken(reason
, wait_status
, output
, success
);
654 case ATF_STATUS_EXPECTED_DEATH
:
655 return convert_expected_death(reason
, wait_status
, output
, success
);
657 case ATF_STATUS_EXPECTED_EXIT
:
658 return convert_expected_exit(status_arg
, reason
, wait_status
, output
,
661 case ATF_STATUS_EXPECTED_FAILURE
:
662 return convert_expected_failure(reason
, wait_status
, output
, success
);
664 case ATF_STATUS_EXPECTED_SIGNAL
:
665 return convert_expected_signal(status_arg
, reason
, wait_status
, output
,
668 case ATF_STATUS_EXPECTED_TIMEOUT
:
669 return convert_expected_timeout(wait_status
, output
, success
);
671 case ATF_STATUS_FAILED
:
672 return convert_failed(reason
, wait_status
, output
, success
);
674 case ATF_STATUS_PASSED
:
675 return convert_passed(wait_status
, output
, success
);
677 case ATF_STATUS_SKIPPED
:
678 return convert_skipped(reason
, wait_status
, output
, success
);
682 #if defined(__minix) && defined(NDEBUG)
684 #endif /* defined(__minix) && !defined(NDEBUG) */
688 /// Writes a generic result file based on an ATF result file and an exit code.
690 /// \param input_name Path to the ATF result file to parse.
691 /// \param output_name Path to the generic result file to create.
692 /// \param wait_status Exit code of the test program as returned by wait().
693 /// \param timed_out Whether the test program timed out or not.
694 /// \param [out] success Whether the result should be considered a success or
695 /// not; e.g. passed and skipped are successful, but failed is not.
697 /// \return An error if the conversion fails; OK otherwise.
699 kyua_atf_result_rewrite(const char* input_name
, const char* output_name
,
700 const int wait_status
, const bool timed_out
,
703 enum atf_status status
; int status_arg
; char reason
[1024];
704 status
= ATF_STATUS_BROKEN
; // Initialize to shut up gcc warning.
705 const kyua_error_t error
= read_atf_result(input_name
, &status
, &status_arg
,
706 reason
, sizeof(reason
));
707 if (kyua_error_is_set(error
)) {
708 // Errors while parsing the ATF result file can often be attributed to
709 // the result file being bogus. Therefore, just mark the test case as
710 // broken, because it possibly is.
711 status
= ATF_STATUS_BROKEN
;
712 kyua_error_format(error
, reason
, sizeof(reason
));
713 kyua_error_free(error
);
716 // Errors converting the loaded result to the final result file are not due
717 // to a bad test program: they are because our own code fails (e.g. cannot
718 // create the output file). These need to be returned to the caller.
719 return convert_result(status
, status_arg
, reason
, wait_status
, timed_out
,
720 output_name
, success
);
724 /// Creates a result file for a failed cleanup routine.
726 /// This function is supposed to be invoked after the body has had a chance to
727 /// create its own result file, and only if the body has terminated with a
728 /// non-failure result.
730 /// \param output_name Path to the generic result file to create.
731 /// \param wait_status Exit code of the test program as returned by wait().
732 /// \param timed_out Whether the test program timed out or not.
733 /// \param [out] success Whether the result should be considered a success or
734 /// not; i.e. a clean exit is successful, but anything else is a failure.
736 /// \return An error if there is a problem writing the result; OK otherwise.
738 kyua_atf_result_cleanup_rewrite(const char* output_name
, int wait_status
,
739 const bool timed_out
, bool* success
)
743 return kyua_result_write(
744 output_name
, KYUA_RESULT_BROKEN
, "Test case cleanup timed out");
746 if (WIFEXITED(wait_status
)) {
747 if (WEXITSTATUS(wait_status
) == EXIT_SUCCESS
) {
749 // Reuse the result file created by the body. I.e. avoid
750 // creating a new file here.
751 return kyua_error_ok();
754 return kyua_result_write(
755 output_name
, KYUA_RESULT_BROKEN
, "Test case cleanup exited "
756 "with code %d", WEXITSTATUS(wait_status
));
760 return kyua_result_write(
761 output_name
, KYUA_RESULT_BROKEN
, "Test case cleanup received "
762 "signal %d%s", WTERMSIG(wait_status
),
763 WCOREDUMP(wait_status
) ? " (core dumped)" : "");