aboutsummaryrefslogtreecommitdiff
path: root/src/ClientPattern.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ClientPattern.cc')
-rw-r--r--src/ClientPattern.cc251
1 files changed, 251 insertions, 0 deletions
diff --git a/src/ClientPattern.cc b/src/ClientPattern.cc
new file mode 100644
index 0000000..f41ccb0
--- /dev/null
+++ b/src/ClientPattern.cc
@@ -0,0 +1,251 @@
1// ClientPattern.cc for Fluxbox Window Manager
2// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net)
3// and Simon Bowden (rathnor at users.sourceforge.net)
4//
5// Permission is hereby granted, free of charge, to any person obtaining a
6// copy of this software and associated documentation files (the "Software"),
7// to deal in the Software without restriction, including without limitation
8// the rights to use, copy, modify, merge, publish, distribute, sublicense,
9// and/or sell copies of the Software, and to permit persons to whom the
10// Software is furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21// DEALINGS IN THE SOFTWARE.
22
23// $Id: ClientPattern.cc,v 1.6 2003/12/17 01:19:39 fluxgen Exp $
24
25#include "ClientPattern.hh"
26#include "RegExp.hh"
27#include "StringUtil.hh"
28#include "WinClient.hh"
29
30// use GNU extensions
31#ifndef _GNU_SOURCE
32#define _GNU_SOURCE
33#endif // _GNU_SOURCE
34
35
36#include <iostream>
37#include <fstream>
38#include <string>
39#include <memory>
40#include <cstdio>
41
42// needed as well for index on some systems (e.g. solaris)
43#include <strings.h>
44
45#ifdef HAVE_CONFIG_H
46#include "config.h"
47#endif // HAVE_CONFIG_H
48
49#ifdef HAVE_SSTREAM
50#include <sstream>
51#define FB_istringstream istringstream
52#elif HAVE_STRSTREAM
53#include <strstream>
54#define FB_istringstream istrstream
55#else
56#error "You dont have sstream or strstream headers!"
57#endif // HAVE_STRSTREAM
58
59
60using namespace std;
61
62ClientPattern::ClientPattern():
63 m_matchlimit(0),
64 m_nummatches(0) {}
65
66// parse the given pattern (to end of line)
67ClientPattern::ClientPattern(const char *str):
68 m_matchlimit(0),
69 m_nummatches(0)
70{
71 /* A rough grammar of a pattern is:
72 PATTERN ::= MATCH+ LIMIT?
73 MATCH ::= '(' word ')'
74 | '(' propertyname '=' word ')'
75 LIMIT ::= '{' number '}'
76
77 i.e. one or more match definitions, followed by
78 an optional limit on the number of apps to match to
79
80 Match definitions are enclosed in parentheses, and if no
81 property name is given, then CLASSNAME is assumed.
82 If no limit is specified, no limit is applied (i.e. limit = infinity)
83 */
84
85 int had_error = 0;
86
87 int pos = 0;
88 string match;
89 int err = 1; // for starting first loop
90 while (had_error == 0 && err > 0) {
91 err = FbTk::StringUtil::getStringBetween(match,
92 str + pos,
93 '(', ')', " \t\n", true);
94 if (err > 0) {
95 size_t eq = match.find_first_of('=');
96 if (eq == match.npos) {
97 if (!addTerm(match, NAME)) {
98 had_error = pos + match.find_first_of('(') + 1;
99 break;
100 }
101 } else {
102 // need to determine the property used
103 string memstr, expr;
104 WinProperty prop;
105 memstr.assign(match, 0, eq); // memstr = our identifier
106 expr.assign(match, eq+1, match.length());
107 if (strcasecmp(memstr.c_str(), "name") == 0) {
108 prop = NAME;
109 } else if (strcasecmp(memstr.c_str(), "class") == 0) {
110 prop = CLASS;
111 } else if (strcasecmp(memstr.c_str(), "title") == 0) {
112 prop = TITLE;
113 } else {
114 had_error = pos + match.find_first_of('(') + 1;
115 break;
116 }
117 if (!addTerm(expr, prop)) {
118 had_error = pos + ((str+pos) - index(str+pos, '=')) + 1;
119 break;
120 }
121 }
122 pos += err;
123 }
124 }
125 if (pos == 0 && had_error == 0) {
126 // no match terms given, this is not allowed
127 had_error = 1;
128 }
129
130 if (had_error == 0) {
131 // otherwise, we check for a number
132 string number;
133 err = FbTk::StringUtil::getStringBetween(number,
134 str+pos,
135 '{', '}');
136 if (err > 0) {
137 FB_istringstream iss(number.c_str());
138 iss >> m_matchlimit;
139 pos+=err;
140 }
141 // we don't care if there isn't one
142
143 // there shouldn't be anything else on the line
144 match = str + pos;
145 err = match.find_first_not_of(" \t\n", pos);
146 if ((unsigned) err != match.npos) {
147 // found something, not good
148 had_error = err;
149 }
150 }
151
152 if (had_error > 0) {
153 m_matchlimit = had_error;
154 // delete all the terms
155 while (!m_terms.empty()) {
156 Term * term = m_terms.back();
157 delete term;
158 m_terms.pop_back();
159 }
160 }
161}
162
163ClientPattern::~ClientPattern() {
164 // delete all the terms
165 while (!m_terms.empty()) {
166 delete m_terms.back();
167 m_terms.pop_back();
168 }
169}
170
171// return a string representation of this pattern
172std::string ClientPattern::toString() const {
173 string pat;
174 Terms::const_iterator it = m_terms.begin();
175 Terms::const_iterator it_end = m_terms.end();
176 for (; it != it_end; ++it) {
177 pat.append(" (");
178
179 switch ((*it)->prop) {
180 case NAME:
181 // do nothing -> this is the default
182 break;
183 case CLASS:
184 pat.append("class=");
185 break;
186 case TITLE:
187 pat.append("title=");
188 break;
189 }
190
191 pat.append((*it)->orig);
192 pat.append(")");
193 }
194
195 if (m_matchlimit > 0) {
196 char num[20];
197 sprintf(num, " {%d}", m_matchlimit);
198 pat.append(num);
199 }
200 return pat;
201}
202
203// does this client match this pattern?
204bool ClientPattern::match(const WinClient &win) const {
205 if (m_matchlimit != 0 && m_nummatches >= m_matchlimit ||
206 m_terms.empty())
207 return false; // already matched out
208
209 // regmatch everything
210 // currently, we use an "AND" policy for multiple terms
211 // changing to OR would require minor modifications in this function only
212 Terms::const_iterator it = m_terms.begin();
213 Terms::const_iterator it_end = m_terms.end();
214 for (; it != it_end; ++it) {
215 if (!(*it)->regexp.match(getProperty((*it)->prop, win)))
216 return false;
217 }
218 return true;
219}
220
221// add an expression to match against
222// The first argument is a regular expression, the second is the member
223// function that we wish to match against.
224bool ClientPattern::addTerm(const std::string &str, WinProperty prop) {
225
226 Term *term = new Term(str, true);
227 term->orig = str;
228 term->prop = prop;
229
230 if (term->regexp.error()) {
231 delete term;
232 return false;
233 }
234 m_terms.push_back(term);
235 return true;
236}
237
238std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const {
239 switch (prop) {
240 case TITLE:
241 return client.title();
242 break;
243 case CLASS:
244 return client.getWMClassClass();
245 break;
246 case NAME:
247 return client.getWMClassName();
248 break;
249 }
250 return client.getWMClassName();
251}