C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
lf.c
Go to the documentation of this file.
1/** @file lf.c
2 @brief list files matching a regular expression
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10#include <argp.h>
11#include <cm.h>
12#include <dirent.h>
13#include <fcntl.h>
14#include <grp.h>
15#include <pwd.h>
16#include <regex.h>
17#include <stdbool.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <unistd.h>
23
25const char *argp_program_bug_address = "billxwaller@gmail.com";
26const char doc[] = "lf list files\vIf specified, DIRECTORY is the top-level "
27 "directory to search. REGULAR_EXPRESSION is a properly "
28 "formatted regular expression for which matching files "
29 "will be listed.";
30
31static char args_doc[] = "[DIRECTORY] [REGULAR_EXPRESSION]";
32
33static struct argp_option options[] = {
34 {"max_depth", 'd', "number", 0, "Depth into directory", 0},
35 {"exclude", 'e', "regex", 0, "Exclude regular expression", 0},
36 {"f_ignore_case", 'i', "bool", 0, "Search ignore case", 0},
37 {"f_hide", 'n', 0, 0, "Don't list hidden files", 0},
38 {"file_types", 't', "bcdplfsu", 0,
39 "b - block device, c - character device, d - directory, p - named pipe, "
40 "l - symbolic link, f - regular file, s - socket, u - unknown",
41 0},
42 {0}};
43
44struct lf {
45 int max_depth;
46 char *exclude;
47 bool f_ignore_case;
48 int flags;
49 char *file_types_p;
50 char *args[2];
51 int argc;
52};
53
54static error_t parse_opt(int key, char *arg, struct argp_state *state) {
55 struct lf *lf = state->input;
56 int i = 0;
57 switch (key) {
58 case 'd':
59 lf->max_depth = atoi(arg);
60 break;
61 case 'e':
62 lf->exclude = arg;
63 lf->flags |= LF_EXC_REGEX;
64 break;
65 case 'i':
66 lf->flags |= LF_ICASE;
67 break;
68 case 'n':
69 lf->flags |= LF_HIDE;
70 break;
71 case 't':
72 lf->file_types_p = arg;
73 while (lf->file_types_p[i]) {
74 switch (lf->file_types_p[i++]) {
75 case 'b':
76 lf->flags |= FT_BLK << 8;
77 break;
78 case 'c':
79 lf->flags |= FT_CHR << 8;
80 break;
81 case 'd':
82 lf->flags |= FT_DIR << 8;
83 break;
84 case 'p':
85 lf->flags |= FT_FIFO << 8;
86 break;
87 case 'l':
88 lf->flags |= FT_LNK << 8;
89 break;
90 case 'f':
91 lf->flags |= FT_REG << 8;
92 break;
93 case 's':
94 lf->flags |= FT_SOCK << 8;
95 break;
96 case 'u':
97 lf->flags |= FT_UNKNOWN << 8;
98 break;
99 default:
100 break;
101 }
102 }
103 break;
104 case ARGP_KEY_ARG:
105 if (state->arg_num == 0 || state->arg_num == 1) {
106 lf->args[state->arg_num] = arg;
107 lf->argc = state->arg_num + 1;
108 } else {
109 argp_usage(state);
110 }
111 break;
112 case ARGP_KEY_END:
113 break;
114 default:
115 return ARGP_ERR_UNKNOWN;
116 }
117 return 0;
118}
119static struct argp argp = {options, parse_opt, args_doc, doc,
121
122int main(int argc, char **argv) {
123 char dir[MAXLEN];
124 char re[MAXLEN];
125 dir[0] = '\0';
126 re[0] = '\0';
127
128 struct lf lf = {0};
129 lf.max_depth = 0;
130 lf.exclude = nullptr;
131 lf.f_ignore_case = false;
132 lf.file_types_p = 0;
133 lf.args[0] = nullptr;
134 lf.args[1] = nullptr;
135 argp_parse(&argp, argc, argv, 0, 0, &lf);
136 if (lf.argc > 0) {
137 if (is_directory(lf.args[0])) {
138 strnz__cpy(dir, lf.args[0], MAXLEN - 1);
139 } else {
140 if (is_valid_regex(lf.args[0])) {
141 strnz__cpy(re, lf.args[0], MAXLEN - 1);
142 lf.flags |= LF_REGEX;
143 } else {
144 printf("arg1: '%s' is neither a directory nor a valid regex.\n",
145 lf.args[0]);
146 }
147 }
148 }
149 if (lf.argc > 1) {
150 if (dir[0] == '\0' && is_directory(lf.args[1])) {
151 strnz__cpy(dir, lf.args[1], MAXLEN - 1);
152 } else {
153 if (re[0] == '\0' && is_valid_regex(lf.args[1])) {
154 strnz__cpy(re, lf.args[1], MAXLEN - 1);
155 lf.flags |= LF_REGEX;
156 } else {
157 printf("arg2: '%s' is neither a directory nor a valid regex.\n",
158 lf.args[1]);
159 }
160 }
161 }
162 if (dir[0] == '\0')
163 strncpy(dir, ".", MAXLEN - 1);
164 lf_find(dir, re, lf.exclude, lf.max_depth, lf.flags);
165 return 0;
166}
@ FT_UNKNOWN
Definition cm.h:164
@ FT_SOCK
Definition cm.h:163
@ FT_DIR
Definition cm.h:159
@ FT_FIFO
Definition cm.h:160
@ FT_REG
Definition cm.h:162
@ FT_CHR
Definition cm.h:158
@ FT_BLK
Definition cm.h:157
@ FT_LNK
Definition cm.h:161
@ LF_EXC_REGEX
Definition cm.h:151
@ LF_HIDE
Definition cm.h:149
@ LF_ICASE
Definition cm.h:150
@ LF_REGEX
Definition cm.h:152
#define nullptr
Definition cm.h:23
#define CM_VERSION
Definition version.h:7
#define MAXLEN
Definition curskeys.c:15
const char doc[]
Definition lf.c:26
const char * argp_program_version
Definition init.c:83
const char * argp_program_bug_address
Definition init.c:84
int main(int argc, char **argv)
Capture the current terminal settings for later restoration.
Definition enterchr.c:38
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:269
bool is_directory(const char *)
Checks if the given path is a directory.
Definition futil.c:1245
bool lf_find(const char *, const char *, const char *, int, int)
Find files in a directory matching a regular expression.
Definition futil.c:977
bool is_valid_regex(const char *)
Checks if the given regular expression pattern is valid.
Definition futil.c:1256