├── .gitignore ├── LICENSE ├── README ├── Makefile └── canything.c /.gitignore: -------------------------------------------------------------------------------- 1 | /canything 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | public domain. 2 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # Install 2 | 3 | % make 4 | % mkdir -p ~/bin && mv canything ~/bin 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(OS),Windows_NT) 2 | LIBS=-lpdcurses -lkernel32 3 | else ifeq ($(shell uname -s),Darwin) 4 | LIBS=-lncurses 5 | else 6 | LIBS=-lncursesw 7 | endif 8 | 9 | canything: canything.c 10 | @gcc -Wall -O3 -o $@ $< ${LIBS} && echo make canything 11 | clean: 12 | @rm -f canything && echo clean canything 13 | test: canything 14 | @/bin/ls -Fa /dev | ./$< 15 | -------------------------------------------------------------------------------- /canything.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #ifdef _WIN32 8 | #include 9 | #include 10 | #else 11 | #include 12 | #include 13 | #endif 14 | 15 | #define STDBUFCOLUMMAX 512 16 | #define STDBUFLINEMAX 256 17 | #define QUERYANDMAX 10 18 | #define QUERYITEMSIZEMAX 50 19 | 20 | static void initoption(int argc, const char **argv); 21 | static void readfile(); 22 | static void inittty(const char *ttyfile, const char *ttyname); 23 | static void endtty(); 24 | static void strtolower(const char *src, char *dst); 25 | static char *istrstr(const char *src, const char *dst); 26 | static int inputloop(); 27 | 28 | char **stdbuf = NULL; 29 | int stdbufl = 0; 30 | 31 | struct Option { 32 | int ignorecase; 33 | } option = { 34 | /* ignorecase */ 0, 35 | }; 36 | 37 | static void initoption(int i, const char **argv){ 38 | while (--i) { 39 | if ('-' == *argv[i]) { 40 | if (strcmp("-i", argv[i]) == 0) { 41 | option.ignorecase = 1; 42 | } 43 | } 44 | } 45 | } 46 | 47 | static void readfile(){ 48 | int buflen = 1012; 49 | int i = 0; 50 | stdbuf = calloc(sizeof(char *), buflen); 51 | while (fgets(stdbuf[i] = malloc(STDBUFLINEMAX), STDBUFLINEMAX, stdin) != NULL) { 52 | if (buflen <= i) 53 | stdbuf = realloc(stdbuf, (buflen = 2 * buflen) * sizeof(char *)); 54 | i++; 55 | } 56 | stdbufl = i; 57 | } 58 | 59 | FILE *oldout; 60 | 61 | static void inittty(const char *ttyfile, const char *ttyname){ 62 | setlocale(LC_ALL, ""); 63 | #ifdef _WIN32 64 | HANDLE h = CreateConsoleScreenBuffer( 65 | GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 66 | NULL, CONSOLE_TEXTMODE_BUFFER, NULL); 67 | SetStdHandle(STD_OUTPUT_HANDLE, h); 68 | freopen("CONIN$", "r", stdin); 69 | newterm((char *)ttyname, stdin, stdout); 70 | #else 71 | FILE *termfd = fopen(ttyfile, "r+w"); 72 | if (isatty(1)) 73 | stdout = termfd; 74 | else 75 | setbuf(termfd, NULL); 76 | newterm((char *)ttyname, termfd, termfd); 77 | #endif 78 | raw(); 79 | noecho(); 80 | cbreak(); 81 | keypad(stdscr, TRUE); 82 | } 83 | 84 | static void endtty(){ 85 | endwin(); 86 | } 87 | 88 | static void strtolower(const char *src, char *dst){ 89 | char c; 90 | while ((c = *src++)) 91 | *dst++ = tolower(c); 92 | *dst++ = '\0'; 93 | } 94 | 95 | static char *istrstr(const char *src, const char *dst){ 96 | static char srcbuf[STDBUFCOLUMMAX]; 97 | static char dstbuf[STDBUFCOLUMMAX]; 98 | strtolower(src, srcbuf); 99 | strtolower(dst, dstbuf); 100 | char *res = strstr(srcbuf, dstbuf); 101 | return (char *)(res ? src + (res - srcbuf) : NULL); 102 | } 103 | 104 | static void printmatchline(const char *line, int selected, const char **highlights){ 105 | attr_t attr = selected ? A_STANDOUT : A_NORMAL; 106 | attron(attr); 107 | { 108 | addstr(line); 109 | int x, y; 110 | getyx(stdscr, y, x); 111 | const char *cur, *pos; 112 | while ((cur = *highlights++)) { 113 | pos = istrstr(line, cur); 114 | move(y - 1, pos - line); 115 | chgat(strlen(cur), A_UNDERLINE | attr, 0, NULL); 116 | } 117 | move(y, x); 118 | } 119 | attroff(attr); 120 | } 121 | 122 | static int inputloop(){ 123 | int key = 0; 124 | int here = 0; 125 | int curline = 0; 126 | int showlinemax = 0; 127 | int realcurline = 0; 128 | 129 | char query[QUERYANDMAX][QUERYITEMSIZEMAX]; 130 | int queryindex = 0; 131 | 132 | goto refresh; 133 | 134 | while ((key = getch())) { 135 | switch (key) { 136 | case KEY_BREAK: case '': case '': 137 | return 1; 138 | 139 | case KEY_BACKSPACE: case '': 140 | if (here > 0) { 141 | here--; 142 | query[queryindex][here] = '\0'; 143 | } else if (queryindex > 0) { 144 | here = strlen(query[--queryindex]); 145 | } 146 | curline = 0; 147 | goto refresh; 148 | 149 | case KEY_DOWN: case '': 150 | if (showlinemax-1 > curline) curline++; 151 | goto refresh; 152 | 153 | case KEY_UP: case '': 154 | if (curline > 0) curline--; 155 | goto refresh; 156 | 157 | case '': case '': 158 | here = 0; 159 | queryindex = 0; 160 | query[queryindex][here] = '\0'; 161 | goto refresh; 162 | 163 | case '': 164 | if (queryindex < 1) 165 | here = 0; 166 | else { 167 | queryindex--; 168 | here = strlen(query[queryindex]); 169 | } 170 | goto refresh; 171 | 172 | /* case KEY_STAB: */ 173 | /* goto refresh; */ 174 | 175 | case '\n': case '\r': case KEY_IL: 176 | endtty(); 177 | if (showlinemax) 178 | fputs(stdbuf[realcurline], stdout); 179 | else { 180 | int i; 181 | for (i = 0; i < queryindex; i++) 182 | fputs(query[i], stdout); 183 | } 184 | exit(0); 185 | 186 | case ' ': 187 | here = 0; 188 | queryindex++; 189 | query[queryindex][here] = '\0'; 190 | goto refresh; 191 | 192 | default: 193 | query[queryindex][here++] = key; 194 | query[queryindex][here] = '\0'; 195 | goto refresh; 196 | 197 | refresh: 198 | query[queryindex][here] = '\0'; 199 | query[queryindex+1][0] = '\0'; 200 | /* Get window size */ 201 | int winy = getmaxy(stdscr); 202 | erase(); 203 | move(1, 0); 204 | { 205 | int i = 0; 206 | int cl = 0; 207 | while (i < stdbufl) { 208 | int qi, matchflag = 1; 209 | for (qi = 0; qi <= queryindex; qi++) { 210 | if (!(option.ignorecase ? istrstr : strstr)(stdbuf[i], query[qi])) 211 | matchflag = 0; 212 | } 213 | if (matchflag) { 214 | int selected = curline == cl; 215 | if (winy < (cl + 2)) break; 216 | if (selected) realcurline = i; 217 | { 218 | const char *highlights[QUERYITEMSIZEMAX]; 219 | int qii; 220 | for (qii = 0; qii <= queryindex; qii++) 221 | highlights[qii] = query[qii]; 222 | highlights[qii] = NULL; 223 | printmatchline(stdbuf[i], selected, highlights); 224 | } 225 | cl++; 226 | } 227 | i++; 228 | } 229 | showlinemax = cl; 230 | } 231 | int pl = 0; 232 | move(0, 0); 233 | addstr(": "); 234 | pl += 1; 235 | int qi; 236 | for (qi = 0; qi <= queryindex; qi++) { 237 | addstr(query[qi]); 238 | pl += strlen(query[qi]); 239 | addstr(" "); 240 | pl += 1; 241 | } 242 | addstr("\n"); 243 | move(0, pl); 244 | refresh(); 245 | } 246 | } 247 | return 0; 248 | } 249 | 250 | int main(int argc, const char **argv){ 251 | initoption(argc, argv); 252 | readfile(); 253 | inittty("/dev/tty", getenv("TERM")); 254 | int retid = inputloop(); 255 | endtty(); 256 | return retid; 257 | } 258 | --------------------------------------------------------------------------------