about summary refs log blame commit diff stats
path: root/rf.c
blob: 327a3f7fc313111c97e98aacec8def4d429a0aad (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                       

                   
                    



                   
                      





                      

                        
                 


                   

                   

  
                                



                                                        
                                                                       

 
                                    


                                                                      
 
                 

 
                                                                 
                  
 
                               
                                                       


                                 
 
                 

 
                                               
                  
 
                                                  
                                                                      
 

                                 

                 
 
                 

 
                          
                                                                      
                        
 






















                                                                    
 












                                                                                             

                                                  
         

 

                                                                           
                                    

                 
                                       







                                                                                    
                                                               




                                                              


                                                                                                  


                                                                          

                                                                                                         







                                                                        
                                                    




                                                                    

                                                                                      
                                                                                   












                                              
                                      
                                                                     












                                                                               
 
 
                                 
                               

                       





                                
                                                              
               


                        




                                                          
 

                                   

                              




                                                                            
 
                                     
 

                              







                                                               

                              

                                   



                              











                                                                    

















                                                                  


                                         

                                         





                                                                             

                            


                 
 
#define _BSD_SOURCE
#define _DEFAULT_SOURCE

#include <dirent.h>
#include <errno.h>
#include <fnmatch.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "config.h"

extern char *__progname;

struct switches {
	int invert;
	int limit;
	int count;
	int unlink;
	char *cmd;
};

static void usage(char *error) {
	if (error != NULL) {
		fprintf(stderr, "Error: %s\n\n", error);
	}

	fprintf(stderr, "usage: %s [-lvSU] pattern ...\n", __progname);
}

static int is_child(char *dirname) {
	if (strcmp("..", dirname) == 0 || strcmp(".", dirname) == 0) {
		return 0;
	}

	return 1;
}

static int not_in_array(char **arr, char *dirname, size_t size) {
	int i = 0;

	for (; i < size; i++) {
		if (fnmatch(arr[i], dirname, 0) == 0) {
			return 0;
		}
	}

	return 1;
}

static int excluded_extension(char *filename) {
	int i = 0;

	for (; i < ignored_extensions_size; i++) {
		int res = fnmatch(ignored_extensions[i], filename, 0);

		if (res == 0) {
			return 1;
		}
	}

	return 0;
}

static void handle_result(
	char *path, struct switches *switches, struct dirent *entry) {
	int i, j, k = 0;

	char cmd[MAXPATHLEN];
	memset(cmd, '\0', MAXPATHLEN);

	char full_path[MAXPATHLEN];
	full_path[0] = '\0';
	strcat(full_path, path);
	strcat(full_path, "/");
	strcat(full_path, entry->d_name);

	if (is_child(entry->d_name) != 0) {
		if (switches->unlink) {
			int r = unlink(full_path);

			if (r < 0) {
				perror("unlink");
			} else {
				printf("removed '%s'\n", full_path);
			}
		} else if (switches->cmd != NULL) {
			int l = strlen(switches->cmd);

			for (i = 0, j = 0; i < l; i++) {
				char c = switches->cmd[i];

				if (c == '%' && (i + 1 < l) && switches->cmd[i + 1] == 's') {
					i++;

					for (k = 0; k < strlen(full_path); k++) {
						cmd[j++] = full_path[k];
					}
				} else {
					cmd[j++] = c;
				}
			}

			system(cmd);
		} else {
			printf("%s\n", full_path);
		}
	}
}

/* return 1 if breaking early (e.g. reaching limit) otherwise return 0 */
static int recurse_find(char **patterns, int *pattern_count, char *dirname,
	struct switches *switches) {
	DIR *dir;

	char path[MAXPATHLEN] = {'\0'};
	int break_early = 0;
	strcat(path, dirname);
	dir = opendir(path);

	if (dir != NULL && not_in_array(ignored_dirs, dirname, ignored_dirs_size)) {
		struct dirent *entry;

		while ((entry = readdir(dir)) != NULL) {
			int matched = switches->invert ? 1 : 0;
			int p = 0;

			switch (entry->d_type) {
			case DT_DIR:
				if (is_child(entry->d_name) &&
					not_in_array(
						ignored_dirs, entry->d_name, ignored_dirs_size)) {
					char child_path[MAXPATHLEN] = {'\0'};
					strcat(child_path, path);
					strcat(child_path, "/");
					strcat(child_path, entry->d_name);
					if (recurse_find(
							patterns, pattern_count, child_path, switches)) {
						break_early = 1;
						break;
					};
				}

				break;
			case DT_REG:
				if (excluded_extension(entry->d_name)) {
					matched = 0;
					break;
				}

				for (; p < *pattern_count; p++) {
					char *pattern = patterns[p];

					if (fnmatch(pattern, entry->d_name, 0) == 0) {
						matched = switches->invert ? 0 : 1;
					}
				}

				break;
			default:
				break;
			} /* switch */

			if (break_early) {
				closedir(dir);
				return 1;
			}

			if (matched) {
				handle_result(path, switches, entry);

				if (switches->limit > 0 &&
					++switches->count == switches->limit) {
					closedir(dir);
					return 1;
				}
			}
		} /* while */

		closedir(dir);
	} /* if */

	return 0;
}

int main(int argc, char **argv) {
	/* printing switches */
	int invert = 0;
	int limit = 0;
	int printing = 0;

	/* operating switches */
	int unlink = 0;
	char *cmd;

	int count = 0; /* used to count how matches we find */
	int ch;

	char *remainder;

	while ((ch = getopt(argc, argv, "l:vS:U")) > -1) {
		switch (ch) {
		case 'h':
			usage(NULL);
			exit(EXIT_SUCCESS);

		case 'v':
			invert = 1;
			break;

		case 'S':
			if (system(NULL) == 0) {
				fprintf(stderr, "A shell isn't available.");
				exit(EXIT_FAILURE);
			}

			cmd = optarg;

			break;

		case 'l':
			limit = strtol(optarg, &remainder, 10);

			if (limit < 0) {
				usage("Invalid limit.");
				exit(EXIT_FAILURE);
			}

			break;

		case 'U':
			unlink = 1;
			break;
		}
	}

	/* sanity check opts for conflicts */
	printing = invert + limit;
	/* int operating = unlink; */

	if (unlink == 1 && printing > 0) {
		fprintf(stderr, "Cannot use -U with any of -lv.\n");
		exit(EXIT_FAILURE);
	} else if (cmd != NULL && printing > 0) {
		fprintf(stderr, "Cannot use -S with any of -lv.\n");
		exit(EXIT_FAILURE);
	}

	if (optind < argc) {
		while (optind < argc) {
			optind++;
		}
	}

	if (optind > 1) {
		int i = 0;
		struct switches switches;
		int pattern_count = optind - 1;
		char **patterns = malloc(sizeof(char *) * optind);

		memset(patterns, '\0', optind);

		for (; i < optind; i++) {
			patterns[i] = argv[i + 1];
		}

		switches.invert = invert;
		switches.limit = limit;
		switches.count = count;
		switches.unlink = unlink;
		switches.cmd = cmd;

		if (recurse_find(patterns, &pattern_count, ".", &switches)) {
			/* finished early because we reached the limit */
		};

		free(patterns);
	} else {
		usage(NULL);
	}

	return 0;
}