Experimenting a new option to output header
[iomenu.git] / buffer.c
blobaace5dc1cc6ad2fc969ab81cf938fd4de9af7074
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 = parse_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 parse_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 = "";
96 if (sep) {
97 line->comment = malloc((strlen(s) - pos) * sizeof(char));
98 strcpy(line->comment, s + pos + strlen(separator));
101 /* strip trailing whitespaces from line->content */
102 for (pos--; pos > 0 && isspace(line->content[pos]); pos--)
103 line->content[pos] = '\0';
105 /* strip leading whitespaces from line->comment */
106 for (pos = 0; isspace(line->comment[pos]); pos++);
107 line->comment += pos;
109 return line;
114 * Free the buffer, also recursing the doubly linked list.
116 void
117 free_buffer(Buffer *buffer)
119 while (buffer->first) {
121 buffer->first->content = NULL;
122 free(buffer->first->content);
124 buffer->first->comment = NULL;
125 free(buffer->first->comment);
127 free(buffer->first);
129 buffer->first = buffer->first->next;
132 free(buffer);
137 * Set the line->matching state according to the return value of match_line,
138 * and buffer->matching to number of matching candidates.
140 * The incremental parameter sets whether check already matching or
141 * non-matching lines only. This is for performance concerns.
143 void
144 filter_lines(Buffer *buffer, int inc)
146 Line *line = buffer->first;
147 char **tokv = NULL;
148 char *s, buf[sizeof buffer->input];
149 size_t n = 0, tokc = 0;
151 /* tokenize input from space characters, this comes from dmenu */
152 strcpy(buf, buffer->input);
153 for (s = strtok(buf, " "); s; s = strtok(NULL, " ")) {
154 if (++tokc > n && !(tokv = realloc(tokv, ++n * sizeof(*tokv))))
155 die("cannot realloc memory for tokv\n");
157 tokv[tokc - 1] = s;
160 /* match lines */
161 buffer->matching = 0;
162 while (line) {
163 if (buffer->input[0] && !strcmp(buffer->input, line->content)) {
164 line->matches = 1;
165 buffer->current = line;
166 } else if ((inc && line->matches) || (!inc && !line->matches)) {
167 line->matches = match_line(line, tokv, tokc);
168 buffer->matching += line->header ? 0 : line->matches;
171 line = line->next;
177 * Return whecher the line matches every string from tokv.
180 match_line(Line *line, char **tokv, size_t tokc)
182 size_t i, match = 1, offset = 0;
184 if (line->header)
185 return 1;
187 for (i = 0; i < tokc && match; i++)
188 match = !!strstr(line->content + offset, tokv[i]);
190 return match;
195 * Seek the previous matching line, or NULL if none matches.
197 Line *
198 matching_prev(Line *line)
200 while ((line = line->prev) && (!line->matches || line->header));
201 return line;
206 * Seek the next matching line, or NULL if none matches.
208 Line *
209 matching_next(Line *line)
211 while ((line = line->next) && (!line->matches || line->header));
212 return line;