/* * comcom64 - 64bit command.com * compl.c: command completion machinery * Copyright (C) 2024 @stsp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "command.h" #include "cmdbuf.h" #include "compl.h" struct compl_s { void *opaque; const char *(*get_name)(int idx, void *arg); int num; }; #define MAX_COMPLS 10 struct cmpl_s { int num; struct compl_s compls[MAX_COMPLS]; }; static int cmpstr(const char *s1, const char *s2) { int cnt = 0; while (s1[cnt] && s2[cnt] && s1[cnt] == s2[cnt]) cnt++; return cnt; } static const char *get_cmd_name(int idx, void *arg) { struct built_in_cmd *cmd = arg; assert(idx < CMD_TABLE_COUNT); return cmd[idx].cmd_name; } static const char *get_fname(int idx, void *arg) { glob_t *gl = arg; assert(idx < gl->gl_pathc); return gl->gl_pathv[idx]; } static const char *get_compl_name(int idx, void *arg) { struct cmpl_s *cmpl = arg; int i; for (i = 0; i < cmpl->num; i++) { struct compl_s *c = &cmpl->compls[i]; if (idx >= c->num) { idx -= c->num; continue; } return c->get_name(idx, c->opaque); } return NULL; } static int do_compl(const char *prefix, int print, int *r_len, char *r_p, const char *(*get)(int idx, void *arg), void *arg, int num) { int i, cnt = 0, idx = -1, len = strlen(prefix); char suff[MAX_CMD_BUFLEN] = ""; for (i = 0; i < num; i++) { const char *c = get(i, arg); /* Note: even though strncasecmp() is used, file-name completions * are still case-sensitive because the completion candidates are * added via glob() fn, which is case-sensitive. */ if (strncasecmp(prefix, c, len) == 0) { const char *p = c + len; int l = cmpstr(p, suff); strcpy(suff, p); if (cnt) suff[l] = '\0'; cnt++; idx = i; if (print) puts(c); } } if (cnt == 0) return -1; *r_len = strlen(suff); strcpy(r_p, get(idx, arg) + len); if (cnt == 1) return 1; return 0; } static void glb_add(struct cmpl_s *cmpl, glob_t *gl) { struct compl_s *c = &cmpl->compls[cmpl->num++]; c->opaque = gl; c->get_name = get_fname; c->num = gl->gl_pathc; } int compl_cmds(const char *prefix, int print, int *r_len, char *r_p) { char buf[MAXPATH]; struct cmpl_s cmpl = { }; glob_t gl_bat, gl_exe, gl_com; int err, ret = -1, cnt = 0; const char *p; const char *suff = ((p = strchr(prefix, '.')) ? "" : "*."); if (p && p[1] != '\0') return compl_fname(prefix, print, r_len, r_p); snprintf(buf, MAXPATH, "%s%sbat", prefix, suff); err = glob(buf, GLOB_ERR, NULL, &gl_bat); if (err && err != GLOB_NOMATCH) return -1; if (!err) { glb_add(&cmpl, &gl_bat); cnt += gl_bat.gl_pathc; } snprintf(buf, MAXPATH, "%s%sexe", prefix, suff); err = glob(buf, GLOB_ERR, NULL, &gl_exe); if (err && err != GLOB_NOMATCH) goto err1; if (!err) { glb_add(&cmpl, &gl_exe); cnt += gl_exe.gl_pathc; } snprintf(buf, MAXPATH, "%s%scom", prefix, suff); err = glob(buf, GLOB_ERR, NULL, &gl_com); if (err && err != GLOB_NOMATCH) goto err2; if (!err) { glb_add(&cmpl, &gl_com); cnt += gl_com.gl_pathc; } if (!p) { cmpl.compls[cmpl.num].opaque = cmd_table; cmpl.compls[cmpl.num].get_name = get_cmd_name; cmpl.compls[cmpl.num].num = CMD_TABLE_COUNT; cmpl.num++; cnt += CMD_TABLE_COUNT; } ret = do_compl(prefix, print, r_len, r_p, get_compl_name, &cmpl, cnt); globfree(&gl_com); err2: globfree(&gl_exe); err1: globfree(&gl_bat); return ret; } int compl_fname(const char *prefix, int print, int *r_len, char *r_p) { char buf[MAXPATH]; glob_t gl; int err, ret; snprintf(buf, MAXPATH, "%s*", prefix); err = glob(buf, GLOB_ERR, NULL, &gl); if (err) return -1; ret = do_compl(prefix, print, r_len, r_p, get_fname, &gl, gl.gl_pathc); globfree(&gl); return ret; }