view toys/pending/find.c @ 953:13916d161ec0

xzcat: remove XZ_(PREALLOC|SINGLE), inline xz_dec_bcj_create Because we only use XZ_DYNALLOC, there's a bunch of dead code. This patch removes the #ifdef's and if()s associated with support for multiple modes. single_call was only used to store the mode; it is no longer needed. A little bit of reorganization was needed to reduce the number of prototypes. Documentation associated with dead code was dropped. There are still some relics of multiple modes in the continued presence of "XZ_DYNALLOC" and xz_mode. Additionally, I inlined xz_dec_bcj_create; it was called once. This loses about 125 lines, mostly comments.
author Isaac Dunham <idunham@lavabit.com>
date Wed, 17 Jul 2013 17:25:07 -0500
parents b042cd34e564
children c99c215b4604
line wrap: on
line source

/* vi: set sw=4 ts=4:
 *
 * find.c - find files matching a criteria
 *
 * Copyright 2012 Tim Bird <tbird20d@gmail.com>
 *
 * See http://opengroup.org/onlinepubs/9699919799/utilities/find.html

USE_FIND(NEWTOY(find, "?", TOYFLAG_USR|TOYFLAG_BIN))

config FIND
  bool "find"
  default n
  help
    usage: find [<dir>] [<options]

    -name <pattern>    match pattern
    -type [fcdl]       match file type
    !, -a, -o          not, and , or
    (, )               group expressions  
    -mtime [-+]n       match file modification time (to within 24 hours)
    \t\t               +=greater (older) than, -=less (younger) than
*/

/* To Do:
 * -exec action
 */ 

#define FOR_find
#include "toys.h"

struct filter_node {
  struct filter_node *next;
  int op;
  union {
    char *name_regex;
    struct {
      int sign;
      time_t time;
    } t;
    mode_t type;
    struct {
      int arg_path_index;
      char **exec_args;
    } e;
  } data;
};

GLOBALS(
  char *dir;
  struct filter_node *filter_root;
  time_t itime;
)

/* filter operation types */
#define OP_UNKNOWN	0
#define OP_OR		2
#define OP_AND		3
#define OP_NOT		4

#define LPAREN		1 
#define RPAREN		5 

#define MAX_OP		5

#define CHECK_NAME	7
#define CHECK_MTIME	8
#define CHECK_TYPE	9

#define ACTION_PRINT	20
#define ACTION_PRINT0	21
#define ACTION_EXEC	22

#define IS_ACTION(x)    (x >= 20)

/* executes the command for a filter node
   returns 0 for failure or 1 for success
 */
static int do_exec(struct filter_node *filter, struct dirtree *node)
{
  char *path = NULL;
  char **arg_array;
  pid_t pid;
  int status;

  arg_array = filter->data.e.exec_args;
  if (filter->data.e.arg_path_index) {
    int plen;
    path = dirtree_path(node, &plen);
    arg_array[filter->data.e.arg_path_index] = path;
  }

  if (!(pid = fork())) xexec(arg_array);
  free(path);
  waitpid(pid, &status, 0);

  return !WEXITSTATUS(status);
}

/* prefix evaluator */
/* returns 0 for failure or 1 for success */
static int evaluate(struct filter_node *filter, struct dirtree *node,
  struct filter_node **fnext)
{
  int result;
  int op;
  static int skip = 0;

  /* if no filters, success */
  if (!filter) {
    *fnext = NULL;
    return 1;
  }
  op = filter->op;

  if (op==OP_NOT) return !evaluate(filter->next, node, fnext);

  if (op==OP_OR || op==OP_AND) {
    result = evaluate(filter->next, node, fnext);
    if(!skip) {
      if (op==OP_OR) {
        skip = result;
        result = evaluate(*fnext, node, fnext) || result;
      } else {
        skip = !result;
        result = evaluate(*fnext, node, fnext) && result;
      }
      skip = 0;
    }
    else result = evaluate(*fnext, node, fnext);
    return result;
  }

  // we're down to single operations, mark our position
  *fnext = filter->next;
  if(skip) return 0;

  // TODO: Do a regex comparison
  if (op==CHECK_NAME)
    return !strcmp(filter->data.name_regex, node->name);

  if (op==CHECK_MTIME) {
    long delta = (long)(((double)TT.itime - node->st.st_mtime) / 86400)
                 -filter->data.t.time;
    return filter->data.t.sign == (delta > 0) - (delta < 0);
  }
  if (op==CHECK_TYPE) return (node->st.st_mode & S_IFMT) == filter->data.type;


  if (op==ACTION_PRINT || op==ACTION_PRINT0) {
    char *path;
    int plen = 0;
    char terminator = (op==ACTION_PRINT)*'\n';

    path = dirtree_path(node, &plen);
    printf("%s%c", path, terminator);
    free(path);
    return 1;
  }

  if (op==ACTION_EXEC) return !do_exec(filter, node);

  error_msg("Ran out of operations in filter list!");
  return 1;
}

static int check_node_callback(struct dirtree *node)
{
  struct filter_node *junk;

  /* only recurse on "." at the root */
  /* so, don't recurse on 1) non-root "." and 2) any ".." */

  if (node->name[0] == '.' && 
    ((!node->name[1] && node->parent) ||
    (node->name[1]=='.' && !node->name[2])))
    return 0;

  evaluate(TT.filter_root, node, &junk);
  return DIRTREE_RECURSE;
}


static void build_filter_list(void)
{
  struct filter_node *node_list, *op_stack, *node, *op_node, *next;
  char **arg;
  int j;
  int prevop = 0;
  int have_action = 0;

  /* part optargs here and build a filter list in prefix format */

  TT.dir = ".";
  node_list = NULL;
  for (arg = toys.optargs; *arg; arg++) {
    struct {
      char *arg;
      int op;
      int extrarg;
    } arg_map[] = {{"-o", OP_OR, 0},
                   {"-a", OP_AND, 0},
                   {"!", OP_NOT, 0},
                   {"(", LPAREN, 0},
                   {")", RPAREN, 0},
                   {"-name", CHECK_NAME, 1},
                   {"-mtime", CHECK_MTIME, 1},
                   {"-type", CHECK_TYPE, 1},
                   {"-print", ACTION_PRINT, 0},
                   {"-print0", ACTION_PRINT0, 0},
                   {"-exec", ACTION_EXEC, 1}
    };
    mode_t types[]={S_IFREG,S_IFDIR,S_IFCHR,S_IFBLK,S_IFLNK,S_IFSOCK,S_IFIFO};
    char **arg_array;
    node = (struct filter_node *) xmalloc(sizeof(struct filter_node));
    node->op = OP_UNKNOWN;
    for (j=0; j < sizeof(arg_map)/sizeof(*arg_map); j++) {
      if (!strcmp(*arg, arg_map[j].arg)) {
        node->op = arg_map[j].op;
        if (arg_map[j].extrarg && !*(++arg))
	   error_exit("Missing argument to %s", arg_map[j].arg);
	break;
      }
    }

    switch(node->op) {
      case CHECK_NAME:
        node->data.name_regex = *arg;
        break;
      case CHECK_MTIME:
        switch(**arg) {
          case '+':
            node->data.t.sign=+1;
            (*arg)++;
            break;
          case '-':
            node->data.t.sign=-1;
            (*arg)++;
            break;
          default:
            node->data.t.sign=0;
            break;
        }
        node->data.t.time = atoi(*arg);
        break;
      case CHECK_TYPE:
        if (-1 == (j = stridx("fdcblsp", **arg)))
          error_exit("bad type '%s'", *arg);
        else node->data.type = types[j];
        break;
      case ACTION_EXEC:
        arg_array = xmalloc(sizeof(char *));
        for (j = 0; *arg && strcmp(*arg, ";"); j++) {
          /* new method */
          arg_array = xrealloc(arg_array, sizeof(char *) * (j+2));
          arg_array[j] = *arg;
          if (!strcmp(*arg, "{}")) node->data.e.arg_path_index = j;

          arg++;
        }
        if (!*arg) error_exit("need ';' in exec");
        arg_array[j] = 0;
        node->data.e.exec_args = arg_array;
        break;
    }

    have_action |= IS_ACTION(node->op);

    if (node->op == OP_UNKNOWN) {
      if (**arg == '-') error_exit("bad option '%s'", *arg);
      else TT.dir = *arg;
    }  else {
      // add OP_AND where necessary
      if (node_list) {
        int o1 = node_list->op, o2 = node->op;
        if ((o1>MAX_OP && o2>MAX_OP) ||
           (o1==RPAREN && o2>=OP_NOT && o2!=RPAREN) ||
           (o1>=OP_NOT && o1!=LPAREN && o2==LPAREN)) {
          struct filter_node *n = (struct filter_node *)
                                  xmalloc(sizeof(struct filter_node));
          n->op = OP_AND;
          n->next = node_list;
          node_list = n;
        }
      }
      /* push node */
      node->next = node_list;;
      node_list = node;
    }

  }

  /* now convert from infix to prefix */
  if(have_action) TT.filter_root = NULL;
  else TT.filter_root = &(struct filter_node){0, ACTION_PRINT};
  op_stack = NULL;
  node = node_list;
  while( node ) {
    int op = node->op;
    next = node->next;
    if (op==LPAREN || op==RPAREN) {
      free(node);
      node = 0;
    }
    if (op<=MAX_OP) {
      if (prevop > op) {
        /* pop opstack to output */
        op_node = op_stack;
        while (op_node) {
          /* remove from op_stack */
          op_stack = op_node->next;
          /* push to output */
          op_node->next = TT.filter_root;
          TT.filter_root = op_node;
          /* get next node */
          op_node = op_stack;
        }
      }
      if (node) {
        /* push to opstack */
        node->next = op_stack;
        op_stack = node;
      }
      prevop = op*(op!=RPAREN);
    }
    else {
        /* push to output */
        node->next = TT.filter_root;
        TT.filter_root = node;
    }
    node = next;
  }
  /* end of input - push opstack to output */
  /* pop opstack to output till empty */
  op_node = op_stack;
  while (op_node) {
    op_stack = op_node->next;
    op_node->next = TT.filter_root;
    TT.filter_root = op_node;
    op_node = op_stack;
  }
  if(!have_action) {
    node = TT.filter_root;
    TT.filter_root = (struct filter_node*) xmalloc(sizeof(struct filter_node));
    TT.filter_root->next = node;
    TT.filter_root->op = OP_AND;
  }
}

void find_main(void)
{
  TT.itime = time(NULL);
  /* parse filters, if present */
  build_filter_list();

  /* FIXTHIS - parse actions, if present */

  dirtree_read(TT.dir, check_node_callback);
}