#develop (short for SharpDevelop) is a free IDE for .NET programming languages.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

427 lines
12 KiB

// <file>
// <copyright see="prj:///doc/copyright.txt">2002-2005 AlphaSierraPapa</copyright>
// <license see="prj:///doc/license.txt">GNU General Public License</license>
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace CheckFileHeaders
{
class MainClass
{
static int Main(string[] args)
{
int count;
try {
MainClass m = new MainClass();
if (args.Length == 0) {
Console.WriteLine("Checking files in {0} ...", Path.GetFullPath(@"..\..\..\..\"));
count = m.Run(@"..\..\..\..\");
} else {
Console.WriteLine("Checking files in {0} ...", Path.GetFullPath(args[0]));
count = m.Run(args[0]);
}
Console.WriteLine("Finished! (checked {0} files, changed {1} files, ignored {2} files)", count, m.changeCount, m.ignoreCount);
} catch (Exception ex) {
Console.WriteLine(ex);
}
Console.Write("Press any key to continue...");
Console.ReadKey(true);
return 0;
}
int Run(string dir)
{
int count = 0;
foreach (string file in Directory.GetFiles(dir, "*.cs")) {
if (file.EndsWith(".Designer.cs"))
continue;
count++;
ProcessFile(file);
}
foreach (string subdir in Directory.GetDirectories(dir)) {
if (subdir.EndsWith("\\.svn"))
continue;
if (subdir.EndsWith("Libraries\\DockPanel_Src"))
continue;
if (subdir.EndsWith("Libraries\\log4net"))
continue;
if (subdir.EndsWith("Libraries\\NUnit.Framework"))
continue;
if (Path.GetFullPath(subdir).EndsWith("src\\Tools"))
continue;
// Disabled addins:
if (subdir.EndsWith("AddIns\\BackendBindings\\CPPNetBinding"))
continue;
if (subdir.EndsWith("AddIns\\BackendBindings\\ILAsmBinding"))
continue;
if (subdir.EndsWith("AddIns\\BackendBindings\\WixBinding"))
continue;
if (subdir.EndsWith("AddIns\\DisplayBindings\\AssemblyScout"))
continue;
if (subdir.EndsWith("AddIns\\Misc\\Debugger\\TreeListView\\Project"))
continue;
count += Run(subdir);
}
return count;
}
// must be splitted because this file is under version control, too
Regex resetVersionRegex = new Regex(@"// <version>\$Revi" + @"sion: \d+ \$</version>", RegexOptions.Compiled);
int changeCount, ignoreCount;
void ProcessFile(string file)
{
string content = GetFileContent(file);
string author, email;
int lastLine;
int headerType = AnalyzeHeader(content, out author, out email, out lastLine);
if (headerType == 5) {
//Console.WriteLine("unknown file: " + file);
ignoreCount++;
return;
}
if (author == null)
author = "";
if (author == "") {
Console.Write(file);
char ch;
do {
Console.WriteLine();
Console.Write(" Mike/Daniel/Other/None/Ignore (M/D/O/N/I): ");
}
while ((ch = char.ToUpper(Console.ReadKey().KeyChar)) != 'M'
&& ch != 'N' && ch != 'I' && ch != 'O' && ch != 'D');
Console.WriteLine();
if (ch == 'M') {
author = "Omnibrain";
} else if (ch == 'D') {
author = "Daniel Grunwald";
} else if (ch == 'O') {
bool ok;
do {
Console.Write("Enter author name: ");
author = Console.ReadLine();
if (author == "David") author = "David Srbecky";
if (author == "Markus") author = "Markus Palme";
email = CheckAuthor(ref author);
ok = author != null;
} while (!ok);
} else if (ch == 'I') {
ignoreCount++;
return;
} else {
author = "none";
}
}
string oldAuthor = author;
email = CheckAuthor(ref author);
if (author == null) {
Console.WriteLine("Unknown author: " + oldAuthor + " in " + file);
Console.WriteLine(" File was ignored.");
return;
}
StringBuilder builder = new StringBuilder();
builder.AppendLine("// <file>");
builder.AppendLine("// <copyright see=\"prj:///doc/copyright.txt\"/>");
builder.AppendLine("// <license see=\"prj:///doc/license.txt\"/>");
builder.Append("// <owner name=\"");
builder.Append(author);
builder.Append("\" email=\"");
builder.Append(email);
builder.AppendLine("\"/>");
// must be splitted because this file is under version control, too
const string versionLine = "// <version>$Revi" + "sion$</version>";
builder.AppendLine(versionLine);
builder.AppendLine("// </file>");
builder.AppendLine();
int offset = FindLineOffset(content, lastLine + 1);
builder.Append(content.Substring(offset).Trim());
builder.AppendLine();
string newContent = builder.ToString();
string resettedVersion = resetVersionRegex.Replace(content, versionLine);
if (newContent != resettedVersion) {
using (StreamWriter w = new StreamWriter(file, false, GetOptimalEncoding(newContent))) {
changeCount++;
w.Write(newContent);
}
}
}
string CheckAuthor(ref string author)
{
switch (author) {
case "Mike Krger":
case "Mike Kr<EFBFBD>ger":
case "Mike Krueger":
case "Omnibrain":
author = "Mike Kr<EFBFBD>ger";
return "mike@icsharpcode.net";
case "Daniel Grunwald":
return "daniel@danielgrunwald.de";
case "David Srbeck<EFBFBD>":
case "David Srbecky":
case "David Srbeck":
author = "David Srbeck<EFBFBD>";
return "dsrbecky@gmail.com";
case "Andrea Paatz":
author = "Andrea Paatz";
return "andrea@icsharpcode.net";
case "Matthew Ward":
return "mrward@users.sourceforge.net";
case "Mathias Simmack":
return "mathias@simmack.de";
case "Poul Staugaard":
return "poul@staugaard.dk";
case "Roman Taranchenko":
return "rnt@smtp.ru";
case "Markus Palme":
return "MarkusPalme@gmx.de";
case "David McCloskey":
return "dave_a_mccloskey@hotmail.com";
case "Shinsaku Nakagawa":
return "shinsaku@users.sourceforge.jp";
case "Denis ERCHOFF":
return "d_erchoff@hotmail.com";
case "Georg Brandl":
return "g.brandl@gmx.net";
case "Ivo Kovacka":
return "ivok@internet.sk";
case "Scott Ferrett":
return "surf@softvelocity.com";
case "David Alpert":
return "david@spinthemoose.com";
case "none":
return "";
default:
author = null;
return null;
}
}
Encoding GetOptimalEncoding(string content)
{
foreach (char ch in content) {
if ((int)ch >= 128)
return Encoding.UTF8;
}
return Encoding.ASCII;
}
int FindLineOffset(string content, int lineNumber)
{
int num = 0;
for (int i = 0; i < content.Length; i++) {
if (num == lineNumber)
return i;
if (content[i] == '\n')
num++;
}
throw new ApplicationException("Cannot find line " + lineNumber);
}
#region AnalyzeHeader
Regex xmlRegex = new Regex(@"<owner name=""(\w[\w\s]*\w)"" email=""([\w\s@\.]*)""\s?/>");
Regex sdRegex = new Regex(@"\* User: (.*)");
// Returns:
// 0 = no header
// 1 = XML header
// 2 = SharpDevelop header
// 3 = GPL header (unused)
// 4 = unknown header
// 5 = outcommented file
int AnalyzeHeader(string content, out string author, out string email, out int lastLine)
{
string content2 = content;
author = null;
email = null;
int lineNumber = -1;
byte state = 0;
// state:
// 0 = start
// 1 = parse XML header
// 2 = parse SharpDevelop header
// 3 = search end of GPL header
// 4 = block comment start
using (StringReader r = new StringReader(content)) {
string line;
while ((line = r.ReadLine()) != null) {
lineNumber++;
line = line.Trim();
if (state == 0) {
if (line.Length == 0)
continue;
if (line.StartsWith("using ")) {
lastLine = -1;
return 0;
} else if (line == "// <file>") {
state = 1;
} else if (line.StartsWith("////") || line.StartsWith("#line 1 \"")) {
lastLine = -1;
return 5;
} else if (line == "/*") {
state = 4;
} else if (line == "//------------------------------------------------------------------------------") {
// TlbImp auto-generated style (preserve)
lastLine = -1;
return 5;
} else if (line.StartsWith("//")) {
// ignore
} else {
break;
}
} else if (state == 1) {
if (line == "// </file>") {
lastLine = lineNumber;
return 1;
} else if (xmlRegex.IsMatch(line)) {
Match m = xmlRegex.Match(line);
author = m.Groups[1].Value;
email = m.Groups[2].Value;
}
} else if (state == 2) {
if (line == "*/") {
lastLine = lineNumber;
return 2;
} else if (sdRegex.IsMatch(line)) {
Match m = sdRegex.Match(line);
author = m.Groups[1].Value;
}
} else if (state == 3) {
if (line.Length == 0)
continue;
if (!line.StartsWith("//")) {
lastLine = lineNumber - 1;
return 3;
}
} else if (state == 4) {
if (line == "* Created by SharpDevelop.") {
state = 2;
} else if (line == "* Created by SharpDevelop") {
state = 2;
} else {
break;
}
} else {
throw new NotSupportedException();
}
}
}
lastLine = -1;
return 4;
}
#endregion
#region Reading files
string GetFileContent(string file)
{
using (StreamReader r = OpenFile(file)) {
return r.ReadToEnd();
}
}
StreamReader OpenFile(string fileName)
{
bool autodetectEncoding = true;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
if (autodetectEncoding && fs.Length > 3) {
// the autodetection of StreamReader is not capable of detecting the difference
// between ISO-8859-1 and UTF-8 without BOM.
int firstByte = fs.ReadByte();
int secondByte = fs.ReadByte();
switch ((firstByte << 8) | secondByte) {
case 0x0000: // either UTF-32 Big Endian or a binary file; use StreamReader
case 0xfffe: // Unicode BOM (UTF-16 LE or UTF-32 LE)
case 0xfeff: // UTF-16 BE BOM
case 0xefbb: // start of UTF-8 BOM
// StreamReader autodetection works
fs.Position = 0;
return new StreamReader(fs);
default:
return AutoDetect(fs, (byte)firstByte, (byte)secondByte);
}
} else {
return new StreamReader(fs);
}
}
StreamReader AutoDetect(FileStream fs, byte firstByte, byte secondByte)
{
int max = (int)Math.Min(fs.Length, 500000); // look at max. 500 KB
const int ASCII = 0;
const int Error = 1;
const int UTF8 = 2;
const int UTF8Sequence = 3;
int state = ASCII;
int sequenceLength = 0;
byte b;
for (int i = 0; i < max; i++) {
if (i == 0) {
b = firstByte;
} else if (i == 1) {
b = secondByte;
} else {
b = (byte)fs.ReadByte();
}
if (b < 0x80) {
// normal ASCII character
if (state == UTF8Sequence) {
state = Error;
break;
}
} else if (b < 0xc0) {
// 10xxxxxx : continues UTF8 byte sequence
if (state == UTF8Sequence) {
--sequenceLength;
if (sequenceLength < 0) {
state = Error;
break;
} else if (sequenceLength == 0) {
state = UTF8;
}
} else {
state = Error;
break;
}
} else if (b > 0xc2 && b < 0xf5) {
// beginning of byte sequence
if (state == UTF8 || state == ASCII) {
state = UTF8Sequence;
if (b < 0xe0) {
sequenceLength = 1; // one more byte following
} else if (b < 0xf0) {
sequenceLength = 2; // two more bytes following
} else {
sequenceLength = 3; // three more bytes following
}
} else {
state = Error;
break;
}
} else {
// 0xc0, 0xc1, 0xf5 to 0xff are invalid in UTF-8 (see RFC 3629)
state = Error;
break;
}
}
fs.Position = 0;
switch (state) {
case Error:
return new StreamReader(fs, Encoding.Default);
default:
return new StreamReader(fs);
}
}
#endregion
}
}