Commit 8c4ed797 authored by platypro's avatar platypro

Added projects. Also added generators for Exercises, and Problem Sets.

Signed-off-by: platypro's avatarAeden McClain <dev@platypro.net>
parent b4ea1dfe
......@@ -31,8 +31,9 @@ add_subdirectory(onion EXCLUDE_FROM_ALL)
set(DEPS_LIBS ${DEPS_LIBS} onion_static)
endif()
set(DEPS_SRC ${PMUSTACHE_PROVIDER_WJE_SRCS} PARENT_SCOPE)
set(DEPS_SRC ${PMUSTACHE_PROVIDER_WJE_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/pcg_basic.c PARENT_SCOPE)
set(DEPS_INCLUDE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/wjelement/include
${CMAKE_CURRENT_SOURCE_DIR}/onion/src
${PMUSTACHE_INCLUDE_DIRS}
......
Subproject commit 88825fe5b2656470544c82a3443a857e9606e3b2
Subproject commit 38165b405ffd676cd27019b7e984602feae96b01
......@@ -5,9 +5,9 @@ set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_FULL_LIBDIR} ${CMAKE_INSTALL_PREFIX}/lib
configure_file("paths.h.in" "${CMAKE_CURRENT_BINARY_DIR}/paths.h")
set(SRCS
pcg_basic.c
quizgrind.c
args.c
directory.c
gen/gen.c
......@@ -23,6 +23,10 @@ set(SRCS
widget/textInput/backend.c
widget/textView/backend.c
project/project.c
project/init.c
project/new.c
${DEPS_SRC}
)
......@@ -48,8 +52,7 @@ add_executable(quizgrind ${SRCS})
install(TARGETS quizgrind DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})
install(DIRECTORY
../content/exercise
../content/pset
template
DESTINATION ${APP_DATA_PATH})
target_link_libraries(quizgrind
......
/* Aeden McClain (c) 2019
* web: https://www.platypro.net
* email: dev@platypro.net
* License info at bottom.
*
* This file is a part of QuizGrind.
*/
#include "common.h"
#include "args.h"
......@@ -5,6 +12,11 @@
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#ifdef _WIN32
#include <malloc.h>
#else
#include <alloca.h>
#endif
void args_show_short(struct option_info* opts)
{
......@@ -31,21 +43,31 @@ void args_show_short(struct option_info* opts)
}
}
void args_show_long(struct option_info* opts)
void args_show_long(struct option_info* opts, int indent)
{
while(opts->id)
{
int yat = 0;
if(opts->name)
yat += printf(" --%s", opts->name);
yat += printf("%*c--%s", indent, ' ', opts->name);
else
yat += printf(" -%c", opts->id);
yat += printf("%*c-%c", indent, ' ', opts->id);
printf("%*c%s\n", 22 - yat, ' ', opts->description);
printf("%*c%s\n", ARGS_ALIGNAT - yat, ' ', opts->description);
opts++;
}
}
void args_show_commands(struct command* cmd, int indent)
{
while(cmd->name)
{
int cmdlen = ARGS_ALIGNAT - strlen(cmd->name) - indent;
printf("%*c%s%*c%s\n", indent, ' ', cmd->name, cmdlen, ' ', cmd->hint);
cmd++;
}
}
char** args_parse(struct option_info* opts,
args_parser parser,
void* udata,
......@@ -124,3 +146,36 @@ char** args_parse(struct option_info* opts,
}
return NULL;
}
bool args_runCommand(struct command* commands,
char** lastarg,
int argc,
void* udata
)
{
struct command* cmd = commands;
while(cmd->name)
{
if(!strcmp(*lastarg, cmd->name))
{
cmd->fn(udata, argc, lastarg);
return true;
}
cmd++;
}
return false;
}
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Aeden McClain (c) 2019
* web: https://www.platypro.net
* email: dev@platypro.net
* License info at bottom.
*
* This file is a part of QuizGrind.
*/
#ifndef INCLUDE_ARGS_H
#define INCLUDE_ARGS_H
......@@ -8,6 +16,8 @@
#define OPT_HELP ARGS_LONGONLY(0)
#define ARGS_ALIGNAT 22
struct option_info
{
uint32_t flags;
......@@ -17,14 +27,39 @@ struct option_info
char* description;
};
struct command
{
char* name;
char* hint;
int (*fn)(void*, int argc, char** argv);
};
typedef bool (*args_parser) (int id, char* arg, void* udata);
extern void args_show_commands(struct command* commands, int indent);
extern void args_show_long(struct option_info* opt_info, int indent);
extern void args_show_short(struct option_info* opt_info);
extern void args_show_long(struct option_info* opt_info);
extern char** args_parse(struct option_info* opts,
args_parser parser,
void* udata,
int* argc, char** argv);
extern bool args_runCommand(struct command* commands,
char** arg, int argc, void* udata);
#endif /* INCLUDE_ARGS_H */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
......@@ -17,7 +17,6 @@
#include <libgen.h>
#ifdef _WIN32
#include <malloc.h>
//#define alloca _alloca
#else
#include <alloca.h>
#endif
......@@ -33,15 +32,27 @@
#include "paths.h"
#include "script.h"
#include "keys.h"
char* exercise_loadResource(char* basepath, char* file, char* ext)
{
size_t fpath_len = strlen(basepath) + strlen(file) + (ext ? strlen(ext) : 0) + 2;
char* fpath = malloc(fpath_len);
snprintf(fpath, fpath_len, "%s/%s%s", basepath, file, ext);
return fpath;
}
#include "directory.h"
#include "project/project.h"
#define GET_EXERCISE_META(buf, proj, id) do { \
size_t buf##_len = strlen(id) + strlen(proj) + 15; \
buf = alloca(buf##_len); \
snprintf(buf, FILENAME_MAX, "%s/%s/exercise.json", proj, id);\
} while(0);
#define GET_EXERCISE_SCRIPT(buf, proj, id) do { \
size_t buf##_len = strlen(id) + strlen(proj) + 15; \
buf = alloca(buf##_len); \
snprintf(buf, FILENAME_MAX, "%s/%s/script.lua", proj, id);\
} while(0);
#define GET_EXERCISE_TEMPLATE(buf, proj, id, template) do { \
size_t buf##_len = strlen(id) + strlen(proj) + 15; \
buf = alloca(buf##_len); \
snprintf(buf, FILENAME_MAX, "%s/%s/t%s", proj, id, template);\
} while(0);
EXERCISE* mkExercise(PG_STATE* state)
{
EXERCISE* exer = calloc(1, sizeof(EXERCISE));
......@@ -71,10 +82,8 @@ EXERTEMPLATE* exercise_mkTemplate(PG_STATE* state, QUESTION* q, char* name)
tc = calloc(1, sizeof(TEMPLATECONTAINER));
if(!tc) return false;
size_t l = strlen(q->eref->basepath) + strlen(name) + 3;
char* filename = alloca(l);
snprintf(filename, l, "%s/t%s", q->eref->basepath, name);
char* filename;
GET_EXERCISE_TEMPLATE(filename, state->project->path_exercise, q->eref->id, name);
tc->src = mustache_eatFile(filename);
if(!tc->src)
......@@ -174,30 +183,23 @@ char* exercise_load(PG_STATE* state, char* filename)
if(!exer)
{
size_t exerPath_len = strlen(state->path_exercise) + strlen(exerId) + 2;
char* exerPath = malloc(exerPath_len);
if(!exerPath) { return NULL; }
snprintf(exerPath, exerPath_len, "%s/%s", state->path_exercise, exerId);
char* file = exercise_loadResource(exerPath, "exercise", ".json");
char* file;
GET_EXERCISE_META(file, state->project->path_exercise, exerId);
FILE* f = fopen(file, "r");
if(!f)
{
free(exerPath);
printf("File %s not found, aborting.\n", file);
free(file);
return NULL;
}
free(file);
WJReader exer_rdr =
WJROpenFILEDocument(f, NULL, 0);
//Load the exercise
exer = mkExercise(state);
exer->basepath = exerPath;
exer->staticQuestion.eref = exer;
exer->id = malloc(strlen(exerId) + 1);
strcpy(exer->id, exerId);
char* obj = WJRNext(NULL, 15, exer_rdr);
......@@ -281,13 +283,12 @@ char* exercise_load(PG_STATE* state, char* filename)
if(exer->staticType != STATTYPE_STATIC)
{
char* scriptPath = exercise_loadResource(exerPath, "script", ".lua");
char* scriptPath;
GET_EXERCISE_SCRIPT(scriptPath, state->project->path_exercise, exerId);
// script_load frees scriptPath
if(!scriptPath || !script_load(state, exerId, scriptPath))
{ WJRCloseDocument(exer_rdr); return NULL; }
}
exer->id = malloc(strlen(exerId) + 1);
strcpy(exer->id, exerId);
WJRCloseDocument(exer_rdr);
fclose(f);
......@@ -317,58 +318,31 @@ char* exercise_load(PG_STATE* state, char* filename)
return pset->name;
}
#ifdef _WIN32
bool foreachfile(char* path, bool (*fun)(void* udata, char* path), void* udata)
{
WIN32_FIND_DATA d;
HANDLE dir = FindFirstFile(path, &d);
do
{
if(!fun(udata, d.cFileName)) return false;
} while (FindNextFile(dir, &d));
return true;
}
#else
bool foreachfile(char* path, bool (*fun)(void* udata, char* path), void* udata)
{
DIR *d;
struct dirent *dir;
d = opendir(path);
while ((dir = readdir(d)))
{
if(dir->d_type == DT_REG)
{
if(!fun(udata, dir->d_name)) return false;
}
}
closedir(d);
return true;
}
#endif
bool exercise_loadAll_fun(void* udata, char* path)
bool exercise_loadAll_fun(void* udata, FILEINFO* set)
{
PG_STATE* state = udata;
char* fpath = exercise_loadResource(state->path_pset, path, NULL);
if(!fpath) return false;
exercise_load(state, fpath);
free(fpath);
if(!strcmp(set->name, "..") || !strcmp(set->name, ".")) return true;
size_t path_len = strlen(set->basePath) + strlen(set->name) + 3;
char* path = alloca(path_len);
snprintf(path, path_len, "%s/%s", set->basePath, set->name);\
exercise_load(state, path);
return true;
}
//TODO: Clean-up deleted exercises
bool exercise_loadAll(PG_STATE* state)
{
foreachfile(state->path_pset, exercise_loadAll_fun, state);
dir_foreach(state->project->path_pset, exercise_loadAll_fun, state);
return true;
}
char* exercise_loadOne(PG_STATE* state, char* set)
{
char* path = exercise_loadResource(state->path_pset, set, ".json");
if(!path) return false;
size_t path_len = strlen(state->project->path_pset) + strlen(set) + 15;
char* path = alloca(path_len);
snprintf(path, path_len, "%s/%s.json", state->project->path_pset, set);
set = exercise_load(state, path);
free(path);
return set;
}
......@@ -770,7 +744,6 @@ void exercise_cleanup(PG_STATE* state)
EXERCISE* exer_next = exer->next;
WJECloseDocument(exer->baseOptions);
WJECloseDocument(exer->staticStore);
free(exer->basepath);
free(exer->calculator);
free(exer->name);
free(exer->id);
......
......@@ -61,7 +61,6 @@ typedef struct Exercise
char* calculator;
char* name;
char* basepath;
STATICTYPE staticType;
char* id;
......
......@@ -302,8 +302,6 @@ uint8_t script_load(PG_STATE* scr, char* id, char* path)
lua_setfield(L, -2, id);
lua_pop(L, 1);
free(path);
return result;
}
......
......@@ -46,8 +46,9 @@ bool daemon_parse_arg(int id, char* arg, void* udata)
return true;
}
int daemon_run(PG_STATE* state, int argc, char** argv)
int daemon_run(void* data, int argc, char** argv)
{
PG_STATE* state = data;
args_parse(daemon_opts,
daemon_parse_arg,
state,
......
......@@ -24,7 +24,7 @@ typedef struct PG_Daemon {
uint8_t type;
} PG_DAEMON;
extern int daemon_run(struct PG_State* state, int argc, char** argv);
extern int daemon_run(void* state, int argc, char** argv);
extern bool daemon_stop(struct PG_State* state);
#endif /* INCLUDE_DAEMON_H */
......
/* Aeden McClain (c) 2019
* web: https://www.platypro.net
* email: dev@platypro.net
* License info at bottom.
*
* This file is a part of QuizGrind.
*/
#include "common.h"
#include "directory.h"
#include <dirent.h>
#ifdef _WIN32
bool foreachfile(char* path, FOREACH_FUN fun, void* udata)
{
WIN32_FIND_DATA d;
HANDLE dir = FindFirstFile(path, &d);
FILEINFO info = {.name = d.cFileName, .basePath = path};
info.type = (d.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILETYPE_DIR : FILETYPE_REGULAR;
do
{
if(!fun(udata, &info)) return false;
} while (FindNextFile(dir, &d));
return true;
}
bool dir_exists(const char* path)
{
DWORD dwAttrib = GetFileAttributes(szPath);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
#else
#include <sys/types.h>
#include <sys/stat.h>
bool dir_foreach(char* path, FOREACH_FUN fun, void* udata)
{
DIR *d;
struct dirent *dir;
d = opendir(path);
while ((dir = readdir(d)))
{
FILEINFO info = {
.name = dir->d_name,
.basePath = path,
.type = (dir->d_type == DT_DIR) ? FILETYPE_DIR : FILETYPE_REGULAR
};
if(!fun(udata, &info)) return false;
}
closedir(d);
return true;
}
bool dir_exists(const char* path)
{
struct stat s;
if(!stat(path, &s))
{
if(S_ISDIR(s.st_mode))
return true;
}
return false;
}
#endif
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Aeden McClain (c) 2019
* web: https://www.platypro.net
* email: dev@platypro.net
* License info at bottom.
*
* This file is a part of QuizGrind.
*/
#ifndef INCLUDE_FOREACHFILE_H
#define INCLUDE_FOREACHFILE_H
typedef enum FileType
{
FILETYPE_REGULAR,
FILETYPE_DIR,
} FILETYPE;
typedef struct FileInfo
{
char* basePath;
char* name;
FILETYPE type;
} FILEINFO;
typedef bool (*FOREACH_FUN)(void* udata, FILEINFO* finfo);
extern bool dir_foreach(char* path, FOREACH_FUN fun, void* udata);
extern bool dir_exists(const char* path);
#ifdef _WIN32
#include <direct.h>
#define mymkdir(path) _mkdir(path);
#else
#include <sys/stat.h>
#define mymkdir(path) mkdir(path, 0777);
#endif
#endif /* INCLUDE_FOREACHFILE_H */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
......@@ -90,7 +90,7 @@ bool gen_parse_arg(int id, char* arg, void* udata)
printf("QuizGrind Generator\nUsage: quizgrind gen ");
args_show_short(gen_opts);
puts("\n\nOptions:");
args_show_long(gen_opts);
args_show_long(gen_opts, 2);
exit(0);
break;
case OPT_OUTPUT_FILE:
......@@ -135,8 +135,9 @@ bool gen_parse_arg(int id, char* arg, void* udata)
return true;
}
int handle_file(PG_STATE* state, int argc, char** argv)
int handle_file(void* data, int argc, char** argv)
{
PG_STATE* state = data;
state->gen.type = GTYPE_JSON;
state->gen.num = 10;
......
......@@ -28,6 +28,6 @@ typedef struct PG_Gen
uint32_t num;
} PG_GEN;
extern int handle_file(struct PG_State* state, int argc, char** argv);
extern int handle_file(void* state, int argc, char** argv);
#endif /* INCLUDE_GEN_H */
#define APP_DATA_PATH "@APP_DATA_PATH@"
#define APP_IPC_PATH "@APP_VAR_PATH@"
#define APP_PROBSET_PATH APP_DATA_PATH "/pset"
#define APP_EXERCISE_PATH APP_DATA_PATH "/exercise"
#define APP_TEMPLATE_PATH APP_DATA_PATH "/template"
#include "common.h"
#include "project.h"
#include <stdio.h>
#include <args.h>
#include <unistd.h>
#include <string.h>
#include "quizgrind.h"
#include "directory.h"
#define OPT_NAMESPACE 'n'
struct option_info cli_init_opts[] =
{
{0, OPT_HELP, "help", NULL, "Show help and exit"},
{ARGS_ARGUMENT, OPT_NAMESPACE, NULL, "NAME", "Namespace to use (i.e. com.example.asdf)"},
{0}
};
bool cli_init_opts_ (int key, char *arg, void* data)
{
PG_PROJECT* optdata = data;
switch(key)
{
case OPT_HELP:
{
puts("QuizGrind Project Initializer\n"
"Usage: quizgrind init");
args_show_short(cli_init_opts);
puts("\n\nOptions:");
args_show_long(cli_init_opts, 2);
exit(0);
}
case OPT_NAMESPACE:
{
optdata->namespace = arg;
break;
}
}
return true;
}
void project_getpath(char* buf, char* path, char* ext)
{
if(!path)
{
getcwd(buf, FILENAME_MAX);
while(*buf) buf++;
*buf = '/';
buf++;
strcpy(buf, ext);
}
else snprintf(buf, FILENAME_MAX, "%s/%s", path, ext);
}
int cli_init(void* data, int argc, char** argv)
{
PG_STATE* state = data;
PG_PROJECT optdata;
args_parse(cli_init_opts, cli_init_opts_, &optdata, &argc, argv);
char* projpath = malloc(FILENAME_MAX);
project_getpath(projpath, state->basePath, PROJECT_META);
if(!access(projpath, F_OK))
{
printf("Project at %s already exists\n", projpath);
free(projpath);
return 0;
}
else
{
printf("Creating project at %s\n", projpath);
FILE* f = fopen(projpath, "w");
if(!f) {free(projpath); return 1;}
WJWriter w = WJWOpenFILEDocument(true, f);
WJWOpenObject(NULL, w);
if(optdata.namespace)
WJWString(KEY_NAMESPACE, optdata.namespace, TRUE, w);
WJWCloseObject(w);
WJWCloseDocument(w);
fclose(f);
project_getpath(projpath, state->basePath, PROJECT_EXERCISE);
mymkdir(projpath);
project_getpath(projpath, state->basePath, PROJECT_PSET);
mymkdir(projpath);
}
free(projpath);
return 0;
}
#include "common.h"
#include "project.h"
#include <args.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <keys.h>
#include "quizgrind.h"
#include "directory.h"
#define OPT_PSET 'p'
#define OPT_PSET_ "pset"
#define OPT_CALCULATOR 'c'
#define OPT_CALCULATOR_ "calculator"
#define OPT_CALCULATOR_DEFAULT "none"
#define OPT_NAME 'n'
#define OPT_NAME_ "name"
#define OPT_DESCRIPTION 'd'
#define OPT_DESCRIPTION_ "description"
#define OPT_TEMPLATE 't'
#define OPT_TEMPLATE_DEFAULT "basic"
int cli_new_help(void* data, int argc, char** argv);
int cli_new_exercise(void* data, int argc, char** argv);
int cli_new_pset(void* data, int argc, char** argv);
struct command cli_new_commands[] =
{
{.name = "--help", .hint = "View help", .fn = cli_new_help},
{.name = "exercise", .hint = "Create a new exercise",
.fn = cli_new_exercise},
{.name = "pset", .hint = "Create a new problem set",
.fn = cli_new_pset},
{0}
};