Doxygen
Loading...
Searching...
No Matches
aliases.cpp File Reference
#include "aliases.h"
#include <unordered_map>
#include "config.h"
#include "debug.h"
#include "message.h"
#include "regex.h"
#include "stringutil.h"
#include "util.h"
Include dependency graph for aliases.cpp:

Go to the source code of this file.

Classes

struct  AliasInfo
struct  Marker

Typedefs

using AliasOverloads = std::unordered_map<int,AliasInfo>
using AliasInfoMap = std::unordered_map<std::string,AliasOverloads>

Functions

static std::string expandAliasRec (StringUnorderedSet &aliasesProcessed, std::string_view s, bool allowRecursion=false)
static int countAliasArguments (std::string_view args, std::string_view sep)
static std::string extractAliasArgs (std::string_view args)
static std::string expandAlias (std::string_view aliasName, std::string_view aliasValue)
static void addValidAliasToMap (std::string_view alias)
static std::string escapeAlias (std::string_view value)
void readAliases ()
static size_t findEndOfCommand (std::string_view s)
 For a string s that starts with a command name, returns the character offset within that string representing the first character after the command.
static std::string replaceAliasArguments (StringUnorderedSet &aliasesProcessed, std::string_view aliasValue, std::string_view argList, std::string_view sep)
 Replaces the markers in an alias definition aliasValue with the corresponding values found in the comma separated argument list argList and the returns the result after recursive alias expansion.
static std::string escapeSeparators (const std::string &s, const std::string &sep)
std::string resolveAliasCmd (std::string_view aliasCmd)
bool isAliasCmd (std::string_view aliasCmd)

Variables

static AliasInfoMap g_aliasInfoMap

Typedef Documentation

◆ AliasInfoMap

using AliasInfoMap = std::unordered_map<std::string,AliasOverloads>

Definition at line 41 of file aliases.cpp.

◆ AliasOverloads

using AliasOverloads = std::unordered_map<int,AliasInfo>

Definition at line 40 of file aliases.cpp.

Function Documentation

◆ addValidAliasToMap()

void addValidAliasToMap ( std::string_view alias)
static

Definition at line 57 of file aliases.cpp.

58{
59 bool valid = true;
60 std::string aliasName;
61 std::string aliasValue;
62 int numParams = 0;
63 std::string separator;
64
65 static std::string_view separators = "!#$%&,.?|;:'+=~`/";
66 auto isValidSeparator = [](char c) -> bool { return separators.find(c)!=std::string::npos; };
67
68 static const reg::Ex re(R"(^(\a[\w-]*)({[^}]*})?\s*=)");
69 reg::Match m;
70 if (reg::search(alias,m,re)) // valid name= or name{...}= part
71 {
72 size_t i=m.length();
73 ASSERT(i!=std::string::npos); // based on re is always a =
74 ASSERT(m.size()==3); // m[0]=full match including '=', m[1]=name, m[2]=optional params
75 aliasName = m[1].str();
76 aliasValue = alias.substr(i);
77 //printf("Alias: found name='%s' value='%s'\n",qPrint(name),qPrint(aliasValue));
78 if (!m[2].empty()) // alias with parameters
79 {
80 separator=",";
81 size_t b = m[2].position(); // index of '{'
82 size_t e = b + m[2].length(); // index of '}'
83 size_t k=b+1;
84 while (k<e-1 && isdigit(alias[k])) k++;
85 numParams = atoi(std::string{alias.substr(b+1,k-b-1)}.c_str());
86 if (numParams>0)
87 {
88 if (k<e-1) // we have a separator
89 {
90 size_t s=k;
91 while (s<e && isValidSeparator(alias[s])) s++;
92 if (s<e-1)
93 {
94 err("Invalid alias '{}': invalid separator character '{:c}' (code {:d}), allowed characters: {}. Check your config file.\n",alias,alias[s],alias[s],separators);
95 valid=false;
96 }
97 else
98 {
99 separator=alias.substr(k,e-k-1);
100 }
101 }
102 if (valid) // valid alias with parameters
103 {
104 Debug::print(Debug::Alias,0,"Alias definition: name='{}' #param='{}' separator='{}' value='{}'\n",
105 aliasName,numParams,separator,aliasValue);
106 }
107 }
108 else
109 {
110 err("Invalid alias '{}': missing number of parameters. Check your config file.\n",alias);
111 valid=false;
112 }
113 }
114 else // valid alias without parameters
115 {
116 numParams = 0;
117 Debug::print(Debug::Alias,0,"Alias definition: name='{}' value='{}'\n",aliasName,aliasValue);
118 }
119 }
120 else
121 {
122 err("Invalid alias '{}': invalid 'name=' or 'name{{...}}=' part. Check you config file.\n",alias);
123 valid=false;
124 }
125
126 if (valid) // alias definition passed all checks, so store it.
127 {
128 auto it = g_aliasInfoMap.find(aliasName);
129 if (it==g_aliasInfoMap.end()) // insert new alias
130 {
131 AliasOverloads overloads { { numParams, AliasInfo(aliasValue, separator) } };
132 g_aliasInfoMap.emplace(aliasName,overloads);
133 }
134 else // replace exiting alias with new definition
135 {
136 auto it2 = it->second.find(numParams);
137 if (it2==it->second.end()) // new alias overload for the given number of parameters
138 {
139 it->second.emplace(numParams, AliasInfo(aliasValue,separator));
140 }
141 else // replace alias with new definition
142 {
143 it2->second = AliasInfo(aliasValue,separator);
144 }
145 }
146 }
147}
std::unordered_map< int, AliasInfo > AliasOverloads
Definition aliases.cpp:40
static AliasInfoMap g_aliasInfoMap
Definition aliases.cpp:53
@ Alias
Definition debug.h:47
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:78
Class representing a regular expression.
Definition regex.h:39
Object representing the matching results.
Definition regex.h:154
size_t size() const
Returns the number of sub matches available in this match.
Definition regex.h:185
size_t position() const
Returns the position of the match or std::string::npos if no position is set.
Definition regex.h:160
std::string str() const
Return a string representing the matching part.
Definition regex.h:166
size_t length() const
Returns the position of the match or std::string::npos if no length is set.
Definition regex.h:163
#define err(fmt,...)
Definition message.h:127
#define ASSERT(x)
Definition message.h:142
bool search(std::string_view str, Match &match, const Ex &re, size_t pos)
Search in a given string str starting at position pos for a match against regular expression re.
Definition regex.cpp:850

References Debug::Alias, ASSERT, err, g_aliasInfoMap, reg::Match::length(), reg::Match::position(), Debug::print(), reg::search(), reg::Match::size(), and reg::Match::str().

Referenced by readAliases().

◆ countAliasArguments()

int countAliasArguments ( std::string_view args,
std::string_view sep )
static

Definition at line 441 of file aliases.cpp.

442{
443 int count = 1;
444 size_t l = args.length();
445 for (size_t i=0;i<l;i++)
446 {
447 char c = args[i];
448 if (!sep.empty() &&
449 c==sep[0] && // start with separator character
450 (i==0 || args[i-1]!='\\') && // is not escaped
451 args.substr(i,sep.length())==sep) // whole separator matches
452 {
453 count++;
454 }
455 else if (c=='@' || c=='\\')
456 {
457 // check if this is the start of another aliased command (see bug704172)
458 i += findEndOfCommand(args.substr(i+1));
459 }
460 }
461 //printf("countAliasArguments(%s,sep=%s)=%d\n",qPrint(args),qPrint(sep),count);
462 return count;
463}
static size_t findEndOfCommand(std::string_view s)
For a string s that starts with a command name, returns the character offset within that string repre...
Definition aliases.cpp:206

References findEndOfCommand().

Referenced by expandAliasRec().

◆ escapeAlias()

std::string escapeAlias ( std::string_view value)
static

Definition at line 152 of file aliases.cpp.

153{
154 std::string newValue = substituteStringView(value,"^^ ","@ilinebr ");
155 newValue = substituteStringView(newValue,"^^","@ilinebr ");
156 //printf("escapeAlias('%s')='%s'\n",qPrint(std::string{value}),qPrint(newValue));
157 return newValue;
158}
std::string substituteStringView(std::string_view s, std::string_view toReplace, std::string_view replaceWith)
Returns a new string where occurrences of substring toReplace in string s are replaced by string repl...
Definition stringutil.h:53

References substituteStringView().

Referenced by readAliases().

◆ escapeSeparators()

std::string escapeSeparators ( const std::string & s,
const std::string & sep )
static

Definition at line 314 of file aliases.cpp.

315{
316 if (s.empty() || sep.empty()) return s;
317 std::string result;
318 result.reserve(s.length()+10);
319 size_t i, p=0, l=sep.length();
320 while ((i=s.find(sep,p))!=std::string::npos)
321 {
322 result += s.substr(p,i-p);
323 if (i>0 && s[i-1]!='\\') // escape the separator
324 {
325 result += '\\';
326 }
327 result += s.substr(i,l);
328 p = i+l;
329 }
330 result += s.substr(p);
331 //printf("escapeSeparators(%s,sep='%s')=%s\n",qPrint(s),qPrint(sep),qPrint(result));
332 return result;
333}

Referenced by expandAliasRec().

◆ expandAlias()

std::string expandAlias ( std::string_view aliasName,
std::string_view aliasValue )
static

Definition at line 505 of file aliases.cpp.

506{
507 std::string result;
508 StringUnorderedSet aliasesProcessed;
509 // avoid expanding this command recursively
510 aliasesProcessed.insert(std::string{aliasName});
511 // expand embedded commands
512 //printf("Expanding: '%s'->'%s'\n",qPrint(aliasName),qPrint(aliasValue));
513 result = expandAliasRec(aliasesProcessed,aliasValue);
514 //printf("Expanding result: '%s'->'%s'\n",qPrint(aliasName),qPrint(result));
515 Debug::print(Debug::Alias,0,"Expanding alias: input='{}' result='{}'\n",std::string{aliasValue},result);
516 return result;
517}
static std::string expandAliasRec(StringUnorderedSet &aliasesProcessed, std::string_view s, bool allowRecursion=false)
Definition aliases.cpp:335
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29

References Debug::Alias, expandAliasRec(), and Debug::print().

Referenced by readAliases().

◆ expandAliasRec()

std::string expandAliasRec ( StringUnorderedSet & aliasesProcessed,
std::string_view s,
bool allowRecursion = false )
static

Definition at line 335 of file aliases.cpp.

336{
337 std::string result;
338 static const reg::Ex re(R"([\\@](\a[\w-]*))");
339 std::string str{s};
341 size_t p = 0;
342 while (reg::search(str,match,re,p))
343 {
344 size_t i = match.position();
345 size_t l = match.length();
346 if (i>p) result+=s.substr(p,i-p);
347
348 std::string args = extractAliasArgs(s.substr(i+l));
349 bool hasArgs = !args.empty(); // found directly after command
350 size_t argsLen = args.length();
351 std::string cmd = match[1].str();
352 int selectedNumArgs = -1;
353 //printf("looking for alias '%s' with params '%s'\n",qPrint(cmd),qPrint(args));
354 auto it = g_aliasInfoMap.find(cmd);
355 if (it == g_aliasInfoMap.end())
356 {
357 // if command has a - then also try part in without it
358 size_t minusPos = cmd.find('-');
359 if (minusPos!=std::string::npos)
360 {
361 it = g_aliasInfoMap.find(cmd.substr(0,minusPos));
362 if (it!=g_aliasInfoMap.end()) // found part before - as alias
363 {
364 cmd = cmd.substr(0,minusPos);
365 args = "";
366 hasArgs = false;
367 argsLen = 0;
368 l = cmd.length()+1; // +1 for the minus sign
369 }
370 }
371 }
372 if (it != g_aliasInfoMap.end()) // cmd is an alias
373 {
374 //printf("found an alias, hasArgs=%d\n",hasArgs);
375 if (hasArgs)
376 {
377 // Find the an alias that matches the number of arguments.
378 // If there are multiple candidates, take the one that matches the most parameters
379 for (const auto &[numParams,aliasInfo] : it->second)
380 {
381 int numArgs = countAliasArguments(args,aliasInfo.separator);
382 if (numParams==numArgs && numArgs>selectedNumArgs)
383 {
384 selectedNumArgs = numArgs;
385 }
386 }
387 if (selectedNumArgs==-1) // no match found, check if there is an alias with one argument
388 {
389 auto it2 = it->second.find(1);
390 if (it2 != it->second.end())
391 {
392 args = escapeSeparators(args,it2->second.separator); // escape separator so that everything is seen as one argument
393 selectedNumArgs = 1;
394 }
395 }
396 }
397 else
398 {
399 selectedNumArgs = 0;
400 }
401 }
402 else
403 {
404 //printf("Alias %s not found\n",qPrint(cmd));
405 }
406 //printf("Found command s='%s' cmd='%s' numArgs=%d args='%s'\n", qPrint(s),qPrint(cmd),selectedNumArgs,qPrint(args));
407 std::string qualifiedName = cmd+":"+std::to_string(selectedNumArgs);
408 if ((allowRecursion || aliasesProcessed.find(qualifiedName)==aliasesProcessed.end()) &&
409 it!=g_aliasInfoMap.end() && selectedNumArgs!=-1 &&
410 it->second.find(selectedNumArgs)!=it->second.end()) // expand the alias
411 {
412 const auto &aliasInfo = it->second.find(selectedNumArgs)->second;
413 //printf("is an alias with separator='%s' selectedNumArgs=%d hasArgs=%d!\n",qPrint(aliasInfo.separator),selectedNumArgs,hasArgs);
414 if (!allowRecursion) aliasesProcessed.insert(qualifiedName);
415 std::string val = aliasInfo.value;
416 if (hasArgs)
417 {
418 //printf("before replaceAliasArguments(val='%s')\n",qPrint(val));
419 val = replaceAliasArguments(aliasesProcessed,val,args,aliasInfo.separator);
420 //printf("after replaceAliasArguments sep='%s' val='%s' args='%s'\n",
421 // qPrint(aliasInfo.separator),qPrint(val),qPrint(args));
422 }
423 result += expandAliasRec(aliasesProcessed,val);
424 if (!allowRecursion) aliasesProcessed.erase(qualifiedName);
425 p = i+l;
426 if (hasArgs) p += argsLen+2;
427 }
428 else // command is not an alias
429 {
430 //printf("not an alias!\n");
431 result += match.str();
432 p = i+l;
433 }
434 }
435 result += s.substr(p);
436 //printf("expandAliases \"%s\"->\"%s\"\n",qPrint(s),qPrint(result));
437 return result;
438}
static std::string replaceAliasArguments(StringUnorderedSet &aliasesProcessed, std::string_view aliasValue, std::string_view argList, std::string_view sep)
Replaces the markers in an alias definition aliasValue with the corresponding values found in the com...
Definition aliases.cpp:221
static std::string escapeSeparators(const std::string &s, const std::string &sep)
Definition aliases.cpp:314
static std::string extractAliasArgs(std::string_view args)
Definition aliases.cpp:465
static int countAliasArguments(std::string_view args, std::string_view sep)
Definition aliases.cpp:441
bool match(std::string_view str, Match &match, const Ex &re)
Matches a given string str for a match against regular expression re.
Definition regex.cpp:861

References countAliasArguments(), escapeSeparators(), expandAliasRec(), extractAliasArgs(), g_aliasInfoMap, replaceAliasArguments(), and reg::search().

Referenced by expandAlias(), expandAliasRec(), replaceAliasArguments(), and resolveAliasCmd().

◆ extractAliasArgs()

std::string extractAliasArgs ( std::string_view args)
static

Definition at line 465 of file aliases.cpp.

466{
467 int bc = 0;
468 char prevChar = 0;
469 if (!args.empty() && args[0]=='{') // alias has argument
470 {
471 for (size_t i=0;i<args.length();i++)
472 {
473 char c = args[i];
474 if (prevChar!='\\') // not escaped
475 {
476 if (c=='{') bc++;
477 if (c=='}') bc--;
478 prevChar=c;
479 }
480 else
481 {
482 prevChar=0;
483 }
484
485 if (bc==0)
486 {
487 //printf("extractAliasArgs('%s')->'%s'\n",qPrint(args),qPrint(args.substr(1,i-1)));
488 return std::string{args.substr(1,i-1)};
489 }
490 }
491 }
492 return std::string{};
493}

Referenced by expandAliasRec(), and findEndOfCommand().

◆ findEndOfCommand()

size_t findEndOfCommand ( std::string_view s)
static

For a string s that starts with a command name, returns the character offset within that string representing the first character after the command.

For an alias with argument, this is the offset to the character just after the argument list.

Examples:

  • s=="a b" returns 1
  • s=="a{2,3} b" returns 6 = s=="#" returns 0

Definition at line 206 of file aliases.cpp.

207{
208 size_t i = 0;
209 while (i < s.size() && isId(s[i])) ++i;
210 if (i < s.size() && s[i] == '{')
211 {
212 i += extractAliasArgs(s.substr(i)).length() + 2; // +2 for '{' and '}'
213 }
214 return i;
215}
bool isId(char c)
Returns true if c is a valid character for an identifier.
Definition dstring.h:895

References extractAliasArgs(), and isId().

Referenced by countAliasArguments(), and replaceAliasArguments().

◆ isAliasCmd()

bool isAliasCmd ( std::string_view aliasCmd)

Definition at line 519 of file aliases.cpp.

520{
521 return g_aliasInfoMap.find(std::string{aliasCmd}) != g_aliasInfoMap.end();
522}

References g_aliasInfoMap.

Referenced by DocPara::handleCommand().

◆ readAliases()

void readAliases ( )

Definition at line 162 of file aliases.cpp.

163{
164 StringVector aliases = Config_getList(ALIASES);
165 // add aliases to a dictionary
166 for (const auto &al : aliases)
167 {
169 }
170 for (auto &[name,overloads] : g_aliasInfoMap)
171 {
172 for (auto &[numParams,aliasInfo] : overloads)
173 {
174 aliasInfo.value = expandAlias(name+":"+std::to_string(numParams),aliasInfo.value);
175 }
176 }
177 for (auto &[name,overloads] : g_aliasInfoMap)
178 {
179 for (auto &[numParams,aliasInfo] : overloads)
180 {
181 aliasInfo.value = escapeAlias(aliasInfo.value);
182 }
183 }
184}
static std::string expandAlias(std::string_view aliasName, std::string_view aliasValue)
Definition aliases.cpp:505
static void addValidAliasToMap(std::string_view alias)
Definition aliases.cpp:57
static std::string escapeAlias(std::string_view value)
Definition aliases.cpp:152
#define Config_getList(name)
Definition config.h:38
std::vector< std::string > StringVector
Definition containers.h:33

References addValidAliasToMap(), Config_getList, escapeAlias(), expandAlias(), and g_aliasInfoMap.

Referenced by adjustConfiguration().

◆ replaceAliasArguments()

std::string replaceAliasArguments ( StringUnorderedSet & aliasesProcessed,
std::string_view aliasValue,
std::string_view argList,
std::string_view sep )
static

Replaces the markers in an alias definition aliasValue with the corresponding values found in the comma separated argument list argList and the returns the result after recursive alias expansion.

Definition at line 221 of file aliases.cpp.

224{
225 //printf("----- replaceAliasArguments(val=[%s],args=[%s],sep=[%s])\n",qPrint(aliasValue),qPrint(argList),qPrint(sep));
226
227 // first make a list of arguments from the comma separated argument list
228 StringViewVector args;
229 size_t l=argList.length();
230 size_t p=0;
231 for (size_t i=0;i<l;i++)
232 {
233 char c = argList[i];
234 if (!sep.empty() &&
235 c==sep[0] && // start with separator character
236 (i==0 || argList[i-1]!='\\') && // is not escaped
237 argList.substr(i,sep.length())==sep) // whole separator matches
238 {
239 args.push_back(argList.substr(p,i-p));
240 p = i+sep.length(); // start of next argument
241 i = p-1; // compensate with -1 for loop iterator
242 }
243 else if (c=='@' || c=='\\') // command
244 {
245 // check if this is the start of another aliased command (see bug704172)
246 i+=findEndOfCommand(argList.substr(i+1));
247 }
248 }
249 if (l>p) args.push_back(argList.substr(p));
250 //printf("found %zu arguments\n",args.size());
251
252 // next we look for the positions of the markers and add them to a list
253 std::vector<Marker> markerList;
254 l = aliasValue.length();
255 char pc = '\0';
256 bool insideMarkerId = false;
257 size_t markerStart = 0;
258 auto isDigit = [](char c) { return c>='0' && c<='9'; };
259 for (size_t i=0;i<=l;i++)
260 {
261 char c = i<l ? aliasValue[i] : '\0';
262 if (insideMarkerId && !isDigit(c)) // found end of a markerId
263 {
264 insideMarkerId = false;
265 size_t markerLen = i-markerStart;
266 markerList.emplace_back(markerStart-1,
267 static_cast<size_t>(std::stoi(std::string{aliasValue.substr(markerStart,markerLen)})),
268 markerLen+1);
269 }
270 if (c=='\\' && (pc=='@' || pc=='\\')) // found escaped backslash
271 {
272 // skip
273 pc = '\0';
274 }
275 else
276 {
277 if (isDigit(c) && pc=='\\') // found start of a markerId
278 {
279 insideMarkerId=true;
280 markerStart=i;
281 }
282 pc = c;
283 }
284 }
285
286 // then we replace the markers with the corresponding arguments in one pass
287 std::string result;
288 p = 0;
289 for (const Marker &m : markerList)
290 {
291 result+=aliasValue.substr(p,m.pos-p);
292 //printf("part before marker: '%s'\n",qPrint(aliasValue.substr(p,m.pos-p)));
293 if (m.number>0 && m.number<=args.size()) // valid number
294 {
295 result+=expandAliasRec(aliasesProcessed,args[m.number-1],true);
296 //printf("marker index=%zu pos=%zu number=%zu size=%zu replacement %s\n",i,m.pos,m.number,m.size,
297 // qPrint(args[m.number-1]));
298 }
299 p=m.pos+m.size; // continue after the marker
300 }
301 result+=aliasValue.substr(p); // append remainder
302 //printf("string after replacement of markers: '%s'\n",qPrint(result));
303
304 // expand the result again
305 substituteInplace(result,"\\{","{");
306 substituteInplace(result,"\\}","}");
307 substituteInplace(result,std::string{"\\"}+std::string{sep},sep);
308 result = expandAliasRec(aliasesProcessed,result);
309
310 //printf("final string '%s'\n",qPrint(result));
311 return result;
312}
std::vector< std::string_view > StringViewVector
Definition containers.h:34
void substituteInplace(std::string &s, std::string_view toReplace, std::string_view replaceWith)
Replaces occurrences of substring toReplace in string s with string replaceWith.
Definition stringutil.h:32

References expandAliasRec(), findEndOfCommand(), and substituteInplace().

Referenced by expandAliasRec().

◆ resolveAliasCmd()

std::string resolveAliasCmd ( std::string_view aliasCmd)

Definition at line 495 of file aliases.cpp.

496{
497 StringUnorderedSet aliasesProcessed;
498 //printf("Expanding: '%s'\n",qPrint(aliasCmd));
499 std::string result = expandAliasRec(aliasesProcessed,aliasCmd);
500 //printf("Expanding result: '%s'->'%s'\n",qPrint(aliasCmd),qPrint(result));
501 Debug::print(Debug::Alias,0,"Resolving alias: cmd='{}' result='{}'\n",std::string{aliasCmd},result);
502 return result;
503}

References Debug::Alias, expandAliasRec(), and Debug::print().

Referenced by replaceAliases().

Variable Documentation

◆ g_aliasInfoMap

AliasInfoMap g_aliasInfoMap
static

Definition at line 53 of file aliases.cpp.

Referenced by addValidAliasToMap(), expandAliasRec(), isAliasCmd(), and readAliases().