From c7c4c0d5f101a2dbaaf72c19a6a573cb5e725511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Mon, 4 Apr 2022 01:48:50 +0200 Subject: some changes --- .gitignore | 2 +- src/api.c | 139 ++++++++++++++++++--------------------- src/generate_parse_functions.php | 112 +++++++++++++++++++++++++++++++ src/h.c | 15 ++--- src/ui.c | 29 ++++---- 5 files changed, 199 insertions(+), 98 deletions(-) create mode 100755 src/generate_parse_functions.php diff --git a/.gitignore b/.gitignore index 06a0a6d..f82c10c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,6 @@ analysis.txt # files I like to keep in my WD src.old/ src/ui.glade~ -src/#ui.glade# +src/#*# # tmp tmp/ diff --git a/src/api.c b/src/api.c index c5fb0b3..2344bf4 100644 --- a/src/api.c +++ b/src/api.c @@ -48,9 +48,9 @@ void dc_api_stack (struct dc_api_io i) { /* stack output struct to be delivered } unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_channel * c) { unsigned long long int p = 0; /* note: this is NOT according to server's implementation of */ - struct dc_role ** role = &c->guild->role; /* perm parsing, but should suffice 4 most cases */ - if (!*role) /* see struct dc_guild: if NULL then assume all permissions - a DM guild */ + if (!c->guild) /* see struct dc_channel: if NULL then assume all permissions - a DM guild */ return DC_ALL_PERMISSIONS; + struct dc_role ** role = &c->guild->role; /* perm parsing, but should suffice 4 most cases */ while (*role) { if (DC_ROLE_EVERYONE(*role) || dc_find_user((*role)->users, (*role)->users_length, u->id)) @@ -72,25 +72,53 @@ unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_c } return p; } -struct dc_user * dc_parse_user (struct dc_user * dst, const cJSON * src) { - char * cp; - if (!dst) - dst = dc_user_init(); - if ((cp = cJSON_GSV(cJSON_GOI(src, "username")))) - dst->username = strdup(cp); - if ((cp = cJSON_GSV(cJSON_GOI(src, "discriminator")))) - dst->discriminator = atoi(cp); - if ((cp = cJSON_GSV(cJSON_GOI(src, "id")))) - dst->id = strtoull(cp, NULL, 10); - if (!dst->username || dst->discriminator == -1) { /* it's quite useless to store only ids */ - dc_user_free(dst, DC_UNSET); +struct dc_user * dc_parse_user (struct dc_program * p, const cJSON * s) { + if (!s) /* in case cJSON_GOIx returns NULL, it will most of the times, this isn't a bug */ + return NULL; + char * c = cJSON_GSV(cJSON_GOI(s, "id")); + if (!c) + return NULL; + unsigned long long int i = strtoull(c, NULL, 10); + struct dc_user * u = dc_find_user(p->users, p->users_length, i); + if (!u) { + u = dc_user_init(); + u->id = i; + dc_addr_user(p, DC_ISAE(p->users), u, 0); + } + if ((c = cJSON_GSV(cJSON_GOI(s, "username"))) && strlen(c)) { + free(u->username); + u->username = strdup(c); + } + if ((c = cJSON_GSV(cJSON_GOI(s, "discriminator")))) + u->discriminator = atoi(c); + return u; +} +struct dc_channel * dc_parse_channel (struct dc_program * p, const cJSON * s) { + cJSON * o; + if (!s) /* in case cJSON_GOIx returns NULL */ + return NULL; + char * c = cJSON_GSV(cJSON_GOI(s, "id")); + if (!c) return NULL; + if (!(DC_CHANNEL_SUPPORTED(cJSON_GNV(cJSON_GOI(s, "type"))))) return NULL; + unsigned long long int i = strtoull(c, NULL, 10); + struct dc_channel * u = dc_find_channel(p->channels, p->channels_length, i); + if (!u) { + u = dc_channel_init(); + u->id = i; + dc_addr_channel(p, DC_ISAE(p->channels), u, 0); } - return dst; + if ((c = cJSON_GSV(cJSON_GOI(s, "name")))) { + free(u->name); + u->name = strdup(c); + } + if ((o = cJSON_GOI(s, "type"))) + u->type = cJSON_GNV(o); + return u; } static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, void * in, size_t len) { struct dc_lws_pass * pass = (struct dc_lws_pass *) us; - unsigned char buf[LWS_PRE+DC_LWS_BUF+1]; /* whooh, boy, this is more than a meg of stack! */ + unsigned char buf[LWS_PRE+DC_LWS_BUF+1]; switch (rs) { case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: /* TODO: handle and report somehow */ if (pass) { @@ -250,78 +278,37 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, pass->api_io.client->ping_interval = cJSON_GNV(obj)/1000-1; pass->api_io.client->last_ping = 1; } -#define DC_PARSEOBJ(w, object, also) if ((obj = object)) { \ - struct dc_##w * w; \ - if ((w = dc_parse_##w(NULL, obj))) \ - w = dc_addr_##w(pass->api_io.program, \ - DC_ISAE(pass->api_io.program->w##s), w, \ - DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE);\ - also \ - } - DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "user"), - if (!pass->api_io.client->user) - pass->api_io.client->user = user; - ) - DC_PARSEOBJ(user, cJSON_GOI3(json, "d", "member", "user"),) - DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "author"),) - DC_PARSEOBJ(user, cJSON_GOI3(json, - "d", "referenced_message", "author"),) -#define DC_PARSEARR(what, arr, also) cJSON_AFE(obj, arr) { \ - struct dc_##what * what = dc_parse_##what(NULL, obj); \ - if (!what) \ - continue; \ - dc_addr_##what(pass->api_io.program, \ - DC_ISAE(pass->api_io.program->what##s), \ - what, DC_MAY_FREE|DC_REPLACE|DC_INCOMPLETE);\ - also \ - } - DC_PARSEARR(user, cJSON_GOI2(json, "d", "users"),) - DC_PARSEARR(user, cJSON_GOI2(json, "d", "mentions"),) - DC_PARSEARR(user, cJSON_GOI3(json, - "d", "referenced_message", "mentions"),) - cJSON_AFE(obj, cJSON_GOI2(json, "d", "private_channels")) { - if (/* !cJSON_GAS(cJSON_GOI(obj, "recipient_ids")) || */ - /* commented. DMs with 0 members're shown /\ */ !cJSON_GSV(cJSON_GOI(obj, "id")) || - !DC_CHANNEL_SUPPORTED( - /* cJSON is called many times here but I don't care */ cJSON_GNV(cJSON_GOI(obj, "type")))) - continue; - struct dc_channel * ch = dc_channel_init(); - if ((st = cJSON_GSV(cJSON_GOI(obj, "name")))) - ch->name = strdup(st); - ch->type = cJSON_GNV(cJSON_GOI(obj, "type")); - ch->id = strtoull(cJSON_GSV(cJSON_GOI(obj, "id")), NULL, 10); - cJSON_AFE(obje, cJSON_GOI(obj, "recipient_ids")) { - if (!(st = cJSON_GSV(obje))) - continue; - struct dc_user * part = dc_user_init(); - part->id = strtoull(st, NULL, 10); - part = dc_add_user( - DC_ISAE(pass->api_io.program->users), - /* no replace here. stored user can be better. */ part, DC_MAY_FREE); - DC_MR(ch->users); /* needn't dc_add here coz start */ - ch->users[ch->users_length++] = part; /* empty ch. */ - } - ch = dc_addr_channel(pass->api_io.program, - DC_ISAE(pass->api_io.program->channels), ch, - /* replace here. ours is better - fresher. */ DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE); - ch->guild = pass->api_io.client->guilds[0]; - } + struct dc_user * user = dc_parse_user(DC_PAIP, cJSON_GOI2(json, "d", "user")); + if (!pass->api_io.client->user) + pass->api_io.client->user = user; + dc_parse_user(DC_PAIP, cJSON_GOI3(json, "d", "member", "user")); + dc_parse_user(DC_PAIP, cJSON_GOI2(json, "d", "author")); + dc_parse_user(DC_PAIP, cJSON_GOI3(json, "d", "referenced_message", "author")); + cJSON_AFE(obj, cJSON_GOI2(json, "d", "users")) + dc_parse_user(DC_PAIP, obj); + cJSON_AFE(obj, cJSON_GOI2(json, "d", "mentions")) + dc_parse_user(DC_PAIP, obj); + cJSON_AFE(obj, cJSON_GOI3(json, "d", "referenced_message", + "mentions")) + dc_parse_user(DC_PAIP, obj); + cJSON_AFE(obj, cJSON_GOI2(json, "d", "private_channels")) + dc_parse_channel(DC_PAIP, obj); cJSON_AFE(ob, cJSON_GOI2(json, "d", "merged_members")) { obj = cJSON_GetArrayItem(ob, 0); if (!cJSON_GAS(cJSON_GOI(obj, "roles"))) continue; struct dc_user * user = dc_user_init(); user->id = strtoull(cJSON_GSV(cJSON_GOI(obj, "user_id")), - NULL, 10); - user = dc_addr_user( + NULL, 10); + user = dc_addr_user(DC_PAIP, DC_ISAE(pass->api_io.program->users), user, - /* again, no replace here, only ID */ DC_MAY_FREE); + DC_MAY_FREE); /* no replace - we only have ID */ cJSON_AFE(obje, cJSON_GOI(obj, "roles")) { if (!(st = cJSON_GSV(obje))) continue; struct dc_role * role = dc_role_init(); role->id = strtoull(st, NULL, 10); - role = dc_addr_role( + role = dc_addr_role(DC_PAIP, DC_ISAE(pass->api_io.program->roles), role, DC_MAY_FREE); /* role may have users. NO FREE! */ dc_add_user(DC_ISAE(role->users), user, DC_UNSET); diff --git a/src/generate_parse_functions.php b/src/generate_parse_functions.php new file mode 100755 index 0000000..23eca49 --- /dev/null +++ b/src/generate_parse_functions.php @@ -0,0 +1,112 @@ +#!/usr/bin/env php + [ + "members" => [ + "username" => [ + "json" => "username", + "type" => "string" + ], + "discriminator" => [ + "json" => "discriminator", + "type" => "number" + ] + ] + ], + "channel" => [ + "members" => [ + "name" => [ + "json" => "name", + "type" => "string" + ], + "type" => [ + "json" => "type", + "type" => "number" + ] + ], + "parsing" => [ + "checks" => [ + 'DC_CHANNEL_SUPPORTED(cJSON_GNV(cJSON_GOI(s, "type")))' + ], + "code" => <<id = strtoull(st, NULL, 10); + u = dc_addr_user(p, DC_ISAE(p->users), u, DC_MAY_FREE); + dc_add_user(DC_ISAE(ch->users), u, 0); + } + HEREDOC, + ] + ] +]; +foreach ($s as $name => $fields) { + echo <<{$name}s, p->{$name}s_length, i); + + HEREDOC; + if (isset($fields["parsing"]) && sizeof($fields["parsing"]["checks"])) { + echo " if ("; + $arr = $fields["parsing"]["checks"]; + foreach ($arr as $n => $check) { + $or = ""; + $end = ""; + if ($n != array_key_first($arr)) + $or = " || "; + if ($n != array_key_last($arr)) + $end = PHP_EOL; + echo "$or!($check)$end"; + } + echo <<id = i; + dc_addr_$name(p, DC_ISAE(p->{$name}s), n, 0); + } + + HEREDOC; + if (isset($fields["parsing"])) + echo $fields["parsing"]["code"] . PHP_EOL; + foreach ($fields["members"] as $member => $meta) { + switch ($meta["type"]) { + case "string": + echo <<$member); + n->$member = strdup(c); + } + + HEREDOC; + break; + case "number": + echo <<$member = cJSON_GNV(o); + + HEREDOC; + break; + } + } + echo << diff --git a/src/h.c b/src/h.c index 8713d9d..8067c5b 100644 --- a/src/h.c +++ b/src/h.c @@ -26,6 +26,7 @@ #else #define DC_IF_UI_GTK(...) #endif +#define DC_PAIP (pass->api_io.program) /* it's strongly recommended to calloc structs during initialization. */ enum dc_status { /* theese are flags and should be and-checked */ DC_UNSET = 0, /* default value when enum is calloced */ /* \/ USEFUL FOR ->next!!! */ @@ -431,9 +432,9 @@ struct dc_guild { char * name; /* yesfree */ char * description; /* yesfree */ unsigned long long int id; /* 0 for virtual DMs guild */ - struct dc_channel * channel; /* nofree - first channel */ struct dc_role * role; /* nofree - first role. NOTE: role->id==guild->id => @everyone */ enum dc_status status; /* /\ if NULL then assume all permissions - a DM guild */ + DC_ISASQ(channel); /* yesfree array only - can't be LL because of DM channels in many g[0] */ #ifdef DC_UI_GTK GtkTreeIter iter; /* GtkTreeModel needs GTK_TREE_MODEL_ITERS_PERSIST for this to work */ gboolean is_iter; /* see parag 8 file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */ @@ -448,6 +449,7 @@ void dc_guild_free (struct dc_guild * s, enum dc_status t) { return; free(s->name); free(s->description); + free(s->channels); if (!(t & DC_REPLACE)) /* we do this because we want to keep the pointer intact sometimes and */ free(s); /* reused; for example when replacing/updating structs */ } @@ -490,10 +492,9 @@ struct dc_channel { DC_STRUCT_PREFIX char * name; /* yesfree - name */ char * topic; /* yesfree - topic */ - unsigned long long int id; - enum dc_channel_type type; - struct dc_guild * guild; /* nofree */ - struct dc_channel * next; /* nofree - next channel (linked list of all channel of dc_guild) */ + unsigned long long int id; /* send message functions must therefore take */ + enum dc_channel_type type; /* vvv client as parameter to get g[0] */ + struct dc_guild * guild; /* nofree - in case of DM channel this is NULL, many g[0] same ch */ struct dc_message * message; /* nofree - first message (ordered by time) */ enum dc_status status; DC_ISASQ(permission); /* yesfree array and members - ch permissions for users/roles */ @@ -817,7 +818,6 @@ void dc_transfer_channel (struct dc_channel * n, struct dc_channel * o) { /* n w DC_TRANSFER_MEMBER(id) DC_TRANSFER_MEMBER(type) DC_TRANSFER_MEMBER(guild) - DC_TRANSFER_MEMBER(next) DC_TRANSFER_MEMBER(message) DC_TRANSFER_ISA(permission) DC_TRANSFER_ISA(user) @@ -831,9 +831,9 @@ void dc_transfer_guild (struct dc_guild * n, struct dc_guild * o) { DC_TRANSFER_MEMBER(name) DC_TRANSFER_MEMBER(description) DC_TRANSFER_MEMBER(id) - DC_TRANSFER_MEMBER(channel) DC_TRANSFER_MEMBER(role) DC_TRANSFER_MEMBER(status) + DC_TRANSFER_ISA(channel) DC_IF_UI_GTK( memmove(&n->iter, &o->iter, sizeof(GtkTreeIter)); n->is_iter = o->is_iter; @@ -873,7 +873,6 @@ void dc_transfer_user (struct dc_user * n, struct dc_user * o) { } DC_GEN_X(user, USER) DC_GEN_X(channel, CHANNEL) -DC_FIND_LL_X(channel) DC_GEN_X(guild, GUILD) DC_GEN_X(role, ROLE) DC_FIND_LL_X(role) diff --git a/src/ui.c b/src/ui.c index d7feb7e..9e207a6 100644 --- a/src/ui.c +++ b/src/ui.c @@ -32,10 +32,13 @@ void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m char t[DC_USMTL]; char * c; GtkGrid * g = GTK_GRID(gtk_builder_get_object(d->b, "dc_main_messages")); + struct dc_guild * guild = m->channel->guild; + if (!guild) /* DM message! */ + guild = d->c->guilds[0]; if (!m) { while (gtk_grid_get_child_at(g, 0, 0)) gtk_grid_remove_row(g, 0); - /* struct dc_channel * c = m->channel->guild->channel; */ /* XXX: I wrote this line of code but then I wen't to sleep and I can't remember what should I do here with the channel :shrug: */ + /* struct dc_channel * c = guild->channel; */ /* XXX: I wrote this line of code but then I wen't to sleep and I can't remember what should I do here with the channel :shrug: */ return; } while ((w = gtk_grid_get_child_at(g, 0, i))) { /* now we get the index BEFORE which message will be placed */ @@ -67,7 +70,7 @@ void dc_ui_spawn_message (struct dc_message * m, struct dc_ui_data * d) { /* !m gtk_container_add(GTK_CONTAINER(b), gtk_label_new(t)); g_object_set_data(G_OBJECT(b), "message", m); gtk_grid_attach(g /* grid */, b /* widget to insert */, 0 /* left */, i /* top */, 1 /* width */, 1 /* height */); - /* if (m->user == m->channel->guild->client->user) */ /* TODO: if I posted the message, make it an editable textview */ + /* if (m->user == guild->client->user) */ /* TODO: if I posted the message, make it an editable textview */ gtk_grid_attach(g, GTK_WIDGET(gtk_label_new(m->message)), 1, i, 1, 1); gtk_widget_show_all(GTK_WIDGET(g)); gtk_widget_show_all(GTK_WIDGET(g)); @@ -80,21 +83,21 @@ void dc_ui_spawn_channel (struct dc_channel * c /* needs a functional guild or s gtk_tree_store_clear(l); for (size_t i = 0; i < d->c->guilds_length; i++) { d->c->guilds[i]->is_iter = FALSE; - struct dc_channel * ch = d->c->guilds[i]->channel; - while (ch) { - ch->is_iter = FALSE; - ch = ch->next; - } + for (size_t j = 0; j < d->c->guilds[i]->channels_length; j++) + d->c->guilds[i]->channels[j]->is_iter = FALSE; } } - if (!c->guild->is_iter) { /* we don't have this guild already rendered */ + struct dc_guild * guild = c->guild; + if (!guild) /* DM channel! */ + guild = d->c->guilds[0]; + if (!guild->is_iter) { /* we don't have this guild already rendered */ gtk_tree_store_insert(l, &i, NULL, -1 /* row position */); - gtk_tree_store_set(l, &i, 0, c->guild->name, -1); - gtk_tree_store_set(l, &i, 1, c->guild, -1); - memcpy(&c->guild->iter, &i, sizeof(GtkTreeIter)); - c->guild->is_iter = TRUE; + gtk_tree_store_set(l, &i, 0, guild->name, -1); + gtk_tree_store_set(l, &i, 1, guild, -1); + memcpy(&guild->iter, &i, sizeof(GtkTreeIter)); + guild->is_iter = TRUE; } - gtk_tree_store_insert(l, &i, &c->guild->iter, -1); /* for this to work, iter must not be same as parent! */ + gtk_tree_store_insert(l, &i, &guild->iter, -1); /* for this to work, iter must not be same as parent! */ gtk_tree_store_set(l, &i, 0, c->name, -1); /* TODO: set hover tooltip for c->topic */ gtk_tree_store_set(l, &i, 1, c, -1); memcpy(&c->iter, &i, sizeof(GtkTreeIter)); -- cgit v1.2.3