10 * Fill the buffer apropriately with the lines and headers.
13 fill_buffer(char *separator
)
15 /* fill buffer with string */
17 Buffer
*buffer
= malloc(sizeof(Buffer
));
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
;
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
;
50 * Add a line to the end of the current buffer.
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 */
61 /* interlink with previous line if exists */
63 prev
->next
= buffer
->last
;
64 buffer
->last
->prev
= prev
;
66 buffer
->first
= buffer
->last
;
74 * Parse the line content to determine if it is a header and identify the
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));
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
;
113 * Free the buffer, also recursing the doubly linked list.
116 free_buffer(Buffer
*buffer
)
120 while (buffer
->first
) {
121 next
= buffer
->first
->next
;
125 buffer
->first
= next
;
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.
140 filter_lines(Buffer
*buffer
, int inc
)
142 Line
*line
= buffer
->first
;
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");
157 buffer
->matching
= 0;
159 if (buffer
->input
[0] && !strcmp(buffer
->input
, line
->content
)) {
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
->header
? 0 : line
->matches
;
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;
183 for (i
= 0; i
< tokc
&& match
; i
++)
184 match
= !!strstr(line
->content
+ offset
, tokv
[i
]);
191 * Seek the previous matching line, or NULL if none matches.
194 matching_prev(Line
*line
)
196 while ((line
= line
->prev
) && (!line
->matches
|| line
->header
));
202 * Seek the next matching line, or NULL if none matches.
205 matching_next(Line
*line
)
207 while ((line
= line
->next
) && (!line
->matches
|| line
->header
));