/*********************************************************************
* bfs *
* Copyright (C) 2015-2016 Tavian Barnes <tavianator@tavianator.com> *
* *
* This program is free software. It comes without any warranty, to *
* the extent permitted by applicable law. You can redistribute it *
* and/or modify it under the terms of the Do What The Fuck You Want *
* To Public License, Version 2, as published by Sam Hocevar. See *
* the COPYING file or http://www.wtfpl.net/ for more details. *
*********************************************************************/
#include "bfs.h"
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
/**
* Create a new expression.
*/
static struct expr *new_expr(eval_fn *eval) {
struct expr *expr = malloc(sizeof(struct expr));
if (!expr) {
perror("malloc()");
return NULL;
}
expr->lhs = NULL;
expr->rhs = NULL;
expr->eval = eval;
return expr;
}
/**
* Singleton true expression instance.
*/
static struct expr expr_true = {
.lhs = NULL,
.rhs = NULL,
.eval = eval_true,
};
/**
* Singleton false expression instance.
*/
static struct expr expr_false = {
.lhs = NULL,
.rhs = NULL,
.eval = eval_false,
};
/**
* Free an expression.
*/
static void free_expr(struct expr *expr) {
if (expr && expr != &expr_true && expr != &expr_false) {
free_expr(expr->lhs);
free_expr(expr->rhs);
free(expr);
}
}
/**
* Create a new unary expression.
*/
static struct expr *new_unary_expr(struct expr *rhs, eval_fn *eval) {
struct expr *expr = new_expr(eval);
if (!expr) {
free_expr(rhs);
return NULL;
}
expr->rhs = rhs;
expr->eval = eval;
return expr;
}
/**
* Create a new binary expression.
*/
static struct expr *new_binary_expr(struct expr *lhs, struct expr *rhs, eval_fn *eval) {
struct expr *expr = new_expr(eval);
if (!expr) {
free_expr(rhs);
free_expr(lhs);
return NULL;
}
expr->lhs = lhs;
expr->rhs = rhs;
expr->eval = eval;
return expr;
}
/**
* Free the parsed command line.
*/
void free_cmdline(struct cmdline *cl) {
if (cl) {
free_expr(cl->expr);
free_colors(cl->colors);
free(cl->roots);
free(cl);
}
}
/**
* Add a root path to the cmdline.
*/
static bool cmdline_add_root(struct cmdline *cl, const char *root) {
size_t i = cl->nroots++;
const char **roots = realloc(cl->roots, cl->nroots*sizeof(const char *));
if (!roots) {
perror("realloc()");
return false;
}
roots[i] = root;
cl->roots = roots;
return true;
}
/**
* Ephemeral state for parsing the command line.
*/
struct parser_state {
/** The command line being parsed. */
struct cmdline *cl;
/** The command line arguments. */
char **argv;
/** Current argument index. */
int i;
/** Whether a -print action is implied. */
bool implicit_print;
/** Whether warnings are enabled (see -warn, -nowarn). */
bool warn;
/** Whether any non-option arguments have been encountered. */
bool non_option_seen;
/** The current time. */
struct timespec now;
};
/**
* Parse the expression specified on the command line.
*/
static struct expr *parse_expr(struct parser_state *state);
/**
* While parsing an expression, skip any paths and add them to the cmdline.
*/
static const char *skip_paths(struct parser_state *state) {
while (true) {
const char *arg = state->argv[state->i];
if (!arg
|| arg[0] == '-'
|| strcmp(arg, "(") == 0
|| strcmp(arg, ")") == 0
|| strcmp(arg, "!") == 0
|| strcmp(arg, ",") == 0) {
return arg;
}
if (!cmdline_add_root(state->cl, arg)) {
return NULL;
}
++state->i;
}
}
/**
* Parse an integer.
*/
static bool parse_int(const char *str, int *value) {
char *endptr;
long result = strtol(str, &endptr, 10