File indexing completed on 2024-04-28 04:48:05

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Gary Steinert <gary.steinert@gmail.com>                           *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "log.h"
0018 
0019 #include <QDir>
0020 #include <QDebug>
0021 #include <QFile>
0022 #include <QTextStream>
0023 #include <QString>
0024 
0025 const QString BLANKLINE( " *                                                                                      *" );
0026 const QString TOPLINE( "/****************************************************************************************" );
0027 QList<QStringList> LICENSES = QList<QStringList>() <<
0028     ( QStringList() <<
0029         " * This program is free software; you can redistribute it and/or modify it under        *" <<
0030         " * the terms of the GNU General Public License as published by the Free Software        *" <<
0031         " * Foundation; either version 2 of the License, or (at your option) any later           *" <<
0032         " * version.                                                                             *" <<
0033         " *                                                                                      *" <<
0034         " * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *" <<
0035         " * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *" <<
0036         " * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *" <<
0037         " *                                                                                      *" <<
0038         " * You should have received a copy of the GNU General Public License along with         *" <<
0039         " * this program.  If not, see <http://www.gnu.org/licenses/>.                           *"
0040     )
0041     <<
0042     ( QStringList() <<
0043         " * This program is free software; you can redistribute it and/or modify it under        *" <<
0044         " * the terms of the GNU Library General Public License as published by the Free         *" <<
0045         " * Software Foundation; either version 2.1 of the License, or (at your option) any      *" <<
0046         " * later version.                                                                       *" <<
0047         " *                                                                                      *" <<
0048         " * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *" <<
0049         " * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *" <<
0050         " * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *" <<
0051         " *                                                                                      *" <<
0052         " * You should have received a copy of the GNU Library General Public License along with *" <<
0053         " * this program.  If not, see <http://www.gnu.org/licenses/>.                           *"
0054     )
0055     <<
0056     ( QStringList() <<
0057         " * This program is free software; you can redistribute it and/or modify it under        *" <<
0058         " * the terms of the GNU General Public License as published by the Free Software        *" <<
0059         " * Foundation; either version 2 of the License, or (at your option) version 3 or        *" <<
0060         " * any later version accepted by the membership of KDE e.V. (or its successor approved  *" <<
0061         " * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of  *" <<
0062         " * version 3 of the license.                                                            *" <<
0063         " *                                                                                      *" <<
0064         " * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *" <<
0065         " * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *" <<
0066         " * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *" <<
0067         " *                                                                                      *" <<
0068         " * You should have received a copy of the GNU General Public License along with         *" <<
0069         " * this program.  If not, see <http://www.gnu.org/licenses/>.                           *"
0070     )
0071     <<
0072     ( QStringList() <<
0073         " * This program is free software; you can redistribute it and/or modify it under        *" <<
0074         " * the terms of the GNU General Public License as published by the Free Software        *" <<
0075         " * Foundation; either version 2 of the License, or (at your option) version 3 or any    *" <<
0076         " * later version publicly approved by Trolltech ASA (or its successor, if any) and the  *" <<
0077         " * KDE Free Qt Foundation.                                                              *" <<
0078         " *                                                                                      *" <<
0079         " * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *" <<
0080         " * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *" <<
0081         " * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *" <<
0082         " *                                                                                      *" <<
0083         " * You should have received a copy of the GNU General Public License along with         *" <<
0084         " * this program.  If not, see <http://www.gnu.org/licenses/>.                           *" <<
0085         " *                                                                                      *" <<
0086         " * In addition, Trolltech gives you certain additional rights as described in the       *" <<
0087         " * Trolltech GPL Exception version 1.2 which can be found at                            *" <<
0088         " * http://www.trolltech.com/products/qt/gplexception/                                   *"
0089     );
0090 
0091 struct {
0092   QString outputFile;
0093   bool recursive;
0094   LogEntry::PrintStyle outputStyle;
0095   bool fullReport;
0096   bool help;
0097   QList<QString> folders;
0098   QString bashScriptFile;
0099 } cliArgs;
0100 
0101 Log log;
0102 
0103 using namespace std;
0104 
0105 void readFile( QString filename ) {
0106 
0107   bool autofixed = false;
0108 
0109   //Set up file variables
0110   QFile file( filename );
0111   if( !file.open( QIODevice::ReadOnly | QIODevice::Text ) )
0112   {
0113     log.append( LogEntry( filename, "Could not open file", LogEntry::failure ) );
0114     return;
0115   }
0116 
0117   QTextStream inStream( &file );
0118 
0119   if( inStream.atEnd() )
0120   {
0121     log.append( LogEntry( filename, "Empty file - no license information", LogEntry::warning ) );
0122     return;
0123   }
0124 
0125   //Search for first comment block (search for /* at start of line)
0126   QString line;
0127   line = inStream.readLine();
0128 
0129   bool headerAtStart = true;
0130   int numLinesBeforeHeader = 0;
0131 
0132   while( !line.contains( "/*" ) && !inStream.atEnd() )
0133   {
0134     line = inStream.readLine();
0135     headerAtStart = false;
0136     numLinesBeforeHeader++;
0137   }
0138 
0139   int startpos = line.indexOf( "/*" );
0140   int endpos = line.lastIndexOf( "*/" );
0141 
0142   if( startpos == -1 ) //No block comments
0143   {
0144     log.append( LogEntry( filename, "No license header - could not find block comment", LogEntry::failure ) );
0145 
0146     log.addProblemFile( filename );
0147 
0148     return;
0149   }
0150 
0151   if( endpos != -1 || endpos > startpos ) //Single line comment - not license header
0152   {
0153     log.append( LogEntry( filename, "First block comment is single-line. Therefore not license header", LogEntry::failure ) );
0154 
0155     log.addProblemFile( filename );
0156 
0157     return;
0158   }
0159 
0160   //Find end of comment block
0161   QList<QString> header;
0162 
0163   header.append(line);
0164 
0165   while( !line.contains( "*/" ) && !inStream.atEnd() )
0166   {
0167     line = inStream.readLine();
0168     header.append( line );
0169   }
0170 
0171   int originalLength = header.count();
0172 
0173 
0174   if( !line.contains( "*/" ) )
0175   {
0176     log.append( LogEntry( filename, "No license header - could not find end of block comment", LogEntry::failure ) );
0177 
0178     log.addProblemFile( filename );
0179 
0180     return;
0181   }
0182 
0183   bool problemFile = false;
0184 
0185   if ( !headerAtStart )
0186   {
0187     log.append( LogEntry( filename, "License header is not at the start of the file", LogEntry::warning ) );
0188 
0189     problemFile = true;
0190   }
0191 
0192 
0193   //START CHECKS
0194   //Check first line *'s and correct length
0195   if( header[0] != TOPLINE )
0196   {
0197     log.append( LogEntry( filename, "First line of header incorrect", LogEntry::error ) );
0198     problemFile = true;
0199   }
0200 
0201   //Find first line of license
0202   int i = 1;
0203 
0204   bool  firstLineFound = false;
0205 
0206   while( i < header.count() )
0207   {
0208     for( int j = 0; j < LICENSES.count(); j++ )
0209     {
0210       if( header[i] == LICENSES[j][0] )
0211       {
0212         firstLineFound = true;
0213         break;
0214       }
0215     }
0216 
0217     if( firstLineFound )
0218       break;
0219 
0220     i++;
0221   }
0222 
0223   if( i == header.count() )
0224   {
0225     log.append( LogEntry( filename, "Required License wording and format not found. (Could not find match for first line)", LogEntry::failure ) );
0226 
0227     log.addProblemFile( filename );
0228     return;
0229   }
0230 
0231   bool licenseFound = false;
0232 
0233   //Declaring k here so that it stays around for the log entry if needed.
0234   int k;
0235 
0236   for( int j = 0; j < LICENSES.count(); j++ )
0237   {
0238     if( header.count() + i < LICENSES[j].count() )
0239       continue; //Too short to be header, so continue
0240 
0241     k = 1;
0242     while( k < LICENSES[j].count() && header[k+i] == LICENSES[j][k] )
0243       k++;
0244 
0245     if( k == LICENSES[j].count() )
0246     {
0247       //Check for extra lines. Means extra terms (+1 to incorporate last line)
0248       if( header.count() - i > LICENSES[j].count() + 1 )
0249         log.append( LogEntry( filename, "Extra license terms in license header.", LogEntry::information ) );
0250 
0251       licenseFound = true;
0252       break;
0253     }
0254   }
0255 
0256   if( !licenseFound )
0257   {
0258     log.append( LogEntry( filename, "Required license wording and format not found.", LogEntry::failure ) );
0259     log.addProblemFile( filename );
0260     return;
0261   }
0262 
0263   //Line i-1 should be a blank line
0264   if( header[i - 1] != BLANKLINE )
0265   {
0266     log.append( LogEntry( filename, "No blank line between copyright holders and license text. - Fixed", LogEntry::information ) );
0267     //Autofix
0268     header.insert( i, BLANKLINE );
0269     autofixed = true;
0270   }
0271 
0272   //Check for copyright holders (i - 1 > 1)
0273   if( i - 1 <= 1 )
0274   {
0275     log.append( LogEntry( filename, "No copyright holders present", LogEntry::warning ) );
0276     log.addProblemFile( filename );
0277   }
0278 
0279   bool overallSuccess = true;
0280 
0281   //For each copyright holder
0282   for( int j = 1; j < i - 1; j++ )
0283   {
0284     bool individualSuccess = true;
0285 
0286     //Check for blank line (may be at top of header for instance)
0287     if( header[j] == BLANKLINE )
0288     {
0289       log.append( LogEntry( filename, QString( "Blank line found in copyright holders. (Line " ).append( (char)(j+48) ).append( " of header) - Fixed" ), LogEntry::information ) );
0290       header.removeAt( j );
0291       j--; //So that the next line isn't skipped
0292       autofixed = true;
0293       continue; //Continue because its a blank line - nothing else needs to be done with it
0294     }
0295 
0296     //Check each part of copyright line.
0297     //Check first 3 characters " * "
0298     if( header[j].mid( 0, 3 ) != " * " )
0299     {
0300       log.append( LogEntry( filename, QString( "First 3 characters of copyright line incorrect (incorrect * border) (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0301       individualSuccess = false;
0302 
0303       problemFile = true;
0304     }
0305 
0306     //Check "Copyright"
0307     //From here, individualSuccess is used to skip remaining tests if one fails
0308     if( individualSuccess && header[j].mid( 3, 9 ) != "Copyright" )
0309     {
0310       if( header[j].mid( 3, 9 ).toLower() == "copyright" )
0311       {
0312         log.append( LogEntry( filename, QString( "Incorrect casing of \"Copyright\" (Line " ).append( (char)(j+48) ).append( " of header) - Fixed" ), LogEntry::information ) );
0313         header[j] = header[j].left( 3 ) + "Copyright" + header[j].mid( 12 );
0314         autofixed = true;
0315       }
0316       else
0317       {
0318         log.append( LogEntry( filename, QString( "\"Copyright\" not found in proper place (Line ").append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0319         individualSuccess = false;
0320 
0321         problemFile = true;
0322       }
0323     }
0324 
0325     //Check space between "Copyright" and "(c)"
0326     if( individualSuccess && header[j].mid( 12, 1 ) != " " )
0327     {
0328       log.append( LogEntry( filename, QString( "No space between \"Copyright\" and \"(c)\" (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0329       individualSuccess = false;
0330 
0331       problemFile = true;
0332     }
0333 
0334     //Check "(c)"
0335     if( individualSuccess && header[j].mid( 13, 3 ) != "(c)" )
0336     {
0337       if( header[j].mid( 13, 3 ).toLower() == "(c)" )
0338       {
0339         log.append( LogEntry( filename, QString( "Incorrect casing of \"(c)\" (Line " ).append( (char)(j+48) ).append( " of header) - Fixed" ), LogEntry::information ) );
0340         header[j] = header[j].left( 13 ) + "(c)" + header[j].mid( 16 );
0341         autofixed = true;
0342       }
0343       else
0344       {
0345         log.append( LogEntry( filename, QString( "\"(c)\" not found in proper place (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0346         individualSuccess = false;
0347 
0348         problemFile = true;
0349       }
0350     }
0351 
0352     //Check space between "(c)" and year
0353     if( individualSuccess && header[j].mid( 16, 1 ) != " " )
0354     {
0355       log.append( LogEntry( filename, QString( "No space between \"(c)\" and copyright holder's name (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0356       individualSuccess = false;
0357 
0358       problemFile = true;
0359     }
0360 
0361     //Process year
0362     //Check first year
0363     bool ok;
0364     int year = header[j].mid( 17, 4 ).toInt( &ok, 10 );
0365 
0366     if( individualSuccess && !ok )
0367     {
0368       log.append( LogEntry( filename, QString( "Year is not found in correct format. (Not a numeric value) (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0369       individualSuccess = false;
0370 
0371       problemFile = true;
0372     }
0373 
0374     int k = 1;
0375     int lastyear;
0376 
0377     while( individualSuccess && ( header[j].mid( 17+(4*k), 1 ) == "-" || header[j].mid( 17+(4*k), 1 ) == "," ) )
0378     {
0379       lastyear = year;
0380       year = header[j].mid( 18+(4*k), 4 ).toInt( &ok, 10 );
0381 
0382       if( !ok )
0383       {
0384         log.append( LogEntry( filename, QString( "Year is not found in correct format. (Not a numeric value) (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0385         individualSuccess = false;
0386 
0387         problemFile = true;
0388         break; //Leave loop so no false warning for in-order years
0389       }
0390 
0391       if( year < lastyear )
0392       {
0393         log.append( LogEntry( filename, QString( "Years are not in ascending order. (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::warning ) );
0394       }
0395 
0396       //Move on to next year
0397       k++;
0398     }
0399 
0400     if( individualSuccess && header[j].mid( 16+(5*k), 1 ) != " " )
0401     {
0402       log.append( LogEntry( filename, QString( "Incorrect separator character between years, or no space between year(s) and name. (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0403       individualSuccess = false;
0404 
0405       problemFile = true;
0406     }
0407 
0408     //Find < at start of email
0409     int startEmail = header[j].indexOf( "<", 16+(5*k) );
0410 
0411     //Check for presence of name (startEmail > 18) (18 is first possible position plus a space)
0412     if( individualSuccess && startEmail <= 17+(5*k) )
0413     {
0414       log.append( LogEntry( filename, QString( "Copyright holder's name not present in correct place. (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0415       individualSuccess = false;
0416 
0417       problemFile = true;
0418     }
0419 
0420     //Check for space at end of name
0421     if( individualSuccess && header[j].mid( startEmail - 1, 1 ) != " " )
0422     {
0423       log.append( LogEntry( filename, QString( "No space between copyright holder's name and email (Line " ).append( (char)(j+48) ).append( " of header) - Fixed" ), LogEntry::information ) );
0424       //Need to check here as this could be due to not enough width for name & email
0425       if( header[j].at( header[j].lastIndexOf( "*" ) ) == ' ' )
0426       {
0427         header[j] = header[j].left( startEmail ) + ' ' + header[j].mid( startEmail );
0428         //Now remove extra space
0429         header[j] = header[j].left( header[j].lastIndexOf( "*" ) - 1 ) + header[j].mid( header[j].lastIndexOf( "*" ) );
0430         autofixed = true;
0431       }
0432     }
0433 
0434     //Check for spaces at start of name (shouldn't be there)
0435     if( individualSuccess && header[j].mid( 17+(5*k), 1 ) == " " )
0436     {
0437       log.append( LogEntry( filename, QString( "Too many spaces between copyright year and copyright holder's name (Line " ).append( (char)(j+48) ).append( " of header) - Fixed" ), LogEntry::information ) );
0438       while( header[j].mid( 17+(5*k), 1 ) == " " )
0439       {
0440         header[j] = header[j].left( 17+(5*k) ) + header[j].mid( 18+(5*k) );
0441         header[j] = header[j].left( header[j].lastIndexOf( '*' ) ) + ' ' + header[j].mid( header[j].lastIndexOf( '*' ) );
0442       }
0443       autofixed = true;
0444     }
0445 
0446     //Check for end of email
0447     if( individualSuccess && header[j].lastIndexOf( ">" ) < startEmail )
0448     {
0449       log.append( LogEntry( filename, QString( "No closing brackets for email address (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::error ) );
0450       individualSuccess = false;
0451 
0452       problemFile = true;
0453     }
0454 
0455     //Check for @ followed by . in email address
0456     int atPos = header[j].indexOf( "@", startEmail );
0457     int dotPos = header[j].lastIndexOf( ".", header[j].lastIndexOf( ">" ) );
0458 
0459     if( individualSuccess && ( atPos == -1 || dotPos < atPos ) )
0460     {
0461       log.append( LogEntry( filename, QString( "Invalid email address (not format *@*.*) (Line " ).append( (char)(j+48) ).append( " of header)" ), LogEntry::warning ) );
0462 
0463     problemFile = true;
0464     }
0465 
0466     //Check for 'by So and So'
0467     if( individualSuccess && header[j].mid( 17+(5*k), 5 ).trimmed().toLower().startsWith( "by " ) )
0468     {
0469       log.append( LogEntry( filename, QString( "Copyright holder's name preceded by 'by'. (Line " ).append( (char)(j+48) ).append( " of header) - Fixed" ), LogEntry::information ) );
0470       header[j] =
0471               header[j].left( header[j].toLower().indexOf( "by ", 17+(5*k) ) ) +
0472               header[j].mid( header[j].indexOf( "by ", 17+(5*k) ) + 3 );
0473       autofixed = true;
0474     }
0475 
0476     //Check length of line (stars in line at end)
0477     if( individualSuccess && header[j].trimmed().length() != 88 )
0478     {
0479       //Easiest way is to rewrite line
0480       header[j] = header[j].left( header[j].lastIndexOf( ">" ) + 1 );
0481       //Now add trailing spaces and *
0482       for ( int k = header[j].length(); k < 88; k++ )
0483         header[j] += ' ';
0484 
0485       header[j] += '*';
0486 
0487       log.append( LogEntry( filename, QString( "Length of copyright holder line incorrect. (Line " ).append( (char)(j+48) ).append( " of header) - Fixed" ), LogEntry::information ) );
0488 
0489       autofixed=true;
0490     }
0491 
0492     //Add copyright holder (only if success)
0493     if( individualSuccess )
0494     {
0495       log.addCopyHolder(
0496               header[j].mid( 17+(5*k), startEmail - (18+(5*k)) ),
0497               header[j].mid( startEmail + 1, header[j].lastIndexOf( ">" ) - (startEmail + 1) ),
0498               filename
0499               );
0500     }
0501 
0502     //Set overallSuccess false if this run failed
0503     if( !individualSuccess )
0504       overallSuccess = false;
0505 
0506 
0507   }
0508 
0509   if( autofixed )
0510   {
0511     inStream.seek( 0 );
0512     QList<QString> newFile;
0513 
0514     for( int i = 0; i < numLinesBeforeHeader; i++ )
0515     {
0516       newFile.append( inStream.readLine() );
0517     }
0518 
0519     newFile.append( header );
0520 
0521     //Skip old header
0522     for( int i = 0; i < originalLength; i++ )
0523     {
0524       inStream.readLine();
0525     }
0526 
0527     while( !inStream.atEnd() )
0528     {
0529       newFile.append( inStream.readLine() );
0530     }
0531 
0532     file.close();
0533     if( !file.open( QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text ) )
0534     {
0535       log.append( LogEntry( filename, "Error opening file for writing autofixed header", LogEntry::error ) );
0536     }
0537     else
0538     {
0539       QTextStream outStream( &file );
0540 
0541       foreach( const QString &i, newFile )
0542         outStream << i << "\n";
0543 
0544       log.append( LogEntry( filename, "Some warnings autofixed", LogEntry::information ) );
0545     }
0546   }
0547 
0548   if( overallSuccess )
0549   {
0550     log.append( LogEntry( filename, "Success", LogEntry::success ) );
0551 
0552     if( problemFile )
0553       log.addProblemFile( filename );
0554   }
0555   else
0556   {
0557     log.append( LogEntry( filename, "Errors in copyright holders (see above errors)", LogEntry::failure ) );
0558 
0559     log.addProblemFile( filename );
0560   }
0561 
0562 }
0563 
0564 void processCliArgs(int argc, char** argv)
0565 {
0566   cliArgs.outputFile = "";
0567   cliArgs.recursive = false;
0568   cliArgs.outputStyle = LogEntry::plainText;
0569   cliArgs.fullReport = true;
0570   cliArgs.help = false;
0571   cliArgs.folders = QList<QString>();
0572   cliArgs.bashScriptFile = "";
0573 
0574   for( int i = 1; i < argc; i++ ) {
0575     if( argv[i][0] != '-' ) //Input folder
0576     {
0577       cliArgs.folders.append( QString( argv[i] ) );
0578       continue;
0579     }
0580 
0581     if( argv[i][1] == 'o' ) //Output filename
0582     {
0583       if( QString( argv[i] ).length() > 2 )
0584       {
0585         cliArgs.outputFile = QString( argv[i] ).mid( 2 ).trimmed();
0586       }
0587       else
0588       {
0589         cliArgs.outputFile = QString( argv[i + 1] );
0590         i++;
0591       }
0592     }
0593     else if( argv[i][1] == 'b' ) //Output filename for bash script
0594     {
0595       if( QString( argv[i] ).length() > 2 )
0596       {
0597         cliArgs.bashScriptFile = QString( argv[i] ).mid( 2 ).trimmed();
0598       }
0599       else
0600       {
0601         cliArgs.bashScriptFile = QString( argv[i + 1] );
0602         i++;
0603       }
0604     }
0605     else if( argv[i][1] == 'r' )
0606       cliArgs.recursive = true;
0607     else if( argv[i][1] == 's' )
0608     {
0609       QString outputString;
0610       if( QString( argv[i] ).length() > 2 )
0611       {
0612         outputString = QString( argv[i] ).mid( 2 ).trimmed();
0613       }
0614       else
0615       {
0616         outputString = QString( argv[i + 1] );
0617         i++;
0618       }
0619 
0620       if( outputString.toLower() == "plaintext" )
0621       {
0622         cliArgs.outputStyle = LogEntry::plainText;
0623       }
0624       else if( outputString.toLower() == "html" )
0625       {
0626         cliArgs.outputStyle = LogEntry::HTML;
0627       }
0628       else
0629       {
0630         cliArgs.help = true;
0631       }
0632     }
0633     else if( argv[i][1] == 't' )
0634     {
0635       QString outputString;
0636       if( QString( argv[i] ).length() > 2 )
0637       {
0638         outputString = QString( argv[i] ).mid( 2 ).trimmed();
0639       }
0640       else
0641       {
0642         outputString = QString( argv[i + 1] );
0643         i++;
0644       }
0645 
0646       if( outputString.toLower() == "full" )
0647       {
0648         cliArgs.fullReport = true;
0649       }
0650       else if( outputString.toLower() == "errors" )
0651       {
0652         cliArgs.fullReport = false;
0653       }
0654       else
0655       {
0656         cliArgs.help = true;
0657       }
0658     }
0659     else if( argv[i][1] == 'h' )
0660       cliArgs.help = true;
0661     else
0662       cliArgs.help = true;
0663   }
0664 }
0665 
0666 QString appendTrailingForwardSlash( const QString a )
0667 {
0668     if( !a.endsWith( '/' ) )
0669         return a + '/';
0670     else
0671         return a;
0672 }
0673 
0674 void iterateFolder( QString folder )
0675 {
0676   QDir dir( folder );
0677   QStringList fileFilter;
0678   fileFilter << "*.h" << "*.cpp";
0679   QStringList files = dir.entryList( fileFilter, QDir::Files );
0680 
0681   if( cliArgs.recursive )
0682   {
0683     QStringList folders = dir.entryList( QStringList(), QDir::Dirs | QDir::NoDotAndDotDot );
0684     foreach( const QString &i, folders )
0685     {
0686       iterateFolder( appendTrailingForwardSlash( folder ) + i );
0687     }
0688 
0689   }
0690 
0691   foreach( const QString &i, files )
0692   {
0693     readFile( appendTrailingForwardSlash( folder ) + i );
0694   }
0695 }
0696 
0697 int main( int argc, char** argv )
0698 {
0699   processCliArgs(argc, argv);
0700 
0701   if( cliArgs.help )
0702   {
0703     QTextStream output( stdout );
0704     output << "Amarok License Header Checker" << "\n";
0705     output << "Usage: " << argv[0] << " -hr -o <filename> -s <style> -t <type> file1, file2..." << "\n\n";
0706     output << "Command Line Options:" << "\n";
0707     output << "  -h             Print this help message and exit" << "\n";
0708     output << "  -r             Recursive processing of directories" << "\n";
0709     output << "  -o <filename>  Output written to <filename>" << "\n";
0710     output << "  -s <style>     Output format. One of plaintext, HTML. Plaintext is default" << "\n";
0711     output << "  -t <type>      Output type. One of full, errors. Full is default" << "\n";
0712     output << "  -b <filename>  Write bash script for editing erroneous files to <filename>" << "\n";
0713 
0714     return 0;
0715   }
0716 
0717   if( cliArgs.folders.count() == 0 )
0718     cliArgs.folders.append( QString() );
0719 
0720   foreach( const QString &i, cliArgs.folders )
0721   {
0722     iterateFolder( i );
0723   }
0724 
0725   if( cliArgs.fullReport )
0726     log.printFullReport( cliArgs.outputStyle, cliArgs.outputFile );
0727   else
0728     log.printErrorReport( cliArgs.outputStyle, true, cliArgs.outputFile );
0729 
0730   if( !cliArgs.bashScriptFile.isEmpty() )
0731     log.writeShellScript( cliArgs.bashScriptFile );
0732 
0733   return 0;
0734 }
0735