C-Menu
0.2.9
A User Interface Toolkit
Toggle main menu visibility
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
24
const
char
*
argp_program_version
=
CM_VERSION
;
25
const
char
*
argp_program_bug_address
=
"billxwaller@gmail.com"
;
26
const
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
31
static
char
args_doc[] =
"[DIRECTORY] [REGULAR_EXPRESSION]"
;
32
33
static
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
44
struct
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
54
static
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
}
119
static
struct
argp argp = {options, parse_opt, args_doc,
doc
,
120
nullptr
,
nullptr
,
nullptr
};
121
122
int
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
@ FT_UNKNOWN
Definition
cm.h:164
FT_SOCK
@ FT_SOCK
Definition
cm.h:163
FT_DIR
@ FT_DIR
Definition
cm.h:159
FT_FIFO
@ FT_FIFO
Definition
cm.h:160
FT_REG
@ FT_REG
Definition
cm.h:162
FT_CHR
@ FT_CHR
Definition
cm.h:158
FT_BLK
@ FT_BLK
Definition
cm.h:157
FT_LNK
@ FT_LNK
Definition
cm.h:161
LF_EXC_REGEX
@ LF_EXC_REGEX
Definition
cm.h:151
LF_HIDE
@ LF_HIDE
Definition
cm.h:149
LF_ICASE
@ LF_ICASE
Definition
cm.h:150
LF_REGEX
@ LF_REGEX
Definition
cm.h:152
nullptr
#define nullptr
Definition
cm.h:23
CM_VERSION
#define CM_VERSION
Definition
version.h:7
MAXLEN
#define MAXLEN
Definition
curskeys.c:15
doc
const char doc[]
Definition
lf.c:26
argp_program_version
const char * argp_program_version
Definition
init.c:83
argp_program_bug_address
const char * argp_program_bug_address
Definition
init.c:84
main
int main(int argc, char **argv)
Capture the current terminal settings for later restoration.
Definition
enterchr.c:38
strnz__cpy
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition
futil.c:269
is_directory
bool is_directory(const char *)
Checks if the given path is a directory.
Definition
futil.c:1245
lf_find
bool lf_find(const char *, const char *, const char *, int, int)
Find files in a directory matching a regular expression.
Definition
futil.c:977
is_valid_regex
bool is_valid_regex(const char *)
Checks if the given regular expression pattern is valid.
Definition
futil.c:1256
lf.c
Generated by
1.17.0