/*
* Copyright (c) 2022 Egor Tensin <Egor.Tensin@gmail.com>
* This file is part of the "cimple" project.
* For details, see https://github.com/egor-tensin/cimple.
* Distributed under the MIT License.
*/
#include "msg.h"
#include "log.h"
#include "net.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
struct msg {
size_t argc;
const char **argv;
};
size_t msg_get_length(const struct msg *msg)
{
return msg->argc;
}
const char **msg_get_words(const struct msg *msg)
{
return msg->argv;
}
int msg_success(struct msg **msg)
{
static const char *argv[] = {"success", NULL};
return msg_from_argv(msg, argv);
}
int msg_error(struct msg **msg)
{
static const char *argv[] = {"error", NULL};
return msg_from_argv(msg, argv);
}
int msg_is_success(const struct msg *msg)
{
return msg->argc == 1 && !strcmp(msg->argv[0], "success");
}
int msg_is_error(const struct msg *msg)
{
return msg->argc == 1 && !strcmp(msg->argv[0], "error");
}
static int msg_copy_argv(struct msg *msg, const char **argv)
{
size_t copied = 0;
msg->argv = calloc(msg->argc + 1, sizeof(const char *));
if (!msg->argv) {
log_errno("calloc");
return -1;
}
for (copied = 0; copied < msg->argc; ++copied) {
msg->argv[copied] = strdup(argv[copied]);
if (!msg->argv[copied]) {
log_errno("strdup");
goto free_copied;
}
}
return 0;
free_copied:
for (size_t i = 0; i < copied; ++i) {
free((char *)msg->argv[i]);
}
free(msg->argv);
return -1;
}
int msg_copy(struct msg **_dest, const struct msg *src)
{
int ret = 0;
struct msg *dest = malloc(sizeof(struct msg));
if (!dest) {
log_errno("malloc");
return -1;
}
dest->argc = src->argc;
ret = msg_copy_argv(dest, (const char **)src->argv);
if (ret < 0)
goto free;
*_dest = dest;
return 0;
free:
free(dest);
return -1;
}
void msg_free(struct msg *msg)
{
for (size_t i = 0; i < msg->argc; ++i)
free((char *)msg->argv[i]);
free(msg->argv);
free(msg);
}
int msg_from_argv(struct msg **_msg, const char **argv)
{
int ret = 0;
struct msg *msg = malloc(sizeof(struct msg));
if (!msg) {
log_errno("malloc");
return -1;
}
msg->argc = 0;
for (const char **s = argv; *s; ++s)
++msg->argc;
ret = msg_copy_argv(msg, argv);
if (ret < 0)
goto free;
*_msg = msg;
return 0;
free:
free(msg);
return -1;
}
static uint32_t calc_buf_size(const struct msg *msg)
{
uint32_t len = 0;
for (size_t i = 0; i < msg->argc; ++i)
len += strlen(msg->argv[i]) + 1;
return len;
}
static size_t calc_argv_len(const void *buf, size_t len)
{
size_t argc = 0;
for (const char *it = buf; it < (const char *)buf + len; it += strlen(it) + 1)
++argc;
return argc;
}
static void argv_pack(char *dest, const struct msg *msg)
{
for (size_t i = 0; i < msg->argc; ++i) {
strcpy(dest, msg->argv[i]);
dest += strlen(msg->argv[i]) + 1;
}
}
static int argv_unpack(struct msg *msg, const char *src)
{
size_t copied = 0;
msg->argv = calloc(msg->argc + 1, sizeof(const char *));
if (!msg->argv) {
log_errno("calloc");
return -1;
}
for (copied = 0; copied < msg->argc; ++copied) {
msg->argv[copied] = strdup(src);
if (!msg->argv[copied]) {
log_errno("strdup");
goto free;
}
src += strlen(msg->argv[copied]) + 1;
}
return 0;
free:
for (size_t i = 0; i < copied; ++i) {
free((char *)msg->argv[i]);
}
msg_free(msg);
return -1;
}
int msg_send(int fd, const struct msg *msg)
{
struct buf *buf = NULL;
int ret = 0;
uint32_t size = calc_buf_size(msg);
char *data = malloc(size);
if (!data) {
log_errno("malloc");
return -1;
}
argv_pack(data, msg);
ret = buf_create(&buf, data, size);
if (ret < 0)
goto free_data;
ret = net_send_buf(fd, buf);
if (ret < 0)
goto destroy_buf;
destroy_buf:
buf_destroy(buf);
free_data:
free(data);
return ret;
}
int msg_send_and_wait(int fd, const struct msg *request, struct msg **response)
{
int ret = 0;
ret = msg_send(fd, request);
if (ret < 0)
return ret;
ret = msg_recv(fd, response);
if (ret < 0)
return ret;
return ret;
}
int msg_recv(int fd, struct msg **_msg)
{
struct buf *buf = NULL;
int ret = 0;
ret = net_recv_buf(fd, &buf);
if (ret < 0)
return ret;
struct msg *msg = malloc(sizeof(struct msg));
if (!msg) {
log_errno("malloc");
ret = -1;
goto destroy_buf;
}
msg->argc = calc_argv_len(buf_get_data(buf), buf_get_size(buf));
ret = argv_unpack(msg, buf_get_data(buf));
if (ret < 0)
goto free_msg;
*_msg = msg;
goto destroy_buf;
free_msg:
free(msg);
destroy_buf:
buf_destroy(buf);
return ret;
}
void msg_dump(const struct msg *msg)
{
log("Message[%zu]:\n", msg->argc);
for (size_t i = 0; i < msg->argc; ++i)
log("\t%s\n", msg->argv[i]);
}