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