1#include <stdlib.h>
  2
  3#include <libpussy/mmarray.h>
  4
  5#include "include/pw.h"
  6
  7static char* kind_str[] = {
  8    "Basic",
  9    "Errno",
 10    "VA_END",
 11    "Undefined"
 12};
 13
 14static char* base_statuses[] = {
 15    [PweSuccess]                 = "Success",
 16    [PweBasic]                   = "Error",
 17    [PweException]               = "Exception",
 18    [PweAssertion]               = "Assertion error",
 19    [PweNotImplemented]          = "Not implemented",
 20    [PweIncompatibleType]        = "Incompatible type",
 21    [PweInterfaceNotDefined]     = "Interface not defined",
 22    [PweIterationInProgress]     = "Iteration in progress",
 23    [PweEOF]                     = "EOF",
 24    [PweTimeout]                 = "Timeout",
 25    [PwePrintf]                  = "printf failed",
 26    [PweStringTooLong]           = "String too long",
 27    [PweDataSizeTooBig]          = "Data size too big",
 28    [PweIndexOutOfRange]         = "Index out of range",
 29    [PweParseError]              = "Parse error",
 30    [PweBadNumber]               = "Bad number",
 31    [PweBadDatetime]             = "Bad date/time",
 32    [PweBadTimestamp]            = "Bad timestamp",
 33    [PweNumericOverflow]         = "Numeric overflow",
 34    [PweBadInput]                = "Bad input",
 35    [PweImmutableRequired]       = "Immutable value required",
 36    [PweExtractFromEmptyArray]   = "Extract from empty array",
 37    [PweDeleteFromEmptyArray]    = "Delete from empty array",
 38    [PweKeyNotFound]             = "Key not found",
 39    [PweFileNotFound]            = "File not found",
 40    [PweFileAlreadyOpened]       = "File already opened",
 41    [PweFdAlreadySet]            = "File descriptor already set",
 42    [PweCantSetFilename]         = "Cannot set file name",
 43    [PweFileClosed]              = "Operation on closed file",
 44    [PweNotRegularFile]          = "Not a regular file",
 45    [PweNotBufferedFile]         = "Not a buffered file",
 46    [PweControlStackOverflow]    = "Control stack overflow",
 47    [PweDataStackOverflow]       = "Data stack overflow",
 48    [PweNoTasksToRun]            = "No tasks to run"
 49
 50};
 51
 52static char** statuses = nullptr;
 53static uint16_t num_statuses = 0;
 54
 55[[ gnu::constructor ]]
 56static void init_statuses()
 57{
 58    if (statuses) {
 59        return;
 60    }
 61    num_statuses = PW_LENGTH(base_statuses);
 62    statuses = mmarray_allocate(0, num_statuses, sizeof(char*));
 63    for(uint16_t i = 0; i < num_statuses; i++) {
 64        char* status = base_statuses[i];
 65        if (!status) {
 66            fprintf(stderr, "Status %u is not defined\n", i);
 67            abort();
 68        }
 69        statuses[i] = status;
 70    }
 71}
 72
 73uint16_t pw_define_status(char* status_code_description)
 74{
 75    // the order constructor are called is undefined, make sure statuses are initialized
 76    init_statuses();
 77
 78    if (num_statuses == 65535) {
 79        pw_panic("Cannot define more statuses than %u\n", num_statuses);
 80    }
 81    statuses = mmarray_append_item(statuses, &status_code_description);
 82    uint16_t status_code = num_statuses++;
 83    return status_code;
 84}
 85
 86char* pw_get_basic_error_description(uint16_t status_code)
 87{
 88    if (status_code < num_statuses) {
 89        return statuses[status_code];
 90    } else {
 91        return "Unknown error";
 92    }
 93}
 94
 95char* pw_get_status_description(PwValuePtr status)
 96{
 97    pw_hard_assert(pw_is_status(status));
 98    size_t file_len = strlen(status->file_desc);
 99    return (char*) (status->file_desc + file_len + 1);
100}
101
102/****************************************************************
103 * Basic interface methods
104 */
105
106static bool status_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
107{
108    // without args the result will be PweSuccess
109
110    PwStatusCtorArgs* args = pw_this_ctor_args();
111    if (args) {
112        result->status_code = args->status_code;
113        result->kind        = args->kind;
114        result->line_number = args->line_number;
115        result->file_desc   = args->file_desc;
116    }
117    return true;
118}
119
120static bool status_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
121{
122    _pw_hash_uint64(ctx, self->type_id);
123    _pw_hash_uint64(ctx, self->kind);
124    if (self->kind == PwStatusKind_Basic) {
125        _pw_hash_uint64(ctx, self->status_code);
126    } else if (self->kind == PwStatusKind_Errno) {
127        _pw_hash_uint64(ctx, self->pw_errno);
128    }
129    return true;
130}
131
132#define _FILE_LINE_DESC(status)  \
133    size_t file_len = strlen((status)->file_desc);  \
134    const char* desc = (status)->file_desc + file_len + 1;  \
135    size_t desc_len = strlen(desc);  \
136    char* format = desc_len? "%s:%u: %s\n" : "%s:%u\n"
137
138static bool append_file_line_desc(PwValuePtr result, PwValuePtr status)
139/*
140 * helper for `status_to_string`
141 */
142{
143    _FILE_LINE_DESC(status);
144    PwMethod_Append_append_string_data* meth_append;
145    if (!pw_method(result->type_id, Append, append_string_data, &meth_append)) {
146        return false;
147    }
148    return pw_aprintf(result, meth_append, format, status->file_desc, status->line_number, desc);
149}
150
151static void dump_file_line_desc(FILE* fp, PwValuePtr status)
152/*
153 * helper for `status_dump`
154 */
155{
156    _FILE_LINE_DESC(status);
157    fprintf(fp, format, status->file_desc, status->line_number, desc);
158}
159
160static bool status_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
161{
162    pw_destroy(result);
163    if (!pw_create_string(result, kind_str[self->kind])) {
164        return false;
165    }
166    PwMethod_Append_append_string_data* meth_append;
167    if (!pw_method(result->type_id, Append, append_string_data, &meth_append)) {
168        return false;
169    }
170    if (self->kind == PwStatusKind_Basic) {
171        char* desc = pw_get_basic_error_description(self->status_code);
172        if (!pw_aprintf(result, meth_append, ": [%u] %s; ", self->status_code, desc)) {
173            return false;
174        }
175        return append_file_line_desc(result, self);
176    }
177    if (self->kind == PwStatusKind_Errno) {
178        if (!pw_aprintf(result, meth_append, ": [%d] %s; ", self->pw_errno, strerror(self->pw_errno))) {
179            return false;
180        }
181        return append_file_line_desc(result, self);
182    }
183    return true;
184}
185
186static bool status_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
187{
188    _pw_print_indent(fp, indent);
189    fprintf(fp, "Status: %s", kind_str[self->kind]);
190    if (self->kind == PwStatusKind_Basic) {
191        char* desc = pw_get_basic_error_description(self->status_code);
192        fprintf(fp, ": [%u] %s; ", self->status_code, desc);
193        dump_file_line_desc(fp, self);
194        return true;
195    }
196    if (self->kind == PwStatusKind_Errno) {
197        fprintf(fp, ": [%d] %s; ", self->pw_errno, strerror(self->pw_errno));
198        dump_file_line_desc(fp, self);
199        return true;
200    }
201    fputs("\n", fp);
202    return true;
203}
204
205static bool status_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
206{
207    if (other->type_id != self->type_id) {
208        // don't go down to the base types because Status can be a mixin
209        return false;
210    }
211    switch (self->kind) {
212        case PwStatusKind_Basic: {
213            if (other->kind != PwStatusKind_Basic) {
214                return false;
215            }
216            return self->status_code == other->status_code;
217        }
218        case PwStatusKind_Errno: {
219            if (other->kind != PwStatusKind_Errno) {
220                return false;
221            }
222            return self->pw_errno == other->pw_errno;
223        }
224        case PwStatusKind_VA_END: {
225            return other->kind == PwStatusKind_VA_END;
226        }
227        default:
228            return false;
229    }
230}
231
232static bool status_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
233{
234    // XXX return true on success?
235    return self->status_code == 0;
236}
237
238#define status_destroy  nullptr
239#define status_clone    nullptr
240#define status_decref   nullptr
241#define status_deepcopy nullptr
242#define status_is_immutable  nullptr
243#define status_iter_children nullptr
244
245PwInterface_Basic _pw_status_basic_interface = {
246#define X(name, ...) .name = { .func = status_##name } __VA_OPT__(,)
247    PW_BASIC_INTERFACE_METHODS
248#undef X
249};