Only 4 errors on Valgrin on a full cycle! \o/
[iomenu.git] / buffer.c
blob4ac34f6428c98c1e4e5307cee22c80d802ca5439
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 /* empty line in case no line come from stdin */
24 buffer->total = buffer->matching = 0;
25 buffer->first = buffer->current = buffer->last = malloc(sizeof(Line));
26 buffer->last->content = buffer->last->comment = "";
27 buffer->last->next = buffer->last->prev = NULL;
29 /* read the file into a doubly linked list of lines */
30 while (fgets(s, LINE_SIZE, fp))
31 buffer->last = add_line( buffer, ++buffer->total, s, separator,
32 buffer->last);
34 /* prevent initial current line to be a header */
35 buffer->current = buffer->first;
36 while (buffer->current->next && buffer->current->header)
37 buffer->current = buffer->current->next;
39 return buffer;
44 * Add a line to the end of the current buffer.
46 Line *
47 add_line(Buffer *buffer, int number, char *s, char *separator, Line *prev)
49 /* allocate new line */
50 buffer->last = parse_line(s, separator);
51 buffer->last->number = number;
52 buffer->last->matches = 1; /* matches by default */
53 buffer->matching++;
55 /* interlink with previous line if exists */
56 if (number == 1) {
57 buffer->first = buffer->last;
58 } else {
59 prev->next = buffer->last;
60 buffer->last->prev = prev;
63 return buffer->last;
68 * Parse the line content to determine if it is a header and identify the
69 * separator if any.
71 Line *
72 parse_line(char *s, char *separator)
74 Line *line = malloc(sizeof(Line));
75 char *sep = separator ? strstr(s, separator) : NULL;
76 int pos = sep ? (int) (sep - s) : (int) strlen(s) - 1;
78 /* header is when separator is the first character of the line */
79 line->header = (sep == s);
81 /* strip trailing newline */
82 s[strlen(s) - 1] = '\0';
84 /* fill line->content */
85 line->content = malloc((pos + 1) * sizeof(char));
86 strncpy(line->content, s, pos);
88 /* fill line->comment */
89 line->comment = "";
90 if (sep) {
91 line->comment = malloc((strlen(s) - pos) * sizeof(char));
92 strcpy(line->comment, s + pos + strlen(separator));
95 /* strip trailing whitespaces from line->content */
96 for (pos--; pos > 0 && isspace(line->content[pos]); pos--)
97 line->content[pos] = '\0';
99 /* strip leading whitespaces from line->comment */
100 for (pos = 0; isspace(line->comment[pos]); pos++);
101 line->comment += pos;
103 return line;
108 * Free the buffer, also recursing the doubly linked list.
110 void
111 free_buffer(Buffer *buffer)
113 while (buffer->first) {
115 buffer->first->content = NULL;
116 free(buffer->first->content);
118 buffer->first->comment = NULL;
119 free(buffer->first->comment);
121 free(buffer->first);
123 buffer->first = buffer->first->next;
126 free(buffer);
131 * Set the line->matching state according to the return value of match_line,
132 * and buffer->matching to number of matching candidates.
134 * The incremental parameter sets whether check already matching or
135 * non-matching lines only. This is for performance concerns.
137 void
138 filter_lines(Buffer *buffer, int inc)
140 Line *line = buffer->first;
141 char **tokv = NULL;
142 char *s, buf[sizeof buffer->input];
143 size_t n = 0, tokc = 0;
145 /* tokenize input from space characters, this comes from dmenu */
146 strcpy(buf, buffer->input);
147 for (s = strtok(buf, " "); s; s = strtok(NULL, " ")) {
148 if (++tokc > n && !(tokv = realloc(tokv, ++n * sizeof(*tokv))))
149 die("cannot realloc memory for tokv\n");
151 tokv[tokc - 1] = s;
154 /* match lines */
155 buffer->matching = 0;
156 while (line) {
157 if (buffer->input[0] && !strcmp(buffer->input, line->content)) {
158 line->matches = 1;
159 buffer->current = line;
160 } else if ((inc && line->matches) || (!inc && !line->matches)) {
161 line->matches = match_line(line, tokv, tokc);
162 buffer->matching += line->matches;
165 line = line->next;
171 * Return whecher the line matches every string from tokv.
174 match_line(Line *line, char **tokv, size_t tokc)
176 size_t i, match = 1, offset = 0;
178 if (line->header)
179 return 1;
181 for (i = 0; i < tokc && match; i++)
182 match = !!strstr(line->content + offset, tokv[i]);
184 return match;
189 * Seek the previous matching line, or NULL if none matches.
191 Line *
192 matching_prev(Line *line)
194 while ((line = line->prev) && (!line->matches || line->header));
195 return line;
200 * Seek the next matching line, or NULL if none matches.
202 Line *
203 matching_next(Line *line)
205 while ((line = line->next) && (!line->matches || line->header));
206 return line;