Doxygen
Loading...
Searching...
No Matches
aliases.cpp File Reference
#include <unordered_map>
#include <cassert>
#include "message.h"
#include "aliases.h"
#include "containers.h"
#include "config.h"
#include "regex.h"
#include "textstream.h"
#include "util.h"
#include "debug.h"
#include "stringutil.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 40 of file aliases.cpp.

◆ AliasOverloads

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

Definition at line 39 of file aliases.cpp.

Function Documentation

◆ addValidAliasToMap()

void addValidAliasToMap ( std::string_view alias)
static

Definition at line 56 of file aliases.cpp.

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

References Debug::Alias, 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 440 of file aliases.cpp.

441{
442 int count = 1;
443 size_t l = args.length();
444 for (size_t i=0;i<l;i++)
445 {
446 char c = args[i];
447 if (!sep.empty() &&
448 c==sep[0] && // start with separator character
449 (i==0 || args[i-1]!='\\') && // is not escaped
450 args.substr(i,sep.length())==sep) // whole separator matches
451 {
452 count++;
453 }
454 else if (c=='@' || c=='\\')
455 {
456 // check if this is the start of another aliased command (see bug704172)
457 i += findEndOfCommand(args.substr(i+1));
458 }
459 }
460 //printf("countAliasArguments(%s,sep=%s)=%d\n",qPrint(args),qPrint(sep),count);
461 return count;
462}
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:205

References findEndOfCommand().

Referenced by expandAliasRec().

◆ escapeAlias()

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

Definition at line 151 of file aliases.cpp.

152{
153 std::string newValue = substituteStringView(value,"^^ ","@ilinebr ");
154 newValue = substituteStringView(newValue,"^^","@ilinebr ");
155 //printf("escapeAlias('%s')='%s'\n",qPrint(std::string{value}),qPrint(newValue));
156 return newValue;
157}
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:50

References substituteStringView().

Referenced by readAliases().

◆ escapeSeparators()

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

Definition at line 313 of file aliases.cpp.

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

Referenced by expandAliasRec().

◆ expandAlias()

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

Definition at line 504 of file aliases.cpp.

505{
506 std::string result;
507 StringUnorderedSet aliasesProcessed;
508 // avoid expanding this command recursively
509 aliasesProcessed.insert(std::string{aliasName});
510 // expand embedded commands
511 //printf("Expanding: '%s'->'%s'\n",qPrint(aliasName),qPrint(aliasValue));
512 result = expandAliasRec(aliasesProcessed,aliasValue);
513 //printf("Expanding result: '%s'->'%s'\n",qPrint(aliasName),qPrint(result));
514 Debug::print(Debug::Alias,0,"Expanding alias: input='{}' result='{}'\n",std::string{aliasValue},result);
515 return result;
516}
static std::string expandAliasRec(StringUnorderedSet &aliasesProcessed, std::string_view s, bool allowRecursion=FALSE)
Definition aliases.cpp:334
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 334 of file aliases.cpp.

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

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 464 of file aliases.cpp.

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

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 205 of file aliases.cpp.

206{
207 size_t i = 0;
208 while (i < s.size() && isId(s[i])) ++i;
209 if (i < s.size() && s[i] == '{')
210 {
211 i += extractAliasArgs(s.substr(i)).length() + 2; // +2 for '{' and '}'
212 }
213 return i;
214}
bool isId(int c)
Definition util.h:208

References extractAliasArgs(), and isId().

Referenced by countAliasArguments(), and replaceAliasArguments().

◆ isAliasCmd()

bool isAliasCmd ( std::string_view aliasCmd)

Definition at line 518 of file aliases.cpp.

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

References g_aliasInfoMap.

Referenced by DocPara::handleCommand().

◆ readAliases()

void readAliases ( )

Definition at line 161 of file aliases.cpp.

162{
163 // add aliases to a dictionary
164 const StringVector &aliasList = Config_getList(ALIASES);
165 for (const auto &al : aliasList)
166 {
168 }
169 for (auto &[name,overloads] : g_aliasInfoMap)
170 {
171 for (auto &[numParams,aliasInfo] : overloads)
172 {
173 aliasInfo.value = expandAlias(name+":"+std::to_string(numParams),aliasInfo.value);
174 }
175 }
176 for (auto &[name,overloads] : g_aliasInfoMap)
177 {
178 for (auto &[numParams,aliasInfo] : overloads)
179 {
180 aliasInfo.value = escapeAlias(aliasInfo.value);
181 }
182 }
183}
static std::string expandAlias(std::string_view aliasName, std::string_view aliasValue)
Definition aliases.cpp:504
static void addValidAliasToMap(std::string_view alias)
Definition aliases.cpp:56
static std::string escapeAlias(std::string_view value)
Definition aliases.cpp:151
#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 220 of file aliases.cpp.

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

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

Referenced by expandAliasRec().

◆ resolveAliasCmd()

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

Definition at line 494 of file aliases.cpp.

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

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

Referenced by replaceAliases().

Variable Documentation

◆ g_aliasInfoMap

AliasInfoMap g_aliasInfoMap
static

Definition at line 52 of file aliases.cpp.

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