Fixed line drawing
[iomenu.git] / buffer.c
blob312a177dad6529ddca2e1f87205e617588f55f4f
1 #include <ctype.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
6 #include "main.h"
9 /*
10 * Fill the buffer apropriately with the lines and headers.
12 Buffer *
13 fill_buffer(char *separator)
15 /* fill buffer with string */
16 char s[LINE_SIZE];
17 Buffer *buffer = malloc(sizeof(Buffer));
18 FILE *fp = stdin;
20 if (!fp)
21 die("Can not open file for reading.");
23 buffer->input[0] = '\0';
24 buffer->total = buffer->matching = 0;
26 /* empty line in case no line come from stdin */
27 buffer->first = buffer->current = buffer->last = malloc(sizeof(Line));
28 buffer->last->content = buffer->last->comment = "";
29 buffer->last->next = buffer->last->prev = NULL;
31 /* read the file into a doubly linked list of lines */
32 while (fgets(s, LINE_SIZE, fp))
33 buffer->last = add_line( buffer, ++buffer->total, s, separator,
34 buffer->last);
36 /* prevent initial current line to be a header */
37 buffer->current = buffer->first;
38 while (buffer->current->next && buffer->current->header)
39 buffer->current = buffer->current->next;
41 return buffer;
46 * Add a line to the end of the current buffer.
48 Line *
49 add_line(Buffer *buffer, int number, char *s, char *separator, Line *prev)
51 /* allocate new line */
52 buffer->last = parse_line(s, separator);
53 buffer->last->number = number;
54 buffer->last->matches = 1; /* matches by default */
55 buffer->matching++;
57 /* interlink with previous line if exists */
58 if (number == 1) {
59 buffer->first = buffer->last;
60 } else {
61 prev->next = buffer->last;
62 buffer->last->prev = prev;
65 return buffer->last;
70 * Parse the line content to determine if it is a header and identify the
71 * separator if any.
73 Line *
74 parse_line(char *s, char *separator)
76 Line *line = malloc(sizeof(Line));
77 char *sep = separator ? strstr(s, separator) : NULL;
78 int pos = sep ? (int) (sep - s) : (int) strlen(s) - 1;
80 /* header is when separator is the first character of the line */
81 line->header = (sep == s);
83 /* strip trailing newline */
84 s[strlen(s) - 1] = '\0';
86 /* fill line->content */
87 line->content = malloc((pos + 1) * sizeof(char));
88 strncpy(line->content, s, pos);
90 /* fill line->comment */
91 line->comment = "";
92 if (sep) {
93 line->comment = malloc((strlen(s) - pos) * sizeof(char));
94 strcpy(line->comment, s + pos + strlen(separator));
97 /* strip trailing whitespaces from line->content */
98 for (pos--; pos > 0 && isspace(line->content[pos]); pos--)
99 line->content[pos] = '\0';
101 /* strip leading whitespaces from line->comment */
102 for (pos = 0; isspace(line->comment[pos]); pos++);
103 line->comment += pos;
105 return line;
110 * Free the buffer, also recursing the doubly linked list.
112 void
113 free_buffer(Buffer *buffer)
115 while (buffer->first) {
117 buffer->first->content = NULL;
118 free(buffer->first->content);
120 buffer->first->comment = NULL;
121 free(buffer->first->comment);
123 free(buffer->first);
125 buffer->first = buffer->first->next;
128 free(buffer);
133 * Set the line->matching state according to the return value of match_line,
134 * and buffer->matching to number of matching candidates.
136 * The incremental parameter sets whether check already matching or
137 * non-matching lines only. This is for performance concerns.
139 void
140 filter_lines(Buffer *buffer, int inc)
142 Line *line = buffer->first;
143 char **tokv = NULL;
144 char *s, buf[sizeof buffer->input];
145 size_t n = 0, tokc = 0;
147 /* tokenize input from space characters, this comes from dmenu */
148 strcpy(buf, buffer->input);
149 for (s = strtok(buf, " "); s; s = strtok(NULL, " ")) {
150 if (++tokc > n && !(tokv = realloc(tokv, ++n * sizeof(*tokv))))
151 die("cannot realloc memory for tokv\n");
153 tokv[tokc - 1] = s;
156 /* match lines */
157 buffer->matching = 0;
158 while (line) {
159 if (buffer->input[0] && !strcmp(buffer->input, line->content)) {
160 line->matches = 1;
161 buffer->current = line;
162 } else if ((inc && line->matches) || (!inc && !line->matches)) {
163 line->matches = match_line(line, tokv, tokc);
164 buffer->matching += line->matches;
167 line = line->next;
173 * Return whecher the line matches every string from tokv.
176 match_line(Line *line, char **tokv, size_t tokc)
178 size_t i, match = 1, offset = 0;
180 if (line->header)
181 return 1;
183 for (i = 0; i < tokc && match; i++)
184 match = !!strstr(line->content + offset, tokv[i]);
186 return match;
191 * Seek the previous matching line, or NULL if none matches.
193 Line *
194 matching_prev(Line *line)
196 while ((line = line->prev) && (!line->matches || line->header));
197 return line;
202 * Seek the next matching line, or NULL if none matches.
204 Line *
205 matching_next(Line *line)
207 while ((line = line->next) && (!line->matches || line->header));
208 return line;