diff --git a/fbfrender.c b/fbfrender.c index 25fee04..9e483bf 100644 --- a/fbfrender.c +++ b/fbfrender.c @@ -375,6 +375,7 @@ int fbf_render_init(const char *font_path) fprintf(stderr, "Could not load font '%s'\n", filename); } } + find_file_close(&ffs); return 0; } diff --git a/modes/shell.c b/modes/shell.c index e1a537c..8de89ab 100644 --- a/modes/shell.c +++ b/modes/shell.c @@ -144,12 +144,13 @@ static int match_error(EditBuffer *b, int start_offset, ShellError *dest); static void set_error_offset(EditBuffer *b, int offset) { - pstrcpy(error_state.buffer, sizeof(error_state.buffer), b ? b->name : ""); - error_state.offset = offset - 1; - error_state.line_num = error_state.col_num = -1; - *error_state.filename = '\0'; - error_state.msg_offset = offset; - *error_state.message = '\0'; + ShellError *sep = &error_state; + pstrcpy(sep->buffer, sizeof(sep->buffer), b ? b->name : ""); + sep->offset = offset - 1; + sep->line_num = sep->col_num = -1; + *sep->filename = '\0'; + sep->msg_offset = offset; + *sep->message = '\0'; } static QEProperty *shell_add_cwd(EditBuffer *b, int offset, const char *cwd, int force) { @@ -3960,7 +3961,6 @@ static void do_compile(EditState *s, const char *cmd) static int match_error(EditBuffer *b, int start_offset, ShellError *dest) { char filename[MAX_FILENAME_SIZE]; - char fullpath[MAX_FILENAME_SIZE]; buf_t fname[1]; int line_num, col_num, len; int offset = start_offset; @@ -4015,19 +4015,15 @@ static int match_error(EditBuffer *b, int start_offset, ShellError *dest) c = eb_nextc(b, offset, &offset); } if (dest) { - canonicalize_absolute_buffer_path(b, start_offset, - fullpath, countof(fullpath), - filename); if (line_num != dest->line_num || col_num != dest->col_num - || !strequal(fullpath, dest->filename)) { + || !strequal(filename, dest->filename)) { dest->offset = start_offset; dest->msg_offset = offset; dest->line_num = line_num; dest->col_num = col_num; - pstrcpy(dest->filename, countof(dest->filename), fullpath); - len = eb_fgets(b, dest->message, countof(dest->message), - offset, &offset); + pstrcpy(dest->filename, countof(dest->filename), filename); + len = eb_fgets(b, dest->message, countof(dest->message), offset, &offset); dest->message[len] = '\0'; /* strip the trailing newline if any */ return 2; // new error } @@ -4037,7 +4033,9 @@ static int match_error(EditBuffer *b, int start_offset, ShellError *dest) static void do_next_error(EditState *s, int arg, int dir) { + char fullname[MAX_FILENAME_SIZE]; QEmacsState *qs = s->qs; + ShellError *sep = &error_state; EditState *e; EditBuffer *b; int offset; @@ -4058,7 +4056,7 @@ static void do_next_error(EditState *s, int arg, int dir) set_error_offset(s->b, s->offset); } - if ((b = qe_find_buffer_name(qs, error_state.buffer)) == NULL) { + if ((b = qe_find_buffer_name(qs, sep->buffer)) == NULL) { if (s->b->flags & BF_ERROR) { b = s->b; } else { @@ -4080,7 +4078,7 @@ static void do_next_error(EditState *s, int arg, int dir) } /* find next/prev error */ - offset = error_state.offset; + offset = sep->offset; /* CG: should use higher level parsing */ for (;;) { @@ -4097,22 +4095,32 @@ static void do_next_error(EditState *s, int arg, int dir) } offset = eb_prev_line(b, offset); } - if (match_error(b, offset, &error_state) > 1) { + if (match_error(b, offset, sep) > 1) { // found a new error break; } } + canonicalize_absolute_buffer_path(b, sep->offset, fullname, countof(fullname), sep->filename); + if ((stat(fullname, &sb) < 0 || !S_ISREG(sb.st_mode)) + && !is_abs_path(sep->filename) && *sep->filename != '~') { + // try and locate filename in a subdirectory up to 2 levels down + char path[MAX_FILENAME_SIZE]; + FindFileState *ffs; + get_default_path(b, offset, path, countof(path)); + ffs = find_file_open(path, sep->filename, FF_NODIR | 2); + find_file_next(ffs, fullname, sizeof(fullname)); + find_file_close(&ffs); + } + /* check file and go to the error */ - if (stat(error_state.filename, &sb) < 0) { - // XXX: should prompt for fullpath as a fallback - put_error(qs->active_window, "Cannot find error in '%s': %s", - error_state.filename, strerror(errno)); + if (stat(fullname, &sb) < 0) { + put_error(s, "Cannot locate file '%s': %s", + fullname, strerror(errno)); return; } else if (!S_ISREG(sb.st_mode)) { - put_error(qs->active_window, "Cannot find error in '%s': %s", - error_state.filename, "not a regular file"); + put_error(s, "Cannot load '%s': not a regular file", fullname); return; } @@ -4134,15 +4142,15 @@ static void do_next_error(EditState *s, int arg, int dir) if (e) { /* update error window to show the error at top */ e->interactive = 0; - e->offset_top = e->offset = error_state.offset; + e->offset_top = e->offset = sep->offset; if (qs->shell_buffer_read_only) b->flags |= BF_READONLY; } /* set and update active window */ qs->active_window = s; - do_find_file(s, error_state.filename, 0); - do_goto_line(s, error_state.line_num, error_state.col_num); + do_find_file(s, fullname, 0); + do_goto_line(s, sep->line_num, sep->col_num); do_center_cursor(s, 1); #if 0 // conflicts with muscle memory (chqrlie) @@ -4151,7 +4159,7 @@ static void do_next_error(EditState *s, int arg, int dir) qe_register_transient_binding(qs, "previous-error", "M-p"); } #endif - put_status(s, "=> %s", error_state.message); + put_status(s, "=> %s", sep->message); } static int match_digits(const char32_t *buf, int n, char32_t sep) { diff --git a/qe-manual.md b/qe-manual.md index 7f109be..6ed29c2 100644 --- a/qe-manual.md +++ b/qe-manual.md @@ -2038,6 +2038,13 @@ Start a directory enumeration. * argument `pattern` a file pattern using `?` and `*` with the classic semantics used by unix shells +* argument `flags` a combination of options: +- `FF_PATH`: the `path` argument is a list of directories +- `FF_NODIR`: do not match directory names +- `FF_NOXXDIR`: do not match `.` or `..` +- `FF_ONLYDIR`: do not match non directories +- 0 ... 15: maximum subdirectory depth for recursive matching + Return a pointer to an opaque FindFileState structure. ### `int from_hex(int c);` @@ -2461,17 +2468,17 @@ and insertion sort for small chunks. The GNU lib C on linux also has a function `qsort_r()` with similar semantics but a different calling convention. -### `int qe_shell_match(const char *string, const char *pattern);` +### `int qe_shell_match(const char *str, const char *pattern);` Test whether a filename or pathname matches a shell-style pattern -* argument `string` a valid pointer to a string representing a +* argument `str` a valid pointer to a string representing a filename * argument `pattern` a valid pointer to a file pattern using `?` and `*` with the classic semantics used by unix shells -Return non zero if `string` matches `pattern`. +Return non zero if `str` matches `pattern`. Note: this function is a simplified version of POSIX function `fnmatch` defined in header `` diff --git a/qe.c b/qe.c index cd6cc1f..3f5bb5a 100644 --- a/qe.c +++ b/qe.c @@ -9078,7 +9078,7 @@ void qe_kill_buffer(QEmacsState *qs, EditBuffer *b) } /* return TRUE if absolute path. works for files and URLs */ -static int is_abs_path(const char *path) +int is_abs_path(const char *path) { size_t prefix; diff --git a/qe.h b/qe.h index f941aa1..2a733ee 100644 --- a/qe.h +++ b/qe.h @@ -134,6 +134,7 @@ struct CompleteState { char current[MAX_FILENAME_SIZE]; }; +int is_abs_path(const char *path); void canonicalize_absolute_path(EditState *s, char *buf, int buf_size, const char *path1); void canonicalize_absolute_buffer_path(EditBuffer *b, int offset, char *buf, int buf_size, diff --git a/util.c b/util.c index ec02591..e8362cb 100644 --- a/util.c +++ b/util.c @@ -35,14 +35,14 @@ #include "config.h" /* for CONFIG_WIN32 */ #include "util.h" -int qe_shell_match(const char *string, const char *pattern) { +int qe_shell_match(const char *str, const char *pattern) { /*@API utils Test whether a filename or pathname matches a shell-style pattern - @argument `string` a valid pointer to a string representing a + @argument `str` a valid pointer to a string representing a filename @argument `pattern` a valid pointer to a file pattern using `?` and `*` with the classic semantics used by unix shells - @return non zero if `string` matches `pattern`. + @return non zero if `str` matches `pattern`. @note this function is a simplified version of POSIX function `fnmatch` defined in header `` */ @@ -51,22 +51,22 @@ int qe_shell_match(const char *string, const char *pattern) { if (c == '*') { if (*pattern == '\0') return 1; - while (*string) { - if (qe_shell_match(string, pattern)) + while (*str) { + if (qe_shell_match(str, pattern)) return 1; - string++; + str++; } return 0; } else if (c == '?') { - if (*string++ == '\0') + if (*str++ == '\0') return 0; } else - if (c != *string++) { + if (c != *str++) { return 0; } } - return *string == '\0'; + return *str == '\0'; } #define MAX_FILENAME_SIZE 1024 /* Size for a filename buffer */ @@ -78,6 +78,7 @@ struct FindFileState { const char *bufptr; int flags; int depth; + size_t dirlen; DIR *dir; DIR *parent_dir[FF_DEPTH]; size_t parent_len[FF_DEPTH]; @@ -89,19 +90,26 @@ FindFileState *find_file_open(const char *path, const char *pattern, int flags) @argument `path` the initial directory for the enumeration. @argument `pattern` a file pattern using `?` and `*` with the classic semantics used by unix shells + @argument `flags` a combination of options: + - `FF_PATH`: the `path` argument is a list of directories + - `FF_NODIR`: do not match directory names + - `FF_NOXXDIR`: do not match `.` or `..` + - `FF_ONLYDIR`: do not match non directories + - 0 ... 15: maximum subdirectory depth for recursive matching @return a pointer to an opaque FindFileState structure. */ - // XXX: should check if pattern has wildcards FindFileState *s; s = qe_mallocz(FindFileState); - if (!s) - return NULL; - pstrcpy(s->path, sizeof(s->path), path); - pstrcpy(s->pattern, sizeof(s->pattern), pattern); - s->bufptr = s->path; - s->dir = NULL; - s->flags = flags; + if (s) { + pstrcpy(s->path, sizeof(s->path), path); + pstrcpy(s->pattern, sizeof(s->pattern), pattern); + s->bufptr = s->path; + s->dir = NULL; + s->flags = flags; + if (!s->pattern[strcspn(s->pattern, "*?")]) + s->flags |= FF_NOPAT; + } return s; } @@ -113,7 +121,7 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) { array in bytes. @return `0` if there is a match, `-1` if no more files match the pattern. */ - // XXX: should match wildcards in directory names + struct stat st; struct dirent *dirent; const char *p; @@ -127,7 +135,8 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) { s->depth--; s->dir = s->parent_dir[s->depth]; s->parent_dir[s->depth] = NULL; - s->dirpath[s->parent_len[s->depth]] = '\0'; + s->dirlen = s->parent_len[s->depth]; + s->dirpath[s->dirlen] = '\0'; continue; } p = s->bufptr; @@ -137,10 +146,25 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) { p += strcspn(p, ":"); else p += strlen(p); - pstrncpy(s->dirpath, sizeof(s->dirpath), s->bufptr, p - s->bufptr); + pstrncpy(s->dirpath, countof(s->dirpath), s->bufptr, p - s->bufptr); if (*p == ':') p++; s->bufptr = p; + new_dir: + s->dirlen = strlen(s->dirpath); + if (s->flags & FF_NOPAT) { + makepath(s->dirpath, countof(s->dirpath), s->dirpath, s->pattern); + if (!stat(s->dirpath, &st)) { + if ((S_ISDIR(st.st_mode) && !(s->flags & FF_NODIR)) + || (S_ISREG(st.st_mode) && !(s->flags & FF_ONLYDIR))) { + pstrcpy(filename, filename_size_max, s->dirpath); + return 0; + } + } + s->dirpath[s->dirlen] = '\0'; + if (!(s->flags & FF_DEPTH)) + continue; + } s->dir = opendir(s->dirpath); } else { int isdir = 0; @@ -160,11 +184,11 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) { } else { if (s->depth < (s->flags & FF_DEPTH)) { s->parent_dir[s->depth] = s->dir; - s->parent_len[s->depth] = strlen(s->dirpath); + s->parent_len[s->depth] = s->dirlen; s->depth++; - makepath(s->dirpath, sizeof(s->dirpath), s->dirpath, dirent->d_name); - s->dir = opendir(s->dirpath); - continue; + s->dir = NULL; + makepath(s->dirpath, countof(s->dirpath), s->dirpath, dirent->d_name); + goto new_dir; } } if (s->flags & FF_NODIR) @@ -173,6 +197,8 @@ int find_file_next(FindFileState *s, char *filename, int filename_size_max) { if (s->flags & FF_ONLYDIR) continue; } + if (s->flags & FF_NOPAT) + continue; if (qe_shell_match(dirent->d_name, s->pattern)) { makepath(filename, filename_size_max, s->dirpath, dirent->d_name); diff --git a/util.h b/util.h index 52513e5..78cc7de 100644 --- a/util.h +++ b/util.h @@ -53,6 +53,7 @@ typedef struct FindFileState FindFileState; #define FF_NODIR 0x020 /* only match regular files */ #define FF_NOXXDIR 0x040 /* do not match . or .. */ #define FF_ONLYDIR 0x080 /* do not match non directories */ +#define FF_NOPAT 0x100 /* do not use shell matching */ #define FF_DEPTH 0x00f /* max recursion depth */ FindFileState *find_file_open(const char *path, const char *pattern, int flags);