It works! Only left the filtering to have core!
[iomenu.git] / complete.c
blob24b352c1fa99815fbcf37ef672363a837bb2c0eb
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <termios.h>
6 #include <unistd.h>
8 #include "complete.h"
9 #include "config.h"
11 enum { FALSE = 0, TRUE = 1 };
13 #define LENGTH(x) (sizeof(x) / sizeof(*x))
14 #define CONTROL(char) (char ^ 0x40)
17 * Fill the buffer apropriately with the lines and headers
19 Buffer *
20 fill_buffer(char *separator)
22 /* fill buffer with string */
23 char string[LINE_SIZE];
24 Line *last = NULL;
25 Buffer *buffer = malloc(sizeof(Buffer));
26 FILE *fp = fopen("complete.c", "r");
28 if (!fp) {
29 die("Can not open file for reading.");
32 /* read the file into a doubly linked list of lines. */
33 while (fgets(string, LINE_SIZE, fp)) {
34 buffer->total++;
36 last = add_line(buffer, buffer->total, string, separator, last);
39 /* set the buffer stats. */
40 buffer->current = buffer->first;
42 return buffer;
46 * Parse the line content to determine if it is a header and identify the
47 * separator if any.
49 Line *
50 parse_line(char *s, char *separator)
52 Line *line = malloc(sizeof(Line));
53 char *sep = strstr(s, separator);
54 int pos = !sep ? (int) strlen(s) - 1 : (int) (sep - s);
56 /* strip trailing newline */
57 s[strlen(s) - 1] = '\0';
59 /* fill line->content */
60 line->content = malloc((pos + 1) * sizeof(char));
61 strncpy(line->content, s, pos);
63 /* fill line->comment */
64 if (sep) {
65 line->comment = malloc((strlen(s) - pos) * sizeof(char));
66 strcpy(line->comment, s + pos);
67 } else {
68 line->comment = "";
71 return line;
75 * Add a line to the end of the current buffer.
77 * This requires to create a new line with a link to the previous line
78 * and to NULL as the next line.
80 * The previous line's 'next' should be relinked to this new line.
82 * The header's last line have to point to this last line
84 Line *
85 add_line(Buffer *buffer, int number, char *string, char *separator, Line *prev)
87 /* allocate new line */
88 Line *line = malloc(sizeof(Line));
89 line = parse_line(string, separator);
90 line->next = NULL;
91 line->prev = NULL;
92 buffer->last = line;
93 line->number = number;
95 /* interlink with previous line if exists */
96 if (number == 1) {
97 buffer->first = line;
98 } else {
99 prev->next = line;
100 line->prev = prev;
103 return line;
107 * Set buffer->candidates to an array of lines that match and update
108 * buffer->matching to number of matching candidates.
110 void
111 filter_lines(Buffer *buffer)
113 Line * line = buffer->first;
115 buffer->matching = 0;
117 while (line) {
118 if (line_match_input(line, "test")) {
119 line->matches = TRUE;
120 buffer->matching++;
121 } else {
122 line->matches = FALSE;
125 line = line->next;
130 * Check if line matches and return TRUE if so
133 line_match_input(Line *line, char *input)
135 if (line && input /* match */) {
136 return TRUE;
137 } else {
138 return FALSE;
143 * Print a line to stderr
145 void
146 print_line(Line *line, int current)
148 /* clean the line in case it was not empty */
149 fputs("\033[K", stderr);
151 /* line number if option set */
152 if (TRUE) {
153 if (current) {
154 fputs("\033[1m", stderr);
155 } else {
156 fputs("\033[1;30m", stderr);
159 fprintf(stderr, "%7d\033[0m", line->number);
162 /* highlight current line */
163 if (current) {
164 fputs("\033[1;33m", stderr);
167 fprintf(stderr, " %-30s\033[1;30m%s", line->content, line->comment);
169 fputs("\033[0m\n", stderr);
174 * Print a header title.
176 void
177 print_header()
182 * Print all the lines from an array of pointer to lines.
184 * The total number oflines printed shall not excess 'count'.
186 void
187 print_lines(Buffer *buffer, int count, int offset)
189 Line *line = buffer->current;
190 int i = 0;
191 int j = 0;
193 /* seek back from current line to the first line to print */
194 while (line && i < count - offset) {
195 i = line->matches ? i + 1 : i;
196 line = line->prev;
198 line = line ? line : buffer->first;
200 /* print up to count lines that match the input */
201 while (line && j < count) {
202 if (line->matches) {
203 print_line(line, line == buffer->current);
204 j++;
207 line = line->next;
210 /* continue up to the end of the screen clearing it */
211 for (; j < count; j++) {
212 fputs("\r\033[K\n", stderr);
217 * Update the screen interface and print all candidates.
219 * This also has to clear the previous lines.
221 void
222 update_screen(Buffer *buffer, int count, int offset)
224 fprintf(stderr, "\n");
225 print_lines(buffer, count, offset);
227 /* go up to the prompt position and update it */
228 fprintf(stderr, "\033[%dA", count + 1);
229 print_prompt(buffer->matching, buffer->total, buffer->input);
232 void clear_screen(int count)
234 int i;
235 for (i = 0; i < count + 1; i++) {
236 fputs("\r\033[K\n", stderr);
239 fprintf(stderr, "\033[%dA", count + 1);
243 * Set terminal to send one char at a time for interactive mode, and return the
244 * last terminal state.
246 struct termios
247 terminal_set()
250 struct termios termio_old;
251 struct termios termio_new;
253 /* set the terminal to send one key at a time. */
255 /* get the terminal's state */
256 if (tcgetattr(0, &termio_old) < 0) {
257 die("Can not get terminal attributes with tcgetattr().");
260 /* create a new modified state by switching the binary flags */
261 termio_new = termio_old;
262 termio_new.c_lflag &= ~(ICANON | ECHO | IGNBRK);
264 /* apply this state to current terminal now (TCSANOW) */
265 tcsetattr(STDIN_FILENO, TCSANOW, &termio_new);
267 return termio_old;
271 * Listen for the user input and call the appropriate functions.
273 void
274 get_input(Buffer *buffer, int count, int offset)
276 /* receive one character at a time from the terminal */
277 struct termios termio_old = terminal_set();
279 /* get input char by char from the keyboard. */
280 while (do_key(getchar(), buffer)) {
281 filter_lines(buffer);
282 update_screen(buffer, count, offset);
285 /* resets the terminal to the previous state. */
286 tcsetattr(STDIN_FILENO, TCSANOW, &termio_old);
290 * Perform action associated with key
293 do_key(char key, Buffer *buffer)
295 size_t length;
296 int i;
298 switch (key) {
300 case CONTROL('C'):
301 return FALSE;
303 case CONTROL('U'):
304 buffer->input[0] = '\0';
305 break;
307 case CONTROL('W'):
308 length = strlen(buffer->input) - 1;
310 for (i = length; i >= 0 && isspace(buffer->input[i]); i--) {
311 buffer->input[i] = '\0';
314 length = strlen(buffer->input) - 1;
315 for (i = length; i >= 0 && !isspace(buffer->input[i]); i--) {
316 buffer->input[i] = '\0';
319 break;
321 case CONTROL('H'):
322 case 127: /* backspace */
323 buffer->input[strlen(buffer->input) - 1] = '\0';
324 break;
326 case CONTROL('N'):
327 if (buffer->current->next) {
328 buffer->current = buffer->current->next;
331 break;
333 case CONTROL('P'):
334 if (buffer->current->prev) {
335 buffer->current = buffer->current->prev;
337 break;
339 case CONTROL('M'):
340 case CONTROL('J'): /* enter */
341 fputs("\r\033[K", stderr);
342 puts(buffer->current->content);
343 return FALSE;
345 default:
346 if (isprint(key)) {
347 length = strlen(buffer->input);
348 buffer->input[length] = key;
349 buffer->input[length + 1] = '\0';
353 return TRUE;
357 * Print the prompt, before the input, with the number of candidates that
358 * match.
360 void
361 print_prompt(int matching, int total, char *input)
363 fprintf(stderr, "\r\033[K%5d/%-5d > %s", matching, total, input);
367 * Reset the terminal state and exit with error.
369 void die(const char *s)
371 /* tcsetattr(STDIN_FILENO, TCSANOW, &termio_old); */
372 fprintf(stderr, "%s\n", s);
373 exit(EXIT_FAILURE);
378 main(int argc, char *argv[])
380 int i;
381 Buffer *buffer = NULL;
382 char *separator = "* ";
383 int count = 30;
384 int offset = 3;
387 /* command line arguments */
388 for (i = 0; i <= argc; i++) {
389 if (argv[i][1] == '-') {
394 /* command line arguments */
395 buffer = fill_buffer(separator);
397 /* set the interface */
398 filter_lines(buffer);
399 update_screen(buffer, count, offset);
401 /* listen and interact to input */
402 get_input(buffer, count, offset);
404 clear_screen(count);
406 return 0;