/* * All rights reserved, copyright (c) 2006, Mitzyuki IMAIZUMI * $Id: popd.c,v 1.1 2006/05/16 05:25:10 mitz Exp $ */ #include #include #include #include #include #include #include #include #include #define CURDIR "." #define DOWN (rtn == 1 && buf[0] == 'j') #define UP (rtn == 1 && buf[0] == 'k') #define RETURN (rtn == 1 && buf[0] == '\n') #define ESCAPE (rtn == 1 && buf[0] == 0x1b) #define SEARCH (rtn == 1 && buf[0] == '/') #define FORWARD (rtn == 1 && buf[0] == 'n') #define BACKWARD (rtn == 1 && buf[0] == 'N') #define back(i) if(i) fprintf(tty, "\033[%dD", i) typedef struct dirent_t{ char *name; struct dirent_t *chain[2]; /* 0: backword / 1: forward */ } dirent_t; /* * 端末の初期化 */ FILE *initial(struct termios *save) { FILE *fp = NULL; struct termios term; if((fp = fopen("/dev/tty", "r+"))){ tcgetattr(fileno(fp), &term); *save = term; term.c_lflag &= ~ICANON; term.c_lflag &= ~ECHO; term.c_cc[VMIN] = 1; term.c_cc[VTIME] = 1; tcsetattr(fileno(fp), TCSANOW, &term); setbuf(fp, NULL); } return(fp); } /* * 終了処理 */ int final(FILE *fp, char *dir, struct termios *save, char *msg, ...) { /* 端末へのメッセージ出力 */ if(msg){ va_list ap; va_start(ap, msg); vfprintf(fp, msg, ap); va_end(ap); } /* 端末モードの復帰 */ if(fp && save) tcsetattr(fileno(fp), TCSANOW, save); /* 移動先ディレクトリの表示 */ printf("%s\n", dir); return(0); } /* * ディレクトリリストを双方向チェーンに追加 * **start を利用する事により開始ノードのチェックを不要とする * 先頭/末尾のチェーンは自己参照とし NULL チェックを省略可とする */ dirent_t *insert_node(char *file, dirent_t **start) { char buf[1024]; FILE *fp; dirent_t **p = start, *q = NULL, *r = NULL; if((fp = fopen(file, "r"))){ while(fgets(buf, sizeof(buf), fp)){ r = (dirent_t *)malloc(sizeof(dirent_t)); r->name = strdup(strtok(buf, "\n")); r->chain[0] = q; r->chain[1] = r; q = *p = r; p = &(r->chain[1]); } fclose(fp); (*start)->chain[0] = *start; } return(r); } /* * 正規表現による検索処理 */ dirent_t *regexp(dirent_t *cur, int direction, int len, regex_t *preg) { while(cur != cur->chain[direction]) if((cur = cur->chain[direction])) if(!regexec(preg, cur->name, 0, NULL, 0)) return(cur); return(NULL); } /* * リニアサーチによる検索処理 */ dirent_t *linear(dirent_t *cur, int direction, int len, regex_t *preg) { return(len ? cur->chain[direction] : cur); } /* * 検索パターンの取得処理 */ int getpattern(FILE *tty, char *pattern, size_t size) { int len = -1; while(1){ read(fileno(tty), &pattern[++len], size); /* Backspace */ if(pattern[len] == '\b'){ pattern[len--] = '\0'; if(len >= 0){ pattern[len--] = '\0'; back(1); fprintf(tty, "\033[K"); } } /* Escape */ else if(pattern[len] == 0x1b){ back(len); /* 入力データを消去 */ return(0); } /* Enter */ else if(pattern[len] == '\n'){ pattern[len] = '\0'; /* '\n' 自体は削除 */ return(len); } else putc(pattern[len], tty); } } int main(int argc, char *argv[]) { int rtn, len = 0, direction = 0; char buf[1024], pattern[1024]; FILE *tty; regex_t preg; struct termios save; dirent_t *cur = NULL, *start = NULL, *(*search)(); /* ディレクトリリストの作成 */ if(!(cur = insert_node(argv[1], &start))) exit(final(NULL, CURDIR, NULL, NULL)); /* 端末の初期化 */ if(!(tty = initial(&save))) exit(final(NULL, CURDIR, NULL, NULL)); /* 検索文字が指定されている場合は検索モードで開始 */ if(argc == 3){ regcomp(&preg, argv[2], REG_EXTENDED); search = regexp; strcpy(pattern, argv[2]); } else{ regcomp(&preg, ".*", REG_EXTENDED); search = linear; } /* メインループ */ while(1){ back(len); if((cur = (*search)(cur, direction, len, &preg))) len = fprintf(tty, "\033[0K%s", cur->name) - 4; else exit(final(tty, CURDIR, &save, "\033[0K%s : No mach\n", pattern)); rtn = read(fileno(tty), buf, sizeof(buf)); if(UP){ direction = 0; search = linear; } else if(DOWN){ direction = 1; search = linear; } else if(FORWARD){ direction = 0; search = regexp; } else if(BACKWARD){ direction = 1; search = regexp; } else if(SEARCH){ back(len); fprintf(tty, "\033[0K/"); if((len = getpattern(tty, pattern, sizeof(pattern)))){ len++; /* '/' 分 */ regcomp(&preg, pattern, REG_EXTENDED); search = regexp; } else{ back(1); /* '/' のみ削除 */ regcomp(&preg, ".*", REG_EXTENDED); search = linear; } direction = 0; } else if(RETURN) exit(final(tty, cur->name, &save, "\n")); else if(ESCAPE) exit(final(tty, CURDIR, &save, "\n")); } }