A tiling window manager
1/*
2 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place, Suite 330, Boston, MA 02111-1307 USA.
17 */
18
19/* Needed on Linux for strcasestr */
20#define _GNU_SOURCE
21#include <string.h>
22
23#include "sdorfehs.h"
24
25rp_completions *
26completions_new(completion_fn list_fn, enum completion_styles style)
27{
28 rp_completions *c;
29
30 c = xmalloc(sizeof(rp_completions));
31
32 INIT_LIST_HEAD(&c->completion_list);
33 c->complete_fn = list_fn;
34 c->last_match = NULL;
35 c->partial = NULL;
36 c->virgin = 1;
37 c->style = style;
38
39 return c;
40}
41
42void
43completions_free(rp_completions *c)
44{
45 struct sbuf *cur;
46 struct list_head *tmp, *iter;
47
48 /* Clear our list */
49 list_for_each_safe_entry(cur, iter, tmp, &c->completion_list, node) {
50 list_del(&cur->node);
51 sbuf_free(cur);
52 }
53
54 /* Free the partial string. */
55 free(c->partial);
56
57 free(c);
58}
59
60static void
61completions_assign(rp_completions *c, struct list_head *new_list)
62{
63 struct sbuf *cur;
64 struct list_head *tmp, *iter;
65
66 /* Clear our list */
67 list_for_each_safe_entry(cur, iter, tmp, &c->completion_list, node) {
68 list_del(&cur->node);
69 sbuf_free(cur);
70 }
71
72 /*
73 * splice the list into completion_list. Note that we SHOULDN'T free
74 * new_list, because they share the same memory.
75 */
76 INIT_LIST_HEAD(&c->completion_list);
77 list_splice(new_list, &c->completion_list);
78
79 list_first(c->last_match, &c->completion_list, node);
80}
81
82static void
83completions_update(rp_completions *c, char *partial)
84{
85 struct list_head *new_list;
86
87 new_list = c->complete_fn(partial);
88
89 c->virgin = 0;
90 free(c->partial);
91 c->partial = xstrdup(partial);
92
93 completions_assign(c, new_list);
94
95 /* Free the head structure for our list. */
96 free(new_list);
97}
98
99/*
100 * Return true if completion is an alternative for partial string, given the
101 * style used.
102 */
103static int
104completions_match(rp_completions *c, char *completion, char *partial)
105{
106 int match = 0;
107
108 switch (c->style) {
109 case BASIC:
110 match = str_comp(completion, partial, strlen(partial));
111 break;
112 case SUBSTRING:
113 match = (strcasestr(completion, partial) != NULL);
114 break;
115 }
116
117 return match;
118}
119
120static char *
121completions_prev_match(rp_completions *c)
122{
123 struct sbuf *cur;
124
125 /*
126 * search forward from our last match through the list looking for
127 * another match.
128 */
129 for (cur = list_prev_entry(c->last_match, &c->completion_list, node);
130 cur != c->last_match;
131 cur = list_prev_entry(cur, &c->completion_list, node)) {
132 if (completions_match(c, sbuf_get(cur), c->partial)) {
133 /*
134 * We found a match so update our last_match pointer
135 * and return the string.
136 */
137 c->last_match = cur;
138 return sbuf_get(cur);
139 }
140 }
141
142 return NULL;
143}
144
145static char *
146completions_next_match(rp_completions *c)
147{
148 struct sbuf *cur;
149
150 /*
151 * search forward from our last match through the list looking for
152 * another match.
153 */
154 for (cur = list_next_entry(c->last_match, &c->completion_list, node);
155 cur != c->last_match;
156 cur = list_next_entry(cur, &c->completion_list, node)) {
157 if (completions_match(c, sbuf_get(cur), c->partial)) {
158 /*
159 * We found a match so update our last_match pointer
160 * and return the string.
161 */
162 c->last_match = cur;
163 return sbuf_get(cur);
164 }
165 }
166
167 return NULL;
168}
169
170/* Return a completed string that starts with partial. */
171char *
172completions_complete(rp_completions *c, char *partial, int direction)
173{
174 if (c->virgin) {
175 completions_update(c, partial);
176
177 /*
178 * Since it's never been completed on and c->last_match points
179 * to the first element of the list which may be a match. So
180 * check it. FIXME: This is a bit of a hack.
181 */
182 if (c->last_match == NULL)
183 return NULL;
184
185 /*
186 * c->last_match contains the first match in the forward
187 * direction. So if we're looking for the previous match, then
188 * check the previous element from last_match.
189 */
190 if (direction == COMPLETION_PREVIOUS)
191 c->last_match = list_prev_entry(c->last_match,
192 &c->completion_list, node);
193
194 /* Now check if last_match is a match for partial. */
195 if (completions_match(c, sbuf_get(c->last_match), c->partial))
196 return sbuf_get(c->last_match);
197 }
198 if (c->last_match == NULL)
199 return NULL;
200
201 /* Depending on the direction, find our "next" match. */
202 if (direction == COMPLETION_NEXT)
203 return completions_next_match(c);
204
205 /* Otherwise get the previous match */
206 return completions_prev_match(c);
207}