hun.c (5028B)
1 #include <stdlib.h> 2 #include <unistd.h> 3 #include <termios.h> 4 #include <ctype.h> 5 #include <stdio.h> 6 #include <errno.h> 7 #include <sys/ioctl.h> 8 #include <string.h> 9 10 #define CTRL_KEY(k) ((k) & 0x1f) 11 12 #define NUN_VERSION "1" 13 14 typedef struct 15 { 16 int cx, cy; 17 struct termios termios_original; 18 int screenrows; 19 int screencols; 20 } EditorConfig; 21 22 EditorConfig ed_state; 23 24 void die(const char *s) 25 { 26 write(STDOUT_FILENO, "\x1b[2J", 4); 27 write(STDOUT_FILENO, "\x1b[H", 3); 28 29 perror(s); 30 exit(1); 31 } 32 33 void raw_mode_disable(void) 34 { 35 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &ed_state.termios_original) == -1) 36 die("tcsetattr in raw_mode_disable"); 37 } 38 39 void raw_mode_enable(void) 40 { 41 struct termios raw; 42 if (tcgetattr(STDIN_FILENO, &ed_state.termios_original) == -1) die("tcgetattr"); 43 atexit(raw_mode_disable); 44 45 raw = ed_state.termios_original; 46 raw.c_iflag &= ~(ICRNL | IXON | BRKINT | INPCK | ISTRIP); 47 raw.c_oflag &= ~(OPOST); 48 raw.c_cflag |= ~(CS8); 49 raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); 50 raw.c_cc[VMIN] = 0; 51 raw.c_cc[VTIME] = 1; 52 53 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) die("tcsetattr"); 54 } 55 56 char editor_read_key(void) 57 { 58 int nread; 59 char ch; 60 61 while ((nread = read(STDIN_FILENO, &ch, 1)) != 1) 62 { 63 if (nread == -1 && errno != EAGAIN) die("read"); 64 } 65 return ch; 66 } 67 68 int get_cursor_position(int *rows, int *cols) 69 { 70 char buf[32]; 71 unsigned int i = 0; 72 73 if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1; 74 75 while (i < sizeof(buf) - 1) 76 { 77 if (read(STDIN_FILENO, &buf[i], 1) != 1) break; 78 if (buf[i] == 'R') break; 79 i++; 80 } 81 buf[i] = '\0'; 82 83 if (buf[0] != '\x1b' || buf[1] != '[') return -1; 84 if (sscanf(&buf[2], "%d;%d", rows, cols) != 2) return -1; 85 86 return 0; 87 } 88 89 int get_window_size(int *rows, int *cols) 90 { 91 struct winsize ws; 92 93 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) 94 { 95 if (write(STDOUT_FILENO, "\x1b[999C\x1b[999B", 12) != 12) return -1; 96 return get_cursor_position(rows, cols); 97 } 98 else 99 { 100 *cols = ws.ws_col; 101 *rows = ws.ws_row; 102 return 0; 103 } 104 } 105 106 /*** append buffer ***/ 107 108 typedef struct { 109 char *b; 110 size_t len; 111 } AppendBuf; 112 113 #define ABUF_INIT {NULL, 0} 114 115 void abuf_append(AppendBuf *ab, const char *s, int len) 116 { 117 char *new = realloc(ab->b, ab->len + len); 118 119 if (new == NULL) return; 120 memcpy(&new[ab->len], s, len); 121 ab->b = new; 122 ab->len += len; 123 } 124 125 void abuf_free(AppendBuf *ab) 126 { 127 free(ab->b); 128 } 129 130 /*** output ***/ 131 132 void editor_draw_rows(AppendBuf *ab) 133 { 134 int y; 135 for (y = 0; y < ed_state.screenrows; y++) 136 { 137 if (y == ed_state.screenrows / 3) 138 { 139 char welcome[80]; 140 int welcomelen; 141 int padding; 142 welcomelen = snprintf(welcome, sizeof(welcome), "Honey Text Editor -- Version %s", NUN_VERSION); 143 if (welcomelen > ed_state.screencols) 144 welcomelen = ed_state.screencols; 145 padding = (ed_state.screencols - welcomelen) / 2; 146 if (padding) 147 { 148 abuf_append(ab, "~", 1); 149 padding--; 150 } 151 while (padding--) abuf_append(ab, " ", 1); 152 abuf_append(ab, welcome, welcomelen); 153 } 154 else 155 { 156 abuf_append(ab, "~", 1); 157 } 158 159 abuf_append(ab, "\x1b[K", 3); 160 if (y < ed_state.screenrows - 1) 161 abuf_append(ab, "\r\n", 2); 162 } 163 } 164 165 void editor_refresh_screen(void) 166 { 167 char buf[32]; 168 AppendBuf ab = ABUF_INIT; 169 170 abuf_append(&ab, "\x1b[?25l", 6); 171 abuf_append(&ab, "\x1b[H", 3); 172 173 editor_draw_rows(&ab); 174 175 snprintf(buf, sizeof(buf), "\x1b[%d;%dH", ed_state.cy + 1, ed_state.cx + 1); 176 abuf_append(&ab, buf, strlen(buf)); 177 178 abuf_append(&ab, "\x1b[?25h", 6); 179 180 write(STDOUT_FILENO, ab.b, ab.len); 181 abuf_free(&ab); 182 } 183 184 /*** input ***/ 185 186 void editor_move_cursor(char key) 187 { 188 switch (key) 189 { 190 case 'n': 191 ed_state.cy++; 192 break; 193 case 'e': 194 ed_state.cy--; 195 break; 196 case 'y': 197 ed_state.cx--; 198 break; 199 case 'o': 200 ed_state.cx++; 201 break; 202 } 203 } 204 205 void editor_process_keypress(void) 206 { 207 char ch = editor_read_key(); 208 209 switch(ch) 210 { 211 case CTRL_KEY('q'): 212 write(STDOUT_FILENO, "\x1b[2J", 4); 213 write(STDOUT_FILENO, "\x1b[H", 3); 214 exit(0); 215 break; 216 217 case 'n': 218 case 'e': 219 case 'y': 220 case 'o': 221 editor_move_cursor(ch); 222 break; 223 } 224 } 225 226 /*** init ***/ 227 228 void editor_init(void) 229 { 230 ed_state.cx = 0; 231 ed_state.cy = 0; 232 if (get_window_size(&ed_state.screenrows, &ed_state.screencols) == -1) die("get_window_size"); 233 } 234 235 int main(void) 236 { 237 raw_mode_enable(); 238 editor_init(); 239 while (1) 240 { 241 editor_refresh_screen(); 242 editor_process_keypress(); 243 } 244 return 0; 245 }