Branch data Line data Source code
1 : : #include "rational.h"
2 : :
3 : : #include <errno.h>
4 : : #include <stdlib.h>
5 : : #include <string.h>
6 : : #include <unistd.h>
7 : :
8 : : _Atomic RATIONAL_COLOR_MODE rational_color_mode = COLOR_MODE_AUTO;
9 : :
10 : : /**
11 : : * @brief Determine whether terminal colors may be written to a stream
12 : : *
13 : : * @details Always mode permits styling for any non-NULL stream, while never
14 : : * mode disables it. In automatic mode, the presence of NO_COLOR or
15 : : * TERM=dumb disables styling. Otherwise, the decision uses the
16 : : * supplied stream's terminal connection at the time of the call,
17 : : * including redirections made by a test runner. A terminal connection
18 : : * does not establish support for a particular color depth or control
19 : : * sequence. The function preserves errno and leaves buffering unchanged
20 : : *
21 : : * @param stream Output stream that would receive terminal color sequences;
22 : : * NULL disables styling in every mode
23 : : * @return true when terminal colors may be written, otherwise false
24 : : */
25 : 1390 : bool rational_color_is_enabled(FILE *stream)
26 : : {
27 : : /* Keep the caller's error code so terminal-detection calls cannot change
28 : : the error reported by code that runs after this policy check */
29 : 1390 : const int saved_errno = errno;
30 : :
31 : : /* Read the shared mode atomically once and use that snapshot throughout
32 : : this call. Relaxed ordering reads the setting without synchronizing
33 : : access to unrelated data */
34 : 1390 : const RATIONAL_COLOR_MODE mode = atomic_load_explicit(&rational_color_mode,memory_order_relaxed);
35 : :
36 : : /* Start with styling disabled. Never mode and a missing output stream
37 : : leave this result unchanged because neither enabling branch applies */
38 : 1390 : bool color_is_enabled = false;
39 : :
40 : : /* Always mode enables styling for the supplied stream without consulting
41 : : environment variables or checking whether the stream is a terminal */
42 [ + + + + ]: 1390 : if(stream != NULL && COLOR_MODE_ALWAYS == mode)
43 : : {
44 : 1298 : color_is_enabled = true;
45 [ + + + + : 92 : } else if(stream != NULL && COLOR_MODE_AUTO == mode && getenv("NO_COLOR") == NULL){
+ + ]
46 : : /* Automatic detection runs only when NO_COLOR is absent. Its presence
47 : : disables automatic styling even when its value is an empty string */
48 : 39 : const char *term = getenv("TERM");
49 : :
50 : : /* The exact TERM value dumb disables automatic styling. An unset TERM
51 : : or any other value allows the stream's terminal check to proceed */
52 [ + + + + ]: 39 : if(term == NULL || 0 != strcmp(term,"dumb"))
53 : : {
54 : : /* Resolve the descriptor of this specific stream on each check.
55 : : This detects its current redirection and lets stdout and stderr
56 : : receive independent decisions */
57 : 33 : const int stream_descriptor = fileno(stream);
58 : :
59 : : /* Require a valid descriptor connected to a terminal or
60 : : pseudoterminal. Files, pipes, and failed checks leave styling
61 : : disabled. This tests the connection, not terminal capabilities */
62 [ + - + + ]: 33 : if(stream_descriptor >= 0 && isatty(stream_descriptor) == 1)
63 : : {
64 : 4 : color_is_enabled = true;
65 : : }
66 : : }
67 : : }
68 : :
69 : : /* Restore the caller's error code and return the decision. The caller
70 : : uses it to select styled or plain text for this output stream */
71 : 1390 : errno = saved_errno;
72 : 1390 : return(color_is_enabled);
73 : : }
|