1#ifndef _GNU_SOURCE
2// for vasprintf
3# define _GNU_SOURCE
4#endif
5
6#include <stdlib.h>
7
8#include <libpussy/mmarray.h>
9
10#include "include/pw.h"
11#include "src/types/status_internal.h"
12#include "src/types/struct_internal.h"
13
14
15#define get_this_status_data(value) _pw_get_this_struct_ptr(_PwStatusData*, mthis, (value))
16
17static char* basic_statuses[] = {
18 [PW_SUCCESS] = "SUCCESS",
19 [PW_STATUS_VA_END] = "VA_END",
20 [PW_ERROR] = "ERROR",
21 [PW_ERROR_ERRNO] = "ERRNO",
22 [PW_ERROR_OOM] = "OOM",
23 [PW_ERROR_NOT_IMPLEMENTED] = "NOT IMPLEMENTED",
24 [PW_ERROR_INCOMPATIBLE_TYPE] = "INCOMPATIBLE_TYPE",
25 [PW_ERROR_INTERFACE_NOT_DEFINED] = "INTERFACE_NOT_DEFINED",
26 [PW_ERROR_EOF] = "EOF",
27 [PW_ERROR_TIMEOUT] = "TIMEOUT",
28 [PW_ERROR_STRING_TOO_LONG] = "PW_ERROR_STRING_TOO_LONG",
29 [PW_ERROR_DATA_SIZE_TOO_BIG] = "DATA_SIZE_TOO_BIG",
30 [PW_ERROR_INDEX_OUT_OF_RANGE] = "INDEX_OUT_OF_RANGE",
31 [PW_ERROR_ITERATION_IN_PROGRESS] = "ITERATION_IN_PROGRESS",
32 [PW_ERROR_BAD_NUMBER] = "BAD_NUMBER",
33 [PW_ERROR_BAD_DATETIME] = "BAD_DATETIME",
34 [PW_ERROR_BAD_TIMESTAMP] = "BAD_TIMESTAMP",
35 [PW_ERROR_NUMERIC_OVERFLOW] = "NUMERIC_OVERFLOW",
36 [PW_ERROR_INCOMPLETE_UTF8] = "INCOMPLETE_UTF8",
37 [PW_ERROR_EXTRACT_FROM_EMPTY_ARRAY] = "EXTRACT_FROM_EMPTY_ARRAY",
38 [PW_ERROR_DELETE_FROM_EMPTY_ARRAY] = "DELETE_FROM_EMPTY_ARRAY",
39 [PW_ERROR_KEY_NOT_FOUND] = "KEY_NOT_FOUND",
40 [PW_ERROR_FILE_ALREADY_OPENED] = "FILE_ALREADY_OPENED",
41 [PW_ERROR_FD_ALREADY_SET] = "FD_ALREADY_SET",
42 [PW_ERROR_CANT_SET_FILENAME] = "CANT_SET_FILENAME",
43 [PW_ERROR_FILE_CLOSED] = "FILE_CLOSED",
44 [PW_ERROR_NOT_REGULAR_FILE] = "NOT_REGULAR_FILE",
45 [PW_ERROR_UNBUFFERED_FILE] = "UNBUFFERED_FILE",
46 [PW_ERROR_WRITE] = "WRITE",
47 [PW_ERROR_UNREAD_FAILED] = "UNREAD_FAILED"
48};
49
50static char** statuses = nullptr;
51static uint16_t num_statuses = 0;
52
53[[ gnu::constructor ]]
54static void init_statuses()
55{
56 if (statuses) {
57 return;
58 }
59 num_statuses = PW_LENGTH(basic_statuses);
60 statuses = mmarray_allocate(0, num_statuses, sizeof(char*));
61 for(uint16_t i = 0; i < num_statuses; i++) {
62 char* status = basic_statuses[i];
63 if (!status) {
64 fprintf(stderr, "Status %u is not defined\n", i);
65 abort();
66 }
67 statuses[i] = status;
68 }
69}
70
71uint16_t pw_define_status(char* status)
72{
73 // the order constructor are called is undefined, make sure statuses are initialized
74 init_statuses();
75
76 if (num_statuses == 65535) {
77 fprintf(stderr, "Cannot define more statuses than %u\n", num_statuses);
78 return PW_ERROR_OOM;
79 }
80 statuses = mmarray_append_item(statuses, &status);
81 uint16_t status_code = num_statuses++;
82 return status_code;
83}
84
85char* pw_status_str(uint16_t status_code)
86{
87 if (status_code < num_statuses) {
88 return statuses[status_code];
89 } else {
90 static char unknown[] = "(unknown)";
91 return unknown;
92 }
93}
94
95void _pw_set_status_location(PwValuePtr status, char* file_name, unsigned line_number)
96{
97 if (status->has_status_data) {
98 _PwStatusData* status_data = _pw_get_struct_ptr(status, PwTypeId_Status);
99 status_data->file_name = file_name;
100 status_data->line_number = line_number;
101 } else {
102 status->file_name = file_name;
103 status->line_number = line_number;
104 }
105}
106
107void _pw_set_status_desc(PwValuePtr status, char* fmt, ...)
108{
109 va_list ap;
110 va_start(ap);
111 _pw_set_status_desc_ap(status, fmt, ap);
112 va_end(ap);
113}
114
115void _pw_set_status_desc_ap(PwValuePtr status, char* fmt, va_list ap)
116{
117 pw_assert_status(status);
118 if (!status->is_error) {
119 // do not set description for PW_SUCCESS
120 // such status must not have any allocated data because it is allowed
121 // to be overwritten without calling pw_destroy on it, similar to null value
122 return;
123 }
124
125 _PwStatusData* status_data;
126
127 if (status->has_status_data) {
128 status_data = _pw_get_struct_ptr(status, PwTypeId_Status);
129 pw_destroy(&status_data->description);
130
131 } else {
132 // allocate structure
133
134 uint8_t* struct_data = _pw_struct_alloc(status->type_id);
135 if (!struct_data) {
136 // ignore error
137 return;
138 }
139 // save fields that will go to the allocated structure
140 char* file_name = status->file_name;
141 uint16_t line_number = status->line_number;
142 int16_t pw_errno = status->pw_errno;
143
144 status->has_status_data = 1;
145 status->struct_data = struct_data;
146
147 // set struct_offset for _pw_get_struct_ptr to work properly
148
149 status->struct_offset = _pw_types[status->type_id]->struct_offsets[0];
150
151 // set fields of the allocated structure
152
153 status_data = _pw_get_struct_ptr(status, PwTypeId_Status);
154
155 status_data->file_name = file_name;
156 status_data->line_number = line_number;
157 status_data->pw_errno = pw_errno;
158 }
159 char* desc;
160 if (vasprintf(&desc, fmt, ap) == -1) {
161 return;
162 }
163 PwValue s = PW_NULL;
164 if (pw_create_string(desc, &s)) {
165 pw_move(&s, &status_data->description);
166 }
167 free(desc);
168}
169
170void pw_print_status(FILE* fp, PwValuePtr status)
171{
172 // XXX rewrite in error-free way
173
174 PwValue desc = PW_NULL;
175 if (!pw_to_string(status, &desc)) {
176 // XXX
177 pw_dump(fp, &desc);
178 } else {
179 PW_CSTRING(desc_cstr, &desc);
180 fputs(desc_cstr, fp);
181 fputc('\n', fp);
182 }
183}
184
185/****************************************************************
186 * Basic interface methods
187 */
188
189static bool status_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
190{
191 // nothing to do here because pw_create2 has already set type_id and zeroed all other fields
192 return true;
193}
194
195static bool status_destroy(PwMethod_Basic_destroy* mthis, PwValuePtr self, _PwCompoundChain* tail)
196{
197 PwValuePtr value_seen = _pw_on_chain(self, tail);
198 if (value_seen) {
199 return true;
200 }
201 if (self->has_status_data) {
202 return pw_super(mthis, self, nullptr);
203 }
204 return true;
205}
206
207static bool status_clone(PwMethod_Basic_clone* mthis, PwValuePtr self)
208{
209 if (self->has_status_data) {
210 return pw_super(mthis, self);
211 }
212 return true;
213}
214
215static bool status_decref(PwMethod_Basic_decref* mthis, PwValuePtr self)
216{
217 if (self->has_status_data) {
218 return pw_super(mthis, self);
219 }
220 return true;
221}
222
223static bool status_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
224{
225 _pw_hash_uint64(ctx, self->type_id);
226 _pw_hash_uint64(ctx, self->status_code);
227 if (self->status_code == PW_ERROR_ERRNO) {
228 int pw_errno;
229 if (self->has_status_data) {
230 pw_errno = get_this_status_data(self)->pw_errno;
231 } else {
232 pw_errno = self->pw_errno;
233 }
234 _pw_hash_uint64(ctx, pw_errno);
235 }
236 // XXX do not hash description?
237 return true;
238}
239
240static bool status_deepcopy(PwMethod_Basic_deepcopy* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
241{
242 pw_destroy(result);
243 *result = *self;
244 if (!self->has_status_data) {
245 return true;
246 }
247
248 result->struct_data = _pw_struct_alloc(result->type_id);
249 if (!result->struct_data) {
250 *result = PwNull(); // reset possible garbage in other fields
251 return false;
252 }
253
254 _PwStatusData* src_status_data = get_this_status_data(self);
255 _PwStatusData* dest_status_data = get_this_status_data(result);
256
257 dest_status_data->file_name = src_status_data->file_name;
258 dest_status_data->line_number = src_status_data->line_number;
259 dest_status_data->pw_errno = src_status_data->pw_errno;
260 if (!pw_deepcopy(&src_status_data->description, &dest_status_data->description)) {
261 pw_destroy(result);
262 return false;
263 }
264 return true;
265}
266
267static bool status_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
268{
269 if (!self->is_error) {
270 pw_destroy(result);
271 *result = PwString("Success");
272 return true;
273 }
274 char* status_str = pw_status_str(self->status_code);
275 char* file_name;
276 unsigned line_number;
277 int pw_errno;
278 PwValuePtr description = nullptr;
279 unsigned description_length = 0;
280 uint8_t char_size = 1;
281
282 if (self->has_status_data) {
283 _PwStatusData* status_data = get_this_status_data(self);
284 file_name = status_data->file_name;
285 line_number = status_data->line_number;
286 pw_errno = status_data->pw_errno;
287 if (pw_is_string(&status_data->description)) {
288 description = &status_data->description;
289 description_length = pw_strlen(description);
290 char_size = description->char_size;
291 }
292 } else {
293 file_name = self->file_name;
294 line_number = self->line_number;
295 pw_errno = self->pw_errno;
296 }
297
298 unsigned errno_length = 0;
299 static char errno_fmt[] = "; errno %d: %s";
300 char* errno_str = "";
301 if (self->status_code == PW_ERROR_ERRNO) {
302 errno_str = strerror(pw_errno);
303 }
304 char errno_desc[strlen(errno_fmt) + 16 + strlen(errno_str)];
305 if (self->status_code == PW_ERROR_ERRNO) {
306 errno_length = snprintf(errno_desc, sizeof(errno_desc), errno_fmt, pw_errno, errno_str);
307 } else {
308 errno_desc[0] = 0;
309 }
310
311 static char fmt[] = "%s; %s:%u%s";
312 char desc[sizeof(fmt) + 16 + strlen(status_str) + strlen(file_name) + errno_length];
313 unsigned length = snprintf(desc, sizeof(desc), fmt, status_str, file_name, line_number, errno_desc);
314
315 if (!pw_create_empty_string(length + description_length + 2, char_size, result)) {
316 goto error;
317 }
318 if (!pw_string_append(result, desc, desc + length)) {
319 goto error;
320 }
321 if (description) {
322 if (!pw_string_append(result, "; ", nullptr)) {
323 goto error;
324 }
325 if (!pw_string_append(result, description)) {
326 goto error;
327 }
328 }
329 return true;
330
331error:
332 pw_destroy(result);
333 *result = PwString("(error)");
334 return false;
335}
336
337static bool status_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
338{
339 if (!pw_super(mthis, self, fp, indent, tail)) {
340 return false;
341 }
342
343 PwValue desc = PW_NULL;
344 if (pw_this_call(to_string, mthis, self, &desc, nullptr)) {
345 // result is always meaningful string, ignore return value
346 }
347 PW_CSTRING(cdesc, &desc);
348 _pw_print_indent(fp, indent);
349 fputs(cdesc, fp);
350 fputc('\n', fp);
351 return true;
352}
353
354[[nodiscard]] static bool status_eq(PwValuePtr self, PwValuePtr other)
355{
356 if (self->status_code == PW_ERROR_ERRNO) {
357 if (other->status_code != PW_ERROR_ERRNO) {
358 return false;
359 }
360 int self_errno, other_errno;
361 if (self->has_status_data) {
362 self_errno = ((_PwStatusData*) _pw_get_struct_ptr(self, PwTypeId_Status))->pw_errno;
363 } else {
364 self_errno = self->pw_errno;
365 }
366 if (other->has_status_data) {
367 other_errno = ((_PwStatusData*) _pw_get_struct_ptr(other, PwTypeId_Status))->pw_errno;
368 } else {
369 other_errno = other->pw_errno;
370 }
371 return self_errno == other_errno;
372 }
373 else {
374 return self->status_code == other->status_code;
375 }
376}
377
378static bool status_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
379{
380 // XXX suboptimal, need to unroll, merge with status_eq, and get rid of _pw_get_struct_ptr
381 // but status is to be revised
382 PW_EQUAL_METHOD_IMPL(PwTypeId_Status, status_eq, self, other);
383 return false;
384}
385
386static bool status_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
387{
388 return self->status_code == PW_SUCCESS;
389}
390
391#define status_iter_children nullptr
392
393PwInterface_Basic _pw_status_basic_interface = {
394#define X(name, ...) .name = { .func = status_##name } __VA_OPT__(,)
395 PW_BASIC_INTERFACE_METHODS
396#undef X
397};