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