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