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