Much less memory leaks.
[iomenu.git] / buffer.c
blobe4fefd9e5bf676facfb1f8d4025f35f359d8cfa9
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;
19 int l;
21 if (!fp)
22 die("Can not open file for reading.");
24 buffer->input[0] = '\0';
25 buffer->total = buffer->matching = 1;
27 /* empty line in case no line come from stdin */
28 buffer->first = buffer->current = malloc(sizeof(Line));
29 buffer->first->content = buffer->first->comment = "";
30 buffer->first->next = buffer->first->prev = NULL;
31 buffer->last = NULL;
33 /* read the file into a doubly linked list of lines */
34 for (l = 1; fgets(s, LINE_SIZE, fp); buffer->total++, l++) {
35 buffer->last = add_line(buffer, l, s, separator, buffer->last);
37 l = buffer->last->header ? 0 : l;
40 /* prevent initial current line to be a header */
41 buffer->current = buffer->first;
42 while (buffer->current->next && buffer->current->header)
43 buffer->current = buffer->current->next;
45 return buffer;
50 * Add a line to the end of the current buffer.
52 Line *
53 add_line(Buffer *buffer, int number, char *s, char *separator, Line *prev)
55 /* allocate new line */
56 buffer->last = new_line(s, separator);
57 buffer->last->number = number;
58 buffer->last->matches = 1; /* matches by default */
59 buffer->matching++;
61 /* interlink with previous line if exists */
62 if (prev) {
63 prev->next = buffer->last;
64 buffer->last->prev = prev;
65 } else {
66 buffer->first = buffer->last;
69 return buffer->last;
74 * Parse the line content to determine if it is a header and identify the
75 * separator if any.
77 Line *
78 new_line(char *s, char *separator)
80 Line *line = malloc(sizeof(Line));
81 char *sep = separator ? strstr(s, separator) : NULL;
82 int pos = sep ? (int) (sep - s) : (int) strlen(s) - 1;
84 /* header is when separator is the first character of the line */
85 line->header = (sep == s);
87 /* strip trailing newline */
88 s[strlen(s) - 1] = '\0';
90 /* fill line->content */
91 line->content = malloc((pos + 1) * sizeof(char));
92 strncpy(line->content, s, pos);
94 /* fill line->comment */
95 line->comment = malloc((strlen(s) - pos) * sizeof(char));
96 if (sep) {
97 strcpy(line->comment, s + pos + strlen(separator));
100 /* strip trailing whitespaces from line->content */
101 for (pos--; pos > 0 && isspace(line->content[pos]); pos--)
102 line->content[pos] = '\0';
104 /* strip leading whitespaces from line->comment */
105 for (pos = 0; isspace(line->comment[pos]); pos++);
106 line->comment += pos;
108 return line;
113 * Free the buffer, also recursing the doubly linked list.
115 void
116 free_buffer(Buffer *buffer)
118 Line *next = NULL;
120 for (; buffer->first; buffer->first = next) {
121 next = buffer->first->next;
123 free(buffer->first->content);
124 buffer->first->content = NULL;
126 free(buffer->first->comment);
127 buffer->first->comment = NULL;
129 free(buffer->first);
130 buffer->first = NULL;
132 buffer->first = next;
135 free(buffer);
136 buffer = NULL;
141 * Set the line->matching state according to the return value of match_line,
142 * and buffer->matching to number of matching candidates.
144 * The incremental parameter sets whether check already matching or
145 * non-matching lines only. This is for performance concerns.
147 void
148 filter_lines(Buffer *buffer, int inc)
150 Line *line = buffer->first;
151 char **tokv = NULL;
152 char *s, buf[sizeof buffer->input];
153 size_t n = 0, tokc = 0;
155 /* tokenize input from space characters, this comes from dmenu */
156 strcpy(buf, buffer->input);
157 for (s = strtok(buf, " "); s; s = strtok(NULL, " ")) {
158 if (++tokc > n && !(tokv = realloc(tokv, ++n * sizeof(*tokv))))
159 die("cannot realloc memory for tokv\n");
161 tokv[tokc - 1] = s;
164 /* match lines */
165 buffer->matching = 0;
166 while (line) {
167 if (buffer->input[0] && !strcmp(buffer->input, line->content)) {
168 line->matches = 1;
169 buffer->current = line;
170 } else if ((inc && line->matches) || (!inc && !line->matches)) {
171 line->matches = match_line(line, tokv, tokc);
172 buffer->matching += line->header ? 0 : line->matches;
175 line = line->next;
181 * Return whecher the line matches every string from tokv.
184 match_line(Line *line, char **tokv, size_t tokc)
186 size_t i, match = 1, offset = 0;
188 if (line->header)
189 return 1;
191 for (i = 0; i < tokc && match; i++)
192 match = !!strstr(line->content + offset, tokv[i]);
194 return match;
199 * Seek the previous matching line, or NULL if none matches.
201 Line *
202 matching_prev(Line *line)
204 while ((line = line->prev) && (!line->matches || line->header));
205 return line;
210 * Seek the next matching line, or NULL if none matches.
212 Line *
213 matching_next(Line *line)
215 while ((line = line->next) && (!line->matches || line->header));
216 return line;