Branch data Line data Source code
1 : : #include "rational.h"
2 : : #include <stdio.h>
3 : :
4 : : /**
5 : : * @brief Normalize one function Return value with global context
6 : : *
7 : : * @details The function normalizes @p status, normalizes global_return_status,
8 : : * stores the normalized global status back, merges only GLOBAL bits
9 : : * from global_return_status into @p status, and normalizes the final
10 : : * result. Technical critical bits take precedence over SUCCESS.
11 : : * NO takes precedence over YES when both binary answer flags are present
12 : : *
13 : : * @param status Status flags to normalize
14 : : * @return Normalized status flags
15 : : */
16 : 3010139 : Return rational_normalize_return(Return status)
17 : : {
18 : : /* CRITICAL marks an internal or blocking problem.
19 : : Such a status cannot also be SUCCESS, even if SUCCESS was ORed in earlier */
20 [ + + ]: 3010139 : if(CRITICAL & status)
21 : : {
22 : 497 : status &= ~SUCCESS;
23 : : }
24 : :
25 : : /* NO is the dominant binary answer.
26 : : If a check answered no, the final binary answer cannot also be yes.
27 : : If both binary answers are present, keep NO and drop YES */
28 [ + + ]: 3010139 : if(NO & status)
29 : : {
30 : 8215 : status &= ~YES;
31 : : }
32 : :
33 : : /* Local status is not the only source of process state.
34 : : global_return_status carries context that may be set outside the current function.
35 : : Read it into a regular Return value, normalize that copy, store the cleaned copy back,
36 : : then merge only explicitly global bits into the function return */
37 : 3010139 : Return global_status = atomic_load(&global_return_status);
38 : :
39 : : /* Global status follows the same contradiction rules as local status.
40 : : The normalized value is stored back so future returns see cleaned flags */
41 [ + + ]: 3010139 : if(CRITICAL & global_status)
42 : : {
43 : 1 : global_status &= ~SUCCESS;
44 : : }
45 : :
46 [ + + ]: 3010139 : if(NO & global_status)
47 : : {
48 : 2 : global_status &= ~YES;
49 : : }
50 : :
51 : : /* Keep the process-wide status normalized for the next function return */
52 : 3010139 : atomic_store(&global_return_status,global_status);
53 : :
54 : : /* Only process-wide context bits may flow from global status to a return.
55 : : Binary answers and local-only flags must not leak between functions */
56 : 3010139 : status |= (global_status & GLOBAL);
57 : :
58 : : /* Merging global context can introduce contradictions.
59 : : Normalize once more before returning the final function status */
60 [ + + ]: 3010139 : if(CRITICAL & status)
61 : : {
62 : 497 : status &= ~SUCCESS;
63 : : }
64 : :
65 [ + + ]: 3010139 : if(NO & status)
66 : : {
67 : 8215 : status &= ~YES;
68 : : }
69 : :
70 : 3010139 : return(status);
71 : : }
72 : :
73 : : /**
74 : : * @brief Consume a yes/no Return answer and merge its technical status
75 : : *
76 : : * @details The returned status must contain a pending yes/no answer. The
77 : : * function reads YES or NO, removes AWAITING and BOOLEAN from the
78 : : * caller's local status, merges only the technical status bits, and
79 : : * returns a regular C bool for direct use in conditions
80 : : *
81 : : * @param status Local caller status that accumulates technical return flags
82 : : * @param returned Return value produced by a check function
83 : : * @param func Name of the caller function for diagnostics
84 : : * @param line Source line of the ask() call for diagnostics
85 : : * @return true when @p returned contains a non-critical YES answer
86 : : */
87 : 4455 : bool rational_ask(
88 : : Return *status,
89 : : Return returned,
90 : : const char *func,
91 : : const int line)
92 : : {
93 : 4455 : bool answer = false;
94 : :
95 [ + + ]: 4455 : if(status == NULL)
96 : : {
97 : 1 : slog(ERROR,"%s:%d ask() received NULL status storage\n",func,line);
98 : :
99 : 1 : return(false);
100 : : }
101 : :
102 : 4454 : returned = rational_normalize_return(returned);
103 : :
104 [ + + ]: 4454 : if((AWAITING & returned) == 0)
105 : : {
106 : 1 : slog(ERROR,"%s:%d ask() expected a yes/no Return answer\n",func,line);
107 : 1 : *status = rational_normalize_return((*status & ~(AWAITING | BOOLEAN)) | FAILURE);
108 : :
109 : 1 : return(false);
110 : : }
111 : :
112 [ + + ]: 4453 : if((BOOLEAN & returned) == 0)
113 : : {
114 : 1 : slog(ERROR,"%s:%d ask() received a pending Return answer without YES or NO\n",func,line);
115 : 1 : *status = rational_normalize_return((*status & ~(AWAITING | BOOLEAN)) | FAILURE);
116 : :
117 : 1 : return(false);
118 : : }
119 : :
120 [ + + + + ]: 4452 : if((CRITICAL & returned) == 0 && (YES & returned))
121 : : {
122 : 345 : answer = true;
123 : : }
124 : :
125 : 8904 : *status = rational_normalize_return(
126 : 4452 : (*status & ~(AWAITING | BOOLEAN))
127 : 4452 : | (returned & ~(AWAITING | BOOLEAN)));
128 : :
129 : 4452 : return(answer);
130 : : }
131 : :
132 : : /// Converts Return status flags to a string
133 : 769 : const char *show_status(const Return status)
134 : : {
135 : 769 : const char *status_label = "UNKNOWN";
136 : :
137 : : /* Keep behavior explicit for the zero status value. */
138 [ + + ]: 769 : if(status == OK)
139 : : {
140 : 1 : status_label = "OK";
141 : : } else {
142 : : static char buffer[MAX_CHARACTERS];
143 : 768 : buffer[0] = '\0';
144 : :
145 : : static const struct {
146 : : Return flag;
147 : : const char *name;
148 : : } mapping[] = {
149 : : {FAILURE,"FAILURE"},
150 : : {SUCCESS,"SUCCESS"},
151 : : {HALTED,"HALTED"},
152 : : {WARNING,"WARNING"},
153 : : {DONOTHING,"DONOTHING"},
154 : : {INFO,"INFO"},
155 : : {YES,"YES"},
156 : : {NO,"NO"},
157 : : {AWAITING,"AWAITING"},
158 : : {0,NULL}
159 : : };
160 : :
161 : 768 : size_t used = 0U;
162 : 768 : bool first = true;
163 : 768 : unsigned int remaining = (unsigned int)status;
164 : :
165 [ + + ]: 7662 : for(size_t i = 0U; mapping[i].name != NULL; i++)
166 : : {
167 : 6896 : const unsigned int flag = (unsigned int)mapping[i].flag;
168 : :
169 [ + + ]: 6896 : if((remaining & flag) == 0U)
170 : : {
171 : 6045 : continue;
172 : : }
173 : :
174 : 851 : const int written = snprintf(
175 : : buffer + used,
176 : : MAX_CHARACTERS - used,
177 : : "%s%s",
178 : : first ? "" : "|",
179 [ + + ]: 851 : mapping[i].name);
180 : :
181 [ + + ]: 851 : if(written < 0)
182 : : {
183 : 1 : break;
184 : : }
185 : :
186 [ + + ]: 850 : if((size_t)written >= (MAX_CHARACTERS - used))
187 : : {
188 : 1 : buffer[MAX_CHARACTERS - 1U] = '\0';
189 : 1 : status_label = buffer;
190 : 1 : first = false;
191 : 1 : break;
192 : : }
193 : :
194 : 849 : used += (size_t)written;
195 : 849 : first = false;
196 : 849 : remaining &= ~flag;
197 : : }
198 : :
199 [ + + ]: 768 : if(first == false)
200 : : {
201 : 766 : status_label = buffer;
202 : : }
203 : : }
204 : 769 : return(status_label);
205 : : }
|