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()

static 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 re1(R"(^\a[\w-]*\s*=)");
68 static const reg::Ex re2(R"(^\a[\w-]*{[^}]*}\s*=)");
69 if (reg::search(std::string{alias},re1) || reg::search(std::string{alias},re2)) // valid name= or name{...}= part
70 {
71 size_t i=alias.find('=');
72 assert(i!=std::string::npos); // based on re1 and re2 there is always a =
73 std::string name{ stripWhiteSpace(alias.substr(0,i)) };
74 aliasValue = alias.substr(i+1);
75 //printf("Alias: found name='%s' value='%s'\n",qPrint(name),qPrint(aliasValue));
76 size_t l = name.length();
77 size_t j = name.find('{');
78 if (j!=std::string::npos) // alias with parameters
79 {
80 if (name[l-1]=='}')
81 {
82 separator=",";
83 size_t k=j+1;
84 while (k<l-1 && isdigit(name[k])) k++;
85 numParams = atoi(name.substr(j+1,k-j-1).c_str());
86 if (numParams>0)
87 {
88 if (k<l-1) // we have a separator
89 {
90 size_t m=k;
91 while (m<l && isValidSeparator(name[m])) m++;
92 if (m<l-1)
93 {
94 err("Invalid alias '%s': invalid separator character '%c' (code %d), allowed characters: %s. Check your config file.\n",qPrint(alias),name[m],name[m],qPrint(std::string{separators}));
95 valid=false;
96 }
97 else
98 {
99 separator=name.substr(k,l-k-1);
100 }
101 }
102 if (valid) // valid alias with parameters
103 {
104 aliasName = name.substr(0,j);
105 Debug::print(Debug::Alias,0,"Alias definition: name='%s' #param='%d' separator='%s' value='%s'\n",
106 qPrint(aliasName),numParams,qPrint(separator),qPrint(aliasValue));
107 }
108 }
109 else
110 {
111 err("Invalid alias '%s': missing number of parameters. Check your config file.\n",qPrint(std::string{alias}));
112 valid=false;
113 }
114 }
115 else
116 {
117 err("Invalid alias '%s': missing closing bracket. Check your config file.\n",qPrint(std::string{alias}));
118 valid=false;
119 }
120 }
121 else // valid alias without parameters
122 {
123 aliasName = name;
124 numParams = 0;
125 Debug::print(Debug::Alias,0,"Alias definition: name='%s' value='%s'\n",qPrint(aliasName),qPrint(aliasValue));
126 }
127 }
128 else
129 {
130 err("Invalid alias '%s': invalid 'name=' or 'name{...}=' part. Check you config file.\n",qPrint(std::string{alias}));
131 valid=false;
132 }
133
134 if (valid) // alias definition passed all checks, so store it.
135 {
136 auto it = g_aliasInfoMap.find(aliasName);
137 if (it==g_aliasInfoMap.end()) // insert new alias
138 {
139 AliasOverloads overloads { { numParams, AliasInfo(aliasValue, separator) } };
140 g_aliasInfoMap.emplace(aliasName,overloads);
141 }
142 else // replace exiting alias with new definition
143 {
144 auto it2 = it->second.find(numParams);
145 if (it2==it->second.end()) // new alias overload for the given number of parameters
146 {
147 it->second.emplace(numParams, AliasInfo(aliasValue,separator));
148 }
149 else // replace alias with new definition
150 {
151 it2->second = AliasInfo(aliasValue,separator);
152 }
153 }
154 }
155}
std::unordered_map< int, AliasInfo > AliasOverloads
Definition aliases.cpp:39
static AliasInfoMap g_aliasInfoMap
Definition aliases.cpp:52
@ Alias
Definition debug.h:45
static void print(DebugMask mask, int prio, const char *fmt,...)
Definition debug.cpp:81
Class representing a regular expression.
Definition regex.h:39
#define err(fmt,...)
Definition message.h:84
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:748
const char * qPrint(const char *s)
Definition qcstring.h:672
std::string_view stripWhiteSpace(std::string_view s)
Given a string view s, returns a new, narrower view on that string, skipping over any leading or trai...
Definition stringutil.h:72

References Debug::Alias, err, g_aliasInfoMap, Debug::print(), qPrint(), reg::search(), and stripWhiteSpace().

Referenced by readAliases().

◆ countAliasArguments()

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

Definition at line 450 of file aliases.cpp.

451{
452 int count = 1;
453 size_t l = args.length();
454 for (size_t i=0;i<l;i++)
455 {
456 char c = args[i];
457 if (!sep.empty() &&
458 c==sep[0] && // start with separator character
459 (i==0 || args[i-1]!='\\') && // is not escaped
460 args.substr(i,sep.length())==sep) // whole separator matches
461 {
462 count++;
463 }
464 else if (c=='@' || c=='\\')
465 {
466 // check if this is the start of another aliased command (see bug704172)
467 i += findEndOfCommand(args.substr(i+1));
468 }
469 }
470 //printf("countAliasArguments(%s,sep=%s)=%d\n",qPrint(args),qPrint(sep),count);
471 return count;
472}
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:214

References findEndOfCommand().

Referenced by expandAliasRec().

◆ escapeAlias()

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

Definition at line 160 of file aliases.cpp.

161{
162 std::string newValue = substituteStringView(value,"^^ ","@ilinebr ");
163 newValue = substituteStringView(newValue,"^^","@ilinebr ");
164 //printf("escapeAlias('%s')='%s'\n",qPrint(std::string{value}),qPrint(newValue));
165 return newValue;
166}
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()

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

Definition at line 323 of file aliases.cpp.

324{
325 if (s.empty() || sep.empty()) return s;
326 std::string result;
327 result.reserve(s.length()+10);
328 size_t i, p=0, l=sep.length();
329 while ((i=s.find(sep,p))!=std::string::npos)
330 {
331 result += s.substr(p,i-p);
332 if (i>0 && s[i-1]!='\\') // escape the separator
333 {
334 result += '\\';
335 }
336 result += s.substr(i,l);
337 p = i+l;
338 }
339 result += s.substr(p);
340 //printf("escapeSeparators(%s,sep='%s')=%s\n",qPrint(s),qPrint(sep),qPrint(result));
341 return result;
342}

Referenced by expandAliasRec().

◆ expandAlias()

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

Definition at line 514 of file aliases.cpp.

515{
516 std::string result;
517 StringUnorderedSet aliasesProcessed;
518 // avoid expanding this command recursively
519 aliasesProcessed.insert(std::string{aliasName});
520 // expand embedded commands
521 //printf("Expanding: '%s'->'%s'\n",qPrint(aliasName),qPrint(aliasValue));
522 result = expandAliasRec(aliasesProcessed,aliasValue);
523 //printf("Expanding result: '%s'->'%s'\n",qPrint(aliasName),qPrint(result));
524 Debug::print(Debug::Alias,0,"Expanding alias: input='%s' result='%s'\n",qPrint(std::string{aliasValue}),qPrint(result));
525 return result;
526}
static std::string expandAliasRec(StringUnorderedSet &aliasesProcessed, std::string_view s, bool allowRecursion=FALSE)
Definition aliases.cpp:344
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29

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

Referenced by readAliases().

◆ expandAliasRec()

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

Definition at line 344 of file aliases.cpp.

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

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

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

◆ extractAliasArgs()

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

Definition at line 474 of file aliases.cpp.

475{
476 int bc = 0;
477 char prevChar = 0;
478 if (!args.empty() && args[0]=='{') // alias has argument
479 {
480 for (size_t i=0;i<args.length();i++)
481 {
482 char c = args[i];
483 if (prevChar!='\\') // not escaped
484 {
485 if (c=='{') bc++;
486 if (c=='}') bc--;
487 prevChar=c;
488 }
489 else
490 {
491 prevChar=0;
492 }
493
494 if (bc==0)
495 {
496 //printf("extractAliasArgs('%s')->'%s'\n",qPrint(args),qPrint(args.substr(1,i-1)));
497 return std::string{args.substr(1,i-1)};
498 }
499 }
500 }
501 return std::string{};
502}

Referenced by expandAliasRec(), and findEndOfCommand().

◆ findEndOfCommand()

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

215{
216 char c=' ';
217 size_t i=0;
218 if (!s.empty())
219 {
220 while (i<s.length() && (c=s[i]) && isId(c)) i++;
221 if (c=='{') i+=extractAliasArgs(s.substr(i)).length()+2; // +2 for '{' and '}'
222 }
223 return i;
224}
bool isId(int c)
Definition util.h:202

References extractAliasArgs(), and isId().

Referenced by countAliasArguments(), and replaceAliasArguments().

◆ isAliasCmd()

bool isAliasCmd ( std::string_view aliasCmd)

Definition at line 528 of file aliases.cpp.

529{
530 return g_aliasInfoMap.find(std::string{aliasCmd}) != g_aliasInfoMap.end();
531}

References g_aliasInfoMap.

Referenced by DocPara::handleCommand().

◆ readAliases()

void readAliases ( )

Definition at line 170 of file aliases.cpp.

171{
172 // add aliases to a dictionary
173 const StringVector &aliasList = Config_getList(ALIASES);
174 for (const auto &al : aliasList)
175 {
177 }
178 for (auto &[name,overloads] : g_aliasInfoMap)
179 {
180 for (auto &[numParams,aliasInfo] : overloads)
181 {
182 aliasInfo.value = expandAlias(name+":"+std::to_string(numParams),aliasInfo.value);
183 }
184 }
185 for (auto &[name,overloads] : g_aliasInfoMap)
186 {
187 for (auto &[numParams,aliasInfo] : overloads)
188 {
189 aliasInfo.value = escapeAlias(aliasInfo.value);
190 }
191 }
192}
static std::string expandAlias(std::string_view aliasName, std::string_view aliasValue)
Definition aliases.cpp:514
static void addValidAliasToMap(std::string_view alias)
Definition aliases.cpp:56
static std::string escapeAlias(std::string_view value)
Definition aliases.cpp:160
#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()

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

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

505{
506 StringUnorderedSet aliasesProcessed;
507 //printf("Expanding: '%s'\n",qPrint(aliasCmd));
508 std::string result = expandAliasRec(aliasesProcessed,aliasCmd);
509 //printf("Expanding result: '%s'->'%s'\n",qPrint(aliasCmd),qPrint(result));
510 Debug::print(Debug::Alias,0,"Resolving alias: cmd='%s' result='%s'\n",qPrint(std::string{aliasCmd}),qPrint(result));
511 return result;
512}

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

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().