106
doc/src/docs.c
Normal file
106
doc/src/docs.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include <linux/limits.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "docs.h"
|
||||
|
||||
#define DOCS_LANG_CODE_LEN 2
|
||||
|
||||
bool __docs_parse_name(docs_t *docs, char *ext) {
|
||||
// check the extension
|
||||
uint64_t ext_pos = util_endswith(docs->name, ext);
|
||||
|
||||
if (ext_pos == 0)
|
||||
return false;
|
||||
|
||||
// example.en.json\0 => example.en\0json\0
|
||||
docs->name[ext_pos] = 0;
|
||||
|
||||
// example.en\0json\0
|
||||
// |
|
||||
// `--- find this
|
||||
for (docs->lang = docs->name; *docs->lang != 0 && *docs->lang != '.'; docs->lang++)
|
||||
continue;
|
||||
|
||||
if (*docs->lang != '.')
|
||||
return false;
|
||||
|
||||
// example.en\0json\0 => example.en\0json\0
|
||||
*docs->lang++ = 0;
|
||||
|
||||
// example\0en\0json
|
||||
// | | |
|
||||
// | | `--- ext_pos
|
||||
// | `-- lang
|
||||
// `-- name
|
||||
return strlen(docs->lang) == DOCS_LANG_CODE_LEN && *docs->name != 0;
|
||||
}
|
||||
|
||||
void __docs_clean(docs_t *docs) {
|
||||
if (NULL == docs->file)
|
||||
return;
|
||||
|
||||
util_file_free(docs->file);
|
||||
docs->file = NULL;
|
||||
}
|
||||
|
||||
bool docs_init(docs_t *docs, char *dir) {
|
||||
if (NULL == docs || NULL == dir) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
bzero(docs, sizeof(*docs));
|
||||
return NULL != (docs->dir = opendir(dir));
|
||||
}
|
||||
|
||||
char *docs_next(docs_t *docs, char *name, bool content) {
|
||||
if (NULL == docs) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent *ent = NULL;
|
||||
__docs_clean(docs);
|
||||
|
||||
while (NULL != (ent = readdir(docs->dir))) {
|
||||
if (*ent->d_name == '.')
|
||||
continue;
|
||||
|
||||
strcpy(docs->name, ent->d_name);
|
||||
|
||||
if (!__docs_parse_name(docs, content ? ".md" : ".json"))
|
||||
continue;
|
||||
|
||||
if (NULL == name || strncmp(docs->name, name, NAME_MAX) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (NULL == ent) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (NULL == (docs->file = util_file_load(dirfd(docs->dir), ent->d_name)))
|
||||
return NULL;
|
||||
|
||||
return docs->file->content;
|
||||
}
|
||||
|
||||
void docs_reset(docs_t *docs) {
|
||||
if (NULL != docs)
|
||||
rewinddir(docs->dir);
|
||||
}
|
||||
|
||||
void docs_free(docs_t *docs) {
|
||||
if (NULL == docs)
|
||||
return;
|
||||
|
||||
__docs_clean(docs);
|
||||
closedir(docs->dir);
|
||||
|
||||
bzero(docs, sizeof(*docs));
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
#include <cjson/cJSON.h>
|
||||
#include <dirent.h>
|
||||
#include <linux/limits.h>
|
||||
#include <ctorm/ctorm.h>
|
||||
|
||||
@ -7,72 +9,62 @@
|
||||
|
||||
#include "routes.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "docs.h"
|
||||
|
||||
void route_get(ctorm_req_t *req, ctorm_res_t *res) {
|
||||
config_t *conf = REQ_LOCAL("config");
|
||||
char *docs_dir = config_get(conf, "docs_dir");
|
||||
const char *name = REQ_PARAM("name");
|
||||
config_t *conf = REQ_LOCAL("config");
|
||||
char *doc_name = REQ_PARAM("name");
|
||||
char *docs_dir = config_get(conf, "docs_dir"), *doc_data = NULL;
|
||||
cJSON *json = NULL, *doc_json = NULL;
|
||||
docs_t docs;
|
||||
|
||||
if (NULL == name) {
|
||||
ctorm_fail("doc name is not specified");
|
||||
return util_send(res, 500, NULL);
|
||||
if (NULL == doc_name) {
|
||||
ctorm_fail("documentation name not specified (how did that even happend)");
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (strlen(name) > NAME_MAX - 6)
|
||||
return util_send(res, 404, NULL);
|
||||
if (!docs_init(&docs, docs_dir)) {
|
||||
ctorm_fail("docs_init failed: %s", ctorm_geterror());
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
char full_path[PATH_MAX + 1], full_name[NAME_MAX + 1];
|
||||
util_file_t *doc_file = NULL, *json_file = NULL;
|
||||
cJSON *doc_json = NULL;
|
||||
if (NULL == (json = cJSON_CreateObject())) {
|
||||
ctorm_fail("failed to create cJSON object");
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
// read the doc markdown
|
||||
snprintf(full_name, sizeof(full_name), "%s.md", name);
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", docs_dir, full_name);
|
||||
while (NULL != (doc_data = docs_next(&docs, doc_name, false))) {
|
||||
if (NULL == (doc_json = cJSON_Parse(doc_data))) {
|
||||
ctorm_fail("failed to parse JSON: %s (%s)", docs.name, docs.lang);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!util_dir_contains(docs_dir, full_name)) {
|
||||
cJSON_AddStringToObject(doc_json, "content", "");
|
||||
cJSON_AddItemToObject(json, docs.lang, doc_json);
|
||||
}
|
||||
|
||||
if (NULL == doc_json) {
|
||||
util_send(res, 404, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (NULL == (doc_file = util_file_load(full_path))) {
|
||||
ctorm_fail("failed to load file: %s", full_path);
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
docs_reset(&docs);
|
||||
|
||||
while (NULL != (doc_data = docs_next(&docs, doc_name, true))) {
|
||||
if (NULL == (doc_json = cJSON_GetObjectItem(json, docs.lang)))
|
||||
continue;
|
||||
|
||||
cJSON_DeleteItemFromObject(doc_json, "content");
|
||||
cJSON_AddStringToObject(doc_json, "content", doc_data);
|
||||
}
|
||||
|
||||
// read the doc JSON
|
||||
snprintf(full_name, sizeof(full_name), "%s.json", name);
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", docs_dir, full_name);
|
||||
|
||||
if (!util_dir_contains(docs_dir, full_name)) {
|
||||
util_send(res, 404, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (NULL == (json_file = util_file_load(full_path))) {
|
||||
ctorm_fail("failed to load file: %s", full_path);
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
// parse the doc JSON
|
||||
if (NULL == (doc_json = cJSON_Parse(json_file->content))) {
|
||||
ctorm_fail("failed to parse file: %s", full_path);
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
cJSON_AddStringToObject(doc_json, "content", doc_file->content);
|
||||
util_send(res, 200, doc_json);
|
||||
util_send(res, 200, json);
|
||||
|
||||
end:
|
||||
if (NULL != doc_file)
|
||||
util_file_free(doc_file);
|
||||
|
||||
if (NULL != json_file)
|
||||
util_file_free(json_file);
|
||||
|
||||
if (NULL != doc_json)
|
||||
cJSON_free(doc_json);
|
||||
docs_free(&docs);
|
||||
if (NULL != json)
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
@ -1,72 +1,53 @@
|
||||
#include <linux/limits.h>
|
||||
#include <cjson/cJSON.h>
|
||||
#include <ctorm/ctorm.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "routes.h"
|
||||
#include "config.h"
|
||||
#include "docs.h"
|
||||
|
||||
void route_list(ctorm_req_t *req, ctorm_res_t *res) {
|
||||
config_t *conf = REQ_LOCAL("config");
|
||||
char *docs_dir = config_get(conf, "docs_dir");
|
||||
char *docs_dir = config_get(conf, "docs_dir"), *doc_data = NULL;
|
||||
cJSON *array = NULL, *json = NULL, *doc_json = NULL;
|
||||
docs_t docs;
|
||||
|
||||
cJSON *array = NULL, *json = NULL;
|
||||
DIR *docs_dir_fd = NULL;
|
||||
|
||||
if (NULL == (array = cJSON_CreateArray())) {
|
||||
ctorm_fail("failed to create cJSON array");
|
||||
return util_send(res, 500, NULL);
|
||||
if (!docs_init(&docs, docs_dir)) {
|
||||
ctorm_fail("docs_init failed: %s", ctorm_geterror());
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (NULL == (docs_dir_fd = opendir(docs_dir))) {
|
||||
ctorm_fail("failed to open the docs dir (%s): %s", docs_dir, ctorm_geterror());
|
||||
return util_send(res, 500, NULL);
|
||||
}
|
||||
|
||||
char doc_path[PATH_MAX + 1], doc_name[NAME_MAX + 1];
|
||||
util_file_t *doc_file = NULL;
|
||||
struct dirent *doc = NULL;
|
||||
uint64_t ext_indx = 0;
|
||||
|
||||
while (NULL != (doc = readdir(docs_dir_fd))) {
|
||||
if ((ext_indx = util_endswith(doc->d_name, ".json")) == 0)
|
||||
continue;
|
||||
|
||||
snprintf(doc_path, sizeof(doc_path), "%s/%s", docs_dir, doc->d_name);
|
||||
|
||||
if (NULL == (doc_file = util_file_load(doc_path))) {
|
||||
ctorm_fail("failed to load the JSON file: %s", doc_path);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (NULL == (json = cJSON_Parse(doc_file->content))) {
|
||||
ctorm_fail("failed to parse the JSON file: %s", doc_path);
|
||||
goto next;
|
||||
}
|
||||
|
||||
strcpy(doc_name, doc->d_name);
|
||||
util_tolower(doc_name);
|
||||
doc_name[ext_indx] = 0;
|
||||
|
||||
cJSON_AddStringToObject(json, "name", doc_name);
|
||||
cJSON_AddItemToArray(array, json);
|
||||
|
||||
next:
|
||||
if (NULL != doc_file)
|
||||
util_file_free(doc_file);
|
||||
doc_file = NULL;
|
||||
}
|
||||
|
||||
closedir(docs_dir_fd);
|
||||
|
||||
if (NULL == (json = cJSON_CreateObject())) {
|
||||
ctorm_fail("failed to create cJSON object");
|
||||
return util_send(res, 500, NULL);
|
||||
util_send(res, 500, NULL);
|
||||
goto end;
|
||||
}
|
||||
|
||||
while (NULL != (doc_data = docs_next(&docs, NULL, false))) {
|
||||
if (NULL == (array = cJSON_GetObjectItem(json, docs.lang)) &&
|
||||
NULL == (array = cJSON_AddArrayToObject(json, docs.lang))) {
|
||||
ctorm_fail("failed to create an array object for the language %s", docs.lang);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NULL == (doc_json = cJSON_Parse(doc_data))) {
|
||||
ctorm_fail("failed to parse JSON: %s (%s)", docs.name, docs.lang);
|
||||
continue;
|
||||
}
|
||||
|
||||
cJSON_AddStringToObject(doc_json, "name", docs.name);
|
||||
cJSON_AddItemToArray(array, doc_json);
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(json, "list", array);
|
||||
util_send(res, 200, json);
|
||||
cJSON_free(json);
|
||||
|
||||
end:
|
||||
docs_free(&docs);
|
||||
if (NULL != json)
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
@ -58,31 +58,7 @@ void util_send(ctorm_res_t *res, uint16_t code, cJSON *json) {
|
||||
RES_JSON(json);
|
||||
}
|
||||
|
||||
bool util_dir_contains(char *dir, const char *file) {
|
||||
if (NULL == dir || NULL == file) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent *dirent = NULL;
|
||||
DIR *dirfd = NULL;
|
||||
|
||||
if (NULL == (dirfd = opendir(dir)))
|
||||
return false; // errno set by opendir
|
||||
|
||||
while ((dirent = readdir(dirfd)) != NULL) {
|
||||
if (util_compare_name(dirent->d_name, ".") || util_compare_name(dirent->d_name, ".."))
|
||||
continue;
|
||||
|
||||
if (util_compare_name(dirent->d_name, file))
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(dirfd);
|
||||
return NULL != dirent;
|
||||
}
|
||||
|
||||
util_file_t *util_file_load(char *path) {
|
||||
util_file_t *util_file_load(int dirfd, char *path) {
|
||||
if (NULL == path) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
@ -95,7 +71,7 @@ util_file_t *util_file_load(char *path) {
|
||||
if (NULL == (file = malloc(sizeof(util_file_t))))
|
||||
goto end; // errno set by malloc
|
||||
|
||||
if ((fd = open(path, O_RDONLY)) < 0)
|
||||
if ((fd = openat(dirfd, path, O_RDONLY)) < 0)
|
||||
goto end; // errno set by open
|
||||
|
||||
if (fstat(fd, &buf) < 0)
|
||||
|
Reference in New Issue
Block a user