view lisplint.c @ 1065:6253a5651a47

static: make URI prefix configurable This changes the definition of the base-url config knob to be a list instead of a cons. The list should have one or two items: the scheme+host+port portion and the URI prefix portion. The migration is simple - just replace: (base-url . "http://example.com") With: (base-url "http://example.com") That is, the cons cell turned into a list. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Thu, 13 Aug 2020 11:22:19 -0400
parents 3ca4f367c752
children
line wrap: on
line source

/*
 * Copyright (c) 2015-2020 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * 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 OR COPYRIGHT HOLDERS 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.
 */

#include <stdlib.h>

#include <jeffpc/jeffpc.h>
#include <jeffpc/error.h>
#include <jeffpc/sexpr.h>
#include <jeffpc/val.h>

#include "utils.h"
#include "config.h"

static char *prog;

static bool (*checker)(const char *fname, struct val *);
static bool verbose = false;

#define error(fname, ...)				\
	do {						\
		fprintf(stderr, "%s: ", (fname));	\
		fprintf(stderr, __VA_ARGS__);		\
	} while (0)

static bool check_type(const char *fname, struct val *lv,
		       const char *varname, enum val_type type,
		       bool mandatory)
{
	struct val *v;

	v = sexpr_alist_lookup_val(lv, varname);
	if (!v) {
		if (mandatory)
			error(fname, "missing '%s'\n", varname);
		return !mandatory;
	}

	if (v->type != type) {
		error(fname, "'%s' has wrong type: got %u, expected %u\n",
		      varname, v->type, type);
		val_putref(v);
		return false;
	}

	val_putref(v);
	return true;
}

static bool check_post(const char *fname, struct val *lv)
{
	struct val *fmtval;
	uint64_t fmt;
	bool ret;

	/*
	 * check fmt presence & correctness
	 */
	fmtval = sexpr_alist_lookup_val(lv, "fmt");
	if (!fmtval) {
		error(fname, "missing 'fmt'\n");
		return false;
	}

	if (fmtval->type != VT_INT) {
		error(fname, "fmt is not an int\n");
		val_putref(fmtval);
		return false;
	}

	fmt = fmtval->i;

	val_putref(fmtval);

	/*
	 * Depending on the fmt, check other parts of the metadata
	 */
	switch (fmt) {
	case 1:
	case 2:
	case 3:
		ret = check_type(fname, lv, "time", VT_STR, true);
		ret = check_type(fname, lv, "title", VT_STR, true) && ret;
		ret = check_type(fname, lv, "tags", VT_CONS, false) && ret;
		ret = check_type(fname, lv, "cats", VT_CONS, false) && ret;
		ret = check_type(fname, lv, "comments", VT_CONS, false) && ret;
		ret = check_type(fname, lv, "listed", VT_BOOL, false) && ret;
		return ret;
	case 4:
		return true;
	default:
		error(fname, "invalid format version\n");
		return false;
	}
}

static bool check_comment(const char *fname, struct val *lv)
{
	bool ret;

	ret = check_type(fname, lv, "author", VT_STR, true);
	ret = check_type(fname, lv, "email", VT_STR, true) && ret;
	ret = check_type(fname, lv, "time", VT_STR, true) && ret;
	ret = check_type(fname, lv, "ip", VT_STR, false) && ret;
	ret = check_type(fname, lv, "url", VT_STR, false) && ret;
	ret = check_type(fname, lv, "moderated", VT_BOOL, true) && ret;

	return ret;
}

static bool check_config(const char *fname, struct val *lv)
{
	bool ret;

	ret = check_type(fname, lv, CONFIG_SCGI_HOST, VT_STR, false);
	ret = check_type(fname, lv, CONFIG_SCGI_PORT, VT_INT, false);
	ret = check_type(fname, lv, CONFIG_SCGI_THREADS, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_HTML_INDEX_STORIES, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_FEED_INDEX_STORIES, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_COMMENT_MAX_THINK, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_COMMENT_MIN_THINK, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_COMMENT_CAPTCHA_A, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_COMMENT_CAPTCHA_B, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_DATA_DIR, VT_STR, false) && ret;
	ret = check_type(fname, lv, CONFIG_THEME_DIR, VT_STR, false) && ret;
	ret = check_type(fname, lv, CONFIG_BASE_URL, VT_CONS, false) && ret;
	ret = check_type(fname, lv, CONFIG_BUG_BASE_URL, VT_STR, false) && ret;
	ret = check_type(fname, lv, CONFIG_WIKI_BASE_URL, VT_STR, false) && ret;
	ret = check_type(fname, lv, CONFIG_PHOTO_BASE_URL, VT_STR, false) && ret;
	ret = check_type(fname, lv, CONFIG_TAGCLOUD_MIN_COUNT, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_TAGCLOUD_MIN_SIZE, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_TAGCLOUD_MAX_SIZE, VT_INT, false) && ret;
	ret = check_type(fname, lv, CONFIG_CATEGORY_TO_TAG, VT_CONS, false) && ret;

	return ret;
}

static bool onefile(const char *fname, char *ibuf, size_t len)
{
	struct val *lv;
	bool ret;

	lv = sexpr_parse(ibuf, len);
	if (IS_ERR(lv)) {
		fprintf(stderr, "Error parsing %s: %s\n",
			fname, xstrerror(PTR_ERR(lv)));
		return false;
	}

	if (verbose) {
		sexpr_dump_file(stderr, lv, false);
		fprintf(stderr, "\n");
	}

	ret = checker(fname, lv);

	val_putref(lv);

	return ret;
}

static void usage(void)
{
	fprintf(stderr, "Usage: %s {-p | -c | -C} [-v] <filename> ...\n", prog);
	exit(2);
}

int main(int argc, char **argv)
{
	char *in;
	int i;
	int result;
	char opt;

	prog = argv[0];

	result = 0;

	ASSERT0(putenv("UMEM_DEBUG=default,verbose"));

	while ((opt = getopt(argc, argv, "pcCv")) != -1) {
		switch (opt) {
			case 'p':
				/* post */
				checker = check_post;
				break;
			case 'c':
				/* comment */
				checker = check_comment;
				break;
			case 'C':
				/* config */
				checker = check_config;
				break;
			case 'v':
				verbose = true;
				break;
			default:
				usage();
				break;
		}
	}

	if (optind == argc)
		usage();

	if (checker == NULL)
		usage();

	for (i = optind; i < argc; i++) {
		in = read_file(argv[i]);
		if (IS_ERR(in)) {
			fprintf(stderr, "%s: cannot read: %s\n",
				argv[i], xstrerror(PTR_ERR(in)));
			result++;
			continue;
		}

		if (!onefile(argv[i], in, strlen(in)))
			result++;

		free(in);
	}

	if (!result)
		printf("OK.\n");
	else
		fprintf(stderr, "%d failed files\n", result);

	return !!result;
}