Doxygen
dotdirdeps.cpp 文件参考
#include "dotdirdeps.h"
#include "util.h"
#include "doxygen.h"
#include "config.h"
#include "image.h"
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstdint>
#include <math.h>
#include <cassert>
#include <map>
#include <memory>
#include <string>
#include <vector>
+ dotdirdeps.cpp 的引用(Include)关系图:

浏览源代码.

struct  DotDirProperty
 Properties are used to format the directories in the graph distinctively. 更多...
 
class  DotDirPropertyBuilder
 Builder helper to create instances of the DotDirProperty struct 更多...
 

类型定义

using DirDefMap = std::map< std::string, const DirDef * >
 
typedef std::vector< std::pair< std::unique_ptr< DirRelation >, bool > > DirRelations
 Elements consist of (1) directory relation and (2) whether it is pointing only to inherited dependees. 更多...
 

函数

static QCString getDirectoryBackgroundColor (int depthIndex)
 Returns a DOT color name according to the directory depth. 更多...
 
static const char * getDirectoryBorderColor (const DotDirProperty &property)
 Returns a DOT color name according to the directory properties. 更多...
 
static std::string getDirectoryBorderStyle (const DotDirProperty &property)
 Returns a DOT node style according to the directory properties. 更多...
 
static void drawDirectory (TextStream &t, const DirDef *const directory, const DotDirProperty &property, DirDefMap &directoriesInGraph, int startLevel)
 Puts DOT code for drawing directory to stream and adds it to the list. 更多...
 
static bool isAtMaxDepth (const DirDef *const directory, const int startLevel)
 Checks, if the directory is a the maximum drawn directory level. 更多...
 
static void drawClusterOpening (TextStream &outputStream, const DirDef *const directory, const DotDirProperty &directoryProperty, DirDefMap &directoriesInGraph, const bool isAncestor, int startLevel)
 Writes DOT code for opening a cluster subgraph to stream. 更多...
 
static void drawClusterClosing (TextStream &t)
 
static void addDependencies (DirRelations &dependencies, const DirDef *const srcDir, bool isLeaf)
 Assembles a list of the directory relations and whether or not they result from "inheritance". 更多...
 
static void drawTree (DirRelations &dependencies, TextStream &t, const DirDef *const directory, int startLevel, DirDefMap &directoriesInGraph, const bool isTreeRoot)
 Recursively draws directory tree. 更多...
 
void writeDotDirDepGraph (TextStream &t, const DirDef *dd, bool linkRelations)
 Write DOT code for directory dependency graph. 更多...
 

类型定义说明

◆ DirDefMap

using DirDefMap = std::map<std::string,const DirDef *>

在文件 dotdirdeps.cpp33 行定义.

◆ DirRelations

typedef std::vector< std::pair< std::unique_ptr<DirRelation>, bool> > DirRelations

Elements consist of (1) directory relation and (2) whether it is pointing only to inherited dependees.

在文件 dotdirdeps.cpp60 行定义.

函数说明

◆ addDependencies()

static void addDependencies ( DirRelations dependencies,
const DirDef *const  srcDir,
bool  isLeaf 
)
static

Assembles a list of the directory relations and whether or not they result from "inheritance".

参数
dependenciesArray to add the dependencies to.
srcDiris the source of the dependency.
isLeaftrue, if no children are drawn for this directory.

在文件 dotdirdeps.cpp212 行定义.

213 {
214  for (const auto &usedDirectory : srcDir->usedDirs())
215  {
216  const auto dstDir = usedDirectory->dir();
217  if (!dstDir->isParentOf(srcDir) && (isLeaf || usedDirectory->hasDirectSrcDeps()))
218  {
219  QCString relationName;
220  relationName.sprintf("dir_%06d_%06d", srcDir->dirCount(), dstDir->dirCount());
221  bool directRelation = isLeaf ? usedDirectory->hasDirectDstDeps() : usedDirectory->hasDirectDeps();
222  auto &&dependency = std::make_unique<DirRelation>(relationName, srcDir, usedDirectory.get());
223  auto &&pair = std::make_pair(std::move(dependency),directRelation);
224  dependencies.emplace_back(std::move(pair));
225  }
226  }
227 }

引用了 DirDef::dirCount(), QCString::sprintf() , 以及 DirDef::usedDirs().

被这些函数引用 drawTree().

◆ drawClusterClosing()

static void drawClusterClosing ( TextStream t)
static

在文件 dotdirdeps.cpp201 行定义.

202 {
203  t << " }\n";
204 }

被这些函数引用 drawTree().

◆ drawClusterOpening()

static void drawClusterOpening ( TextStream outputStream,
const DirDef *const  directory,
const DotDirProperty directoryProperty,
DirDefMap directoriesInGraph,
const bool  isAncestor,
int  startLevel 
)
static

Writes DOT code for opening a cluster subgraph to stream.

Ancestor clusters directly get a label. Other clusters get a plain text node with a label instead. This is because the plain text node can be used to draw dependency relationships.

在文件 dotdirdeps.cpp174 行定义.

176 {
177  outputStream << " subgraph cluster" << directory->getOutputFileBase() << " {\n"
178  " graph [ "
179  "bgcolor=\"" << getDirectoryBackgroundColor(directory->level()-startLevel) << "\", "
180  "pencolor=\"" << getDirectoryBorderColor(directoryProperty) << "\", "
181  "style=\"" << getDirectoryBorderStyle(directoryProperty) << "\", "
182  "label=\"";
183  if (isAncestor)
184  {
185  outputStream << directory->shortName();
186  }
187  outputStream << "\", "
188  "fontname=\"" << Config_getString(DOT_FONTNAME) << "\", "
189  "fontsize=\"" << Config_getInt(DOT_FONTSIZE) << "\", "
190  "URL=\"" << addHtmlExtensionIfMissing(directory->getOutputFileBase()) << "\""
191  "]\n";
192  if (!isAncestor)
193  {
194  outputStream << " " << directory->getOutputFileBase() << " [shape=plaintext, "
195  "label=\"" << directory->shortName() << "\""
196  "];\n";
197  directoriesInGraph.insert(std::make_pair(directory->getOutputFileBase().str(), directory));
198  }
199 }

引用了 addHtmlExtensionIfMissing(), Config_getInt, Config_getString, getDirectoryBackgroundColor(), getDirectoryBorderColor(), getDirectoryBorderStyle(), DirDef::getOutputFileBase(), QCString::insert(), DirDef::level(), DirDef::shortName() , 以及 QCString::str().

被这些函数引用 drawTree() , 以及 writeDotDirDepGraph().

◆ drawDirectory()

static void drawDirectory ( TextStream t,
const DirDef *const  directory,
const DotDirProperty property,
DirDefMap directoriesInGraph,
int  startLevel 
)
static

Puts DOT code for drawing directory to stream and adds it to the list.

参数
[in,out]tstream to which the DOT code is written to
[in]directorywill be mapped to a node in DOT code
[in]propertyare evaluated for formatting
[in,out]directoriesInGraphlists the directories which have been written to the output stream
[in]startLevelcurrent level to calculate relative distances from to determine the background color

在文件 dotdirdeps.cpp148 行定义.

150 {
151  t << " " << directory->getOutputFileBase() << " ["
152  "shape=box, "
153  "label=\"" << directory->shortName() << "\", "
154  "style=\"" << getDirectoryBorderStyle(property) << "\", "
155  "fillcolor=\"" << getDirectoryBackgroundColor(directory->level()-startLevel) << "\", "
156  "color=\"" << getDirectoryBorderColor(property) << "\", "
157  "URL=\"" << addHtmlExtensionIfMissing(directory->getOutputFileBase()) << "\""
158  "];\n";
159  directoriesInGraph.insert(std::make_pair(directory->getOutputFileBase().str(), directory));
160 }

引用了 addHtmlExtensionIfMissing(), getDirectoryBackgroundColor(), getDirectoryBorderColor(), getDirectoryBorderStyle(), DirDef::getOutputFileBase(), QCString::insert(), DirDef::level(), DirDef::shortName() , 以及 QCString::str().

被这些函数引用 drawTree().

◆ drawTree()

static void drawTree ( DirRelations dependencies,
TextStream t,
const DirDef *const  directory,
int  startLevel,
DirDefMap directoriesInGraph,
const bool  isTreeRoot 
)
static

Recursively draws directory tree.

在文件 dotdirdeps.cpp230 行定义.

232 {
233  if (!directory->hasSubdirs())
234  {
235  const DotDirProperty directoryProperty = DotDirPropertyBuilder().makeOriginal(isTreeRoot);
236  drawDirectory(t, directory, directoryProperty, directoriesInGraph,startLevel);
237  addDependencies(dependencies, directory, true);
238  }
239  else
240  {
241  if (isAtMaxDepth(directory, startLevel)) // maximum nesting level reached
242  {
243  const DotDirProperty directoryProperty = DotDirPropertyBuilder().makeOriginal(isTreeRoot);
244  drawDirectory(t, directory, directoryProperty, directoriesInGraph,startLevel);
245  addDependencies(dependencies, directory, true);
246  }
247  else // start a new nesting level
248  {
249  // open cluster
250  {
251  const DotDirProperty directoryProperty = DotDirPropertyBuilder().makeOriginal(isTreeRoot);
252  drawClusterOpening(t, directory, directoryProperty, directoriesInGraph, false, startLevel);
253  addDependencies(dependencies, directory, false);
254  }
255 
256  // process all sub directories
257  for (const auto subDirectory : directory->subDirs())
258  {
259  drawTree(dependencies, t, subDirectory, startLevel, directoriesInGraph, false);
260  }
261 
262  // close cluster
263  {
265  }
266  }
267  }
268 }

引用了 addDependencies(), drawClusterClosing(), drawClusterOpening(), drawDirectory(), DirDef::hasSubdirs(), isAtMaxDepth(), DotDirPropertyBuilder::makeOriginal() , 以及 DirDef::subDirs().

◆ getDirectoryBackgroundColor()

static QCString getDirectoryBackgroundColor ( int  depthIndex)
static

Returns a DOT color name according to the directory depth.

在文件 dotdirdeps.cpp63 行定义.

64 {
65  static int hue = Config_getInt(HTML_COLORSTYLE_HUE);
66  static int sat = Config_getInt(HTML_COLORSTYLE_SAT);
67  static int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
68  assert(depthIndex>=0 && depthIndex<=Config_getInt(DIR_GRAPH_MAX_DEPTH));
69  float fraction = (float)depthIndex/(float)Config_getInt(DIR_GRAPH_MAX_DEPTH);
70  const char hex[] = "0123456789abcdef";
71  int range = 0x40; // range from darkest color to lightest color
72  int luma = 0xef-(int)(fraction*range); // interpolation
73  double r,g,b;
74  ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
75  pow(luma/255.0,gamma/100.0),&r,&g,&b);
76  int red = (int)(r*255.0);
77  int green = (int)(g*255.0);
78  int blue = (int)(b*255.0);
79  assert(red>=0 && red<=255);
80  assert(green>=0 && green<=255);
81  assert(blue>=0 && blue<=255);
82  char colStr[8];
83  colStr[0]='#';
84  colStr[1]=hex[red>>4];
85  colStr[2]=hex[red&0xf];
86  colStr[3]=hex[green>>4];
87  colStr[4]=hex[green&0xf];
88  colStr[5]=hex[blue>>4];
89  colStr[6]=hex[blue&0xf];
90  colStr[7]=0;
91  //printf("i=%d max=%d fraction=%f luma=%d %02x %02x %02x -> color=%s\n",
92  // depthIndex,Config_getInt(DIR_GRAPH_MAX_DEPTH),fraction,luma,red,green,blue,colStr);
93  return colStr;
94 }

引用了 Config_getInt, hex , 以及 ColoredImage::hsl2rgb().

被这些函数引用 drawClusterOpening() , 以及 drawDirectory().

◆ getDirectoryBorderColor()

static const char* getDirectoryBorderColor ( const DotDirProperty property)
static

Returns a DOT color name according to the directory properties.

在文件 dotdirdeps.cpp97 行定义.

98 {
99  if (property.isTruncated && property.isOrphaned)
100  {
101  return "red";
102  }
103  else if (property.isTruncated)
104  {
105  return "red";
106  }
107  else if (property.isOrphaned)
108  {
109  return "grey50";
110  }
111  else
112  {
113  return "grey25";
114  }
115 }

引用了 DotDirProperty::isOrphaned , 以及 DotDirProperty::isTruncated.

被这些函数引用 drawClusterOpening() , 以及 drawDirectory().

◆ getDirectoryBorderStyle()

static std::string getDirectoryBorderStyle ( const DotDirProperty property)
static

Returns a DOT node style according to the directory properties.

在文件 dotdirdeps.cpp118 行定义.

119 {
120  std::string style;
121  if (!property.isPeripheral)
122  {
123  style += "filled,";
124  }
125  if (property.isOriginal)
126  {
127  style += "bold,";
128  }
129  if (property.isIncomplete)
130  {
131  style += "dashed,";
132  }
133  else if (property.isTruncated && property.isOrphaned)
134  {
135  style += "dashed,";
136  }
137  return style;
138 }

引用了 DotDirProperty::isIncomplete, DotDirProperty::isOriginal, DotDirProperty::isOrphaned, DotDirProperty::isPeripheral , 以及 DotDirProperty::isTruncated.

被这些函数引用 drawClusterOpening() , 以及 drawDirectory().

◆ isAtMaxDepth()

static bool isAtMaxDepth ( const DirDef *const  directory,
const int  startLevel 
)
static

Checks, if the directory is a the maximum drawn directory level.

在文件 dotdirdeps.cpp163 行定义.

164 {
165  return (directory->level() - startLevel) >= Config_getInt(DIR_GRAPH_MAX_DEPTH);
166 }

引用了 Config_getInt , 以及 DirDef::level().

被这些函数引用 drawTree().

◆ writeDotDirDepGraph()

void writeDotDirDepGraph ( TextStream t,
const DirDef dd,
bool  linkRelations 
)

Write DOT code for directory dependency graph.

Code is generated for a directory. Successors (sub-directories) of this directory are recursively drawn. Recursion is limited by DIR_GRAPH_MAX_DEPTH. The dependencies of those directories are drawn.

If a dependee is not part of directory tree above, then the dependency is drawn to the first parent of the dependee, whose parent is an ancestor (sub-directory) of the original directory.

参数
tstream where the DOT code is written to
dddirectory for which the graph is generated for
linkRelationsif true, hyperlinks to the list of file dependencies are added

在文件 dotdirdeps.cpp284 行定义.

285 {
286  DirDefMap dirsInGraph;
287 
288  dirsInGraph.insert(std::make_pair(dd->getOutputFileBase().str(),dd));
289 
290  std::vector<const DirDef *> usedDirsNotDrawn, usedDirsDrawn;
291  for (const auto& usedDir : dd->usedDirs())
292  {
293  usedDirsNotDrawn.push_back(usedDir->dir());
294  }
295 
296  auto moveDrawnDirs = [&usedDirsDrawn,&usedDirsNotDrawn](const std::vector<const DirDef *>::iterator &newEnd)
297  {
298  // usedDirsNotDrawn is split into two segments: [begin()....newEnd-1] and [newEnd....end()]
299  // where the second segment starting at newEnd has been drawn, so append this segment to the usedDirsDrawn list and
300  // remove it from the usedDirsNotDrawn list.
301  std::move(newEnd, std::end(usedDirsNotDrawn), std::back_inserter(usedDirsDrawn));
302  usedDirsNotDrawn.erase(newEnd, usedDirsNotDrawn.end());
303  };
304 
305  // if dd has a parent draw it as the outer layer
306  const auto parent = dd->parent();
307  if (parent)
308  {
309  const DotDirProperty parentDirProperty = DotDirPropertyBuilder().
310  makeIncomplete().
311  makeOrphaned(parent->parent()!=nullptr);
312  drawClusterOpening(t, parent, parentDirProperty, dirsInGraph, true, parent->level());
313 
314  {
315  // draw all directories which have `dd->parent()` as parent and `dd` as dependent
316  const auto newEnd = std::stable_partition(usedDirsNotDrawn.begin(), usedDirsNotDrawn.end(),
317  [&](const DirDef *const usedDir)
318  {
319  if (dd!=usedDir && dd->parent()==usedDir->parent()) // usedDir and dd share the same parent
320  {
321  const DotDirProperty usedDirProperty = DotDirPropertyBuilder().makeTruncated(usedDir->hasSubdirs());
322  drawDirectory(t, usedDir, usedDirProperty, dirsInGraph, parent->level());
323  return false; // part of the drawn partition
324  }
325  return true; // part of the not-drawn partition
326  });
327  moveDrawnDirs(newEnd);
328  }
329  }
330 
331  // draw the directory tree with dd as root
332  DirRelations dependencies;
333  drawTree(dependencies, t, dd, dd->level(), dirsInGraph, true);
334 
335  if (parent)
336  {
338  }
339 
340  // add nodes for other used directories (i.e. outside of the cluster of directories directly connected to dd)
341  {
342  const auto newEnd = std::stable_partition(usedDirsNotDrawn.begin(), usedDirsNotDrawn.end(),
343  [&](const DirDef *const usedDir) // for each used dir (=directly used or a parent of a directly used dir)
344  {
345  const DirDef *dir=dd;
346  while (dir)
347  {
348  if (dir!=usedDir && dir->parent()==usedDir->parent()) // include if both have the same parent (or no parent)
349  {
350  const DotDirProperty usedDirProperty = DotDirPropertyBuilder().
351  makeOrphaned(usedDir->parent()!=nullptr).
352  makeTruncated(usedDir->hasSubdirs()).
353  makePeripheral();
354  drawDirectory(t, usedDir, usedDirProperty, dirsInGraph, dir->level());
355  return false; // part of the drawn partition
356  }
357  dir=dir->parent();
358  }
359  return true; // part of the not-drawn partition
360  });
361  moveDrawnDirs(newEnd);
362  }
363 
364  // add relations between all selected directories
365  {
366  for (const auto &relationPair : dependencies)
367  {
368  const auto &relation = relationPair.first;
369  const bool directRelation = relationPair.second;
370  const auto udir = relation->destination();
371  const auto usedDir = udir->dir();
372  const bool destIsSibling = std::find(std::begin(usedDirsDrawn), std::end(usedDirsDrawn), usedDir) != std::end(usedDirsDrawn);
373  const bool destIsDrawn = dirsInGraph.find(usedDir->getOutputFileBase().str())!=dirsInGraph.end(); // only point to nodes that are in the graph
374  const bool atMaxDepth = isAtMaxDepth(usedDir, dd->level());
375 
376  if (destIsSibling || (destIsDrawn && (directRelation || atMaxDepth)))
377  {
378  const auto relationName = relation->getOutputFileBase();
379  const auto dir = relation->source();
380  Doxygen::dirRelations.add(relationName,
381  std::make_unique<DirRelation>(
382  relationName,dir,udir));
383  size_t nrefs = udir->filePairs().size();
384  t << " " << dir->getOutputFileBase() << "->"
385  << usedDir->getOutputFileBase();
386  t << " [headlabel=\"" << (uint)nrefs << "\", labeldistance=1.5";
387  if (linkRelations)
388  {
389  t << " headhref=\"" << addHtmlExtensionIfMissing(relationName) << "\"";
390  }
391  t << "];\n";
392  }
393  }
394  }
395 }

引用了 drawClusterOpening(), end(), DirDef::getOutputFileBase(), DirDef::parent(), QCString::str() , 以及 DirDef::usedDirs().

被这些函数引用 DotDirDeps::computeTheGraph().

ColoredImage::hsl2rgb
static void hsl2rgb(double h, double s, double l, double *pRed, double *pGreen, double *pBlue)
Definition: image.cpp:412
DotDirProperty::isIncomplete
bool isIncomplete
true if not all successors of a cluster are drawn
Definition: dotdirdeps.cpp:38
DirRelations
std::vector< std::pair< std::unique_ptr< DirRelation >, bool > > DirRelations
Elements consist of (1) directory relation and (2) whether it is pointing only to inherited dependees...
Definition: dotdirdeps.cpp:60
DirDef::dirCount
virtual int dirCount() const =0
DirDef::usedDirs
virtual const UsedDirLinkedMap & usedDirs() const =0
DotDirProperty::isOrphaned
bool isOrphaned
true if parent is not drawn
Definition: dotdirdeps.cpp:39
DirDef
A model of a directory symbol.
Definition: dirdef.h:110
LinkedMap::add
T * add(const char *k, Args &&... args)
Adds a new object to the ordered vector if it was not added already.
Definition: linkedmap.h:103
DotDirProperty::isTruncated
bool isTruncated
true has successors, none is drawn
Definition: dotdirdeps.cpp:40
QCString::str
std::string str() const
Definition: qcstring.h:442
DotDirProperty::isPeripheral
bool isPeripheral
true if no successor of parent of original directory
Definition: dotdirdeps.cpp:42
drawClusterOpening
static void drawClusterOpening(TextStream &outputStream, const DirDef *const directory, const DotDirProperty &directoryProperty, DirDefMap &directoriesInGraph, const bool isAncestor, int startLevel)
Writes DOT code for opening a cluster subgraph to stream.
Definition: dotdirdeps.cpp:174
DirDef::hasSubdirs
virtual bool hasSubdirs() const =0
DirDef::level
virtual int level() const =0
begin
DirIterator begin(DirIterator it) noexcept
Definition: dir.cpp:123
end
DirIterator end(const DirIterator &) noexcept
Definition: dir.cpp:128
getDirectoryBackgroundColor
static QCString getDirectoryBackgroundColor(int depthIndex)
Returns a DOT color name according to the directory depth.
Definition: dotdirdeps.cpp:63
uint
unsigned uint
Definition: qcstring.h:40
DotDirProperty
Properties are used to format the directories in the graph distinctively.
Definition: dotdirdeps.cpp:36
DirDef::getOutputFileBase
virtual QCString getOutputFileBase() const =0
addHtmlExtensionIfMissing
QCString addHtmlExtensionIfMissing(const QCString &fName)
Definition: util.cpp:5275
DirDef::shortName
virtual const QCString shortName() const =0
Config_getInt
#define Config_getInt(name)
Definition: config.h:34
QCString::insert
QCString & insert(size_t index, const QCString &s)
Definition: qcstring.h:274
drawTree
static void drawTree(DirRelations &dependencies, TextStream &t, const DirDef *const directory, int startLevel, DirDefMap &directoriesInGraph, const bool isTreeRoot)
Recursively draws directory tree.
Definition: dotdirdeps.cpp:230
DirDefMap
std::map< std::string, const DirDef * > DirDefMap
Definition: dotdirdeps.cpp:33
DotDirPropertyBuilder
Builder helper to create instances of the DotDirProperty struct
Definition: dotdirdeps.cpp:46
getDirectoryBorderStyle
static std::string getDirectoryBorderStyle(const DotDirProperty &property)
Returns a DOT node style according to the directory properties.
Definition: dotdirdeps.cpp:118
drawDirectory
static void drawDirectory(TextStream &t, const DirDef *const directory, const DotDirProperty &property, DirDefMap &directoriesInGraph, int startLevel)
Puts DOT code for drawing directory to stream and adds it to the list.
Definition: dotdirdeps.cpp:148
Doxygen::dirRelations
static DirRelationLinkedMap dirRelations
Definition: doxygen.h:110
drawClusterClosing
static void drawClusterClosing(TextStream &t)
Definition: dotdirdeps.cpp:201
hex
static const char * hex
Definition: htmldocvisitor.cpp:65
Config_getString
#define Config_getString(name)
Definition: config.h:32
getDirectoryBorderColor
static const char * getDirectoryBorderColor(const DotDirProperty &property)
Returns a DOT color name according to the directory properties.
Definition: dotdirdeps.cpp:97
DotDirProperty::isOriginal
bool isOriginal
true if is the directory for which the graph is drawn
Definition: dotdirdeps.cpp:41
isAtMaxDepth
static bool isAtMaxDepth(const DirDef *const directory, const int startLevel)
Checks, if the directory is a the maximum drawn directory level.
Definition: dotdirdeps.cpp:163
addDependencies
static void addDependencies(DirRelations &dependencies, const DirDef *const srcDir, bool isLeaf)
Assembles a list of the directory relations and whether or not they result from "inheritance".
Definition: dotdirdeps.cpp:212
DotDirPropertyBuilder::makeOriginal
DotDirPropertyBuilder & makeOriginal(bool b=true)
Definition: dotdirdeps.cpp:52
DirDef::subDirs
virtual const DirList & subDirs() const =0
DirDef::parent
virtual DirDef * parent() const =0
QCString::sprintf
QCString & sprintf(const char *format,...)
Definition: qcstring.cpp:24
QCString
This is an alternative implementation of QCString.
Definition: qcstring.h:108