First commit
This commit is contained in:
commit
815cfbbcef
24
LICENSE
Normal file
24
LICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org/>
|
23
Makefile
Normal file
23
Makefile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
TARGET_LIB ?= libqments.a
|
||||||
|
|
||||||
|
BD ?= build
|
||||||
|
SRC_DIRS ?= src
|
||||||
|
INCLUDE_DIRS ?= include
|
||||||
|
|
||||||
|
SRC_FILES := $(shell find $(SRC_DIRS) -name *.c)
|
||||||
|
OBJ_FILES := $(patsubst %.c,$(BD)/%.o,$(SRC_FILES))
|
||||||
|
|
||||||
|
$(BD)/$(TARGET_LIB): $(OBJ_FILES)
|
||||||
|
mkdir -p $(@D)
|
||||||
|
ar rcs $@ $(OBJ_FILES)
|
||||||
|
|
||||||
|
$(BD)/%.o: %.c
|
||||||
|
mkdir -p $(@D)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -I $(INCLUDE_DIRS)
|
||||||
|
|
||||||
|
all: $(BD)/$(TARGET_LIB)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rfI $(BD)/
|
||||||
|
|
||||||
|
.PHONY: all clean
|
66
examples/main.c
Normal file
66
examples/main.c
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* Reads text from stdio and creates comment using unix fs driver in current directory */
|
||||||
|
/* TODO: add path, displayname as arguments */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "comment.h"
|
||||||
|
#include "driver.h"
|
||||||
|
#include "drivers/unix_fs/unix_fs_driver.h"
|
||||||
|
|
||||||
|
char *
|
||||||
|
get_username()
|
||||||
|
{
|
||||||
|
uid_t uid = geteuid();
|
||||||
|
struct passwd *pw = getpwuid(uid);
|
||||||
|
if (pw) {
|
||||||
|
return pw->pw_name;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
const Driver *driver = &unix_fs_driver;
|
||||||
|
char *path = ".";
|
||||||
|
|
||||||
|
char *user_sid = get_username();
|
||||||
|
char *user_displayname = user_sid;
|
||||||
|
int reply_id = 0;
|
||||||
|
|
||||||
|
size_t cur_buf_size = 256;
|
||||||
|
size_t idx = 0;
|
||||||
|
|
||||||
|
char *text = malloc(cur_buf_size);
|
||||||
|
|
||||||
|
int ch;
|
||||||
|
while ((ch = getchar()) != EOF) {
|
||||||
|
if (idx + 1 == cur_buf_size) {
|
||||||
|
cur_buf_size *= 2;
|
||||||
|
text = realloc(text, cur_buf_size);
|
||||||
|
}
|
||||||
|
text[idx++] = ch;
|
||||||
|
}
|
||||||
|
text[idx] = '\0';
|
||||||
|
|
||||||
|
time_t current_time;
|
||||||
|
time(¤t_time);
|
||||||
|
|
||||||
|
UnixFsDriverData driver_data = { path };
|
||||||
|
|
||||||
|
CommentHeader *header = mk_comment_header(reply_id, current_time, strlen(text), user_sid, user_displayname);
|
||||||
|
|
||||||
|
int retval = driver->leave_comment(&driver_data, header, text);
|
||||||
|
free(text);
|
||||||
|
if (retval < 0) {
|
||||||
|
return retval;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
24
include/comment.h
Normal file
24
include/comment.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef COMMENT_H
|
||||||
|
#define COMMENT_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int reply_id; /* should be 0 if comment is top-level */
|
||||||
|
time_t creation_time;
|
||||||
|
size_t text_length;
|
||||||
|
char *user_sid, *user_displayname;
|
||||||
|
} CommentHeader;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int id;
|
||||||
|
CommentHeader *header;
|
||||||
|
char *text;
|
||||||
|
} Comment;
|
||||||
|
|
||||||
|
/* does not copy strings given by pointers */
|
||||||
|
CommentHeader *
|
||||||
|
mk_comment_header(int reply_id, time_t creation_time, size_t text_length, char *user_sid, char *user_displayname);
|
||||||
|
|
||||||
|
#endif /* COMMENT_H */
|
15
include/driver.h
Normal file
15
include/driver.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef DRIVER_H
|
||||||
|
#define DRIVER_H
|
||||||
|
|
||||||
|
#include "comment.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* should return (id > 0) on success, and negative number on error */
|
||||||
|
int (*leave_comment)(void*, CommentHeader*, char*);
|
||||||
|
/* retrieve header information by id */
|
||||||
|
int (*get_header)(void*, CommentHeader*, int);
|
||||||
|
/* retrieve text of comment by id */
|
||||||
|
int (*get_text)(void*, char*, int);
|
||||||
|
} Driver;
|
||||||
|
|
||||||
|
#endif /* DRIVER_H */
|
22
include/drivers/unix_fs/unix_fs_driver.h
Normal file
22
include/drivers/unix_fs/unix_fs_driver.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef UNIX_FS_DRIVER_H
|
||||||
|
#define UNIX_FS_DRIVER_H
|
||||||
|
|
||||||
|
#include "driver.h"
|
||||||
|
|
||||||
|
#define ID_CTRL_FILE "max_id"
|
||||||
|
#define TEXT_FILE_FMT "%s/%d.txt"
|
||||||
|
#define HEADER_FILE_FMT "%s/%d.header"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *path;
|
||||||
|
} UnixFsDriverData;
|
||||||
|
|
||||||
|
int unix_fs_driver_leave_comment(void *driver_data_ptr, CommentHeader *header, char *text);
|
||||||
|
|
||||||
|
int unix_fs_driver_get_header(void *driver_data_ptr, CommentHeader *header, int id);
|
||||||
|
|
||||||
|
int unix_fs_driver_get_text(void *driver_data_ptr, char *text, int id);
|
||||||
|
|
||||||
|
static const Driver unix_fs_driver = { unix_fs_driver_leave_comment, unix_fs_driver_get_header, unix_fs_driver_get_text };
|
||||||
|
|
||||||
|
#endif /* UNIX_FS_DRIVER_H */
|
17
src/comment.c
Normal file
17
src/comment.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "comment.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
CommentHeader *
|
||||||
|
mk_comment_header(int reply_id, time_t creation_time, size_t text_length, char *user_sid, char *user_displayname)
|
||||||
|
{
|
||||||
|
CommentHeader *header = malloc(sizeof(CommentHeader));
|
||||||
|
|
||||||
|
header->reply_id = reply_id;
|
||||||
|
header->creation_time = creation_time;
|
||||||
|
header->text_length = text_length;
|
||||||
|
|
||||||
|
header->user_sid = user_sid;
|
||||||
|
header->user_displayname = user_displayname;
|
||||||
|
|
||||||
|
return header;
|
||||||
|
}
|
234
src/drivers/unix_fs/unix_fs_driver.c
Normal file
234
src/drivers/unix_fs/unix_fs_driver.c
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#define _GNU_SOURCE /* ahh, didn't know asprintf is a GNU extension */
|
||||||
|
/* TODO: remove this bullshit */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "comment.h"
|
||||||
|
#include "drivers/unix_fs/unix_fs_driver.h"
|
||||||
|
|
||||||
|
#define READ_BUF_SZ 256
|
||||||
|
|
||||||
|
int
|
||||||
|
is_valid_directory(const char *path)
|
||||||
|
{
|
||||||
|
struct stat path_stat;
|
||||||
|
if (stat(path, &path_stat) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return S_ISDIR(path_stat.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: handle errors */
|
||||||
|
int
|
||||||
|
serialize_header(int fd, const CommentHeader *header)
|
||||||
|
{
|
||||||
|
write(fd, &header->reply_id, sizeof(int));
|
||||||
|
write(fd, &header->creation_time, sizeof(time_t));
|
||||||
|
write(fd, &header->text_length, sizeof(size_t));
|
||||||
|
write(fd, header->user_sid, strlen(header->user_sid) + 1);
|
||||||
|
write(fd, header->user_displayname, strlen(header->user_displayname) + 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: handle errors */
|
||||||
|
/* header string buffers must be long enough */
|
||||||
|
int
|
||||||
|
deserialize_header(int fd, CommentHeader *header)
|
||||||
|
{
|
||||||
|
char buf[READ_BUF_SZ], *dst_bufs[2];
|
||||||
|
int bytes_read;
|
||||||
|
size_t len, dst_idx, i;
|
||||||
|
|
||||||
|
dst_bufs[0] = header->user_sid;
|
||||||
|
dst_bufs[1] = header->user_displayname;
|
||||||
|
dst_idx = 0;
|
||||||
|
|
||||||
|
read(fd, &header->reply_id, sizeof(int));
|
||||||
|
read(fd, &header->creation_time, sizeof(time_t));
|
||||||
|
read(fd, &header->text_length, sizeof(size_t));
|
||||||
|
|
||||||
|
while ((bytes_read = read(fd, buf, READ_BUF_SZ))) {
|
||||||
|
for (; i < bytes_read; ++i) {
|
||||||
|
if (buf[i] != '\0') {
|
||||||
|
*(dst_bufs[dst_idx]++) = buf[i];
|
||||||
|
} else {
|
||||||
|
dst_idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
unix_fs_driver_leave_comment(void *driver_data_ptr, CommentHeader *header, char *text)
|
||||||
|
{
|
||||||
|
char *path, *header_file_path, *text_file_path, *id_ctrl_file_path;
|
||||||
|
FILE *id_ctrl_file;
|
||||||
|
size_t path_len, text_len;
|
||||||
|
int max_id;
|
||||||
|
int retval;
|
||||||
|
int header_fd, text_fd, id_ctrl_fd;
|
||||||
|
|
||||||
|
/* assure safe free calls */
|
||||||
|
id_ctrl_file_path = header_file_path = text_file_path = NULL;
|
||||||
|
id_ctrl_file = NULL;
|
||||||
|
|
||||||
|
header_fd = text_fd = -1;
|
||||||
|
|
||||||
|
path = ((UnixFsDriverData*) driver_data_ptr)->path;
|
||||||
|
if (!is_valid_directory(path)) {
|
||||||
|
retval = -EINVAL;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
path_len = strlen(path);
|
||||||
|
text_len = strlen(text);
|
||||||
|
|
||||||
|
if (asprintf(&id_ctrl_file_path, "%s/%s", path, ID_CTRL_FILE) < 0) {
|
||||||
|
id_ctrl_file_path = NULL;
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
id_ctrl_file = fopen(id_ctrl_file_path, "r+");
|
||||||
|
|
||||||
|
if (!id_ctrl_file) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
id_ctrl_fd = fileno(id_ctrl_file);
|
||||||
|
|
||||||
|
if (lockf(id_ctrl_fd, F_LOCK, 0) < 0) {
|
||||||
|
id_ctrl_fd = -1;
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fscanf(id_ctrl_file, "%d", &max_id) != 1) {
|
||||||
|
max_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asprintf(&header_file_path, HEADER_FILE_FMT, path, max_id) < 0) {
|
||||||
|
header_file_path = NULL;
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
if (asprintf(&text_file_path, TEXT_FILE_FMT, path, max_id) < 0) {
|
||||||
|
text_file_path = NULL;
|
||||||
|
retval = -ENOMEM;
|
||||||
|
goto defer;
|
||||||
|
};
|
||||||
|
|
||||||
|
header_fd = open(header_file_path, O_RDWR | O_CREAT, 0644);
|
||||||
|
text_fd = open(text_file_path, O_RDWR | O_CREAT, 0644);
|
||||||
|
|
||||||
|
if (header_fd < 0 || text_fd < 0) {
|
||||||
|
retval = -EINVAL;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(text_fd, text, text_len) < text_len) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = serialize_header(header_fd, header);
|
||||||
|
if (retval < 0) {
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(id_ctrl_file = freopen(id_ctrl_file_path, "w+", id_ctrl_file))) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(id_ctrl_file, "%d", max_id + 1); /* update id only on success */
|
||||||
|
retval = max_id + 1;
|
||||||
|
defer:
|
||||||
|
if (id_ctrl_fd >= 0) {
|
||||||
|
if (lockf(id_ctrl_fd, F_ULOCK, 0) < 0) {
|
||||||
|
fprintf(stderr, "Failed to release id control lock, aborting...\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (id_ctrl_file) fclose(id_ctrl_file);
|
||||||
|
free(id_ctrl_file_path);
|
||||||
|
free(header_file_path);
|
||||||
|
free(text_file_path);
|
||||||
|
if (header_fd >= 0) close(header_fd);
|
||||||
|
if (text_fd >= 0) close(text_fd);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
unix_fs_driver_get_header(void *driver_data_ptr, CommentHeader *header, int id)
|
||||||
|
{
|
||||||
|
char *path, *header_file_path;
|
||||||
|
int fd, retval;
|
||||||
|
|
||||||
|
path = ((UnixFsDriverData*) driver_data_ptr)->path;
|
||||||
|
if (!asprintf(&header_file_path, HEADER_FILE_FMT, path, id)) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(header_file_path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
free(header_file_path);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = deserialize_header(fd, header);
|
||||||
|
free(header_file_path);
|
||||||
|
close(fd);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
unix_fs_driver_get_text(void *driver_data_ptr, char *text, int id)
|
||||||
|
{
|
||||||
|
char *path, *text_file_path;
|
||||||
|
int fd, retval;
|
||||||
|
off_t length;
|
||||||
|
|
||||||
|
path = ((UnixFsDriverData*) driver_data_ptr)->path;
|
||||||
|
if (!asprintf(&text_file_path, TEXT_FILE_FMT, path, id)) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(text_file_path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = lseek(fd, 0, SEEK_END);
|
||||||
|
if (length < 0) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
if (lseek(fd, 0, SEEK_SET) < 0) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(fd, text, length) < length) {
|
||||||
|
retval = -EIO;
|
||||||
|
goto defer;
|
||||||
|
}
|
||||||
|
|
||||||
|
text[length] = '\0';
|
||||||
|
retval = 0;
|
||||||
|
|
||||||
|
defer:
|
||||||
|
free(text_file_path);
|
||||||
|
if (fd >= 0) close(fd);
|
||||||
|
return retval;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user