mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-09-10 02:36:32 +00:00
system/grep: Add grep command with regex support
Add a standalone grep application under apps/system/grep/. It uses the NuttX libc regex library (regcomp/regexec) to support -i, -n, -v, -r, -H and -h options plus stdin input and recursive directory search. SYSTEM_GREP depends on LIBC_REGEX, so the regex library must be enabled first -- CONFIG_SYSTEM_GREP only becomes selectable once LIBC_REGEX is turned on. Signed-off-by: Junbo Zheng <zhengjunbo1@xiaomi.com>
This commit is contained in:
parent
5a7ab4200d
commit
46b8774f7f
5 changed files with 434 additions and 0 deletions
36
system/grep/CMakeLists.txt
Normal file
36
system/grep/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# ##############################################################################
|
||||
# apps/system/grep/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with this work for
|
||||
# additional information regarding copyright ownership. The ASF licenses this
|
||||
# file to you under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
if(CONFIG_SYSTEM_GREP)
|
||||
nuttx_add_application(
|
||||
MODULE
|
||||
${CONFIG_SYSTEM_GREP}
|
||||
NAME
|
||||
${CONFIG_SYSTEM_GREP_PROGNAME}
|
||||
STACKSIZE
|
||||
${CONFIG_SYSTEM_GREP_STACKSIZE}
|
||||
PRIORITY
|
||||
${CONFIG_SYSTEM_GREP_PRIORITY}
|
||||
SRCS
|
||||
grep_main.c)
|
||||
|
||||
endif()
|
||||
30
system/grep/Kconfig
Normal file
30
system/grep/Kconfig
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config SYSTEM_GREP
|
||||
tristate "system 'grep' command"
|
||||
default n
|
||||
depends on LIBC_REGEX
|
||||
---help---
|
||||
Enable support for the system 'grep' command (regex-based search).
|
||||
|
||||
if SYSTEM_GREP
|
||||
|
||||
config SYSTEM_GREP_PROGNAME
|
||||
string "grep program name"
|
||||
default "grep"
|
||||
---help---
|
||||
This is the name of the program that will be used when the grep
|
||||
program is installed.
|
||||
|
||||
config SYSTEM_GREP_PRIORITY
|
||||
int "grep task priority"
|
||||
default 100
|
||||
|
||||
config SYSTEM_GREP_STACKSIZE
|
||||
int "grep stack size"
|
||||
default DEFAULT_TASK_STACKSIZE
|
||||
|
||||
endif
|
||||
25
system/grep/Make.defs
Normal file
25
system/grep/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/system/grep/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_SYSTEM_GREP),)
|
||||
CONFIGURED_APPS += $(APPDIR)/system/grep
|
||||
endif
|
||||
36
system/grep/Makefile
Normal file
36
system/grep/Makefile
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
############################################################################
|
||||
# apps/system/grep/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
# Standalone grep command
|
||||
|
||||
PROGNAME = $(CONFIG_SYSTEM_GREP_PROGNAME)
|
||||
PRIORITY = $(CONFIG_SYSTEM_GREP_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_SYSTEM_GREP_STACKSIZE)
|
||||
MODULE = $(CONFIG_SYSTEM_GREP)
|
||||
|
||||
# Files
|
||||
|
||||
MAINSRC = grep_main.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
307
system/grep/grep_main.c
Normal file
307
system/grep/grep_main.c
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/****************************************************************************
|
||||
* apps/system/grep/grep_main.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
#include <limits.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define GREP_F_INVERT (1u << 0) /* -v: invert match */
|
||||
#define GREP_F_ICASE (1u << 1) /* -i: case-insensitive */
|
||||
#define GREP_F_LINENO (1u << 2) /* -n: print line number */
|
||||
#define GREP_F_RECURSE (1u << 3) /* -r: recurse into directories */
|
||||
#define GREP_F_NAME (1u << 4) /* -H: always print filename */
|
||||
#define GREP_F_NONAME (1u << 5) /* -h: never print filename */
|
||||
|
||||
#define GREP_LINE_MAX 512
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static int grep_stream(FILE *in, FAR const char *label,
|
||||
FAR const regex_t *re, unsigned int flags)
|
||||
{
|
||||
char line[GREP_LINE_MAX];
|
||||
unsigned long lineno = 0;
|
||||
int matched = 0;
|
||||
|
||||
while (fgets(line, sizeof(line), in) != NULL)
|
||||
{
|
||||
size_t n = strlen(line);
|
||||
int hit;
|
||||
|
||||
if (n > 0 && line[n - 1] == '\n')
|
||||
{
|
||||
line[n - 1] = '\0';
|
||||
}
|
||||
|
||||
lineno++;
|
||||
|
||||
hit = (regexec(re, line, 0, NULL, 0) == 0);
|
||||
if (flags & GREP_F_INVERT)
|
||||
{
|
||||
hit = !hit;
|
||||
}
|
||||
|
||||
if (hit)
|
||||
{
|
||||
matched = 1;
|
||||
if (label != NULL && !(flags & GREP_F_NONAME))
|
||||
{
|
||||
printf("%s:", label);
|
||||
}
|
||||
|
||||
if (flags & GREP_F_LINENO)
|
||||
{
|
||||
printf("%lu:", lineno);
|
||||
}
|
||||
|
||||
printf("%s\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
return matched ? 0 : 1;
|
||||
}
|
||||
|
||||
static int grep_file(FAR const char *path, FAR const regex_t *re,
|
||||
unsigned int flags, int multi)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
FAR const char *label = (multi || (flags & GREP_F_NAME)) ? path : NULL;
|
||||
int rc;
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
fprintf(stderr, "grep: %s: %s\n", path, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
rc = grep_stream(f, label, re, flags);
|
||||
fclose(f);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int grep_dir(FAR const char *path, FAR const regex_t *re,
|
||||
unsigned int flags, int multi)
|
||||
{
|
||||
FAR DIR *d = opendir(path);
|
||||
char full[PATH_MAX];
|
||||
int rc = 1;
|
||||
FAR struct dirent *e;
|
||||
|
||||
if (d == NULL)
|
||||
{
|
||||
fprintf(stderr, "grep: %s: %s\n", path, strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
while ((e = readdir(d)) != NULL)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(full, sizeof(full), "%s/%s", path, e->d_name);
|
||||
|
||||
if (stat(full, &st) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
{
|
||||
if (flags & GREP_F_RECURSE)
|
||||
{
|
||||
int r = grep_dir(full, re, flags, multi);
|
||||
|
||||
if (r == 0)
|
||||
{
|
||||
rc = 0;
|
||||
}
|
||||
else if (r == 2 && rc != 0)
|
||||
{
|
||||
rc = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (S_ISREG(st.st_mode))
|
||||
{
|
||||
int r = grep_file(full, re, flags, multi);
|
||||
|
||||
if (r == 0)
|
||||
{
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, FAR char **argv)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
FAR const char *pattern;
|
||||
int idx;
|
||||
int cflags = REG_NOSUB;
|
||||
int nfiles;
|
||||
int multi;
|
||||
int rc = 1;
|
||||
regex_t re;
|
||||
int err;
|
||||
|
||||
for (idx = 1; idx < argc; idx++)
|
||||
{
|
||||
FAR const char *p;
|
||||
|
||||
if (argv[idx][0] != '-' || argv[idx][1] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (p = argv[idx] + 1; *p != '\0'; p++)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case 'i':
|
||||
flags |= GREP_F_ICASE;
|
||||
break;
|
||||
case 'v':
|
||||
flags |= GREP_F_INVERT;
|
||||
break;
|
||||
case 'n':
|
||||
flags |= GREP_F_LINENO;
|
||||
break;
|
||||
case 'r':
|
||||
flags |= GREP_F_RECURSE;
|
||||
break;
|
||||
case 'H':
|
||||
flags |= GREP_F_NAME;
|
||||
break;
|
||||
case 'h':
|
||||
flags |= GREP_F_NONAME;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "grep: invalid option -%c\n", *p);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idx >= argc)
|
||||
{
|
||||
fprintf(stderr, "usage: grep [-invrHh] <pattern> [<file>...]\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
pattern = argv[idx++];
|
||||
|
||||
if (flags & GREP_F_ICASE)
|
||||
{
|
||||
cflags |= REG_ICASE;
|
||||
}
|
||||
|
||||
err = regcomp(&re, pattern, cflags);
|
||||
if (err != 0)
|
||||
{
|
||||
char errbuf[128];
|
||||
|
||||
regerror(err, &re, errbuf, sizeof(errbuf));
|
||||
fprintf(stderr, "grep: %s\n", errbuf);
|
||||
return 2;
|
||||
}
|
||||
|
||||
nfiles = argc - idx;
|
||||
multi = (nfiles > 1) || (flags & GREP_F_RECURSE);
|
||||
|
||||
if (nfiles == 0)
|
||||
{
|
||||
rc = grep_stream(stdin, NULL, &re, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; idx < argc; idx++)
|
||||
{
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
if (stat(argv[idx], &st) != 0)
|
||||
{
|
||||
fprintf(stderr, "grep: %s: %s\n", argv[idx], strerror(errno));
|
||||
rc = 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
{
|
||||
if (flags & GREP_F_RECURSE)
|
||||
{
|
||||
r = grep_dir(argv[idx], &re, flags, multi);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "grep: %s: Is a directory\n", argv[idx]);
|
||||
r = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r = grep_file(argv[idx], &re, flags, multi);
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
{
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
regfree(&re);
|
||||
return rc;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue