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