Doxygen XLinks
by
V: 2511R0
Website: doxygen
Loading...
Searching...
No Matches
sourcereplacer.cpp
1//==================================================================================================
2// This implementation-file is part of DoxygenXLinks - A doxygen post-processor that allows to
3// define smarter <b>Doxygen</b>-links.
4//
5// \emoji :copyright: 2025-2026 A-Worx GmbH, Germany.
6// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8#include "jobs.hpp"
9#include "dxl.hpp"
10#include "dxlapp.hpp"
11#include "mappedfile.hpp"
12#include "ALib.ALox.H"
13#include "ALib.App.H" // TODO(251204 09:14): we need this only for the definition of LOX_LOX.
14 // How can we avoid to include the whole app?
15
16#include <iostream>
17#include <fstream>
18
19using namespace alib;
20using namespace std;
21
22namespace dxl {
23
25 Lox_SetDomain("DXL/SREPL/JOB", Scope::Method )
26 Lox_Info( "Reading source file {!Q} of size {}", srcFileNode.Name(),
28 dxl.Stats.ReplSourceFileSize.fetch_add(int(srcFileNode->Size()));
29
30 Path path;
31 {ALIB_LOCK_SHARED_WITH(dxl.GetSourceTreeLock())
32 srcFileNode.AssembleRealPath(path, lang::Inclusion::Include);
33 }
34
35 // read exclamations applicable to this file once
37 dxl.Exclamations.Get(srcFileNode.Name(), exclamations);
38
40
41 Lox_Info("Reading source file: {}", path )
42 MappedFile& sourceFile= poolWorker->InputFile;
44 try {
45 mfc= sourceFile.Open(path.Terminate(), srcFileNode->Size(), false);
46 } catch (std::exception&) {
47 app.cErr->Add(app.cli.ExitCodeDecls.Find(ExitCodes::CantOpenReplSrcFile).Mapped()->FormatString(),
48 path);
49 app.machine.SetExitCode(ExitCodes::CantOpenReplSrcFile);
50 return true;
51 }
52
53 // output buffer (fileSize * 3). And: we add some padding bytes, to be able to test
54 // backward contents without checking actual write-size.
55 AString& writeBuffer= poolWorker->WriteBuffer;
56 writeBuffer.EnsureRemainingCapacity(integer(srcFileNode->Size() * 3));
57 constexpr integer writeBufferPadSize= 20;
58 writeBuffer._(Fill(0, writeBufferPadSize));
59 char* writeBufferStart= writeBuffer.VBuffer();
60 char* wb = writeBufferStart + writeBufferPadSize;
61
62 // if the application has an exit code, we stop right now
63 // todo: this periodical check makes sense but was never tested, yet.
64 // it has to go into the loop below and also to other jobs
65 if ( app.machine.GetExitCode().Integral() )
66 return true;
67
68 // we want maximum speed and even spare the fast log calls in the loop.
69 Verbosity verbosity;
70 Lox_GetVerbosity(verbosity)
71
72 //------------------- main loop -------------------
73 bool fileChanged = false;
74 int lineNo = 1;
75 String512 linkString;
76 size_t lineStartRemaining = mfc.Remaining();
77 while (!mfc.IsEOF()) {
78 char c= char( mfc.Next<NC>() );
79 if (c == '\n') { *wb++= '\n'; lineNo++; lineStartRemaining= mfc.Remaining(); continue; }
80 if (c != '#' ) { *wb++= c; continue; }
81
82 if (mfc.Remaining()<3) {
83 *wb++= c;
84 while (mfc.Remaining()){
85 c= char(mfc.Next<NC>());
86 if ( c == '\n')
87 ++lineNo;
88 *wb++= c;
89 }
90 break;
91 }
92
93 //---- 2nd character ----
94 c= char(mfc.Next<NC>());
95
96 // if double hash is given, skip it.
97 // Note: This is mainly needed for the documentation of this tool ;-)
98 if (c == '#' ) { *wb++= '#'; *wb++= '#'; continue;}
99
100 // not '"' ?
101 if ( c != '\"' ) {
102 *wb++= '#';
103 *wb++= c;
104 if ( c == '\n')
105 ++lineNo;
106 continue;
107 }
108
109 //---- 3rd character: not an allowed link start? ----
110 c= char(mfc.Next<NC>());
111 if ( !isalpha(c) && String(".%^_<").IndexOf(c) < 0 ) {
112 *wb++= '#';
113 *wb++= '\"';
114 *wb++= c;
115 if ( c == '\n')
116 ++lineNo;
117 continue;
118 }
119
120 int colNo= int(lineStartRemaining - mfc.Remaining() - 2);
121
122 // search for exclamations
123 { auto exclIt= exclamations.begin();
124 for (; exclIt!=exclamations.end(); ++exclIt )
125 if ( (*exclIt)->Matches(lineNo, colNo ) )
126 break;
127 if (exclIt != exclamations.end()) {
128 *wb++= '#';
129 *wb++= '\"';
130 *wb++= c;
131 continue;
132 } }
133
134 // This seems to be an XLink!
135 bool suppressedAnchor;
136 linkString.Reset(c); {
137 bool foundEnd= false;
138 while (mfc.Remaining()) {
139 c= char(mfc.Next<NC>());
140 if ( c == '\\') { linkString._<NC>(c); linkString._<NC>(char(mfc.Next())); continue; }
141 if ( c == '\"') { foundEnd= true; break;}
142 if ( c == '\n') { lineNo++; break; }
143 linkString._<NC>(c);
144 if (linkString.Length() == 511 ) {
145 Lox_Warning( "Found unterminated XLink pattern {!Q} in HTML file {}:{}:{}",
146 linkString, path, lineNo, colNo )
147 break;
148 }
149 }
150
151 suppressedAnchor= linkString.CharAtStart() == '%';
152
153 // end not found?
154 if (!foundEnd) {
155 *wb++= '#';
156 *wb++= '\"';
157 for ( auto lsC : linkString )
158 *wb++= lsC;
159 *wb++= '\n';
160 Lox_Warning( "Found unterminated XLink pattern {!Q} in replacement source file {}:{}:{}",
161 linkString, path, lineNo -1, colNo )
162 continue;
163 }
164
165 if (suppressedAnchor)
166 linkString[0]= ' ';
167 linkString.Trim();
168 }
169
170 if (verbosity >= Verbosity::Info)
171 Lox_Info( "Found XLink pattern {!Q} in replacement source file {}:{}:{}",
172 linkString, path, lineNo, colNo )
173
174 XLink* xLink= dxl.GetXLink(linkString, File());
175 // attach this source file to the XLink and vice versa
176 {ALIB_LOCK_WITH(xLink->Lock)
177 xLink->SourceLocations.push_back({srcFileNode, lineNo, colNo}); }
178 { ALIB_LOCK_WITH(dxl.GetSourceTreeLock())
179 if (!srcFileNode.HasCustomData())
180 srcFileNode.AttachCustomData<XLinkList>(dxl.GetSourceTree().GetAllocator());
181 srcFileNode.GetCustomData<XLinkList>().emplace_back( xLink, lineNo, colNo );
182 }
183
184 // re-activate AString
185 ALIB_ASSERT_ERROR(wb - writeBufferStart < writeBuffer.Capacity(), "DXL/SREPL/JOB",
186 "Write buffer overflow detected" )
187 writeBuffer.SetLength(wb - writeBufferStart);
188
189 // not resolved?
190 if ( !xLink->IsResolved() ) {
191 // paste the original XLink to the output
192 writeBuffer._<NC>( "#")._<NC>( "\"");
193 if ( suppressedAnchor )
194 writeBuffer._<NC>( "%");
195 writeBuffer._<NC>(linkString)._<NC>( "\"");
196 } else {
197 fileChanged= true;
198 auto& node = xLink->Result().Node;
199
200 // write replacement
201 String256 entityPath;
203 ALIB_LOCK_SHARED_WITH(node.Index().SLock)
204 ( node.IsA(Target::EnumElement) ? node.Parent() : node ).Path(entityPath);
205 if( node.IsA(Target::Function))
206 entityPath.ShortenTo(entityPath.IndexOfOrLength('('));
207 } else
208 entityPath= node.Name();
209
210 ConvertHTMLEntitiesToAscii(entityPath);
211 String256 entityDisplay(xLink->Display);
212 ConvertHTMLEntitiesToAscii(entityDisplay);
213 writeBuffer._<NC>( "\\ref " )
214 ._<NC>(entityPath)
215 ._<NC>(" \"")
216 ._<NC>(entityDisplay)
217 ._<NC>('"');
218 }
219 wb= writeBuffer.VBuffer() + writeBuffer.Length();
220
221 } // the read-loop
222
223 // add stats
224 dxl.Stats.ReplSourceFileLines.fetch_add(lineNo);
225
226 //-------------------------- write file ---------------------------------
227 if ( fileChanged && app.cli.DryRun != cli::DryRunModes::Application) {
228 Lox_Verbose("Writing replacement source file: {}", path )
229
230 Path tempPath;
231 tempPath << path << ".tmp";
232 ofstream outFile(tempPath.Terminate());
233 if ( !outFile.is_open() ) {
234 app.cErr->Add( app.cli.ExitCodeDecls.Find(ExitCodes::CantWriteReplSrcFile).Mapped()->FormatString(),
235 tempPath);
236 return true;
237 }
238 ALIB_ASSERT_ERROR(wb - writeBufferStart < writeBuffer.Capacity(), "DXL/SREPL/JOB",
239 "Write buffer overflow detected" )
240 outFile.write(writeBuffer.Buffer() + writeBufferPadSize, wb - writeBufferStart - writeBufferPadSize);
241 outFile.close();
242
243 if ( outFile.fail() ) {
244 app.cErr->Add( app.cli.ExitCodeDecls.Find(ExitCodes::CantWriteReplSrcFile).Mapped()->FormatString(),
245 tempPath);
246 return true;
247 }
248
249 sourceFile.Close();
250 std::error_code ec;
251 std::filesystem::rename(tempPath.Terminate(), path.Terminate(), ec);
252 if ( ec.value() != 0 ) {
253 app.cErr->Add( app.cli.ExitCodeDecls.Find(ExitCodes::CantWriteReplSrcFile).Mapped()->FormatString(),
254 path, ec.message());
255 return true;
256 }
257 }
258 return true;
259}
260} //namespace [dxl]
261
#define ALIB_LOCK_SHARED_WITH(lock)
#define ALIB_ASSERT_ERROR(cond, domain,...)
#define ALIB_LOCK_WITH(lock)
#define Lox_Info(...)
#define Lox_SetDomain(...)
#define Lox_GetVerbosity(result,...)
#define Lox_Verbose(...)
#define Lox_Warning(...)
constexpr const TChar * Terminate() const
integer Capacity() const
TAString & ShortenTo(integer newLength)
TChar * VBuffer() const
TAString & Trim(const TCString< TChar > &trimChars=CStringConstantsTraits< TChar >::DefaultWhitespaces())
void EnsureRemainingCapacity(integer spaceNeeded)
void SetLength(integer newLength)
constexpr integer Length() const
TChar CharAtStart() const
constexpr const TChar * Buffer() const
integer IndexOfOrLength(TChar needle) const
class DXLApp
Definition dxlapp.hpp:37
bool IsEOF() const noexcept
std::size_t Remaining() const noexcept
Data Open(const char *path, std::size_t knownSize=std::numeric_limits< std::size_t >::max(), bool disableMMap=false)
void Close() noexcept
Release resources (unmap / free buffer).
@ Function
Denotes a namespace- or member-function.
Definition target.hpp:46
@ File
Denotes a source file.
Definition target.hpp:31
@ EnumElement
Denotes an enumeration element.
Definition target.hpp:48
@ Macro
Denotes a preprocessor definition.
Definition target.hpp:43
@ Dir
Denotes a source folder.
Definition target.hpp:30
@ DocAnchor
Denotes a preprocessor definition.
Definition target.hpp:34
TApp & Get()
lox::Verbosity Verbosity
strings::TFill< character > Fill
files::File File
lang::integer integer
strings::TString< character > String
system::Path Path
LocalString< 256 > String256
strings::TAString< character, lang::HeapAllocator > AString
std::vector< T, StdMA< T > > StdVectorMA
LocalString< 512 > String512
todox
Definition doxyfile.cpp:20
alib::StdVectorMA< ResolvedLocation > XLinkList
Definition xlink.hpp:40
void ConvertHTMLEntitiesToAscii(alib::AString &buffer)
Definition dxl.cpp:104
@ CantOpenReplSrcFile
A replacement source file was not found or could not be accessed.
Definition dxl.hpp:101
@ CantWriteReplSrcFile
A replacement source file was not found or could not be accessed.
Definition dxl.hpp:102
DXLPoolWorker * poolWorker
The pool worker that executes this job.
Node Node
The cursor pointing to the result.
Definition index.hpp:386
alib::files::File srcFileNode
The source-file to load and search for DoxygenXLinks links.
Definition jobs.hpp:110