1// astyle_main.cpp
2// Copyright (c) 2018 by Jim Pattee <jimp03@email.com>.
3// This code is licensed under the MIT License.
4// License.md describes the conditions under which this software may be distributed.
5
6/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7 * AStyle_main source file map.
8 * This source file contains several classes.
9 * They are arranged as follows.
10 * ---------------------------------------
11 * namespace astyle {
12 * ASStreamIterator methods
13 * ASConsole methods
14 * // Windows specific
15 * // Linux specific
16 * ASLibrary methods
17 * // Windows specific
18 * // Linux specific
19 * ASOptions methods
20 * ASEncoding methods
21 * } // end of astyle namespace
22 * Global Area ---------------------------
23 * Java Native Interface functions
24 * AStyleMainUtf16 entry point
25 * AStyleMain entry point
26 * AStyleGetVersion entry point
27 * main entry point
28 * ---------------------------------------
29 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30 */
31
32//-----------------------------------------------------------------------------
33// headers
34//-----------------------------------------------------------------------------
35
36#include "astyle_main.h"
37
38#include <algorithm>
39#include <cerrno>
40#include <clocale> // needed by some compilers
41#include <cstdlib>
42#include <fstream>
43#include <sstream>
44
45// includes for recursive getFileNames() function
46#ifdef _WIN32
47 #undef UNICODE // use ASCII windows functions
48 #include <Windows.h>
49#else
50 #include <dirent.h>
51 #include <sys/stat.h>
52 #include <unistd.h>
53 #ifdef __VMS
54 #include <unixlib.h>
55 #include <rms.h>
56 #include <ssdef.h>
57 #include <stsdef.h>
58 #include <lib$routines.h>
59 #include <starlet.h>
60 #endif /* __VMS */
61#endif
62
63//-----------------------------------------------------------------------------
64// declarations
65//-----------------------------------------------------------------------------
66
67// turn off MinGW automatic file globbing
68// this CANNOT be in the astyle namespace
69#ifndef ASTYLE_LIB
70 int _CRT_glob = 0;
71#endif
72
73//----------------------------------------------------------------------------
74// astyle namespace
75//----------------------------------------------------------------------------
76
77namespace astyle {
78//
79// console build variables
80#ifndef ASTYLE_LIB
81 #ifdef _WIN32
82 char g_fileSeparator = '\\'; // Windows file separator
83 bool g_isCaseSensitive = false; // Windows IS NOT case sensitive
84 #else
85 char g_fileSeparator = '/'; // Linux file separator
86 bool g_isCaseSensitive = true; // Linux IS case sensitive
87 #endif // _WIN32
88#endif // ASTYLE_LIB
89
90// java library build variables
91#ifdef ASTYLE_JNI
92 JNIEnv* g_env;
93 jobject g_obj;
94 jmethodID g_mid;
95#endif
96
97const char* g_version = "3.2 beta";
98
99//-----------------------------------------------------------------------------
100// ASStreamIterator class
101// typename will be stringstream for AStyle
102// it could be istream or wxChar for plug-ins
103//-----------------------------------------------------------------------------
104
105template<typename T>
106ASStreamIterator<T>::ASStreamIterator(T* in)
107{
108 inStream = in;
109 buffer.reserve(200);
110 eolWindows = 0;
111 eolLinux = 0;
112 eolMacOld = 0;
113 peekStart = 0;
114 prevLineDeleted = false;
115 checkForEmptyLine = false;
116 // get length of stream
117 inStream->seekg(0, inStream->end);
118 streamLength = inStream->tellg();
119 inStream->seekg(0, inStream->beg);
120}
121
122template<typename T>
123ASStreamIterator<T>::~ASStreamIterator() = default;
124
125/**
126* get the length of the input stream.
127* streamLength variable is set by the constructor.
128*
129* @return length of the input file stream, converted to an int.
130*/
131template<typename T>
132int ASStreamIterator<T>::getStreamLength() const
133{
134 return static_cast<int>(streamLength);
135}
136
137/**
138 * read the input stream, delete any end of line characters,
139 * and build a string that contains the input line.
140 *
141 * @return string containing the next input line minus any end of line characters
142 */
143template<typename T>
144string ASStreamIterator<T>::nextLine(bool emptyLineWasDeleted)
145{
146 // verify that the current position is correct
147 assert(peekStart == 0);
148
149 // a deleted line may be replaced if break-blocks is requested
150 // this sets up the compare to check for a replaced empty line
151 if (prevLineDeleted)
152 {
153 prevLineDeleted = false;
154 checkForEmptyLine = true;
155 }
156 if (!emptyLineWasDeleted)
157 prevBuffer = buffer;
158 else
159 prevLineDeleted = true;
160
161 // read the next record
162 buffer.clear();
163 char ch;
164 inStream->get(ch);
165
166 while (!inStream->eof() && ch != '\n' && ch != '\r')
167 {
168 buffer.append(1, ch);
169 inStream->get(ch);
170 }
171
172 if (inStream->eof())
173 {
174 return buffer;
175 }
176
177 int peekCh = inStream->peek();
178
179 // find input end-of-line characters
180 if (!inStream->eof())
181 {
182 if (ch == '\r') // CR+LF is windows otherwise Mac OS 9
183 {
184 if (peekCh == '\n')
185 {
186 inStream->get();
187 eolWindows++;
188 }
189 else
190 eolMacOld++;
191 }
192 else // LF is Linux, allow for improbable LF/CR
193 {
194 if (peekCh == '\r')
195 {
196 inStream->get();
197 eolWindows++;
198 }
199 else
200 eolLinux++;
201 }
202 }
203 else
204 {
205 inStream->clear();
206 }
207
208 // has not detected an input end of line
209 if (!eolWindows && !eolLinux && !eolMacOld)
210 {
211#ifdef _WIN32
212 eolWindows++;
213#else
214 eolLinux++;
215#endif
216 }
217
218 // set output end of line characters
219 if (eolWindows >= eolLinux)
220 {
221 if (eolWindows >= eolMacOld)
222 outputEOL = "\r\n"; // Windows (CR+LF)
223 else
224 outputEOL = "\r"; // MacOld (CR)
225 }
226 else if (eolLinux >= eolMacOld)
227 outputEOL = "\n"; // Linux (LF)
228 else
229 outputEOL = "\r"; // MacOld (CR)
230
231 return buffer;
232}
233
234// save the current position and get the next line
235// this can be called for multiple reads
236// when finished peeking you MUST call peekReset()
237// call this function from ASFormatter ONLY
238template<typename T>
239string ASStreamIterator<T>::peekNextLine()
240{
241 assert(hasMoreLines());
242 string nextLine_;
243 char ch;
244
245 if (!peekStart)
246 peekStart = inStream->tellg();
247
248 // read the next record
249 inStream->get(ch);
250 while (!inStream->eof() && ch != '\n' && ch != '\r')
251 {
252 nextLine_.append(1, ch);
253 inStream->get(ch);
254 }
255
256 if (inStream->eof())
257 {
258 return nextLine_;
259 }
260
261 int peekCh = inStream->peek();
262
263 // remove end-of-line characters
264 if (!inStream->eof())
265 {
266 if ((peekCh == '\n' || peekCh == '\r') && peekCh != ch)
267 inStream->get();
268 }
269
270 return nextLine_;
271}
272
273// reset current position and EOF for peekNextLine()
274template<typename T>
275void ASStreamIterator<T>::peekReset()
276{
277 assert(peekStart != 0);
278 inStream->clear();
279 inStream->seekg(peekStart);
280 peekStart = 0;
281}
282
283// save the last input line after input has reached EOF
284template<typename T>
285void ASStreamIterator<T>::saveLastInputLine()
286{
287 assert(inStream->eof());
288 prevBuffer = buffer;
289}
290
291// return position of the get pointer
292template<typename T>
293streamoff ASStreamIterator<T>::tellg()
294{
295 return inStream->tellg();
296}
297
298// check for a change in line ends
299template<typename T>
300bool ASStreamIterator<T>::getLineEndChange(int lineEndFormat) const
301{
302 assert(lineEndFormat == LINEEND_DEFAULT
303 || lineEndFormat == LINEEND_WINDOWS
304 || lineEndFormat == LINEEND_LINUX
305 || lineEndFormat == LINEEND_MACOLD);
306
307 bool lineEndChange = false;
308 if (lineEndFormat == LINEEND_WINDOWS)
309 lineEndChange = (eolLinux + eolMacOld != 0);
310 else if (lineEndFormat == LINEEND_LINUX)
311 lineEndChange = (eolWindows + eolMacOld != 0);
312 else if (lineEndFormat == LINEEND_MACOLD)
313 lineEndChange = (eolWindows + eolLinux != 0);
314 else
315 {
316 if (eolWindows > 0)
317 lineEndChange = (eolLinux + eolMacOld != 0);
318 else if (eolLinux > 0)
319 lineEndChange = (eolWindows + eolMacOld != 0);
320 else if (eolMacOld > 0)
321 lineEndChange = (eolWindows + eolLinux != 0);
322 }
323 return lineEndChange;
324}
325
326//-----------------------------------------------------------------------------
327// ASConsole class
328// main function will be included only in the console build
329//-----------------------------------------------------------------------------
330
331#ifndef ASTYLE_LIB
332
333ASConsole::ASConsole(ASFormatter& formatterArg) : formatter(formatterArg)
334{
335 errorStream = &cerr;
336 // command line options
337 isRecursive = false;
338 isDryRun = false;
339 noBackup = false;
340 preserveDate = false;
341 isVerbose = false;
342 isQuiet = false;
343 isFormattedOnly = false;
344 ignoreExcludeErrors = false;
345 ignoreExcludeErrorsDisplay = false;
346 useAscii = false;
347 // other variables
348 bypassBrowserOpen = false;
349 hasWildcard = false;
350 filesAreIdentical = true;
351 lineEndsMixed = false;
352 origSuffix = ".orig";
353 mainDirectoryLength = 0;
354 filesFormatted = 0;
355 filesUnchanged = 0;
356 linesOut = 0;
357}
358
359// rewrite a stringstream converting the line ends
360void ASConsole::convertLineEnds(ostringstream& out, int lineEnd)
361{
362 assert(lineEnd == LINEEND_WINDOWS || lineEnd == LINEEND_LINUX || lineEnd == LINEEND_MACOLD);
363 const string& inStr = out.str(); // avoids strange looking syntax
364 string outStr; // the converted output
365 int inLength = (int) inStr.length();
366 for (int pos = 0; pos < inLength; pos++)
367 {
368 if (inStr[pos] == '\r')
369 {
370 if (inStr[pos + 1] == '\n')
371 {
372 // CRLF
373 if (lineEnd == LINEEND_CR)
374 {
375 outStr += inStr[pos]; // Delete the LF
376 pos++;
377 continue;
378 }
379 if (lineEnd == LINEEND_LF)
380 {
381 outStr += inStr[pos + 1]; // Delete the CR
382 pos++;
383 continue;
384 }
385 outStr += inStr[pos]; // Do not change
386 outStr += inStr[pos + 1];
387 pos++;
388 continue;
389 }
390 else // NOLINT
391 {
392 // CR
393 if (lineEnd == LINEEND_CRLF)
394 {
395 outStr += inStr[pos]; // Insert the CR
396 outStr += '\n'; // Insert the LF
397 continue;
398 }
399 if (lineEnd == LINEEND_LF)
400 {
401 outStr += '\n'; // Insert the LF
402 continue;
403 }
404 outStr += inStr[pos]; // Do not change
405 continue;
406 }
407 }
408 else if (inStr[pos] == '\n')
409 {
410 // LF
411 if (lineEnd == LINEEND_CRLF)
412 {
413 outStr += '\r'; // Insert the CR
414 outStr += inStr[pos]; // Insert the LF
415 continue;
416 }
417 if (lineEnd == LINEEND_CR)
418 {
419 outStr += '\r'; // Insert the CR
420 continue;
421 }
422 outStr += inStr[pos]; // Do not change
423 continue;
424 }
425 else
426 {
427 outStr += inStr[pos]; // Write the current char
428 }
429 }
430 // replace the stream
431 out.str(outStr);
432}
433
434void ASConsole::correctMixedLineEnds(ostringstream& out)
435{
436 LineEndFormat lineEndFormat = LINEEND_DEFAULT;
437 if (outputEOL == "\r\n")
438 lineEndFormat = LINEEND_WINDOWS;
439 if (outputEOL == "\n")
440 lineEndFormat = LINEEND_LINUX;
441 if (outputEOL == "\r")
442 lineEndFormat = LINEEND_MACOLD;
443 convertLineEnds(out, lineEndFormat);
444}
445
446// check files for 16 or 32 bit encoding
447// the file must have a Byte Order Mark (BOM)
448// NOTE: some string functions don't work with NULLs (e.g. length())
449FileEncoding ASConsole::detectEncoding(const char* data, size_t dataSize) const
450{
451 FileEncoding encoding = ENCODING_8BIT;
452
453 if (dataSize >= 3 && memcmp(data, "\xEF\xBB\xBF", 3) == 0)
454 encoding = UTF_8BOM;
455 else if (dataSize >= 4 && memcmp(data, "\x00\x00\xFE\xFF", 4) == 0)
456 encoding = UTF_32BE;
457 else if (dataSize >= 4 && memcmp(data, "\xFF\xFE\x00\x00", 4) == 0)
458 encoding = UTF_32LE;
459 else if (dataSize >= 2 && memcmp(data, "\xFE\xFF", 2) == 0)
460 encoding = UTF_16BE;
461 else if (dataSize >= 2 && memcmp(data, "\xFF\xFE", 2) == 0)
462 encoding = UTF_16LE;
463
464 return encoding;
465}
466
467// error exit without a message
468void ASConsole::error() const
469{
470 (*errorStream) << _("Artistic Style has terminated\n") << endl;
471 exit(EXIT_FAILURE);
472}
473
474// error exit with a message
475void ASConsole::error(const char* why, const char* what) const
476{
477 (*errorStream) << why << ' ' << what << endl;
478 error();
479}
480
481/**
482 * If no files have been given, use cin for input and cout for output.
483 *
484 * This is used to format text for text editors.
485 * Do NOT display any console messages when this function is used.
486 */
487void ASConsole::formatCinToCout()
488{
489 // check for files from --stdin= and --stdout=
490 if (!stdPathIn.empty())
491 {
492 if (!freopen(stdPathIn.c_str(), "r", stdin))
493 error("Cannot open input file", stdPathIn.c_str());
494 }
495 if (!stdPathOut.empty())
496 {
497 if (!freopen(stdPathOut.c_str(), "w", stdout))
498 error("Cannot open output file", stdPathOut.c_str());
499
500 }
501 // Using cin.tellg() causes problems with both Windows and Linux.
502 // The Windows problem occurs when the input is not Windows line-ends.
503 // The tellg() will be out of sequence with the get() statements.
504 // The Linux cin.tellg() will return -1 (invalid).
505 // Copying the input sequentially to a stringstream before
506 // formatting solves the problem for both.
507 istream* inStream = &cin;
508 stringstream outStream;
509 char ch;
510 inStream->get(ch);
511 while (!inStream->eof() && !inStream->fail())
512 {
513 outStream.put(ch);
514 inStream->get(ch);
515 }
516 ASStreamIterator<stringstream> streamIterator(&outStream);
517 // Windows pipe or redirection always outputs Windows line-ends.
518 // Linux pipe or redirection will output any line end.
519#ifdef _WIN32
520 LineEndFormat lineEndFormat = LINEEND_DEFAULT;
521#else
522 LineEndFormat lineEndFormat = formatter.getLineEndFormat();
523#endif // _WIN32
524 initializeOutputEOL(lineEndFormat);
525 formatter.init(&streamIterator);
526
527 while (formatter.hasMoreLines())
528 {
529 cout << formatter.nextLine();
530 if (formatter.hasMoreLines())
531 {
532 setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
533 cout << outputEOL;
534 }
535 else
536 {
537 // this can happen if the file if missing a closing brace and break-blocks is requested
538 if (formatter.getIsLineReady())
539 {
540 setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
541 cout << outputEOL;
542 cout << formatter.nextLine();
543 }
544 }
545 }
546 cout.flush();
547}
548
549/**
550 * Open input file, format it, and close the output.
551 *
552 * @param fileName_ The path and name of the file to be processed.
553 */
554void ASConsole::formatFile(const string& fileName_)
555{
556 stringstream in;
557 ostringstream out;
558 FileEncoding encoding = readFile(fileName_, in);
559
560 // Unless a specific language mode has been set, set the language mode
561 // according to the file's suffix.
562 if (!formatter.getModeManuallySet())
563 {
564 if (stringEndsWith(fileName_, string(".java")))
565 formatter.setJavaStyle();
566 else if (stringEndsWith(fileName_, string(".cs")))
567 formatter.setSharpStyle();
568 else
569 formatter.setCStyle();
570 }
571
572 // set line end format
573 string nextLine; // next output line
574 filesAreIdentical = true; // input and output files are identical
575 LineEndFormat lineEndFormat = formatter.getLineEndFormat();
576 initializeOutputEOL(lineEndFormat);
577 // do this AFTER setting the file mode
578 ASStreamIterator<stringstream> streamIterator(&in);
579 formatter.init(&streamIterator);
580
581 // format the file
582 while (formatter.hasMoreLines())
583 {
584 nextLine = formatter.nextLine();
585 out << nextLine;
586 linesOut++;
587 if (formatter.hasMoreLines())
588 {
589 setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
590 out << outputEOL;
591 }
592 else
593 {
594 streamIterator.saveLastInputLine(); // to compare the last input line
595 // this can happen if the file if missing a closing brace and break-blocks is requested
596 if (formatter.getIsLineReady())
597 {
598 setOutputEOL(lineEndFormat, streamIterator.getOutputEOL());
599 out << outputEOL;
600 nextLine = formatter.nextLine();
601 out << nextLine;
602 linesOut++;
603 streamIterator.saveLastInputLine();
604 }
605 }
606
607 if (filesAreIdentical)
608 {
609 if (streamIterator.checkForEmptyLine)
610 {
611 if (nextLine.find_first_not_of(" \t") != string::npos)
612 filesAreIdentical = false;
613 }
614 else if (!streamIterator.compareToInputBuffer(nextLine))
615 filesAreIdentical = false;
616 streamIterator.checkForEmptyLine = false;
617 }
618 }
619 // correct for mixed line ends
620 if (lineEndsMixed)
621 {
622 correctMixedLineEnds(out);
623 filesAreIdentical = false;
624 }
625
626 // remove targetDirectory from filename if required by print
627 string displayName;
628 if (hasWildcard)
629 displayName = fileName_.substr(targetDirectory.length() + 1);
630 else
631 displayName = fileName_;
632
633 // if file has changed, write the new file
634 if (!filesAreIdentical || streamIterator.getLineEndChange(lineEndFormat))
635 {
636 if (!isDryRun)
637 writeFile(fileName_, encoding, out);
638 printMsg(_("Formatted %s\n"), displayName);
639 filesFormatted++;
640 }
641 else
642 {
643 if (!isFormattedOnly)
644 printMsg(_("Unchanged %s\n"), displayName);
645 filesUnchanged++;
646 }
647
648 assert(formatter.getChecksumDiff() == 0);
649}
650
651/**
652 * Searches for a file named fileName_ in the current directory. If it is not
653 * found, recursively searches for fileName_ in the current directory's parent
654 * directories, returning the location of the first instance of fileName_
655 * found. If fileName_ is not found, an empty string is returned.
656 *
657 * @param fileName_ The filename the function should attempt to locate.
658 * @return The full path to fileName_ in the current directory or
659 * nearest parent directory if found, otherwise an empty
660 * string.
661 */
662string ASConsole::findProjectOptionFilePath(const string& fileName_) const
663{
664 string parent;
665
666 if (!fileNameVector.empty())
667 parent = getFullPathName(fileNameVector.front());
668 else if (!stdPathIn.empty())
669 parent = getFullPathName(stdPathIn);
670 else
671 parent = getFullPathName(getCurrentDirectory(fileName_));
672
673 // remove filename from path
674 size_t endPath = parent.find_last_of(g_fileSeparator);
675 if (endPath != string::npos)
676 parent = parent.substr(0, endPath + 1);
677
678 while (!parent.empty())
679 {
680 string filepath = parent + fileName_;
681 if (fileExists(filepath.c_str()))
682 return filepath;
683 if (fileName_ == ".astylerc")
684 {
685 filepath = parent + "_astylerc";
686 if (fileExists(filepath.c_str()))
687 return filepath;
688 }
689 parent = getParentDirectory(parent);
690 }
691 return string();
692}
693
694// for unit testing
695vector<bool> ASConsole::getExcludeHitsVector() const
696{ return excludeHitsVector; }
697
698// for unit testing
699vector<string> ASConsole::getExcludeVector() const
700{ return excludeVector; }
701
702// for unit testing
703vector<string> ASConsole::getFileName() const
704{ return fileName; }
705
706// for unit testing
707vector<string> ASConsole::getFileNameVector() const
708{ return fileNameVector; }
709
710// for unit testing
711vector<string> ASConsole::getFileOptionsVector() const
712{ return fileOptionsVector; }
713
714// for unit testing
715bool ASConsole::getFilesAreIdentical() const
716{ return filesAreIdentical; }
717
718// for unit testing
719int ASConsole::getFilesFormatted() const
720{ return filesFormatted; }
721
722// for unit testing
723bool ASConsole::getIgnoreExcludeErrors() const
724{ return ignoreExcludeErrors; }
725
726// for unit testing
727bool ASConsole::getIgnoreExcludeErrorsDisplay() const
728{ return ignoreExcludeErrorsDisplay; }
729
730// for unit testing
731bool ASConsole::getIsDryRun() const
732{ return isDryRun; }
733
734// for unit testing
735bool ASConsole::getIsFormattedOnly() const
736{ return isFormattedOnly; }
737
738// for unit testing
739string ASConsole::getLanguageID() const
740{ return localizer.getLanguageID(); }
741
742// for unit testing
743bool ASConsole::getIsQuiet() const
744{ return isQuiet; }
745
746// for unit testing
747bool ASConsole::getIsRecursive() const
748{ return isRecursive; }
749
750// for unit testing
751bool ASConsole::getIsVerbose() const
752{ return isVerbose; }
753
754// for unit testing
755bool ASConsole::getLineEndsMixed() const
756{ return lineEndsMixed; }
757
758// for unit testing
759bool ASConsole::getNoBackup() const
760{ return noBackup; }
761
762// for unit testing
763string ASConsole::getOptionFileName() const
764{ return optionFileName; }
765
766// for unit testing
767vector<string> ASConsole::getOptionsVector() const
768{ return optionsVector; }
769
770// for unit testing
771string ASConsole::getOrigSuffix() const
772{ return origSuffix; }
773
774// for unit testing
775bool ASConsole::getPreserveDate() const
776{ return preserveDate; }
777
778// for unit testing
779string ASConsole::getProjectOptionFileName() const
780{
781 assert(projectOptionFileName.length() > 0);
782 // remove the directory path
783 size_t start = projectOptionFileName.find_last_of(g_fileSeparator);
784 if (start == string::npos)
785 start = 0;
786 return projectOptionFileName.substr(start + 1);
787}
788
789// for unit testing
790vector<string> ASConsole::getProjectOptionsVector() const
791{ return projectOptionsVector; }
792
793// for unit testing
794string ASConsole::getStdPathIn() const
795{ return stdPathIn; }
796
797// for unit testing
798string ASConsole::getStdPathOut() const
799{ return stdPathOut; }
800
801// for unit testing
802void ASConsole::setBypassBrowserOpen(bool state)
803{ bypassBrowserOpen = state; }
804
805// for unit testing
806ostream* ASConsole::getErrorStream() const
807{
808 return errorStream;
809}
810
811void ASConsole::setErrorStream(ostream* errStreamPtr)
812{
813 errorStream = errStreamPtr;
814}
815
816// build a vector of argv options
817// the program path argv[0] is excluded
818vector<string> ASConsole::getArgvOptions(int argc, char** argv)
819{
820 if (argc > 0)
821 astyleExePath = getFullPathName(argv[0]);
822 vector<string> argvOptions;
823 for (int i = 1; i < argc; i++)
824 {
825 argvOptions.emplace_back(string(argv[i]));
826 }
827 return argvOptions;
828}
829
830string ASConsole::getParam(const string& arg, const char* op)
831{
832 return arg.substr(strlen(op));
833}
834
835void ASConsole::getTargetFilenames(string& targetFilename_,
836 vector<string>& targetFilenameVector) const
837{
838 size_t beg = 0;
839 size_t sep = 0;
840 while (beg < targetFilename_.length())
841 {
842 // find next target
843 sep = targetFilename_.find_first_of(",;", beg);
844 if (sep == string::npos)
845 sep = targetFilename_.length();
846 string fileExtension = targetFilename_.substr(beg, sep - beg);
847 beg = sep + 1;
848 // remove whitespace
849 while (fileExtension.length() > 0
850 && (fileExtension[0] == ' ' || fileExtension[0] == '\t'))
851 fileExtension = fileExtension.erase(0, 1);
852 while (fileExtension.length() > 0
853 && (fileExtension[fileExtension.length() - 1] == ' '
854 || fileExtension[fileExtension.length() - 1] == '\t'))
855 fileExtension = fileExtension.erase(fileExtension.length() - 1, 1);
856 if (fileExtension.length() > 0)
857 targetFilenameVector.emplace_back(fileExtension);
858 }
859 if (targetFilenameVector.empty())
860 {
861 fprintf(stderr, _("Missing filename in %s\n"), targetFilename_.c_str());
862 error();
863 }
864}
865
866// initialize output end of line
867void ASConsole::initializeOutputEOL(LineEndFormat lineEndFormat)
868{
869 assert(lineEndFormat == LINEEND_DEFAULT
870 || lineEndFormat == LINEEND_WINDOWS
871 || lineEndFormat == LINEEND_LINUX
872 || lineEndFormat == LINEEND_MACOLD);
873
874 outputEOL.clear(); // current line end
875 prevEOL.clear(); // previous line end
876 lineEndsMixed = false; // output has mixed line ends, LINEEND_DEFAULT only
877
878 if (lineEndFormat == LINEEND_WINDOWS)
879 outputEOL = "\r\n";
880 else if (lineEndFormat == LINEEND_LINUX)
881 outputEOL = "\n";
882 else if (lineEndFormat == LINEEND_MACOLD)
883 outputEOL = "\r";
884 else
885 outputEOL.clear();
886}
887
888// read a file into the stringstream 'in'
889FileEncoding ASConsole::readFile(const string& fileName_, stringstream& in) const
890{
891 const int blockSize = 65536; // 64 KB
892 ifstream fin(fileName_.c_str(), ios::binary);
893 if (!fin)
894 error("Cannot open file", fileName_.c_str());
895 char* data = new (nothrow) char[blockSize];
896 if (data == nullptr)
897 error("Cannot allocate memory to open file", fileName_.c_str());
898 fin.read(data, blockSize);
899 if (fin.bad())
900 error("Cannot read file", fileName_.c_str());
901 size_t dataSize = static_cast<size_t>(fin.gcount());
902 FileEncoding encoding = detectEncoding(data, dataSize);
903 if (encoding == UTF_32BE || encoding == UTF_32LE)
904 error(_("Cannot process UTF-32 encoding"), fileName_.c_str());
905 bool firstBlock = true;
906 bool isBigEndian = (encoding == UTF_16BE);
907 while (dataSize != 0)
908 {
909 if (encoding == UTF_16LE || encoding == UTF_16BE)
910 {
911 // convert utf-16 to utf-8
912 size_t utf8Size = encode.utf8LengthFromUtf16(data, dataSize, isBigEndian);
913 char* utf8Out = new (nothrow) char[utf8Size];
914 if (utf8Out == nullptr)
915 error("Cannot allocate memory for utf-8 conversion", fileName_.c_str());
916 size_t utf8Len = encode.utf16ToUtf8(data, dataSize, isBigEndian, firstBlock, utf8Out);
917 assert(utf8Len <= utf8Size);
918 in << string(utf8Out, utf8Len);
919 delete[] utf8Out;
920 }
921 else
922 in << string(data, dataSize);
923 fin.read(data, blockSize);
924 if (fin.bad())
925 error("Cannot read file", fileName_.c_str());
926 dataSize = static_cast<size_t>(fin.gcount());
927 firstBlock = false;
928 }
929 fin.close();
930 delete[] data;
931 return encoding;
932}
933
934void ASConsole::setIgnoreExcludeErrors(bool state)
935{ ignoreExcludeErrors = state; }
936
937void ASConsole::setIgnoreExcludeErrorsAndDisplay(bool state)
938{ ignoreExcludeErrors = state; ignoreExcludeErrorsDisplay = state; }
939
940void ASConsole::setIsFormattedOnly(bool state)
941{ isFormattedOnly = state; }
942
943void ASConsole::setIsQuiet(bool state)
944{ isQuiet = state; }
945
946void ASConsole::setIsRecursive(bool state)
947{ isRecursive = state; }
948
949void ASConsole::setIsDryRun(bool state)
950{ isDryRun = state; }
951
952void ASConsole::setIsVerbose(bool state)
953{ isVerbose = state; }
954
955void ASConsole::setNoBackup(bool state)
956{ noBackup = state; }
957
958void ASConsole::setOptionFileName(const string& name)
959{ optionFileName = name; }
960
961void ASConsole::setOrigSuffix(const string& suffix)
962{ origSuffix = suffix; }
963
964void ASConsole::setPreserveDate(bool state)
965{ preserveDate = state; }
966
967void ASConsole::setProjectOptionFileName(const string& optfilepath)
968{ projectOptionFileName = optfilepath; }
969
970void ASConsole::setStdPathIn(const string& path)
971{ stdPathIn = path; }
972
973void ASConsole::setStdPathOut(const string& path)
974{ stdPathOut = path; }
975
976// set outputEOL variable
977void ASConsole::setOutputEOL(LineEndFormat lineEndFormat, const string& currentEOL)
978{
979 if (lineEndFormat == LINEEND_DEFAULT)
980 {
981 outputEOL = currentEOL;
982 if (prevEOL.empty())
983 prevEOL = outputEOL;
984 if (prevEOL != outputEOL)
985 {
986 lineEndsMixed = true;
987 filesAreIdentical = false;
988 prevEOL = outputEOL;
989 }
990 }
991 else
992 {
993 prevEOL = currentEOL;
994 if (prevEOL != outputEOL)
995 filesAreIdentical = false;
996 }
997}
998
999#ifdef _WIN32 // Windows specific
1000
1001/**
1002 * WINDOWS function to display the last system error.
1003 */
1004void ASConsole::displayLastError()
1005{
1006 LPSTR msgBuf;
1007 DWORD lastError = GetLastError();
1008 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
1009 nullptr,
1010 lastError,
1011 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1012 (LPSTR) &msgBuf,
1013 0,
1014 nullptr
1015 );
1016 // Display the string.
1017 (*errorStream) << "Error (" << lastError << ") " << msgBuf << endl;
1018 // Free the buffer.
1019 LocalFree(msgBuf);
1020}
1021
1022/**
1023 * WINDOWS function to get the current directory.
1024 * NOTE: getenv("CD") does not work for Windows Vista.
1025 * The Windows function GetCurrentDirectory is used instead.
1026 *
1027 * @return The path of the current directory
1028 */
1029string ASConsole::getCurrentDirectory(const string& fileName_) const
1030{
1031 char currdir[MAX_PATH];
1032 currdir[0] = '\0';
1033 if (!GetCurrentDirectory(sizeof(currdir), currdir))
1034 error("Cannot find file", fileName_.c_str());
1035 return string(currdir);
1036}
1037
1038/**
1039 * WINDOWS function to resolve wildcards and recurse into sub directories.
1040 * The fileName vector is filled with the path and names of files to process.
1041 *
1042 * @param directory The path of the directory to be processed.
1043 * @param wildcards A vector of wildcards to be processed (e.g. *.cpp).
1044 */
1045void ASConsole::getFileNames(const string& directory, const vector<string>& wildcards)
1046{
1047 vector<string> subDirectory; // sub directories of directory
1048 WIN32_FIND_DATA findFileData; // for FindFirstFile and FindNextFile
1049
1050 // Find the first file in the directory
1051 // Find will get at least "." and "..".
1052 string firstFile = directory + "\\*";
1053 HANDLE hFind = FindFirstFile(firstFile.c_str(), &findFileData);
1054
1055 if (hFind == INVALID_HANDLE_VALUE)
1056 {
1057 // Error (3) The system cannot find the path specified.
1058 // Error (123) The filename, directory name, or volume label syntax is incorrect.
1059 // ::FindClose(hFind); before exiting
1060 displayLastError();
1061 error(_("Cannot open directory"), directory.c_str());
1062 }
1063
1064 // save files and sub directories
1065 do
1066 {
1067 // skip hidden or read only
1068 if (findFileData.cFileName[0] == '.'
1069 || (findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
1070 || (findFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
1071 continue;
1072
1073 // is this a sub directory
1074 if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1075 {
1076 if (!isRecursive)
1077 continue;
1078 // if a sub directory and recursive, save sub directory
1079 string subDirectoryPath = directory + g_fileSeparator + findFileData.cFileName;
1080 if (isPathExclued(subDirectoryPath))
1081 printMsg(_("Exclude %s\n"), subDirectoryPath.substr(mainDirectoryLength));
1082 else
1083 subDirectory.emplace_back(subDirectoryPath);
1084 continue;
1085 }
1086
1087 string filePathName = directory + g_fileSeparator + findFileData.cFileName;
1088 // check exclude before wildcmp to avoid "unmatched exclude" error
1089 bool isExcluded = isPathExclued(filePathName);
1090 // save file name if wildcard match
1091 for (const string& wildcard : wildcards)
1092 {
1093 if (wildcmp(wildcard.c_str(), findFileData.cFileName))
1094 {
1095 if (isExcluded)
1096 printMsg(_("Exclude %s\n"), filePathName.substr(mainDirectoryLength));
1097 else
1098 fileName.emplace_back(filePathName);
1099 break;
1100 }
1101 }
1102 }
1103 while (FindNextFile(hFind, &findFileData) != 0);
1104
1105 // check for processing error
1106 ::FindClose(hFind);
1107 DWORD dwError = GetLastError();
1108 if (dwError != ERROR_NO_MORE_FILES)
1109 error("Error processing directory", directory.c_str());
1110
1111 // recurse into sub directories
1112 // if not doing recursive subDirectory is empty
1113 for (const string& subDirectoryName : subDirectory)
1114 getFileNames(subDirectoryName, wildcards);
1115}
1116
1117// WINDOWS function to get the full path name from the relative path name
1118// Return the full path name or an empty string if failed.
1119string ASConsole::getFullPathName(const string& relativePath) const
1120{
1121 char fullPath[MAX_PATH];
1122 GetFullPathName(relativePath.c_str(), MAX_PATH, fullPath, nullptr);
1123 return fullPath;
1124}
1125
1126/**
1127 * WINDOWS function to format a number according to the current locale.
1128 * This formats positive integers only, no float.
1129 *
1130 * @param num The number to be formatted.
1131 * @param lcid The LCID of the locale to be used for testing.
1132 * @return The formatted number.
1133 */
1134string ASConsole::getNumberFormat(int num, size_t lcid) const
1135{
1136#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__GNUC__)
1137 // Compilers that don't support C++ locales should still support this assert.
1138 // The C locale should be set but not the C++.
1139 // This function is not necessary if the C++ locale is set.
1140 // The locale().name() return value is not portable to all compilers.
1141 assert(locale().name() == "C");
1142#endif
1143 // convert num to a string
1144 stringstream alphaNum;
1145 alphaNum << num;
1146 string number = alphaNum.str();
1147 if (useAscii)
1148 return number;
1149
1150 // format the number using the Windows API
1151 if (lcid == 0)
1152 lcid = LOCALE_USER_DEFAULT;
1153 int outSize = ::GetNumberFormat(lcid, 0, number.c_str(), nullptr, nullptr, 0);
1154 char* outBuf = new (nothrow) char[outSize];
1155 if (outBuf == nullptr)
1156 return number;
1157 ::GetNumberFormat(lcid, 0, number.c_str(), nullptr, outBuf, outSize);
1158 string formattedNum(outBuf);
1159 delete[] outBuf;
1160 // remove the decimal
1161 int decSize = ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, nullptr, 0);
1162 char* decBuf = new (nothrow) char[decSize];
1163 if (decBuf == nullptr)
1164 return number;
1165 ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, decBuf, decSize);
1166 size_t i = formattedNum.rfind(decBuf);
1167 delete[] decBuf;
1168 if (i != string::npos)
1169 formattedNum.erase(i);
1170 if (!formattedNum.length())
1171 formattedNum = "0";
1172 return formattedNum;
1173}
1174
1175/**
1176 * WINDOWS function to check for a HOME directory
1177 *
1178 * @param absPath The path to be evaluated.
1179 * @returns true if absPath is HOME or is an invalid absolute
1180 * path, false otherwise.
1181 */
1182bool ASConsole::isHomeOrInvalidAbsPath(const string& absPath) const
1183{
1184 const char* const env = getenv("USERPROFILE");
1185 if (env == nullptr)
1186 return true;
1187
1188 if (absPath.c_str() == env)
1189 return true;
1190
1191 if (absPath.compare(0, strlen(env), env) != 0)
1192 return true;
1193
1194 return false;
1195}
1196
1197/**
1198 * WINDOWS function to open a HTML file in the default browser.
1199 */
1200void ASConsole::launchDefaultBrowser(const char* filePathIn /*nullptr*/) const
1201{
1202 struct stat statbuf;
1203 const char* envPaths[] = { "PROGRAMFILES(X86)", "PROGRAMFILES" };
1204 size_t pathsLen = sizeof(envPaths) / sizeof(envPaths[0]);
1205 string htmlDefaultPath;
1206 for (size_t i = 0; i < pathsLen; i++)
1207 {
1208 const char* const envPath = getenv(envPaths[i]);
1209 if (envPath == nullptr)
1210 continue;
1211 htmlDefaultPath = envPath;
1212 if (htmlDefaultPath.length() > 0
1213 && htmlDefaultPath[htmlDefaultPath.length() - 1] == g_fileSeparator)
1214 htmlDefaultPath.erase(htmlDefaultPath.length() - 1);
1215 htmlDefaultPath.append("\\AStyle\\doc");
1216 if (stat(htmlDefaultPath.c_str(), &statbuf) == 0 && statbuf.st_mode & S_IFDIR)
1217 break;
1218 }
1219 htmlDefaultPath.append("\\");
1220
1221 // build file path
1222 string htmlFilePath;
1223 if (filePathIn == nullptr)
1224 htmlFilePath = htmlDefaultPath + "astyle.html";
1225 else
1226 {
1227 if (strpbrk(filePathIn, "\\/") == nullptr)
1228 htmlFilePath = htmlDefaultPath + filePathIn;
1229 else
1230 htmlFilePath = filePathIn;
1231 }
1232 standardizePath(htmlFilePath);
1233 if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG))
1234 {
1235 printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str());
1236 return;
1237 }
1238
1239 SHELLEXECUTEINFO sei = { sizeof(sei), {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} };
1240 sei.fMask = SEE_MASK_FLAG_NO_UI;
1241 sei.lpVerb = "open";
1242 sei.lpFile = htmlFilePath.c_str();
1243 sei.nShow = SW_SHOWNORMAL;
1244
1245 // browser open will be bypassed in test programs
1246 printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str());
1247 if (!bypassBrowserOpen)
1248 {
1249 int ret = ShellExecuteEx(&sei);
1250 if (!ret)
1251 error(_("Command execute failure"), htmlFilePath.c_str());
1252 }
1253}
1254
1255#else // Linux specific
1256
1257/**
1258 * LINUX function to get the current directory.
1259 * This is done if the fileName does not contain a path.
1260 * It is probably from an editor sending a single file.
1261 *
1262 * @param fileName_ The filename is used only for the error message.
1263 * @return The path of the current directory
1264 */
1265string ASConsole::getCurrentDirectory(const string& fileName_) const
1266{
1267 const char* const currdir = getenv("PWD");
1268 if (currdir == nullptr)
1269 error("Cannot find file", fileName_.c_str());
1270 return string(currdir);
1271}
1272
1273/**
1274 * LINUX function to resolve wildcards and recurse into sub directories.
1275 * The fileName vector is filled with the path and names of files to process.
1276 *
1277 * @param directory The path of the directory to be processed.
1278 * @param wildcards A vector of wildcards to be processed (e.g. *.cpp).
1279 */
1280void ASConsole::getFileNames(const string& directory, const vector<string>& wildcards)
1281{
1282 struct dirent* entry; // entry from readdir()
1283 struct stat statbuf; // entry from stat()
1284 vector<string> subDirectory; // sub directories of this directory
1285
1286 // errno is defined in <errno.h> and is set for errors in opendir, readdir, or stat
1287 errno = 0;
1288
1289 DIR* dp = opendir(directory.c_str());
1290 if (dp == nullptr)
1291 error(_("Cannot open directory"), directory.c_str());
1292
1293 // save the first fileName entry for this recursion
1294 const unsigned firstEntry = fileName.size();
1295
1296 // save files and sub directories
1297 while ((entry = readdir(dp)) != nullptr)
1298 {
1299 // get file status
1300 string entryFilepath = directory + g_fileSeparator + entry->d_name;
1301 if (stat(entryFilepath.c_str(), &statbuf) != 0)
1302 {
1303 if (errno == EOVERFLOW) // file over 2 GB is OK
1304 {
1305 errno = 0;
1306 continue;
1307 }
1308 perror("errno message");
1309 error("Error getting file status in directory", directory.c_str());
1310 }
1311 // skip hidden or read only
1312 if (entry->d_name[0] == '.' || !(statbuf.st_mode & S_IWUSR))
1313 continue;
1314 // if a sub directory and recursive, save sub directory
1315 if (S_ISDIR(statbuf.st_mode) && isRecursive)
1316 {
1317 if (isPathExclued(entryFilepath))
1318 printMsg(_("Exclude %s\n"), entryFilepath.substr(mainDirectoryLength));
1319 else
1320 subDirectory.emplace_back(entryFilepath);
1321 continue;
1322 }
1323
1324 // if a file, save file name
1325 if (S_ISREG(statbuf.st_mode))
1326 {
1327 // check exclude before wildcmp to avoid "unmatched exclude" error
1328 bool isExcluded = isPathExclued(entryFilepath);
1329 // save file name if wildcard match
1330 for (const string& wildcard : wildcards)
1331 {
1332 if (wildcmp(wildcard.c_str(), entry->d_name) != 0)
1333 {
1334 if (isExcluded)
1335 printMsg(_("Exclude %s\n"), entryFilepath.substr(mainDirectoryLength));
1336 else
1337 fileName.emplace_back(entryFilepath);
1338 break;
1339 }
1340 }
1341 }
1342 }
1343
1344 if (closedir(dp) != 0)
1345 {
1346 perror("errno message");
1347 error("Error reading directory", directory.c_str());
1348 }
1349
1350 // sort the current entries for fileName
1351 if (firstEntry < fileName.size())
1352 sort(fileName.begin() + firstEntry, fileName.end());
1353
1354 // recurse into sub directories
1355 // if not doing recursive, subDirectory is empty
1356 if (subDirectory.size() > 1)
1357 sort(subDirectory.begin(), subDirectory.end());
1358 for (unsigned i = 0; i < subDirectory.size(); i++)
1359 {
1360 getFileNames(subDirectory[i], wildcards);
1361 }
1362}
1363
1364// LINUX function to get the full path name from the relative path name
1365// Return the full path name or an empty string if failed.
1366string ASConsole::getFullPathName(const string& relativePath) const
1367{
1368 // ignore realPath attribute warning, only with cmake
1369#pragma GCC diagnostic push
1370#pragma GCC diagnostic ignored "-Wunused-result"
1371 char fullPath[PATH_MAX];
1372 fullPath[0] = '\0';
1373 realpath(relativePath.c_str(), fullPath);
1374 return fullPath;
1375#pragma GCC diagnostic pop
1376}
1377
1378// LINUX function to get the documentation file path prefix
1379// from the executable file path.
1380// Return the documentation path prefix or an empty string if failed.
1381string ASConsole::getHtmlInstallPrefix() const
1382{
1383 string astyleHtmlPrefix = astyleExePath;
1384 size_t end = astyleHtmlPrefix.find("/bin/");
1385 if (end == string::npos)
1386 return "";
1387 astyleHtmlPrefix = astyleHtmlPrefix.substr(0, end);
1388 return astyleHtmlPrefix;
1389}
1390
1391/**
1392 * LINUX function to get locale information and call getNumberFormat.
1393 * This formats positive integers only, no float.
1394 *
1395 * @param num The number to be formatted.
1396 * size_t is for compatibility with the Windows function.
1397 * @return The formatted number.
1398 */
1399string ASConsole::getNumberFormat(int num, size_t /*lcid*/) const
1400{
1401#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__BORLANDC__) || defined(__GNUC__)
1402 // Compilers that don't support C++ locales should still support this assert.
1403 // The C locale should be set but not the C++.
1404 // This function is not necessary if the C++ locale is set.
1405 // The locale().name() return value is not portable to all compilers.
1406 assert(locale().name() == "C");
1407#endif
1408
1409 // get the locale info
1410 struct lconv* lc;
1411 lc = localeconv();
1412
1413 // format the number
1414 return getNumberFormat(num, lc->grouping, lc->thousands_sep);
1415}
1416
1417/**
1418 * LINUX function to format a number according to the current locale.
1419 * This formats positive integers only, no float.
1420 *
1421 * @param num The number to be formatted.
1422 * @param groupingArg The grouping string from the locale.
1423 * @param separator The thousands group separator from the locale.
1424 * @return The formatted number.
1425 */
1426string ASConsole::getNumberFormat(int num, const char* groupingArg, const char* separator) const
1427{
1428 // convert num to a string
1429 stringstream alphaNum;
1430 alphaNum << num;
1431 string number = alphaNum.str();
1432 // format the number from right to left
1433 string formattedNum;
1434 size_t ig = 0; // grouping index
1435 int grouping = groupingArg[ig];
1436 int i = number.length();
1437 // check for no grouping
1438 if (grouping == 0)
1439 grouping = number.length();
1440 while (i > 0)
1441 {
1442 // extract a group of numbers
1443 string group;
1444 if (i < grouping)
1445 group = number;
1446 else
1447 group = number.substr(i - grouping);
1448 // update formatted number
1449 formattedNum.insert(0, group);
1450 i -= grouping;
1451 if (i < 0)
1452 i = 0;
1453 if (i > 0)
1454 formattedNum.insert(0, separator);
1455 number.erase(i);
1456 // update grouping
1457 if (groupingArg[ig] != '\0'
1458 && groupingArg[ig + 1] != '\0')
1459 grouping = groupingArg[++ig];
1460 }
1461 return formattedNum;
1462}
1463
1464/**
1465 * LINUX function to check for a HOME directory
1466 *
1467 * @param absPath The path to be evaluated.
1468 * @returns true if absPath is HOME or is an invalid absolute
1469 * path, false otherwise.
1470 */
1471bool ASConsole::isHomeOrInvalidAbsPath(const string& absPath) const
1472{
1473 const char* const env = getenv("HOME");
1474 if (env == nullptr)
1475 return true;
1476
1477 if (absPath.c_str() == env)
1478 return true;
1479
1480 if (absPath.compare(0, strlen(env), env) != 0)
1481 return true;
1482
1483 return false;
1484}
1485
1486/**
1487 * LINUX function to open a HTML file in the default browser.
1488 * Use xdg-open from freedesktop.org cross-desktop compatibility suite xdg-utils.
1489 * see http://portland.freedesktop.org/wiki/
1490 * This is installed on most modern distributions.
1491 */
1492void ASConsole::launchDefaultBrowser(const char* filePathIn /*nullptr*/) const
1493{
1494#ifdef __APPLE__
1495 string htmlDefaultPrefix = "/usr/local";
1496#else
1497 string htmlDefaultPrefix = "/usr";
1498#endif
1499 string htmlDefaultPath = htmlDefaultPrefix + "/share/doc/astyle/html/";
1500 string htmlDefaultFile = "astyle.html";
1501 string htmlFilePath;
1502 struct stat statbuf;
1503
1504 // build html path
1505 if (filePathIn == nullptr)
1506 {
1507 string htmlPrefix = getHtmlInstallPrefix();
1508 if (htmlPrefix.empty())
1509 htmlFilePath = htmlDefaultPrefix + htmlDefaultPath + htmlDefaultFile;
1510 else
1511 htmlFilePath = htmlPrefix + htmlDefaultPath + htmlDefaultFile;
1512 }
1513 else
1514 {
1515 if (strpbrk(filePathIn, "\\/") == nullptr)
1516 htmlFilePath = htmlDefaultPath + filePathIn;
1517 else
1518 htmlFilePath = filePathIn;
1519 }
1520 standardizePath(htmlFilePath);
1521 if (stat(htmlFilePath.c_str(), &statbuf) != 0 || !(statbuf.st_mode & S_IFREG))
1522 {
1523 printf(_("Cannot open HTML file %s\n"), htmlFilePath.c_str());
1524 return;
1525 }
1526
1527 // get search paths
1528 const char* const envPaths = getenv("PATH");
1529 if (envPaths == nullptr)
1530 error("Cannot read PATH environment variable", "");
1531 size_t envlen = strlen(envPaths);
1532 char* paths = new char[envlen + 1];
1533 strcpy(paths, envPaths);
1534 // find xdg-open (usually in /usr/bin)
1535 // Mac uses open instead
1536#ifdef __APPLE__
1537 const char* fileOpen = "open";
1538#else
1539 const char* fileOpen = "xdg-open";
1540#endif
1541 string searchPath;
1542 char* searchDir = strtok(paths, ":");
1543 while (searchDir != nullptr)
1544 {
1545 searchPath = searchDir;
1546 if (searchPath.length() > 0
1547 && searchPath[searchPath.length() - 1] != g_fileSeparator)
1548 searchPath.append(string(1, g_fileSeparator));
1549 searchPath.append(fileOpen);
1550 if (stat(searchPath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFREG))
1551 break;
1552 searchDir = strtok(nullptr, ":");
1553 }
1554 delete[] paths;
1555 if (searchDir == nullptr)
1556 error(_("Command is not installed"), fileOpen);
1557
1558 // browser open will be bypassed in test programs
1559 printf(_("Opening HTML documentation %s\n"), htmlFilePath.c_str());
1560 if (!bypassBrowserOpen)
1561 {
1562 execlp(fileOpen, fileOpen, htmlFilePath.c_str(), nullptr);
1563 // execlp will NOT return if successful
1564 error(_("Command execute failure"), fileOpen);
1565 }
1566}
1567
1568#endif // _WIN32
1569
1570/**
1571 * Returns the parent directory of absPath. If absPath is not a valid absolute
1572 * path or if it does not have a parent, an empty string is returned.
1573 *
1574 * @param absPath The initial directory.
1575 * @return The parent directory of absPath, or an empty string if
1576 * one cannot be found.
1577 */
1578string ASConsole::getParentDirectory(const string& absPath) const
1579{
1580 if (isHomeOrInvalidAbsPath(absPath))
1581 {
1582 return string();
1583 }
1584 size_t offset = absPath.size() - 1;
1585 if (absPath[absPath.size() - 1] == g_fileSeparator)
1586 {
1587 offset -= 1;
1588 }
1589 size_t idx = absPath.rfind(g_fileSeparator, offset);
1590 if (idx == string::npos)
1591 {
1592 return string();
1593 }
1594 string str = absPath.substr(0, idx + 1);
1595 return str;
1596}
1597
1598// get individual file names from the command-line file path
1599void ASConsole::getFilePaths(const string& filePath)
1600{
1601 fileName.clear();
1602 targetDirectory = string();
1603 targetFilename = string();
1604 vector<string> targetFilenameVector;
1605
1606 // separate directory and file name
1607 size_t separator = filePath.find_last_of(g_fileSeparator);
1608 if (separator == string::npos)
1609 {
1610 // if no directory is present, use the currently active directory
1611 targetDirectory = getCurrentDirectory(filePath);
1612 targetFilename = filePath;
1613 mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator
1614 }
1615 else
1616 {
1617 targetDirectory = filePath.substr(0, separator);
1618 targetFilename = filePath.substr(separator + 1);
1619 mainDirectoryLength = targetDirectory.length() + 1; // +1 includes trailing separator
1620 }
1621
1622 if (targetFilename.length() == 0)
1623 {
1624 fprintf(stderr, _("Missing filename in %s\n"), filePath.c_str());
1625 error();
1626 }
1627
1628 // check filename for wildcards
1629 hasWildcard = false;
1630 if (targetFilename.find_first_of("*?") != string::npos)
1631 hasWildcard = true;
1632
1633 // If the filename is not quoted on Linux, bash will replace the
1634 // wildcard instead of passing it to the program.
1635 if (isRecursive && !hasWildcard)
1636 {
1637 fprintf(stderr, "%s\n", _("Recursive option with no wildcard"));
1638#ifndef _WIN32
1639 fprintf(stderr, "%s\n", _("Did you intend quote the filename"));
1640#endif
1641 error();
1642 }
1643
1644 bool hasMultipleTargets = false;
1645 if (targetFilename.find_first_of(",;") != string::npos)
1646 hasMultipleTargets = true;
1647
1648 // display directory name for wildcard processing
1649 if (hasWildcard)
1650 {
1651 printSeparatingLine();
1652 printMsg(_("Directory %s\n"), targetDirectory + g_fileSeparator + targetFilename);
1653 }
1654
1655 // clear exclude hits vector
1656 size_t excludeHitsVectorSize = excludeHitsVector.size();
1657 for (size_t ix = 0; ix < excludeHitsVectorSize; ix++)
1658 excludeHitsVector[ix] = false;
1659
1660 // create a vector of paths and file names to process
1661 if (hasWildcard || isRecursive || hasMultipleTargets)
1662 {
1663 getTargetFilenames(targetFilename, targetFilenameVector);
1664 getFileNames(targetDirectory, targetFilenameVector);
1665 }
1666 else
1667 {
1668 // verify a single file is not a directory (needed on Linux)
1669 string entryFilepath = targetDirectory + g_fileSeparator + targetFilename;
1670 struct stat statbuf;
1671 if (stat(entryFilepath.c_str(), &statbuf) == 0 && (statbuf.st_mode & S_IFREG))
1672 fileName.emplace_back(entryFilepath);
1673 }
1674
1675 // check for unprocessed excludes
1676 bool excludeErr = false;
1677 for (size_t ix = 0; ix < excludeHitsVector.size(); ix++)
1678 {
1679 if (!excludeHitsVector[ix])
1680 {
1681 excludeErr = true;
1682 if (!ignoreExcludeErrorsDisplay)
1683 {
1684 if (ignoreExcludeErrors)
1685 printMsg(_("Exclude (unmatched) %s\n"), excludeVector[ix]);
1686 else
1687 fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str());
1688 }
1689 else
1690 {
1691 if (!ignoreExcludeErrors)
1692 fprintf(stderr, _("Exclude (unmatched) %s\n"), excludeVector[ix].c_str());
1693 }
1694 }
1695 }
1696
1697 if (excludeErr && !ignoreExcludeErrors)
1698 {
1699 if (hasWildcard && !isRecursive)
1700 fprintf(stderr, "%s\n", _("Did you intend to use --recursive"));
1701 error();
1702 }
1703
1704 // check if files were found (probably an input error if not)
1705 if (fileName.empty())
1706 {
1707 fprintf(stderr, _("No file to process %s\n"), filePath.c_str());
1708 if (hasWildcard && !isRecursive)
1709 fprintf(stderr, "%s\n", _("Did you intend to use --recursive"));
1710 error();
1711 }
1712
1713 if (hasWildcard)
1714 printSeparatingLine();
1715}
1716
1717// Check if a file exists
1718bool ASConsole::fileExists(const char* file) const
1719{
1720 struct stat buf;
1721 return (stat(file, &buf) == 0);
1722}
1723
1724bool ASConsole::fileNameVectorIsEmpty() const
1725{
1726 return fileNameVector.empty();
1727}
1728
1729bool ASConsole::isOption(const string& arg, const char* op)
1730{
1731 return arg == op;
1732}
1733
1734bool ASConsole::isOption(const string& arg, const char* a, const char* b)
1735{
1736 return (isOption(arg, a) || isOption(arg, b));
1737}
1738
1739bool ASConsole::isParamOption(const string& arg, const char* option)
1740{
1741 bool retVal = arg.compare(0, strlen(option), option) == 0;
1742 // if comparing for short option, 2nd char of arg must be numeric
1743 if (retVal && strlen(option) == 1 && arg.length() > 1)
1744 if (!isdigit((unsigned char) arg[1]))
1745 retVal = false;
1746 return retVal;
1747}
1748
1749// compare a path to the exclude vector
1750// used for both directories and filenames
1751// updates the g_excludeHitsVector
1752// return true if a match
1753bool ASConsole::isPathExclued(const string& subPath)
1754{
1755 bool retVal = false;
1756
1757 // read the exclude vector checking for a match
1758 for (size_t i = 0; i < excludeVector.size(); i++)
1759 {
1760 string exclude = excludeVector[i];
1761
1762 if (subPath.length() < exclude.length())
1763 continue;
1764
1765 size_t compareStart = subPath.length() - exclude.length();
1766 // subPath compare must start with a directory name
1767 if (compareStart > 0)
1768 {
1769 char lastPathChar = subPath[compareStart - 1];
1770 if (lastPathChar != g_fileSeparator)
1771 continue;
1772 }
1773
1774 string compare = subPath.substr(compareStart);
1775 if (!g_isCaseSensitive)
1776 {
1777 // make it case insensitive for Windows
1778 for (size_t j = 0; j < compare.length(); j++)
1779 compare[j] = (char) tolower(compare[j]);
1780 for (size_t j = 0; j < exclude.length(); j++)
1781 exclude[j] = (char) tolower(exclude[j]);
1782 }
1783 // compare sub directory to exclude data - must check them all
1784 if (compare == exclude)
1785 {
1786 excludeHitsVector[i] = true;
1787 retVal = true;
1788 break;
1789 }
1790 }
1791 return retVal;
1792}
1793
1794void ASConsole::printHelp() const
1795{
1796 cout << endl;
1797 cout << " Artistic Style " << g_version << endl;
1798 cout << " Maintained by: Jim Pattee\n";
1799 cout << " Original Author: Tal Davidson\n";
1800 cout << endl;
1801 cout << "Usage:\n";
1802 cout << "------\n";
1803 cout << " astyle [OPTIONS] File1 File2 File3 [...]\n";
1804 cout << endl;
1805 cout << " astyle [OPTIONS] < Original > Beautified\n";
1806 cout << endl;
1807 cout << " When indenting a specific file, the resulting indented file RETAINS\n";
1808 cout << " the original file-name. The original pre-indented file is renamed,\n";
1809 cout << " with a suffix of \'.orig\' added to the original filename.\n";
1810 cout << endl;
1811 cout << " Wildcards (* and ?) may be used in the filename.\n";
1812 cout << " A \'recursive\' option can process directories recursively.\n";
1813 cout << " Multiple file extensions may be separated by a comma.\n";
1814 cout << endl;
1815 cout << " By default, astyle is set up to indent with four spaces per indent,\n";
1816 cout << " a maximal indentation of 40 spaces inside continuous statements,\n";
1817 cout << " a minimum indentation of eight spaces inside conditional statements,\n";
1818 cout << " and NO formatting options.\n";
1819 cout << endl;
1820 cout << "Options:\n";
1821 cout << "--------\n";
1822 cout << " This program follows the usual GNU command line syntax.\n";
1823 cout << " Long options (starting with '--') must be written one at a time.\n";
1824 cout << " Short options (starting with '-') may be appended together.\n";
1825 cout << " Thus, -bps4 is the same as -b -p -s4.\n";
1826 cout << endl;
1827 cout << "Option Files:\n";
1828 cout << "-------------\n";
1829 cout << " Artistic Style looks for a default option file and/or a project\n";
1830 cout << " option file in the following order:\n";
1831 cout << " 1. The command line options have precedence.\n";
1832 cout << " 2. The project option file has precedence over the default file\n";
1833 cout << " o the file name indicated by the --project= command line option.\n";
1834 cout << " o the file named .astylerc or _ astylerc.\n";
1835 cout << " o the file name identified by ARTISTIC_STYLE_PROJECT_OPTIONS.\n";
1836 cout << " o the file is disabled by --project=none on the command line.\n";
1837 cout << " 3. The default option file that can be used for all projects.\n";
1838 cout << " o the file path indicated by the --options= command line option.\n";
1839 cout << " o the file path indicated by ARTISTIC_STYLE_OPTIONS.\n";
1840 cout << " o the file named .astylerc in the HOME directory (for Linux).\n";
1841 cout << " o the file name astylerc in the APPDATA directory (for Windows).\n";
1842 cout << " o the file is disabled by --project=none on the command line.\n";
1843 cout << " Long options within the option files may be written without '--'.\n";
1844 cout << " Line-end comments begin with a '#'.\n";
1845 cout << endl;
1846 cout << "Disable Formatting:\n";
1847 cout << "-------------------\n";
1848 cout << " Disable Block\n";
1849 cout << " Blocks of code can be disabled with the comment tags *INDENT-OFF*\n";
1850 cout << " and *INDENT-ON*. It must be contained in a one-line comment.\n";
1851 cout << endl;
1852 cout << " Disable Line\n";
1853 cout << " Padding of operators can be disabled on a single line using the\n";
1854 cout << " comment tag *NOPAD*. It must be contained in a line-end comment.\n";
1855 cout << endl;
1856 cout << "Brace Style Options:\n";
1857 cout << "--------------------\n";
1858 cout << " default brace style\n";
1859 cout << " If no brace style is requested, the opening braces will not be\n";
1860 cout << " changed and closing braces will be broken from the preceding line.\n";
1861 cout << endl;
1862 cout << " --style=allman OR --style=bsd OR --style=break OR -A1\n";
1863 cout << " Allman style formatting/indenting.\n";
1864 cout << " Broken braces.\n";
1865 cout << endl;
1866 cout << " --style=java OR --style=attach OR -A2\n";
1867 cout << " Java style formatting/indenting.\n";
1868 cout << " Attached braces.\n";
1869 cout << endl;
1870 cout << " --style=kr OR --style=k&r OR --style=k/r OR -A3\n";
1871 cout << " Kernighan & Ritchie style formatting/indenting.\n";
1872 cout << " Linux braces.\n";
1873 cout << endl;
1874 cout << " --style=stroustrup OR -A4\n";
1875 cout << " Stroustrup style formatting/indenting.\n";
1876 cout << " Linux braces, with broken closing headers.\n";
1877 cout << endl;
1878 cout << " --style=whitesmith OR -A5\n";
1879 cout << " Whitesmith style formatting/indenting.\n";
1880 cout << " Broken, indented braces.\n";
1881 cout << " Indented class blocks and switch blocks.\n";
1882 cout << endl;
1883 cout << " --style=vtk OR -A15\n";
1884 cout << " VTK style formatting/indenting.\n";
1885 cout << " Broken, indented braces except for the opening braces.\n";
1886 cout << endl;
1887 cout << " --style=ratliff OR --style=banner OR -A6\n";
1888 cout << " Ratliff style formatting/indenting.\n";
1889 cout << " Attached, indented braces.\n";
1890 cout << endl;
1891 cout << " --style=gnu OR -A7\n";
1892 cout << " GNU style formatting/indenting.\n";
1893 cout << " Broken braces, indented blocks.\n";
1894 cout << endl;
1895 cout << " --style=linux OR --style=knf OR -A8\n";
1896 cout << " Linux style formatting/indenting.\n";
1897 cout << " Linux braces, minimum conditional indent is one-half indent.\n";
1898 cout << endl;
1899 cout << " --style=horstmann OR --style=run-in OR -A9\n";
1900 cout << " Horstmann style formatting/indenting.\n";
1901 cout << " Run-in braces, indented switches.\n";
1902 cout << endl;
1903 cout << " --style=1tbs OR --style=otbs OR -A10\n";
1904 cout << " One True Brace Style formatting/indenting.\n";
1905 cout << " Linux braces, add braces to all conditionals.\n";
1906 cout << endl;
1907 cout << " --style=google OR -A14\n";
1908 cout << " Google style formatting/indenting.\n";
1909 cout << " Attached braces, indented class modifiers.\n";
1910 cout << endl;
1911 cout << " --style=mozilla OR -A16\n";
1912 cout << " Mozilla style formatting/indenting.\n";
1913 cout << " Linux braces, with broken braces for structs and enums,\n";
1914 cout << " and attached braces for namespaces.\n";
1915 cout << endl;
1916 cout << " --style=webkit OR -A17\n";
1917 cout << " WebKit style formatting/indenting.\n";
1918 cout << " Linux braces, with attached closing headers.\n";
1919 cout << endl;
1920 cout << " --style=pico OR -A11\n";
1921 cout << " Pico style formatting/indenting.\n";
1922 cout << " Run-in opening braces and attached closing braces.\n";
1923 cout << " Uses keep one line blocks and keep one line statements.\n";
1924 cout << endl;
1925 cout << " --style=lisp OR -A12\n";
1926 cout << " Lisp style formatting/indenting.\n";
1927 cout << " Attached opening braces and attached closing braces.\n";
1928 cout << " Uses keep one line statements.\n";
1929 cout << endl;
1930 cout << "Tab Options:\n";
1931 cout << "------------\n";
1932 cout << " default indent option\n";
1933 cout << " If no indentation option is set, the default\n";
1934 cout << " option of 4 spaces per indent will be used.\n";
1935 cout << endl;
1936 cout << " --indent=spaces=# OR -s#\n";
1937 cout << " Indent using # spaces per indent. Not specifying #\n";
1938 cout << " will result in a default of 4 spaces per indent.\n";
1939 cout << endl;
1940 cout << " --indent=tab OR --indent=tab=# OR -t OR -t#\n";
1941 cout << " Indent using tab characters, assuming that each\n";
1942 cout << " indent is # spaces long. Not specifying # will result\n";
1943 cout << " in a default assumption of 4 spaces per indent.\n";
1944 cout << endl;
1945 cout << " --indent=force-tab=# OR -T#\n";
1946 cout << " Indent using tab characters, assuming that each\n";
1947 cout << " indent is # spaces long. Force tabs to be used in areas\n";
1948 cout << " AStyle would prefer to use spaces.\n";
1949 cout << endl;
1950 cout << " --indent=force-tab-x=# OR -xT#\n";
1951 cout << " Allows the tab length to be set to a length that is different\n";
1952 cout << " from the indent length. This may cause the indentation to be\n";
1953 cout << " a mix of both spaces and tabs. This option sets the tab length.\n";
1954 cout << endl;
1955 cout << "Brace Modify Options:\n";
1956 cout << "---------------------\n";
1957 cout << " --attach-namespaces OR -xn\n";
1958 cout << " Attach braces to a namespace statement.\n";
1959 cout << endl;
1960 cout << " --attach-classes OR -xc\n";
1961 cout << " Attach braces to a class statement.\n";
1962 cout << endl;
1963 cout << " --attach-inlines OR -xl\n";
1964 cout << " Attach braces to class inline function definitions.\n";
1965 cout << endl;
1966 cout << " --attach-extern-c OR -xk\n";
1967 cout << " Attach braces to an extern \"C\" statement.\n";
1968 cout << endl;
1969 cout << " --attach-closing-while OR -xV\n";
1970 cout << " Attach closing while of do-while to the closing brace.\n";
1971 cout << endl;
1972 cout << "Indentation Options:\n";
1973 cout << "--------------------\n";
1974 cout << " --indent-classes OR -C\n";
1975 cout << " Indent 'class' blocks so that the entire block is indented.\n";
1976 cout << endl;
1977 cout << " --indent-modifiers OR -xG\n";
1978 cout << " Indent 'class' access modifiers, 'public:', 'protected:' or\n";
1979 cout << " 'private:', one half indent. The rest of the class is not\n";
1980 cout << " indented. \n";
1981 cout << endl;
1982 cout << " --indent-switches OR -S\n";
1983 cout << " Indent 'switch' blocks, so that the inner 'case XXX:'\n";
1984 cout << " headers are indented in relation to the switch block.\n";
1985 cout << endl;
1986 cout << " --indent-cases OR -K\n";
1987 cout << " Indent case blocks from the 'case XXX:' headers.\n";
1988 cout << " Case statements not enclosed in blocks are NOT indented.\n";
1989 cout << endl;
1990 cout << " --indent-namespaces OR -N\n";
1991 cout << " Indent the contents of namespace blocks.\n";
1992 cout << endl;
1993 cout << " --indent-after-parens OR -xU\n";
1994 cout << " Indent, instead of align, continuation lines following lines\n";
1995 cout << " that contain an opening paren '(' or an assignment '='. \n";
1996 cout << endl;
1997 cout << " --indent-continuation=# OR -xt#\n";
1998 cout << " Indent continuation lines an additional # indents.\n";
1999 cout << " The valid values are 0 thru 4 indents.\n";
2000 cout << " The default value is 1 indent.\n";
2001 cout << endl;
2002 cout << " --indent-labels OR -L\n";
2003 cout << " Indent labels so that they appear one indent less than\n";
2004 cout << " the current indentation level, rather than being\n";
2005 cout << " flushed completely to the left (which is the default).\n";
2006 cout << endl;
2007 cout << " --indent-preproc-block OR -xW\n";
2008 cout << " Indent preprocessor blocks at brace level 0.\n";
2009 cout << " Without this option the preprocessor block is not indented.\n";
2010 cout << endl;
2011 cout << " --indent-preproc-cond OR -xw\n";
2012 cout << " Indent preprocessor conditional statements #if/#else/#endif\n";
2013 cout << " to the same level as the source code.\n";
2014 cout << endl;
2015 cout << " --indent-preproc-define OR -w\n";
2016 cout << " Indent multi-line preprocessor #define statements.\n";
2017 cout << endl;
2018 cout << " --indent-col1-comments OR -Y\n";
2019 cout << " Indent line comments that start in column one.\n";
2020 cout << endl;
2021 cout << " --min-conditional-indent=# OR -m#\n";
2022 cout << " Indent a minimal # spaces in a continuous conditional\n";
2023 cout << " belonging to a conditional header.\n";
2024 cout << " The valid values are:\n";
2025 cout << " 0 - no minimal indent.\n";
2026 cout << " 1 - indent at least one additional indent.\n";
2027 cout << " 2 - indent at least two additional indents.\n";
2028 cout << " 3 - indent at least one-half an additional indent.\n";
2029 cout << " The default value is 2, two additional indents.\n";
2030 cout << endl;
2031 cout << " --max-continuation-indent=# OR -M#\n";
2032 cout << " Indent a maximal # spaces in a continuation line,\n";
2033 cout << " relative to the previous line.\n";
2034 cout << " The valid values are 40 thru 120.\n";
2035 cout << " The default value is 40.\n";
2036 cout << endl;
2037 cout << "Padding Options:\n";
2038 cout << "----------------\n";
2039 cout << " --break-blocks OR -f\n";
2040 cout << " Insert empty lines around unrelated blocks, labels, classes, ...\n";
2041 cout << endl;
2042 cout << " --break-blocks=all OR -F\n";
2043 cout << " Like --break-blocks, except also insert empty lines \n";
2044 cout << " around closing headers (e.g. 'else', 'catch', ...).\n";
2045 cout << endl;
2046 cout << " --pad-oper OR -p\n";
2047 cout << " Insert space padding around operators.\n";
2048 cout << endl;
2049 cout << " --pad-comma OR -xg\n";
2050 cout << " Insert space padding after commas.\n";
2051 cout << endl;
2052 cout << " --pad-paren OR -P\n";
2053 cout << " Insert space padding around parenthesis on both the outside\n";
2054 cout << " and the inside.\n";
2055 cout << endl;
2056 cout << " --pad-paren-out OR -d\n";
2057 cout << " Insert space padding around parenthesis on the outside only.\n";
2058 cout << endl;
2059 cout << " --pad-first-paren-out OR -xd\n";
2060 cout << " Insert space padding around first parenthesis in a series on\n";
2061 cout << " the outside only.\n";
2062 cout << endl;
2063 cout << " --pad-paren-in OR -D\n";
2064 cout << " Insert space padding around parenthesis on the inside only.\n";
2065 cout << endl;
2066 cout << " --pad-header OR -H\n";
2067 cout << " Insert space padding after paren headers (e.g. 'if', 'for'...).\n";
2068 cout << endl;
2069 cout << " --unpad-paren OR -U\n";
2070 cout << " Remove unnecessary space padding around parenthesis. This\n";
2071 cout << " can be used in combination with the 'pad' options above.\n";
2072 cout << endl;
2073 cout << " --delete-empty-lines OR -xe\n";
2074 cout << " Delete empty lines.\n";
2075 cout << " It will NOT delete lines added by the break-blocks options.\n";
2076 cout << endl;
2077 cout << " --delete-multiple-empty-lines OR -xm\n";
2078 cout << " Delete consecutive empty lines.\n";
2079 cout << " It will NOT delete lines added by the break-blocks options.\n";
2080 cout << endl;
2081 cout << " --fill-empty-lines OR -E\n";
2082 cout << " Fill empty lines with the white space of their\n";
2083 cout << " previous lines.\n";
2084 cout << endl;
2085 cout << " --align-pointer=type OR -k1\n";
2086 cout << " --align-pointer=middle OR -k2\n";
2087 cout << " --align-pointer=name OR -k3\n";
2088 cout << " Attach a pointer or reference operator (*, &, or ^) to either\n";
2089 cout << " the operator type (left), middle, or operator name (right).\n";
2090 cout << " To align the reference separately use --align-reference.\n";
2091 cout << endl;
2092 cout << " --align-reference=none OR -W0\n";
2093 cout << " --align-reference=type OR -W1\n";
2094 cout << " --align-reference=middle OR -W2\n";
2095 cout << " --align-reference=name OR -W3\n";
2096 cout << " Attach a reference operator (&) to either\n";
2097 cout << " the operator type (left), middle, or operator name (right).\n";
2098 cout << " If not set, follow pointer alignment.\n";
2099 cout << endl;
2100 cout << "Formatting Options:\n";
2101 cout << "-------------------\n";
2102 cout << " --break-closing-braces OR -y\n";
2103 cout << " Break braces before closing headers (e.g. 'else', 'catch', ...).\n";
2104 cout << " Use with --style=java, --style=kr, --style=stroustrup,\n";
2105 cout << " --style=linux, or --style=1tbs.\n";
2106 cout << endl;
2107 cout << " --break-elseifs OR -e\n";
2108 cout << " Break 'else if()' statements into two different lines.\n";
2109 cout << endl;
2110 cout << " --break-one-line-headers OR -xb\n";
2111 cout << " Break one line headers (e.g. 'if', 'while', 'else', ...) from a\n";
2112 cout << " statement residing on the same line.\n";
2113 cout << endl;
2114 cout << " --add-braces OR -j\n";
2115 cout << " Add braces to unbraced one line conditional statements.\n";
2116 cout << endl;
2117 cout << " --add-one-line-braces OR -J\n";
2118 cout << " Add one line braces to unbraced one line conditional\n";
2119 cout << " statements.\n";
2120 cout << endl;
2121 cout << " --remove-braces OR -xj\n";
2122 cout << " Remove braces from a braced one line conditional statements.\n";
2123 cout << endl;
2124 cout << " --break-return-type OR -xB\n";
2125 cout << " --break-return-type-decl OR -xD\n";
2126 cout << " Break the return type from the function name. Options are\n";
2127 cout << " for the function definitions and the function declarations.\n";
2128 cout << endl;
2129 cout << " --attach-return-type OR -xf\n";
2130 cout << " --attach-return-type-decl OR -xh\n";
2131 cout << " Attach the return type to the function name. Options are\n";
2132 cout << " for the function definitions and the function declarations.\n";
2133 cout << endl;
2134 cout << " --keep-one-line-blocks OR -O\n";
2135 cout << " Don't break blocks residing completely on one line.\n";
2136 cout << endl;
2137 cout << " --keep-one-line-statements OR -o\n";
2138 cout << " Don't break lines containing multiple statements into\n";
2139 cout << " multiple single-statement lines.\n";
2140 cout << endl;
2141 cout << " --convert-tabs OR -c\n";
2142 cout << " Convert tabs to the appropriate number of spaces.\n";
2143 cout << endl;
2144 cout << " --close-templates OR -xy\n";
2145 cout << " Close ending angle brackets on template definitions.\n";
2146 cout << endl;
2147 cout << " --remove-comment-prefix OR -xp\n";
2148 cout << " Remove the leading '*' prefix on multi-line comments and\n";
2149 cout << " indent the comment text one indent.\n";
2150 cout << endl;
2151 cout << " --max-code-length=# OR -xC#\n";
2152 cout << " --break-after-logical OR -xL\n";
2153 cout << " max-code-length=# will break the line if it exceeds more than\n";
2154 cout << " # characters. The valid values are 50 thru 200.\n";
2155 cout << " If the line contains logical conditionals they will be placed\n";
2156 cout << " first on the new line. The option break-after-logical will\n";
2157 cout << " cause the logical conditional to be placed last on the\n";
2158 cout << " previous line.\n";
2159 cout << endl;
2160 cout << " --mode=c\n";
2161 cout << " Indent a C or C++ source file (this is the default).\n";
2162 cout << endl;
2163 cout << " --mode=java\n";
2164 cout << " Indent a Java source file.\n";
2165 cout << endl;
2166 cout << " --mode=cs\n";
2167 cout << " Indent a C# source file.\n";
2168 cout << endl;
2169 cout << "Objective-C Options:\n";
2170 cout << "--------------------\n";
2171 cout << " --pad-method-prefix OR -xQ\n";
2172 cout << " Insert space padding after the '-' or '+' Objective-C\n";
2173 cout << " method prefix.\n";
2174 cout << endl;
2175 cout << " --unpad-method-prefix OR -xR\n";
2176 cout << " Remove all space padding after the '-' or '+' Objective-C\n";
2177 cout << " method prefix.\n";
2178 cout << endl;
2179 cout << " --pad-return-type OR -xq\n";
2180 cout << " Insert space padding after the Objective-C return type.\n";
2181 cout << endl;
2182 cout << " --unpad-return-type OR -xr\n";
2183 cout << " Remove all space padding after the Objective-C return type.\n";
2184 cout << endl;
2185 cout << " --pad-param-type OR -xS\n";
2186 cout << " Insert space padding after the Objective-C return type.\n";
2187 cout << endl;
2188 cout << " --unpad-param-type OR -xs\n";
2189 cout << " Remove all space padding after the Objective-C return type.\n";
2190 cout << endl;
2191 cout << " --align-method-colon OR -xM\n";
2192 cout << " Align the colons in an Objective-C method definition.\n";
2193 cout << endl;
2194 cout << " --pad-method-colon=none OR -xP\n";
2195 cout << " --pad-method-colon=all OR -xP1\n";
2196 cout << " --pad-method-colon=after OR -xP2\n";
2197 cout << " --pad-method-colon=before OR -xP3\n";
2198 cout << " Add or remove space padding before or after the colons in an\n";
2199 cout << " Objective-C method call.\n";
2200 cout << endl;
2201 cout << "Other Options:\n";
2202 cout << "--------------\n";
2203 cout << " --suffix=####\n";
2204 cout << " Append the suffix #### instead of '.orig' to original filename.\n";
2205 cout << endl;
2206 cout << " --suffix=none OR -n\n";
2207 cout << " Do not retain a backup of the original file.\n";
2208 cout << endl;
2209 cout << " --recursive OR -r OR -R\n";
2210 cout << " Process subdirectories recursively.\n";
2211 cout << endl;
2212 cout << " --dry-run\n";
2213 cout << " Perform a trial run with no changes made to check for formatting.\n";
2214 cout << endl;
2215 cout << " --exclude=####\n";
2216 cout << " Specify a file or directory #### to be excluded from processing.\n";
2217 cout << endl;
2218 cout << " --ignore-exclude-errors OR -i\n";
2219 cout << " Allow processing to continue if there are errors in the exclude=####\n";
2220 cout << " options. It will display the unmatched excludes.\n";
2221 cout << endl;
2222 cout << " --ignore-exclude-errors-x OR -xi\n";
2223 cout << " Allow processing to continue if there are errors in the exclude=####\n";
2224 cout << " options. It will NOT display the unmatched excludes.\n";
2225 cout << endl;
2226 cout << " --errors-to-stdout OR -X\n";
2227 cout << " Print errors and help information to standard-output rather than\n";
2228 cout << " to standard-error.\n";
2229 cout << endl;
2230 cout << " --preserve-date OR -Z\n";
2231 cout << " Preserve the original file's date and time modified. The time\n";
2232 cout << " modified will be changed a few micro seconds to force a compile.\n";
2233 cout << endl;
2234 cout << " --verbose OR -v\n";
2235 cout << " Verbose mode. Extra informational messages will be displayed.\n";
2236 cout << endl;
2237 cout << " --formatted OR -Q\n";
2238 cout << " Formatted display mode. Display only the files that have been\n";
2239 cout << " formatted.\n";
2240 cout << endl;
2241 cout << " --quiet OR -q\n";
2242 cout << " Quiet mode. Suppress all output except error messages.\n";
2243 cout << endl;
2244 cout << " --lineend=windows OR -z1\n";
2245 cout << " --lineend=linux OR -z2\n";
2246 cout << " --lineend=macold OR -z3\n";
2247 cout << " Force use of the specified line end style. Valid options\n";
2248 cout << " are windows (CRLF), linux (LF), and macold (CR).\n";
2249 cout << endl;
2250 cout << "Command Line Only:\n";
2251 cout << "------------------\n";
2252 cout << " --options=####\n";
2253 cout << " --options=none\n";
2254 cout << " Specify a default option file #### to read and use.\n";
2255 cout << " It must contain a file path and a file name.\n";
2256 cout << " 'none' disables the default option file.\n";
2257 cout << endl;
2258 cout << " --project\n";
2259 cout << " --project=####\n";
2260 cout << " --project=none\n";
2261 cout << " Specify a project option file #### to read and use.\n";
2262 cout << " It must contain a file name only, without a directory path.\n";
2263 cout << " The file should be included in the project top-level directory.\n";
2264 cout << " The default file name is .astylerc or _astylerc.\n";
2265 cout << " 'none' disables the project or environment variable file.\n";
2266 cout << endl;
2267 cout << " --ascii OR -I\n";
2268 cout << " The displayed output will be ascii characters only.\n";
2269 cout << endl;
2270 cout << " --version OR -V\n";
2271 cout << " Print version number.\n";
2272 cout << endl;
2273 cout << " --help OR -h OR -?\n";
2274 cout << " Print this help message.\n";
2275 cout << endl;
2276 cout << " --html OR -!\n";
2277 cout << " Open the HTML help file \"astyle.html\" in the default browser.\n";
2278 cout << " The documentation must be installed in the standard install path.\n";
2279 cout << endl;
2280 cout << " --html=####\n";
2281 cout << " Open a HTML help file in the default browser using the file path\n";
2282 cout << " ####. The path may include a directory path and a file name, or a\n";
2283 cout << " file name only. Paths containing spaces must be enclosed in quotes.\n";
2284 cout << endl;
2285 cout << " --stdin=####\n";
2286 cout << " Use the file path #### as input to single file formatting.\n";
2287 cout << " This is a replacement for redirection.\n";
2288 cout << endl;
2289 cout << " --stdout=####\n";
2290 cout << " Use the file path #### as output from single file formatting.\n";
2291 cout << " This is a replacement for redirection.\n";
2292 cout << endl;
2293 cout << endl;
2294}
2295
2296/**
2297 * Process files in the fileNameVector.
2298 */
2299void ASConsole::processFiles()
2300{
2301 if (isVerbose)
2302 printVerboseHeader();
2303
2304 clock_t startTime = clock(); // start time of file formatting
2305
2306 // loop thru input fileNameVector and process the files
2307 for (const string& fileNameVectorName : fileNameVector)
2308 {
2309 getFilePaths(fileNameVectorName);
2310
2311 // loop thru fileName vector formatting the files
2312 for (const string& file : fileName)
2313 formatFile(file);
2314 }
2315
2316 // files are processed, display stats
2317 if (isVerbose)
2318 printVerboseStats(startTime);
2319}
2320
2321// process options from the command line and option files
2322// build the vectors fileNameVector, excludeVector, optionsVector,
2323// projectOptionsVector and fileOptionsVector
2324void ASConsole::processOptions(const vector<string>& argvOptions)
2325{
2326 bool ok = true;
2327 bool optionFileRequired = false;
2328 bool shouldParseOptionFile = true;
2329 bool projectOptionFileRequired = false;
2330 bool shouldParseProjectOptionFile = true;
2331 string projectOptionArg; // save for display
2332
2333 // get command line options
2334 for (string arg : argvOptions)
2335 {
2336 if (isOption(arg, "-I")
2337 || isOption(arg, "--ascii"))
2338 {
2339 useAscii = true;
2340 setlocale(LC_ALL, "C"); // use English decimal indicator
2341 localizer.setLanguageFromName("en");
2342 }
2343 else if (isOption(arg, "--options=none"))
2344 {
2345 optionFileRequired = false;
2346 shouldParseOptionFile = false;
2347 optionFileName = "";
2348 }
2349 else if (isParamOption(arg, "--options="))
2350 {
2351 optionFileName = getParam(arg, "--options=");
2352 standardizePath(optionFileName);
2353 optionFileName = getFullPathName(optionFileName);
2354 optionFileRequired = true;
2355 }
2356 else if (isOption(arg, "--project=none"))
2357 {
2358 projectOptionFileRequired = false;
2359 shouldParseProjectOptionFile = false;
2360 setProjectOptionFileName("");
2361 }
2362 else if (isParamOption(arg, "--project="))
2363 {
2364 projectOptionFileName = getParam(arg, "--project=");
2365 standardizePath(projectOptionFileName);
2366 projectOptionFileRequired = true;
2367 shouldParseProjectOptionFile = false;
2368 projectOptionArg = projectOptionFileName;
2369 }
2370 else if (isOption(arg, "--project"))
2371 {
2372 projectOptionFileName = ".astylerc";
2373 projectOptionFileRequired = true;
2374 shouldParseProjectOptionFile = false;
2375 projectOptionArg = projectOptionFileName;
2376 }
2377 else if (isOption(arg, "-h")
2378 || isOption(arg, "--help")
2379 || isOption(arg, "-?"))
2380 {
2381 printHelp();
2382 exit(EXIT_SUCCESS);
2383 }
2384 else if (isOption(arg, "-!")
2385 || isOption(arg, "--html"))
2386 {
2387 launchDefaultBrowser();
2388 exit(EXIT_SUCCESS);
2389 }
2390 else if (isParamOption(arg, "--html="))
2391 {
2392 string htmlFilePath = getParam(arg, "--html=");
2393 launchDefaultBrowser(htmlFilePath.c_str());
2394 exit(EXIT_SUCCESS);
2395 }
2396 else if (isOption(arg, "-V")
2397 || isOption(arg, "--version"))
2398 {
2399 printf("Artistic Style Version %s\n", g_version);
2400 exit(EXIT_SUCCESS);
2401 }
2402 else if (isParamOption(arg, "--stdin="))
2403 {
2404 string path = getParam(arg, "--stdin=");
2405 standardizePath(path);
2406 setStdPathIn(path);
2407 }
2408 else if (isParamOption(arg, "--stdout="))
2409 {
2410 string path = getParam(arg, "--stdout=");
2411 standardizePath(path);
2412 setStdPathOut(path);
2413 }
2414 else if (arg[0] == '-')
2415 {
2416 optionsVector.emplace_back(arg);
2417 }
2418 else // file-name
2419 {
2420 standardizePath(arg);
2421 fileNameVector.emplace_back(arg);
2422 }
2423 }
2424
2425 // get option file path and name
2426 if (shouldParseOptionFile)
2427 {
2428 if (optionFileName.empty())
2429 {
2430 const char* const env = getenv("ARTISTIC_STYLE_OPTIONS");
2431 if (env != nullptr)
2432 {
2433 setOptionFileName(env);
2434 standardizePath(optionFileName);
2435 optionFileName = getFullPathName(optionFileName);
2436 }
2437 }
2438 // for Linux
2439 if (optionFileName.empty())
2440 {
2441 const char* const env = getenv("HOME");
2442 if (env != nullptr)
2443 {
2444 string name = string(env) + "/.astylerc";
2445 if (fileExists(name.c_str()))
2446 setOptionFileName(name);
2447 }
2448 }
2449 // for Windows
2450 if (optionFileName.empty())
2451 {
2452 const char* const env = getenv("APPDATA");
2453 if (env != nullptr)
2454 {
2455 string name = string(env) + "\\astylerc";
2456 if (fileExists(name.c_str()))
2457 setOptionFileName(name);
2458 }
2459 }
2460 // for Windows
2461 // NOTE: depreciated with release 3.1, remove when appropriate
2462 // there is NO test data for this option
2463 if (optionFileName.empty())
2464 {
2465 const char* const env = getenv("USERPROFILE");
2466 if (env != nullptr)
2467 {
2468 string name = string(env) + "\\astylerc";
2469 if (fileExists(name.c_str()))
2470 setOptionFileName(name);
2471 }
2472 }
2473 }
2474
2475 // find project option file
2476 if (projectOptionFileRequired)
2477 {
2478 string optfilepath = findProjectOptionFilePath(projectOptionFileName);
2479 if (optfilepath.empty() || projectOptionArg.empty())
2480 error(_("Cannot open project option file"), projectOptionArg.c_str());
2481 standardizePath(optfilepath);
2482 setProjectOptionFileName(optfilepath);
2483 }
2484 if (shouldParseProjectOptionFile)
2485 {
2486 const char* const env = getenv("ARTISTIC_STYLE_PROJECT_OPTIONS");
2487 if (env != nullptr)
2488 {
2489 string optfilepath = findProjectOptionFilePath(env);
2490 standardizePath(optfilepath);
2491 setProjectOptionFileName(optfilepath);
2492 }
2493 }
2494
2495 ASOptions options(formatter, *this);
2496 if (!optionFileName.empty())
2497 {
2498 stringstream optionsIn;
2499 if (!fileExists(optionFileName.c_str()))
2500 error(_("Cannot open default option file"), optionFileName.c_str());
2501 FileEncoding encoding = readFile(optionFileName, optionsIn);
2502 // bypass a BOM, all BOMs have been converted to utf-8
2503 if (encoding == UTF_8BOM || encoding == UTF_16LE || encoding == UTF_16BE)
2504 {
2505 char buf[4];
2506 optionsIn.get(buf, 4);
2507 assert(strcmp(buf, "\xEF\xBB\xBF") == 0);
2508 }
2509 options.importOptions(optionsIn, fileOptionsVector);
2510 ok = options.parseOptions(fileOptionsVector,
2511 string(_("Invalid default options:")));
2512 }
2513 else if (optionFileRequired)
2514 error(_("Cannot open default option file"), optionFileName.c_str());
2515
2516 if (!ok)
2517 {
2518 (*errorStream) << options.getOptionErrors();
2519 (*errorStream) << _("For help on options type 'astyle -h'") << endl;
2520 error();
2521 }
2522
2523 if (!projectOptionFileName.empty())
2524 {
2525 stringstream projectOptionsIn;
2526 if (!fileExists(projectOptionFileName.c_str()))
2527 error(_("Cannot open project option file"), projectOptionFileName.c_str());
2528 FileEncoding encoding = readFile(projectOptionFileName, projectOptionsIn);
2529 // bypass a BOM, all BOMs have been converted to utf-8
2530 if (encoding == UTF_8BOM || encoding == UTF_16LE || encoding == UTF_16BE)
2531 {
2532 char buf[4];
2533 projectOptionsIn.get(buf, 4);
2534 assert(strcmp(buf, "\xEF\xBB\xBF") == 0);
2535 }
2536 options.importOptions(projectOptionsIn, projectOptionsVector);
2537 ok = options.parseOptions(projectOptionsVector,
2538 string(_("Invalid project options:")));
2539 }
2540
2541 if (!ok)
2542 {
2543 (*errorStream) << options.getOptionErrors();
2544 (*errorStream) << _("For help on options type 'astyle -h'") << endl;
2545 error();
2546 }
2547
2548 // parse the command line options vector for errors
2549 ok = options.parseOptions(optionsVector,
2550 string(_("Invalid command line options:")));
2551 if (!ok)
2552 {
2553 (*errorStream) << options.getOptionErrors();
2554 (*errorStream) << _("For help on options type 'astyle -h'") << endl;
2555 error();
2556 }
2557}
2558
2559// remove a file and check for an error
2560void ASConsole::removeFile(const char* fileName_, const char* errMsg) const
2561{
2562 if (remove(fileName_) != 0)
2563 {
2564 if (errno == ENOENT) // no file is OK
2565 errno = 0;
2566 if (errno)
2567 {
2568 perror("errno message");
2569 error(errMsg, fileName_);
2570 }
2571 }
2572}
2573
2574// rename a file and check for an error
2575void ASConsole::renameFile(const char* oldFileName, const char* newFileName, const char* errMsg) const
2576{
2577 int result = rename(oldFileName, newFileName);
2578 if (result != 0)
2579 {
2580 // if file still exists the remove needs more time - retry
2581 if (errno == EEXIST)
2582 {
2583 errno = 0;
2584 waitForRemove(newFileName);
2585 result = rename(oldFileName, newFileName);
2586 }
2587 if (result != 0)
2588 {
2589 perror("errno message");
2590 error(errMsg, oldFileName);
2591 }
2592 }
2593}
2594
2595// make sure file separators are correct type (Windows or Linux)
2596// remove ending file separator
2597// remove beginning file separator if requested and NOT a complete file path
2598void ASConsole::standardizePath(string& path, bool removeBeginningSeparator /*false*/) const
2599{
2600#ifdef __VMS
2601 struct FAB fab;
2602 struct NAML naml;
2603 char less[NAML$C_MAXRSS];
2604 char sess[NAM$C_MAXRSS];
2605 int r0_status;
2606
2607 // If we are on a VMS system, translate VMS style filenames to unix
2608 // style.
2609 fab = cc$rms_fab;
2610 fab.fab$l_fna = (char*) -1;
2611 fab.fab$b_fns = 0;
2612 fab.fab$l_naml = &naml;
2613 naml = cc$rms_naml;
2614 strcpy(sess, path.c_str());
2615 naml.naml$l_long_filename = (char*) sess;
2616 naml.naml$l_long_filename_size = path.length();
2617 naml.naml$l_long_expand = less;
2618 naml.naml$l_long_expand_alloc = sizeof(less);
2619 naml.naml$l_esa = sess;
2620 naml.naml$b_ess = sizeof(sess);
2621 naml.naml$v_no_short_upcase = 1;
2622 r0_status = sys$parse(&fab);
2623 if (r0_status == RMS$_SYN)
2624 {
2625 error("File syntax error", path.c_str());
2626 }
2627 else
2628 {
2629 if (!$VMS_STATUS_SUCCESS(r0_status))
2630 {
2631 (void) lib$signal(r0_status);
2632 }
2633 }
2634 less[naml.naml$l_long_expand_size - naml.naml$b_ver] = '\0';
2635 sess[naml.naml$b_esl - naml.naml$b_ver] = '\0';
2636 if (naml.naml$l_long_expand_size > naml.naml$b_esl)
2637 {
2638 path = decc$translate_vms(less);
2639 }
2640 else
2641 {
2642 path = decc$translate_vms(sess);
2643 }
2644#endif /* __VMS */
2645
2646 // make sure separators are correct type (Windows or Linux)
2647 for (size_t i = 0; i < path.length(); i++)
2648 {
2649 i = path.find_first_of("/\\", i);
2650 if (i == string::npos)
2651 break;
2652 path[i] = g_fileSeparator;
2653 }
2654 // remove beginning separator if requested
2655 if (removeBeginningSeparator && (path[0] == g_fileSeparator))
2656 path.erase(0, 1);
2657}
2658
2659void ASConsole::printMsg(const char* msg, const string& data) const
2660{
2661 if (isQuiet)
2662 return;
2663 printf(msg, data.c_str());
2664}
2665
2666void ASConsole::printSeparatingLine() const
2667{
2668 string line;
2669 for (size_t i = 0; i < 60; i++)
2670 line.append("-");
2671 printMsg("%s\n", line);
2672}
2673
2674void ASConsole::printVerboseHeader() const
2675{
2676 assert(isVerbose);
2677 if (isQuiet)
2678 return;
2679 // get the date
2680 time_t lt;
2681 char str[20];
2682 lt = time(nullptr);
2683 struct tm* ptr = localtime(&lt);
2684 strftime(str, 20, "%x", ptr);
2685 // print the header
2686 // 60 is the length of the separator in printSeparatingLine()
2687 string header = "Artistic Style " + string(g_version);
2688 size_t numSpaces = 60 - header.length() - strlen(str);
2689 header.append(numSpaces, ' ');
2690 header.append(str);
2691 header.append("\n");
2692 printf("%s", header.c_str());
2693 // print option files
2694 if (!optionFileName.empty())
2695 printf(_("Default option file %s\n"), optionFileName.c_str());
2696 // NOTE: depreciated with release 3.1, remove when appropriate
2697 if (!optionFileName.empty())
2698 {
2699 const char* const env = getenv("USERPROFILE");
2700 if (env != nullptr && optionFileName == string(env) + "\\astylerc")
2701 printf("The above option file has been DEPRECIATED\n");
2702 }
2703 // end depreciated
2704 if (!projectOptionFileName.empty())
2705 printf(_("Project option file %s\n"), projectOptionFileName.c_str());
2706}
2707
2708void ASConsole::printVerboseStats(clock_t startTime) const
2709{
2710 assert(isVerbose);
2711 if (isQuiet)
2712 return;
2713 if (hasWildcard)
2714 printSeparatingLine();
2715 string formatted = getNumberFormat(filesFormatted);
2716 string unchanged = getNumberFormat(filesUnchanged);
2717 printf(_(" %s formatted %s unchanged "), formatted.c_str(), unchanged.c_str());
2718
2719 // show processing time
2720 clock_t stopTime = clock();
2721 double secs = (stopTime - startTime) / double(CLOCKS_PER_SEC);
2722 if (secs < 60)
2723 {
2724 if (secs < 2.0)
2725 printf("%.2f", secs);
2726 else if (secs < 20.0)
2727 printf("%.1f", secs);
2728 else
2729 printf("%.0f", secs);
2730 printf("%s", _(" seconds "));
2731 }
2732 else
2733 {
2734 // show minutes and seconds if time is greater than one minute
2735 int min = (int) secs / 60;
2736 secs -= min * 60;
2737 // NOTE: lround is not supported by MinGW and Embarcadero
2738 int minsec = int(secs + .5);
2739 printf(_("%d min %d sec "), min, minsec);
2740 }
2741
2742 string lines = getNumberFormat(linesOut);
2743 printf(_("%s lines\n"), lines.c_str());
2744 printf("\n");
2745}
2746
2747void ASConsole::sleep(int seconds) const
2748{
2749 clock_t endwait;
2750 endwait = clock_t(clock() + seconds * CLOCKS_PER_SEC);
2751 while (clock() < endwait) {}
2752}
2753
2754bool ASConsole::stringEndsWith(const string& str, const string& suffix) const
2755{
2756 int strIndex = (int) str.length() - 1;
2757 int suffixIndex = (int) suffix.length() - 1;
2758
2759 while (strIndex >= 0 && suffixIndex >= 0)
2760 {
2761 if (tolower(str[strIndex]) != tolower(suffix[suffixIndex]))
2762 return false;
2763
2764 --strIndex;
2765 --suffixIndex;
2766 }
2767 // suffix longer than string
2768 if (strIndex < 0 && suffixIndex >= 0)
2769 return false;
2770 return true;
2771}
2772
2773void ASConsole::updateExcludeVector(const string& suffixParam)
2774{
2775 excludeVector.emplace_back(suffixParam);
2776 standardizePath(excludeVector.back(), true);
2777 // do not use emplace_back on vector<bool> until supported by macOS
2778 excludeHitsVector.push_back(false);
2779}
2780
2781int ASConsole::waitForRemove(const char* newFileName) const
2782{
2783 struct stat stBuf;
2784 int seconds;
2785 // sleep a max of 20 seconds for the remove
2786 for (seconds = 1; seconds <= 20; seconds++)
2787 {
2788 sleep(1);
2789 if (stat(newFileName, &stBuf) != 0)
2790 break;
2791 }
2792 errno = 0;
2793 return seconds;
2794}
2795
2796// From The Code Project http://www.codeproject.com/string/wildcmp.asp
2797// Written by Jack Handy - jakkhandy@hotmail.com
2798// Modified to compare case insensitive for Windows
2799int ASConsole::wildcmp(const char* wild, const char* data) const
2800{
2801 const char* cp = nullptr;
2802 const char* mp = nullptr;
2803 bool cmpval;
2804
2805 while ((*data) && (*wild != '*'))
2806 {
2807 if (!g_isCaseSensitive)
2808 cmpval = (tolower(*wild) != tolower(*data)) && (*wild != '?');
2809 else
2810 cmpval = (*wild != *data) && (*wild != '?');
2811
2812 if (cmpval)
2813 {
2814 return 0;
2815 }
2816 wild++;
2817 data++;
2818 }
2819
2820 while (*data)
2821 {
2822 if (*wild == '*')
2823 {
2824 if (!*++wild)
2825 {
2826 return 1;
2827 }
2828 mp = wild;
2829 cp = data + 1;
2830 }
2831 else
2832 {
2833 if (!g_isCaseSensitive)
2834 cmpval = (tolower(*wild) == tolower(*data) || (*wild == '?'));
2835 else
2836 cmpval = (*wild == *data) || (*wild == '?');
2837
2838 if (cmpval)
2839 {
2840 wild++;
2841 data++;
2842 }
2843 else
2844 {
2845 wild = mp;
2846 data = cp++;
2847 }
2848 }
2849 }
2850
2851 while (*wild == '*')
2852 {
2853 wild++;
2854 }
2855 return !*wild;
2856}
2857
2858void ASConsole::writeFile(const string& fileName_, FileEncoding encoding, ostringstream& out) const
2859{
2860 // save date accessed and date modified of original file
2861 struct stat stBuf;
2862 bool statErr = false;
2863 if (stat(fileName_.c_str(), &stBuf) == -1)
2864 statErr = true;
2865
2866 // create a backup
2867 if (!noBackup)
2868 {
2869 string origFileName = fileName_ + origSuffix;
2870 removeFile(origFileName.c_str(), "Cannot remove pre-existing backup file");
2871 renameFile(fileName_.c_str(), origFileName.c_str(), "Cannot create backup file");
2872 }
2873
2874 // write the output file
2875 ofstream fout(fileName_.c_str(), ios::binary | ios::trunc);
2876 if (!fout)
2877 error("Cannot open output file", fileName_.c_str());
2878 if (encoding == UTF_16LE || encoding == UTF_16BE)
2879 {
2880 // convert utf-8 to utf-16
2881 bool isBigEndian = (encoding == UTF_16BE);
2882 size_t utf16Size = encode.utf16LengthFromUtf8(out.str().c_str(), out.str().length());
2883 char* utf16Out = new char[utf16Size];
2884 size_t utf16Len = encode.utf8ToUtf16(const_cast<char*>(out.str().c_str()),
2885 out.str().length(), isBigEndian, utf16Out);
2886 assert(utf16Len <= utf16Size);
2887 fout << string(utf16Out, utf16Len);
2888 delete[] utf16Out;
2889 }
2890 else
2891 fout << out.str();
2892
2893 fout.close();
2894
2895 // change date modified to original file date
2896 // Embarcadero must be linked with cw32mt not cw32
2897 if (preserveDate)
2898 {
2899 if (!statErr)
2900 {
2901 struct utimbuf outBuf;
2902 outBuf.actime = stBuf.st_atime;
2903 // add ticks so 'make' will recognize a change
2904 // Visual Studio 2008 needs more than 1
2905 outBuf.modtime = stBuf.st_mtime + 10;
2906 if (utime(fileName_.c_str(), &outBuf) == -1)
2907 statErr = true;
2908 }
2909 if (statErr)
2910 {
2911 perror("errno message");
2912 (*errorStream) << "********* Cannot preserve file date" << endl;
2913 }
2914 }
2915}
2916
2917#else // ASTYLE_LIB
2918
2919//-----------------------------------------------------------------------------
2920// ASLibrary class
2921// used by shared object (DLL) calls
2922//-----------------------------------------------------------------------------
2923
2924char16_t* ASLibrary::formatUtf16(const char16_t* pSourceIn, // the source to be formatted
2925 const char16_t* pOptions, // AStyle options
2926 fpError fpErrorHandler, // error handler function
2927 fpAlloc fpMemoryAlloc) const // memory allocation function)
2928{
2929 const char* utf8In = convertUtf16ToUtf8(pSourceIn);
2930 if (utf8In == nullptr)
2931 {
2932 fpErrorHandler(121, "Cannot convert input utf-16 to utf-8.");
2933 return nullptr;
2934 }
2935 const char* utf8Options = convertUtf16ToUtf8(pOptions);
2936 if (utf8Options == nullptr)
2937 {
2938 delete[] utf8In;
2939 fpErrorHandler(122, "Cannot convert options utf-16 to utf-8.");
2940 return nullptr;
2941 }
2942 // call the Artistic Style formatting function
2943 // cannot use the callers memory allocation here
2944 char* utf8Out = AStyleMain(utf8In,
2945 utf8Options,
2946 fpErrorHandler,
2947 ASLibrary::tempMemoryAllocation);
2948 // finished with these
2949 delete[] utf8In;
2950 delete[] utf8Options;
2951 utf8In = nullptr;
2952 utf8Options = nullptr;
2953 // AStyle error has already been sent
2954 if (utf8Out == nullptr)
2955 return nullptr;
2956 // convert text to wide char and return it
2957 char16_t* utf16Out = convertUtf8ToUtf16(utf8Out, fpMemoryAlloc);
2958 delete[] utf8Out;
2959 utf8Out = nullptr;
2960 if (utf16Out == nullptr)
2961 {
2962 fpErrorHandler(123, "Cannot convert output utf-8 to utf-16.");
2963 return nullptr;
2964 }
2965 return utf16Out;
2966}
2967
2968// STATIC method to allocate temporary memory for AStyle formatting.
2969// The data will be converted before being returned to the calling program.
2970char* STDCALL ASLibrary::tempMemoryAllocation(unsigned long memoryNeeded)
2971{
2972 char* buffer = new (nothrow) char[memoryNeeded];
2973 return buffer;
2974}
2975
2976/**
2977 * Convert utf-8 strings to utf16 strings.
2978 * Memory is allocated by the calling program memory allocation function.
2979 * The calling function must check for errors.
2980 */
2981char16_t* ASLibrary::convertUtf8ToUtf16(const char* utf8In, fpAlloc fpMemoryAlloc) const
2982{
2983 if (utf8In == nullptr)
2984 return nullptr;
2985 char* data = const_cast<char*>(utf8In);
2986 size_t dataSize = strlen(utf8In);
2987 bool isBigEndian = encode.getBigEndian();
2988 // return size is in number of CHARs, not char16_t
2989 size_t utf16Size = (encode.utf16LengthFromUtf8(data, dataSize) + sizeof(char16_t));
2990 char* utf16Out = fpMemoryAlloc((long) utf16Size);
2991 if (utf16Out == nullptr)
2992 return nullptr;
2993#ifdef NDEBUG
2994 encode.utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out);
2995#else
2996 size_t utf16Len = encode.utf8ToUtf16(data, dataSize + 1, isBigEndian, utf16Out);
2997 assert(utf16Len == utf16Size);
2998#endif
2999 assert(utf16Size == (encode.utf16len(reinterpret_cast<char16_t*>(utf16Out)) + 1) * sizeof(char16_t));
3000 return reinterpret_cast<char16_t*>(utf16Out);
3001}
3002
3003/**
3004 * Convert utf16 strings to utf-8.
3005 * The calling function must check for errors and delete the
3006 * allocated memory.
3007 */
3008char* ASLibrary::convertUtf16ToUtf8(const char16_t* utf16In) const
3009{
3010 if (utf16In == nullptr)
3011 return nullptr;
3012 char* data = reinterpret_cast<char*>(const_cast<char16_t*>(utf16In));
3013 // size must be in chars
3014 size_t dataSize = encode.utf16len(utf16In) * sizeof(char16_t);
3015 bool isBigEndian = encode.getBigEndian();
3016 size_t utf8Size = encode.utf8LengthFromUtf16(data, dataSize, isBigEndian) + 1;
3017 char* utf8Out = new (nothrow) char[utf8Size];
3018 if (utf8Out == nullptr)
3019 return nullptr;
3020#ifdef NDEBUG
3021 encode.utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out);
3022#else
3023 size_t utf8Len = encode.utf16ToUtf8(data, dataSize + 1, isBigEndian, true, utf8Out);
3024 assert(utf8Len == utf8Size);
3025#endif
3026 assert(utf8Size == strlen(utf8Out) + 1);
3027 return utf8Out;
3028}
3029
3030#endif // ASTYLE_LIB
3031
3032//-----------------------------------------------------------------------------
3033// ASOptions class
3034// used by both console and library builds
3035//-----------------------------------------------------------------------------
3036
3037#ifdef ASTYLE_LIB
3038ASOptions::ASOptions(ASFormatter& formatterArg)
3039 : formatter(formatterArg)
3040{ }
3041#else
3042ASOptions::ASOptions(ASFormatter& formatterArg, ASConsole& consoleArg)
3043 : formatter(formatterArg), console(consoleArg)
3044{ }
3045#endif
3046
3047/**
3048 * parse the options vector
3049 * optionsVector can be either a fileOptionsVector (option file),
3050 * a projectOptionsVector (project option file),
3051 * or an optionsVector (command line)
3052 *
3053 * @return true if no errors, false if errors
3054 */
3055bool ASOptions::parseOptions(vector<string>& optionsVector, const string& errorInfo)
3056{
3057 vector<string>::iterator option;
3058 string arg;
3059 string subArg;
3060 optionErrors.clear();
3061
3062 for (option = optionsVector.begin(); option != optionsVector.end(); ++option)
3063 {
3064 arg = *option;
3065
3066 if (arg.compare(0, 2, "--") == 0)
3067 parseOption(arg.substr(2), errorInfo);
3068 else if (arg[0] == '-')
3069 {
3070 size_t i;
3071
3072 for (i = 1; i < arg.length(); ++i)
3073 {
3074 if (i > 1
3075 && isalpha((unsigned char) arg[i])
3076 && arg[i - 1] != 'x')
3077 {
3078 // parse the previous option in subArg
3079 parseOption(subArg, errorInfo);
3080 subArg = "";
3081 }
3082 // append the current option to subArg
3083 subArg.append(1, arg[i]);
3084 }
3085 // parse the last option
3086 parseOption(subArg, errorInfo);
3087 subArg = "";
3088 }
3089 else
3090 {
3091 parseOption(arg, errorInfo);
3092 subArg = "";
3093 }
3094 }
3095 if (optionErrors.str().length() > 0)
3096 return false;
3097 return true;
3098}
3099
3100void ASOptions::parseOption(const string& arg, const string& errorInfo)
3101{
3102 if (isOption(arg, "A1", "style=allman") || isOption(arg, "style=bsd") || isOption(arg, "style=break"))
3103 {
3104 formatter.setFormattingStyle(STYLE_ALLMAN);
3105 }
3106 else if (isOption(arg, "A2", "style=java") || isOption(arg, "style=attach"))
3107 {
3108 formatter.setFormattingStyle(STYLE_JAVA);
3109 }
3110 else if (isOption(arg, "A3", "style=k&r") || isOption(arg, "style=kr") || isOption(arg, "style=k/r"))
3111 {
3112 formatter.setFormattingStyle(STYLE_KR);
3113 }
3114 else if (isOption(arg, "A4", "style=stroustrup"))
3115 {
3116 formatter.setFormattingStyle(STYLE_STROUSTRUP);
3117 }
3118 else if (isOption(arg, "A5", "style=whitesmith"))
3119 {
3120 formatter.setFormattingStyle(STYLE_WHITESMITH);
3121 }
3122 else if (isOption(arg, "A15", "style=vtk"))
3123 {
3124 formatter.setFormattingStyle(STYLE_VTK);
3125 }
3126 else if (isOption(arg, "A6", "style=ratliff") || isOption(arg, "style=banner"))
3127 {
3128 formatter.setFormattingStyle(STYLE_RATLIFF);
3129 }
3130 else if (isOption(arg, "A7", "style=gnu"))
3131 {
3132 formatter.setFormattingStyle(STYLE_GNU);
3133 }
3134 else if (isOption(arg, "A8", "style=linux") || isOption(arg, "style=knf"))
3135 {
3136 formatter.setFormattingStyle(STYLE_LINUX);
3137 }
3138 else if (isOption(arg, "A9", "style=horstmann") || isOption(arg, "style=run-in"))
3139 {
3140 formatter.setFormattingStyle(STYLE_HORSTMANN);
3141 }
3142 else if (isOption(arg, "A10", "style=1tbs") || isOption(arg, "style=otbs"))
3143 {
3144 formatter.setFormattingStyle(STYLE_1TBS);
3145 }
3146 else if (isOption(arg, "A14", "style=google"))
3147 {
3148 formatter.setFormattingStyle(STYLE_GOOGLE);
3149 }
3150 else if (isOption(arg, "A16", "style=mozilla"))
3151 {
3152 formatter.setFormattingStyle(STYLE_MOZILLA);
3153 }
3154 else if (isOption(arg, "A17", "style=webkit"))
3155 {
3156 formatter.setFormattingStyle(STYLE_WEBKIT);
3157 }
3158 else if (isOption(arg, "A11", "style=pico"))
3159 {
3160 formatter.setFormattingStyle(STYLE_PICO);
3161 }
3162 else if (isOption(arg, "A12", "style=lisp") || isOption(arg, "style=python"))
3163 {
3164 formatter.setFormattingStyle(STYLE_LISP);
3165 }
3166 // must check for mode=cs before mode=c !!!
3167 else if (isOption(arg, "mode=cs"))
3168 {
3169 formatter.setSharpStyle();
3170 formatter.setModeManuallySet(true);
3171 }
3172 else if (isOption(arg, "mode=c"))
3173 {
3174 formatter.setCStyle();
3175 formatter.setModeManuallySet(true);
3176 }
3177 else if (isOption(arg, "mode=java"))
3178 {
3179 formatter.setJavaStyle();
3180 formatter.setModeManuallySet(true);
3181 }
3182 else if (isParamOption(arg, "t", "indent=tab="))
3183 {
3184 int spaceNum = 4;
3185 string spaceNumParam = getParam(arg, "t", "indent=tab=");
3186 if (spaceNumParam.length() > 0)
3187 spaceNum = atoi(spaceNumParam.c_str());
3188 if (spaceNum < 2 || spaceNum > 20)
3189 isOptionError(arg, errorInfo);
3190 else
3191 {
3192 formatter.setTabIndentation(spaceNum, false);
3193 }
3194 }
3195 else if (isOption(arg, "indent=tab"))
3196 {
3197 formatter.setTabIndentation(4);
3198 }
3199 else if (isParamOption(arg, "T", "indent=force-tab="))
3200 {
3201 int spaceNum = 4;
3202 string spaceNumParam = getParam(arg, "T", "indent=force-tab=");
3203 if (spaceNumParam.length() > 0)
3204 spaceNum = atoi(spaceNumParam.c_str());
3205 if (spaceNum < 2 || spaceNum > 20)
3206 isOptionError(arg, errorInfo);
3207 else
3208 {
3209 formatter.setTabIndentation(spaceNum, true);
3210 }
3211 }
3212 else if (isOption(arg, "indent=force-tab"))
3213 {
3214 formatter.setTabIndentation(4, true);
3215 }
3216 else if (isParamOption(arg, "xT", "indent=force-tab-x="))
3217 {
3218 int tabNum = 8;
3219 string tabNumParam = getParam(arg, "xT", "indent=force-tab-x=");
3220 if (tabNumParam.length() > 0)
3221 tabNum = atoi(tabNumParam.c_str());
3222 if (tabNum < 2 || tabNum > 20)
3223 isOptionError(arg, errorInfo);
3224 else
3225 {
3226 formatter.setForceTabXIndentation(tabNum);
3227 }
3228 }
3229 else if (isOption(arg, "indent=force-tab-x"))
3230 {
3231 formatter.setForceTabXIndentation(8);
3232 }
3233 else if (isParamOption(arg, "s", "indent=spaces="))
3234 {
3235 int spaceNum = 4;
3236 string spaceNumParam = getParam(arg, "s", "indent=spaces=");
3237 if (spaceNumParam.length() > 0)
3238 spaceNum = atoi(spaceNumParam.c_str());
3239 if (spaceNum < 2 || spaceNum > 20)
3240 isOptionError(arg, errorInfo);
3241 else
3242 {
3243 formatter.setSpaceIndentation(spaceNum);
3244 }
3245 }
3246 else if (isOption(arg, "indent=spaces"))
3247 {
3248 formatter.setSpaceIndentation(4);
3249 }
3250 else if (isParamOption(arg, "xt", "indent-continuation="))
3251 {
3252 int contIndent = 1;
3253 string contIndentParam = getParam(arg, "xt", "indent-continuation=");
3254 if (contIndentParam.length() > 0)
3255 contIndent = atoi(contIndentParam.c_str());
3256 if (contIndent < 0)
3257 isOptionError(arg, errorInfo);
3258 else if (contIndent > 4)
3259 isOptionError(arg, errorInfo);
3260 else
3261 formatter.setContinuationIndentation(contIndent);
3262 }
3263 else if (isParamOption(arg, "m", "min-conditional-indent="))
3264 {
3265 int minIndent = MINCOND_TWO;
3266 string minIndentParam = getParam(arg, "m", "min-conditional-indent=");
3267 if (minIndentParam.length() > 0)
3268 minIndent = atoi(minIndentParam.c_str());
3269 if (minIndent >= MINCOND_END)
3270 isOptionError(arg, errorInfo);
3271 else
3272 formatter.setMinConditionalIndentOption(minIndent);
3273 }
3274 else if (isParamOption(arg, "M", "max-continuation-indent="))
3275 {
3276 int maxIndent = 40;
3277 string maxIndentParam = getParam(arg, "M", "max-continuation-indent=");
3278 if (maxIndentParam.length() > 0)
3279 maxIndent = atoi(maxIndentParam.c_str());
3280 if (maxIndent < 40)
3281 isOptionError(arg, errorInfo);
3282 else if (maxIndent > 120)
3283 isOptionError(arg, errorInfo);
3284 else
3285 formatter.setMaxContinuationIndentLength(maxIndent);
3286 }
3287 else if (isOption(arg, "N", "indent-namespaces"))
3288 {
3289 formatter.setNamespaceIndent(true);
3290 }
3291 else if (isOption(arg, "C", "indent-classes"))
3292 {
3293 formatter.setClassIndent(true);
3294 }
3295 else if (isOption(arg, "xG", "indent-modifiers"))
3296 {
3297 formatter.setModifierIndent(true);
3298 }
3299 else if (isOption(arg, "S", "indent-switches"))
3300 {
3301 formatter.setSwitchIndent(true);
3302 }
3303 else if (isOption(arg, "K", "indent-cases"))
3304 {
3305 formatter.setCaseIndent(true);
3306 }
3307 else if (isOption(arg, "xU", "indent-after-parens"))
3308 {
3309 formatter.setAfterParenIndent(true);
3310 }
3311 else if (isOption(arg, "L", "indent-labels"))
3312 {
3313 formatter.setLabelIndent(true);
3314 }
3315 else if (isOption(arg, "xW", "indent-preproc-block"))
3316 {
3317 formatter.setPreprocBlockIndent(true);
3318 }
3319 else if (isOption(arg, "w", "indent-preproc-define"))
3320 {
3321 formatter.setPreprocDefineIndent(true);
3322 }
3323 else if (isOption(arg, "xw", "indent-preproc-cond"))
3324 {
3325 formatter.setPreprocConditionalIndent(true);
3326 }
3327 else if (isOption(arg, "y", "break-closing-braces"))
3328 {
3329 formatter.setBreakClosingHeaderBracesMode(true);
3330 }
3331 else if (isOption(arg, "O", "keep-one-line-blocks"))
3332 {
3333 formatter.setBreakOneLineBlocksMode(false);
3334 }
3335 else if (isOption(arg, "o", "keep-one-line-statements"))
3336 {
3337 formatter.setBreakOneLineStatementsMode(false);
3338 }
3339 else if (isOption(arg, "P", "pad-paren"))
3340 {
3341 formatter.setParensOutsidePaddingMode(true);
3342 formatter.setParensInsidePaddingMode(true);
3343 }
3344 else if (isOption(arg, "d", "pad-paren-out"))
3345 {
3346 formatter.setParensOutsidePaddingMode(true);
3347 }
3348 else if (isOption(arg, "xd", "pad-first-paren-out"))
3349 {
3350 formatter.setParensFirstPaddingMode(true);
3351 }
3352 else if (isOption(arg, "D", "pad-paren-in"))
3353 {
3354 formatter.setParensInsidePaddingMode(true);
3355 }
3356 else if (isOption(arg, "H", "pad-header"))
3357 {
3358 formatter.setParensHeaderPaddingMode(true);
3359 }
3360 else if (isOption(arg, "U", "unpad-paren"))
3361 {
3362 formatter.setParensUnPaddingMode(true);
3363 }
3364 else if (isOption(arg, "p", "pad-oper"))
3365 {
3366 formatter.setOperatorPaddingMode(true);
3367 }
3368 else if (isOption(arg, "xg", "pad-comma"))
3369 {
3370 formatter.setCommaPaddingMode(true);
3371 }
3372 else if (isOption(arg, "xe", "delete-empty-lines"))
3373 {
3374 formatter.setDeleteEmptyLinesMode(true);
3375 }
3376 else if (isOption(arg, "xm", "delete-multiple-empty-lines"))
3377 {
3378 formatter.setDeleteMultipleEmptyLinesMode(true);
3379 }
3380 else if (isOption(arg, "E", "fill-empty-lines"))
3381 {
3382 formatter.setEmptyLineFill(true);
3383 }
3384 else if (isOption(arg, "c", "convert-tabs"))
3385 {
3386 formatter.setTabSpaceConversionMode(true);
3387 }
3388 else if (isOption(arg, "xy", "close-templates"))
3389 {
3390 formatter.setCloseTemplatesMode(true);
3391 }
3392 else if (isOption(arg, "F", "break-blocks=all"))
3393 {
3394 formatter.setBreakBlocksMode(true);
3395 formatter.setBreakClosingHeaderBlocksMode(true);
3396 }
3397 else if (isOption(arg, "f", "break-blocks"))
3398 {
3399 formatter.setBreakBlocksMode(true);
3400 }
3401 else if (isOption(arg, "e", "break-elseifs"))
3402 {
3403 formatter.setBreakElseIfsMode(true);
3404 }
3405 else if (isOption(arg, "xb", "break-one-line-headers"))
3406 {
3407 formatter.setBreakOneLineHeadersMode(true);
3408 }
3409 else if (isOption(arg, "j", "add-braces"))
3410 {
3411 formatter.setAddBracesMode(true);
3412 }
3413 else if (isOption(arg, "J", "add-one-line-braces"))
3414 {
3415 formatter.setAddOneLineBracesMode(true);
3416 }
3417 else if (isOption(arg, "xj", "remove-braces"))
3418 {
3419 formatter.setRemoveBracesMode(true);
3420 }
3421 else if (isOption(arg, "Y", "indent-col1-comments"))
3422 {
3423 formatter.setIndentCol1CommentsMode(true);
3424 }
3425 else if (isOption(arg, "align-pointer=type"))
3426 {
3427 formatter.setPointerAlignment(PTR_ALIGN_TYPE);
3428 }
3429 else if (isOption(arg, "align-pointer=middle"))
3430 {
3431 formatter.setPointerAlignment(PTR_ALIGN_MIDDLE);
3432 }
3433 else if (isOption(arg, "align-pointer=name"))
3434 {
3435 formatter.setPointerAlignment(PTR_ALIGN_NAME);
3436 }
3437 else if (isParamOption(arg, "k"))
3438 {
3439 int align = 0;
3440 string styleParam = getParam(arg, "k");
3441 if (styleParam.length() > 0)
3442 align = atoi(styleParam.c_str());
3443 if (align < 1 || align > 3)
3444 isOptionError(arg, errorInfo);
3445 else if (align == 1)
3446 formatter.setPointerAlignment(PTR_ALIGN_TYPE);
3447 else if (align == 2)
3448 formatter.setPointerAlignment(PTR_ALIGN_MIDDLE);
3449 else if (align == 3)
3450 formatter.setPointerAlignment(PTR_ALIGN_NAME);
3451 }
3452 else if (isOption(arg, "align-reference=none"))
3453 {
3454 formatter.setReferenceAlignment(REF_ALIGN_NONE);
3455 }
3456 else if (isOption(arg, "align-reference=type"))
3457 {
3458 formatter.setReferenceAlignment(REF_ALIGN_TYPE);
3459 }
3460 else if (isOption(arg, "align-reference=middle"))
3461 {
3462 formatter.setReferenceAlignment(REF_ALIGN_MIDDLE);
3463 }
3464 else if (isOption(arg, "align-reference=name"))
3465 {
3466 formatter.setReferenceAlignment(REF_ALIGN_NAME);
3467 }
3468 else if (isParamOption(arg, "W"))
3469 {
3470 int align = 0;
3471 string styleParam = getParam(arg, "W");
3472 if (styleParam.length() > 0)
3473 align = atoi(styleParam.c_str());
3474 if (align < 0 || align > 3)
3475 isOptionError(arg, errorInfo);
3476 else if (align == 0)
3477 formatter.setReferenceAlignment(REF_ALIGN_NONE);
3478 else if (align == 1)
3479 formatter.setReferenceAlignment(REF_ALIGN_TYPE);
3480 else if (align == 2)
3481 formatter.setReferenceAlignment(REF_ALIGN_MIDDLE);
3482 else if (align == 3)
3483 formatter.setReferenceAlignment(REF_ALIGN_NAME);
3484 }
3485 else if (isParamOption(arg, "max-code-length="))
3486 {
3487 int maxLength = 50;
3488 string maxLengthParam = getParam(arg, "max-code-length=");
3489 if (maxLengthParam.length() > 0)
3490 maxLength = atoi(maxLengthParam.c_str());
3491 if (maxLength < 50)
3492 isOptionError(arg, errorInfo);
3493 else if (maxLength > 200)
3494 isOptionError(arg, errorInfo);
3495 else
3496 formatter.setMaxCodeLength(maxLength);
3497 }
3498 else if (isParamOption(arg, "xC"))
3499 {
3500 int maxLength = 50;
3501 string maxLengthParam = getParam(arg, "xC");
3502 if (maxLengthParam.length() > 0)
3503 maxLength = atoi(maxLengthParam.c_str());
3504 if (maxLength > 200)
3505 isOptionError(arg, errorInfo);
3506 else
3507 formatter.setMaxCodeLength(maxLength);
3508 }
3509 else if (isOption(arg, "xL", "break-after-logical"))
3510 {
3511 formatter.setBreakAfterMode(true);
3512 }
3513 else if (isOption(arg, "xc", "attach-classes"))
3514 {
3515 formatter.setAttachClass(true);
3516 }
3517 else if (isOption(arg, "xV", "attach-closing-while"))
3518 {
3519 formatter.setAttachClosingWhile(true);
3520 }
3521 else if (isOption(arg, "xk", "attach-extern-c"))
3522 {
3523 formatter.setAttachExternC(true);
3524 }
3525 else if (isOption(arg, "xn", "attach-namespaces"))
3526 {
3527 formatter.setAttachNamespace(true);
3528 }
3529 else if (isOption(arg, "xl", "attach-inlines"))
3530 {
3531 formatter.setAttachInline(true);
3532 }
3533 else if (isOption(arg, "xp", "remove-comment-prefix"))
3534 {
3535 formatter.setStripCommentPrefix(true);
3536 }
3537 else if (isOption(arg, "xB", "break-return-type"))
3538 {
3539 formatter.setBreakReturnType(true);
3540 }
3541 else if (isOption(arg, "xD", "break-return-type-decl"))
3542 {
3543 formatter.setBreakReturnTypeDecl(true);
3544 }
3545 else if (isOption(arg, "xf", "attach-return-type"))
3546 {
3547 formatter.setAttachReturnType(true);
3548 }
3549 else if (isOption(arg, "xh", "attach-return-type-decl"))
3550 {
3551 formatter.setAttachReturnTypeDecl(true);
3552 }
3553 // To avoid compiler limit of blocks nested too deep.
3554 else if (!parseOptionContinued(arg, errorInfo))
3555 {
3556 isOptionError(arg, errorInfo);
3557 }
3558} // End of parseOption function
3559
3560// Continuation of parseOption.
3561// To avoid compiler limit of blocks nested too deep.
3562// Return 'true' if the option was found and processed.
3563// Return 'false' if the option was not found.
3564bool ASOptions::parseOptionContinued(const string& arg, const string& errorInfo)
3565{
3566 // Objective-C options
3567 if (isOption(arg, "xQ", "pad-method-prefix"))
3568 {
3569 formatter.setMethodPrefixPaddingMode(true);
3570 }
3571 else if (isOption(arg, "xR", "unpad-method-prefix"))
3572 {
3573 formatter.setMethodPrefixUnPaddingMode(true);
3574 }
3575 else if (isOption(arg, "xq", "pad-return-type"))
3576 {
3577 formatter.setReturnTypePaddingMode(true);
3578 }
3579 else if (isOption(arg, "xr", "unpad-return-type"))
3580 {
3581 formatter.setReturnTypeUnPaddingMode(true);
3582 }
3583 else if (isOption(arg, "xS", "pad-param-type"))
3584 {
3585 formatter.setParamTypePaddingMode(true);
3586 }
3587 else if (isOption(arg, "xs", "unpad-param-type"))
3588 {
3589 formatter.setParamTypeUnPaddingMode(true);
3590 }
3591 else if (isOption(arg, "xM", "align-method-colon"))
3592 {
3593 formatter.setAlignMethodColon(true);
3594 }
3595 else if (isOption(arg, "xP0", "pad-method-colon=none"))
3596 {
3597 formatter.setObjCColonPaddingMode(COLON_PAD_NONE);
3598 }
3599 else if (isOption(arg, "xP1", "pad-method-colon=all"))
3600 {
3601 formatter.setObjCColonPaddingMode(COLON_PAD_ALL);
3602 }
3603 else if (isOption(arg, "xP2", "pad-method-colon=after"))
3604 {
3605 formatter.setObjCColonPaddingMode(COLON_PAD_AFTER);
3606 }
3607 else if (isOption(arg, "xP3", "pad-method-colon=before"))
3608 {
3609 formatter.setObjCColonPaddingMode(COLON_PAD_BEFORE);
3610 }
3611 // NOTE: depreciated options - remove when appropriate
3612 // depreciated options ////////////////////////////////////////////////////////////////////////
3613 else if (isOption(arg, "indent-preprocessor")) // depreciated release 2.04
3614 {
3615 formatter.setPreprocDefineIndent(true);
3616 }
3617 else if (isOption(arg, "style=ansi")) // depreciated release 2.05
3618 {
3619 formatter.setFormattingStyle(STYLE_ALLMAN);
3620 }
3621 // depreciated in release 3.0 /////////////////////////////////////////////////////////////////
3622 else if (isOption(arg, "break-closing-brackets")) // depreciated release 3.0
3623 {
3624 formatter.setBreakClosingHeaderBracketsMode(true);
3625 }
3626 else if (isOption(arg, "add-brackets")) // depreciated release 3.0
3627 {
3628 formatter.setAddBracketsMode(true);
3629 }
3630 else if (isOption(arg, "add-one-line-brackets")) // depreciated release 3.0
3631 {
3632 formatter.setAddOneLineBracketsMode(true);
3633 }
3634 else if (isOption(arg, "remove-brackets")) // depreciated release 3.0
3635 {
3636 formatter.setRemoveBracketsMode(true);
3637 }
3638 else if (isParamOption(arg, "max-instatement-indent=")) // depreciated release 3.0
3639 {
3640 int maxIndent = 40;
3641 string maxIndentParam = getParam(arg, "max-instatement-indent=");
3642 if (maxIndentParam.length() > 0)
3643 maxIndent = atoi(maxIndentParam.c_str());
3644 if (maxIndent < 40)
3645 isOptionError(arg, errorInfo);
3646 else if (maxIndent > 120)
3647 isOptionError(arg, errorInfo);
3648 else
3649 formatter.setMaxInStatementIndentLength(maxIndent);
3650 }
3651 // end depreciated options ////////////////////////////////////////////////////////////////////
3652#ifdef ASTYLE_LIB
3653 // End of options used by GUI /////////////////////////////////////////////////////////////////
3654 else
3655 {
3656 return false;
3657 }
3658 return true;
3659#else
3660 // Options used by only console ///////////////////////////////////////////////////////////////
3661 else if (isOption(arg, "n", "suffix=none"))
3662 {
3663 console.setNoBackup(true);
3664 }
3665 else if (isParamOption(arg, "suffix="))
3666 {
3667 string suffixParam = getParam(arg, "suffix=");
3668 if (suffixParam.length() > 0)
3669 {
3670 console.setOrigSuffix(suffixParam);
3671 }
3672 }
3673 else if (isParamOption(arg, "exclude="))
3674 {
3675 string suffixParam = getParam(arg, "exclude=");
3676 if (suffixParam.length() > 0)
3677 console.updateExcludeVector(suffixParam);
3678 }
3679 else if (isOption(arg, "r", "R") || isOption(arg, "recursive"))
3680 {
3681 console.setIsRecursive(true);
3682 }
3683 else if (isOption(arg, "dry-run"))
3684 {
3685 console.setIsDryRun(true);
3686 }
3687 else if (isOption(arg, "Z", "preserve-date"))
3688 {
3689 console.setPreserveDate(true);
3690 }
3691 else if (isOption(arg, "v", "verbose"))
3692 {
3693 console.setIsVerbose(true);
3694 }
3695 else if (isOption(arg, "Q", "formatted"))
3696 {
3697 console.setIsFormattedOnly(true);
3698 }
3699 else if (isOption(arg, "q", "quiet"))
3700 {
3701 console.setIsQuiet(true);
3702 }
3703 else if (isOption(arg, "i", "ignore-exclude-errors"))
3704 {
3705 console.setIgnoreExcludeErrors(true);
3706 }
3707 else if (isOption(arg, "xi", "ignore-exclude-errors-x"))
3708 {
3709 console.setIgnoreExcludeErrorsAndDisplay(true);
3710 }
3711 else if (isOption(arg, "X", "errors-to-stdout"))
3712 {
3713 console.setErrorStream(&cout);
3714 }
3715 else if (isOption(arg, "lineend=windows"))
3716 {
3717 formatter.setLineEndFormat(LINEEND_WINDOWS);
3718 }
3719 else if (isOption(arg, "lineend=linux"))
3720 {
3721 formatter.setLineEndFormat(LINEEND_LINUX);
3722 }
3723 else if (isOption(arg, "lineend=macold"))
3724 {
3725 formatter.setLineEndFormat(LINEEND_MACOLD);
3726 }
3727 else if (isParamOption(arg, "z"))
3728 {
3729 int lineendType = 0;
3730 string lineendParam = getParam(arg, "z");
3731 if (lineendParam.length() > 0)
3732 lineendType = atoi(lineendParam.c_str());
3733 if (lineendType < 1 || lineendType > 3)
3734 isOptionError(arg, errorInfo);
3735 else if (lineendType == 1)
3736 formatter.setLineEndFormat(LINEEND_WINDOWS);
3737 else if (lineendType == 2)
3738 formatter.setLineEndFormat(LINEEND_LINUX);
3739 else if (lineendType == 3)
3740 formatter.setLineEndFormat(LINEEND_MACOLD);
3741 }
3742 else
3743 {
3744 return false;
3745 }
3746 return true;
3747#endif
3748} // End of parseOptionContinued function
3749
3750// Parse options from the option file.
3751void ASOptions::importOptions(stringstream& in, vector<string>& optionsVector)
3752{
3753 char ch;
3754 bool isInQuote = false;
3755 char quoteChar = ' ';
3756 string currentToken;
3757
3758 while (in)
3759 {
3760 currentToken = "";
3761 do
3762 {
3763 in.get(ch);
3764 if (in.eof())
3765 break;
3766 // treat '#' as line comments
3767 if (ch == '#')
3768 while (in)
3769 {
3770 in.get(ch);
3771 if (ch == '\n' || ch == '\r')
3772 break;
3773 }
3774
3775 // break options on new-lines, tabs, commas, or spaces
3776 // remove quotes from output
3777 if (in.eof() || ch == '\n' || ch == '\r' || ch == '\t' || ch == ',')
3778 break;
3779 if (ch == ' ' && !isInQuote)
3780 break;
3781 if (ch == quoteChar && isInQuote)
3782 break;
3783 if (ch == '"' || ch == '\'')
3784 {
3785 isInQuote = true;
3786 quoteChar = ch;
3787 continue;
3788 }
3789 currentToken.append(1, ch);
3790 }
3791 while (in);
3792
3793 if (currentToken.length() != 0)
3794 optionsVector.emplace_back(currentToken);
3795 isInQuote = false;
3796 }
3797}
3798
3799string ASOptions::getOptionErrors() const
3800{
3801 return optionErrors.str();
3802}
3803
3804string ASOptions::getParam(const string& arg, const char* op)
3805{
3806 return arg.substr(strlen(op));
3807}
3808
3809string ASOptions::getParam(const string& arg, const char* op1, const char* op2)
3810{
3811 return isParamOption(arg, op1) ? getParam(arg, op1) : getParam(arg, op2);
3812}
3813
3814bool ASOptions::isOption(const string& arg, const char* op)
3815{
3816 return arg == op;
3817}
3818
3819bool ASOptions::isOption(const string& arg, const char* op1, const char* op2)
3820{
3821 return (isOption(arg, op1) || isOption(arg, op2));
3822}
3823
3824void ASOptions::isOptionError(const string& arg, const string& errorInfo)
3825{
3826 if (optionErrors.str().length() == 0)
3827 optionErrors << errorInfo << endl; // need main error message
3828 optionErrors << "\t" << arg << endl;
3829}
3830
3831bool ASOptions::isParamOption(const string& arg, const char* option)
3832{
3833 bool retVal = arg.compare(0, strlen(option), option) == 0;
3834 // if comparing for short option, 2nd char of arg must be numeric
3835 if (retVal && strlen(option) == 1 && arg.length() > 1)
3836 if (!isdigit((unsigned char) arg[1]))
3837 retVal = false;
3838 return retVal;
3839}
3840
3841bool ASOptions::isParamOption(const string& arg, const char* option1, const char* option2)
3842{
3843 return isParamOption(arg, option1) || isParamOption(arg, option2);
3844}
3845
3846//----------------------------------------------------------------------------
3847// ASEncoding class
3848//----------------------------------------------------------------------------
3849
3850// Return true if an int is big endian.
3851bool ASEncoding::getBigEndian() const
3852{
3853 char16_t word = 0x0001;
3854 char* byte = reinterpret_cast<char*>(&word);
3855 return (byte[0] ? false : true);
3856}
3857
3858// Swap the two low order bytes of a 16 bit integer value.
3859int ASEncoding::swap16bit(int value) const
3860{
3861 return (((value & 0xff) << 8) | ((value & 0xff00) >> 8));
3862}
3863
3864// Return the length of a utf-16 C string.
3865// The length is in number of char16_t.
3866size_t ASEncoding::utf16len(const utf16* utf16In) const
3867{
3868 size_t length = 0;
3869 while (*utf16In++ != '\0')
3870 length++;
3871 return length;
3872}
3873
3874// Adapted from SciTE UniConversion.cxx.
3875// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
3876// Modified for Artistic Style by Jim Pattee.
3877// Compute the length of an output utf-8 file given a utf-16 file.
3878// Input inLen is the size in BYTES (not wchar_t).
3879size_t ASEncoding::utf8LengthFromUtf16(const char* utf16In, size_t inLen, bool isBigEndian) const
3880{
3881 size_t len = 0;
3882 size_t wcharLen = (inLen / 2) + (inLen % 2);
3883 const char16_t* uptr = reinterpret_cast<const char16_t*>(utf16In);
3884 for (size_t i = 0; i < wcharLen;)
3885 {
3886 size_t uch = isBigEndian ? swap16bit(uptr[i]) : uptr[i];
3887 if (uch < 0x80)
3888 len++;
3889 else if (uch < 0x800)
3890 len += 2;
3891 else if ((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_LEAD_LAST))
3892 {
3893 len += 4;
3894 i++;
3895 }
3896 else
3897 len += 3;
3898 i++;
3899 }
3900 return len;
3901}
3902
3903// Adapted from SciTE Utf8_16.cxx.
3904// Copyright (C) 2002 Scott Kirkwood.
3905// Modified for Artistic Style by Jim Pattee.
3906// Convert a utf-8 file to utf-16.
3907size_t ASEncoding::utf8ToUtf16(char* utf8In, size_t inLen, bool isBigEndian, char* utf16Out) const
3908{
3909 int nCur = 0;
3910 ubyte* pRead = reinterpret_cast<ubyte*>(utf8In);
3911 utf16* pCur = reinterpret_cast<utf16*>(utf16Out);
3912 const ubyte* pEnd = pRead + inLen;
3913 const utf16* pCurStart = pCur;
3914 eState state = eStart;
3915
3916 // the BOM will automatically be converted to utf-16
3917 while (pRead < pEnd)
3918 {
3919 switch (state)
3920 {
3921 case eStart:
3922 if ((0xF0 & *pRead) == 0xF0)
3923 {
3924 nCur = (0x7 & *pRead) << 18;
3925 state = eSecondOf4Bytes;
3926 }
3927 else if ((0xE0 & *pRead) == 0xE0)
3928 {
3929 nCur = (~0xE0 & *pRead) << 12;
3930 state = ePenultimate;
3931 }
3932 else if ((0xC0 & *pRead) == 0xC0)
3933 {
3934 nCur = (~0xC0 & *pRead) << 6;
3935 state = eFinal;
3936 }
3937 else
3938 {
3939 nCur = *pRead;
3940 state = eStart;
3941 }
3942 break;
3943 case eSecondOf4Bytes:
3944 nCur |= (0x3F & *pRead) << 12;
3945 state = ePenultimate;
3946 break;
3947 case ePenultimate:
3948 nCur |= (0x3F & *pRead) << 6;
3949 state = eFinal;
3950 break;
3951 case eFinal:
3952 nCur |= (0x3F & *pRead);
3953 state = eStart;
3954 break;
3955 // no default case is needed
3956 }
3957 ++pRead;
3958
3959 if (state == eStart)
3960 {
3961 int codePoint = nCur;
3962 if (codePoint >= SURROGATE_FIRST_VALUE)
3963 {
3964 codePoint -= SURROGATE_FIRST_VALUE;
3965 int lead = (codePoint >> 10) + SURROGATE_LEAD_FIRST;
3966 *pCur++ = static_cast<utf16>(isBigEndian ? swap16bit(lead) : lead);
3967 int trail = (codePoint & 0x3ff) + SURROGATE_TRAIL_FIRST;
3968 *pCur++ = static_cast<utf16>(isBigEndian ? swap16bit(trail) : trail);
3969 }
3970 else
3971 *pCur++ = static_cast<utf16>(isBigEndian ? swap16bit(codePoint) : codePoint);
3972 }
3973 }
3974 // return value is the output length in BYTES (not wchar_t)
3975 return (pCur - pCurStart) * 2;
3976}
3977
3978// Adapted from SciTE UniConversion.cxx.
3979// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
3980// Modified for Artistic Style by Jim Pattee.
3981// Compute the length of an output utf-16 file given a utf-8 file.
3982// Return value is the size in BYTES (not wchar_t).
3983size_t ASEncoding::utf16LengthFromUtf8(const char* utf8In, size_t len) const
3984{
3985 size_t ulen = 0;
3986 size_t charLen;
3987 for (size_t i = 0; i < len;)
3988 {
3989 unsigned char ch = static_cast<unsigned char>(utf8In[i]);
3990 if (ch < 0x80)
3991 charLen = 1;
3992 else if (ch < 0x80 + 0x40 + 0x20)
3993 charLen = 2;
3994 else if (ch < 0x80 + 0x40 + 0x20 + 0x10)
3995 charLen = 3;
3996 else
3997 {
3998 charLen = 4;
3999 ulen++;
4000 }
4001 i += charLen;
4002 ulen++;
4003 }
4004 // return value is the length in bytes (not wchar_t)
4005 return ulen * 2;
4006}
4007
4008// Adapted from SciTE Utf8_16.cxx.
4009// Copyright (C) 2002 Scott Kirkwood.
4010// Modified for Artistic Style by Jim Pattee.
4011// Convert a utf-16 file to utf-8.
4012size_t ASEncoding::utf16ToUtf8(char* utf16In, size_t inLen, bool isBigEndian,
4013 bool firstBlock, char* utf8Out) const
4014{
4015 int nCur16 = 0;
4016 int nCur = 0;
4017 ubyte* pRead = reinterpret_cast<ubyte*>(utf16In);
4018 ubyte* pCur = reinterpret_cast<ubyte*>(utf8Out);
4019 const ubyte* pEnd = pRead + inLen;
4020 const ubyte* pCurStart = pCur;
4021 static eState state = eStart; // state is retained for subsequent blocks
4022 if (firstBlock)
4023 state = eStart;
4024
4025 // the BOM will automatically be converted to utf-8
4026 while (pRead < pEnd)
4027 {
4028 switch (state)
4029 {
4030 case eStart:
4031 if (pRead >= pEnd)
4032 {
4033 ++pRead;
4034 break;
4035 }
4036 if (isBigEndian)
4037 {
4038 nCur16 = static_cast<utf16>(*pRead++ << 8);
4039 nCur16 |= static_cast<utf16>(*pRead);
4040 }
4041 else
4042 {
4043 nCur16 = *pRead++;
4044 nCur16 |= static_cast<utf16>(*pRead << 8);
4045 }
4046 if (nCur16 >= SURROGATE_LEAD_FIRST && nCur16 <= SURROGATE_LEAD_LAST)
4047 {
4048 ++pRead;
4049 int trail;
4050 if (isBigEndian)
4051 {
4052 trail = static_cast<utf16>(*pRead++ << 8);
4053 trail |= static_cast<utf16>(*pRead);
4054 }
4055 else
4056 {
4057 trail = *pRead++;
4058 trail |= static_cast<utf16>(*pRead << 8);
4059 }
4060 nCur16 = (((nCur16 & 0x3ff) << 10) | (trail & 0x3ff)) + SURROGATE_FIRST_VALUE;
4061 }
4062 ++pRead;
4063
4064 if (nCur16 < 0x80)
4065 {
4066 nCur = static_cast<ubyte>(nCur16 & 0xFF);
4067 state = eStart;
4068 }
4069 else if (nCur16 < 0x800)
4070 {
4071 nCur = static_cast<ubyte>(0xC0 | (nCur16 >> 6));
4072 state = eFinal;
4073 }
4074 else if (nCur16 < SURROGATE_FIRST_VALUE)
4075 {
4076 nCur = static_cast<ubyte>(0xE0 | (nCur16 >> 12));
4077 state = ePenultimate;
4078 }
4079 else
4080 {
4081 nCur = static_cast<ubyte>(0xF0 | (nCur16 >> 18));
4082 state = eSecondOf4Bytes;
4083 }
4084 break;
4085 case eSecondOf4Bytes:
4086 nCur = static_cast<ubyte>(0x80 | ((nCur16 >> 12) & 0x3F));
4087 state = ePenultimate;
4088 break;
4089 case ePenultimate:
4090 nCur = static_cast<ubyte>(0x80 | ((nCur16 >> 6) & 0x3F));
4091 state = eFinal;
4092 break;
4093 case eFinal:
4094 nCur = static_cast<ubyte>(0x80 | (nCur16 & 0x3F));
4095 state = eStart;
4096 break;
4097 // no default case is needed
4098 }
4099 *pCur++ = static_cast<ubyte>(nCur);
4100 }
4101 return pCur - pCurStart;
4102}
4103
4104//----------------------------------------------------------------------------
4105
4106} // namespace astyle
4107
4108//----------------------------------------------------------------------------
4109
4110using namespace astyle;
4111
4112//----------------------------------------------------------------------------
4113// ASTYLE_JNI functions for Java library builds
4114//----------------------------------------------------------------------------
4115
4116#ifdef ASTYLE_JNI
4117
4118// called by a java program to get the version number
4119// the function name is constructed from method names in the calling java program
4120extern "C" EXPORT
4121jstring STDCALL Java_AStyleInterface_AStyleGetVersion(JNIEnv* env, jclass)
4122{
4123 return env->NewStringUTF(g_version);
4124}
4125
4126// called by a java program to format the source code
4127// the function name is constructed from method names in the calling java program
4128extern "C" EXPORT
4129jstring STDCALL Java_AStyleInterface_AStyleMain(JNIEnv* env,
4130 jobject obj,
4131 jstring textInJava,
4132 jstring optionsJava)
4133{
4134 g_env = env; // make object available globally
4135 g_obj = obj; // make object available globally
4136
4137 jstring textErr = env->NewStringUTF(""); // zero length text returned if an error occurs
4138
4139 // get the method ID
4140 jclass cls = env->GetObjectClass(obj);
4141 g_mid = env->GetMethodID(cls, "ErrorHandler", "(ILjava/lang/String;)V");
4142 if (g_mid == nullptr)
4143 {
4144 cout << "Cannot find java method ErrorHandler" << endl;
4145 return textErr;
4146 }
4147
4148 // convert jstring to char*
4149 const char* textIn = env->GetStringUTFChars(textInJava, nullptr);
4150 const char* options = env->GetStringUTFChars(optionsJava, nullptr);
4151
4152 // call the C++ formatting function
4153 char* textOut = AStyleMain(textIn, options, javaErrorHandler, javaMemoryAlloc);
4154 // if an error message occurred it was displayed by errorHandler
4155 if (textOut == nullptr)
4156 return textErr;
4157
4158 // release memory
4159 jstring textOutJava = env->NewStringUTF(textOut);
4160 delete[] textOut;
4161 env->ReleaseStringUTFChars(textInJava, textIn);
4162 env->ReleaseStringUTFChars(optionsJava, options);
4163
4164 return textOutJava;
4165}
4166
4167// Call the Java error handler
4168void STDCALL javaErrorHandler(int errorNumber, const char* errorMessage)
4169{
4170 jstring errorMessageJava = g_env->NewStringUTF(errorMessage);
4171 g_env->CallVoidMethod(g_obj, g_mid, errorNumber, errorMessageJava);
4172}
4173
4174// Allocate memory for the formatted text
4175char* STDCALL javaMemoryAlloc(unsigned long memoryNeeded)
4176{
4177 // error condition is checked after return from AStyleMain
4178 char* buffer = new (nothrow) char[memoryNeeded];
4179 return buffer;
4180}
4181
4182#endif // ASTYLE_JNI
4183
4184//----------------------------------------------------------------------------
4185// ASTYLE_LIB functions for library builds
4186//----------------------------------------------------------------------------
4187
4188#ifdef ASTYLE_LIB
4189
4190//----------------------------------------------------------------------------
4191// ASTYLE_LIB entry point for AStyleMainUtf16 library builds
4192//----------------------------------------------------------------------------
4193/*
4194* IMPORTANT Visual C DLL linker for WIN32 must have the additional options:
4195* /EXPORT:AStyleMain=_AStyleMain@16
4196* /EXPORT:AStyleMainUtf16=_AStyleMainUtf16@16
4197* /EXPORT:AStyleGetVersion=_AStyleGetVersion@0
4198* No /EXPORT is required for x64
4199*/
4200extern "C" EXPORT char16_t* STDCALL AStyleMainUtf16(const char16_t* pSourceIn, // the source to be formatted
4201 const char16_t* pOptions, // AStyle options
4202 fpError fpErrorHandler, // error handler function
4203 fpAlloc fpMemoryAlloc) // memory allocation function
4204{
4205 if (fpErrorHandler == nullptr) // cannot display a message if no error handler
4206 return nullptr;
4207
4208 if (pSourceIn == nullptr)
4209 {
4210 fpErrorHandler(101, "No pointer to source input.");
4211 return nullptr;
4212 }
4213 if (pOptions == nullptr)
4214 {
4215 fpErrorHandler(102, "No pointer to AStyle options.");
4216 return nullptr;
4217 }
4218 if (fpMemoryAlloc == nullptr)
4219 {
4220 fpErrorHandler(103, "No pointer to memory allocation function.");
4221 return nullptr;
4222 }
4223#ifndef _WIN32
4224 // check size of char16_t on Linux
4225 int sizeCheck = 2;
4226 if (sizeof(char16_t) != sizeCheck)
4227 {
4228 fpErrorHandler(104, "char16_t is not the correct size.");
4229 return nullptr;
4230 }
4231#endif
4232
4233 ASLibrary library;
4234 char16_t* utf16Out = library.formatUtf16(pSourceIn, pOptions, fpErrorHandler, fpMemoryAlloc);
4235 return utf16Out;
4236}
4237
4238//----------------------------------------------------------------------------
4239// ASTYLE_LIB entry point for library builds
4240//----------------------------------------------------------------------------
4241/*
4242 * IMPORTANT Visual C DLL linker for WIN32 must have the additional options:
4243 * /EXPORT:AStyleMain=_AStyleMain@16
4244 * /EXPORT:AStyleMainUtf16=_AStyleMainUtf16@16
4245 * /EXPORT:AStyleGetVersion=_AStyleGetVersion@0
4246 * No /EXPORT is required for x64
4247 */
4248extern "C" EXPORT char* STDCALL AStyleMain(const char* pSourceIn, // the source to be formatted
4249 const char* pOptions, // AStyle options
4250 fpError fpErrorHandler, // error handler function
4251 fpAlloc fpMemoryAlloc) // memory allocation function
4252{
4253 if (fpErrorHandler == nullptr) // cannot display a message if no error handler
4254 return nullptr;
4255
4256 if (pSourceIn == nullptr)
4257 {
4258 fpErrorHandler(101, "No pointer to source input.");
4259 return nullptr;
4260 }
4261 if (pOptions == nullptr)
4262 {
4263 fpErrorHandler(102, "No pointer to AStyle options.");
4264 return nullptr;
4265 }
4266 if (fpMemoryAlloc == nullptr)
4267 {
4268 fpErrorHandler(103, "No pointer to memory allocation function.");
4269 return nullptr;
4270 }
4271
4272 ASFormatter formatter;
4273 ASOptions options(formatter);
4274
4275 vector<string> optionsVector;
4276 stringstream opt(pOptions);
4277
4278 options.importOptions(opt, optionsVector);
4279
4280 bool ok = options.parseOptions(optionsVector, "Invalid Artistic Style options:");
4281 if (!ok)
4282 fpErrorHandler(130, options.getOptionErrors().c_str());
4283
4284 stringstream in(pSourceIn);
4285 ASStreamIterator<stringstream> streamIterator(&in);
4286 ostringstream out;
4287 formatter.init(&streamIterator);
4288
4289 while (formatter.hasMoreLines())
4290 {
4291 out << formatter.nextLine();
4292 if (formatter.hasMoreLines())
4293 out << streamIterator.getOutputEOL();
4294 else
4295 {
4296 // this can happen if the file if missing a closing brace and break-blocks is requested
4297 if (formatter.getIsLineReady())
4298 {
4299 out << streamIterator.getOutputEOL();
4300 out << formatter.nextLine();
4301 }
4302 }
4303 }
4304
4305 size_t textSizeOut = out.str().length();
4306 char* pTextOut = fpMemoryAlloc((long) textSizeOut + 1); // call memory allocation function
4307 if (pTextOut == nullptr)
4308 {
4309 fpErrorHandler(120, "Allocation failure on output.");
4310 return nullptr;
4311 }
4312
4313 strcpy(pTextOut, out.str().c_str());
4314#ifndef NDEBUG
4315 // The checksum is an assert in the console build and ASFormatter.
4316 // This error returns the incorrectly formatted file to the editor.
4317 // This is done to allow the file to be saved for debugging purposes.
4318 if (formatter.getChecksumDiff() != 0)
4319 fpErrorHandler(220,
4320 "Checksum error.\n"
4321 "The incorrectly formatted file will be returned for debugging.");
4322#endif
4323 return pTextOut;
4324}
4325
4326extern "C" EXPORT const char* STDCALL AStyleGetVersion(void)
4327{
4328 return g_version;
4329}
4330
4331// ASTYLECON_LIB is defined to exclude "main" from the test programs
4332#elif !defined(ASTYLECON_LIB)
4333
4334//----------------------------------------------------------------------------
4335// main function for ASConsole build
4336//----------------------------------------------------------------------------
4337
4338int main(int argc, char** argv)
4339{
4340 // create objects
4341 ASFormatter formatter;
4342 unique_ptr<ASConsole> console(new ASConsole(formatter));
4343
4344 // process command line and option files
4345 // build the vectors fileNameVector, optionsVector, and fileOptionsVector
4346 vector<string> argvOptions;
4347 argvOptions = console->getArgvOptions(argc, argv);
4348 console->processOptions(argvOptions);
4349
4350 // if no files have been given, use cin for input and cout for output
4351 if (!console->fileNameVectorIsEmpty())
4352 console->processFiles();
4353 else
4354 console->formatCinToCout();
4355
4356 return EXIT_SUCCESS;
4357}
4358
4359#endif // ASTYLE_LIB
4360