10 * Fill the buffer apropriately with the lines and headers.
13 fill_buffer(char *separator
)
15 /* fill buffer with string */
17 Line
*last
= malloc(sizeof(Line
));
18 Buffer
*buffer
= malloc(sizeof(Buffer
));
22 die("Can not open file for reading.");
24 /* empty line in case no line come from stdin */
25 last
->content
= last
->comment
= "";
26 buffer
->first
= buffer
->current
= buffer
->last
= last
;
27 buffer
->total
= buffer
->matching
= 0;
29 /* read the file into a doubly linked list of lines */
30 while (fgets(s
, LINE_SIZE
, fp
))
31 last
= add_line(buffer
, ++buffer
->total
, s
, separator
, last
);
33 /* prevent initial current line to be a header */
34 buffer
->current
= buffer
->first
;
35 while (buffer
->current
->next
&& buffer
->current
->header
)
36 buffer
->current
= buffer
->current
->next
;
43 * Add a line to the end of the current buffer.
46 add_line(Buffer
*buffer
, int number
, char *s
, char *separator
, Line
*prev
)
48 /* allocate new line */
49 buffer
->last
= parse_line(s
, separator
);
50 buffer
->last
->number
= number
;
51 buffer
->last
->matches
= 1; /* matches by default */
54 /* interlink with previous line if exists */
56 buffer
->first
= buffer
->last
;
58 prev
->next
= buffer
->last
;
59 buffer
->last
->prev
= prev
;
67 * Parse the line content to determine if it is a header and identify the
71 parse_line(char *s
, char *separator
)
73 Line
*line
= malloc(sizeof(Line
));
74 char *sep
= separator
? strstr(s
, separator
) : NULL
;
75 int pos
= sep
? (int) (sep
- s
) : (int) strlen(s
) - 1;
77 /* header is when separator is the first character of the line */
78 line
->header
= (sep
== s
);
80 /* strip trailing newline */
81 s
[strlen(s
) - 1] = '\0';
83 /* fill line->content */
84 line
->content
= malloc((pos
+ 1) * sizeof(char));
85 strncpy(line
->content
, s
, pos
);
87 /* fill line->comment */
89 line
->comment
= malloc((strlen(s
) - pos
) * sizeof(char));
90 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
;
108 * Free the buffer, also recursing the doubly linked list.
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
);
123 buffer
->first
= buffer
->first
->next
;
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.
138 filter_lines(Buffer
*buffer
, int inc
)
140 Line
*line
= buffer
->first
;
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");
155 buffer
->matching
= 0;
157 if (buffer
->input
[0] && !strcmp(buffer
->input
, line
->content
)) {
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
;
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;
181 for (i
= 0; i
< tokc
&& match
; i
++)
182 match
= !!strstr(line
->content
+ offset
, tokv
[i
]);
189 * Seek the previous matching line, or NULL if none matches.
192 matching_prev(Line
*line
)
194 while ((line
= line
->prev
) && (!line
->matches
|| line
->header
));
200 * Seek the next matching line, or NULL if none matches.
203 matching_next(Line
*line
)
205 while ((line
= line
->next
) && (!line
->matches
|| line
->header
));