#!/usr/bin/php
<?php

/*

DOSCONV, Version 2009-08-05
(C) 2009 ViaThinkSoft

DOSCONV wandelt alle Macintosh- und Unix-Zeilenumbrüche in das CRLF DOS-Format um.

Das CRLF-Format ist im Vergleich zu dem Linux LF-Format kompatibler zu Texteditoren.

Es werden nur Dateien korrigiert, die in der Filterliste aufgeführt sind, darunter
PHP, HTML, JavaScript und XML Dateien. Es besteht folglich kein Gefahr für den
Datenbestand.

Verwendung
	php dosconv.php <verzeichnis> [-fix] [-nolist]

Ohne die Angabe von -fix wird nur eine Auflistung durchgeführt.

*/

// TODO

// - Shebang automatisch erkennen und dann nicht nach Windows normieren?

// Konfig

define('SHOW_OUTPUT', true); // gilt nicht für Fehler
define('RECURSIVE_DIR', true);

$allowed_ext = array();
/*
$allowed_ext[] = "*.js";
$allowed_ext[] = "*.vb";
$allowed_ext[] = "*.*htm*";
$allowed_ext[] = "*.txt";
$allowed_ext[] = "*.php*";
$allowed_ext[] = "*.cgi";
$allowed_ext[] = "*.c";
$allowed_ext[] = "*.cpp";
$allowed_ext[] = "*.h";
$allowed_ext[] = "*.hpp";
$allowed_ext[] = "*.pas";
$allowed_ext[] = "*.bas";
$allowed_ext[] = "*.tex";
$allowed_ext[] = "*.pl";
$allowed_ext[] = ".htpasswd";
$allowed_ext[] = ".htaccess";
$allowed_ext[] = "*.xtml";
$allowed_ext[] = "*.css";
$allowed_ext[] = "*.cfg";
$allowed_ext[] = "*.ini";
//$allowed_ext[] = "*.sh";
$allowed_ext[] = "*.xml";
$allowed_ext[] = "*.java";
*/
$allowed_ext[] = "*";

$exclude_ext = array();
$exclude_ext[] = "*.sh";

$max_fsize = 2; // MB

/* ------------ */

if (isset($_SERVER["SERVER_NAME"]))
{
	die("Bitte f&uuml;hren Sie dieses Script separat &uuml;ber den PHP-Interpreter aus.\n");
}

$argv2 = isset($_SERVER['argv'][2]) ? $_SERVER['argv'][2] : '';
$argv3 = isset($_SERVER['argv'][3]) ? $_SERVER['argv'][3] : '';

if (($_SERVER['argc'] == 1) || ($_SERVER['argc'] > 4) ||
  (($argv2 != '') && ($argv2 != '-fix') && ($argv2 != '-nolist')) ||
  (($argv3 != '') && ($argv3 != '-fix') && ($argv3 != '-nolist')))
{
	die("Verwendung: php dosconv.php <verzeichnis> [-fix] [-nolist]\n");
}

$my_dir = $_SERVER['argv'][1];
$do_fix = ($argv2 == '-fix') || ($argv3 == '-fix');
$no_list = ($argv2 == '-nolist') || ($argv3 == '-nolist');

define('CR', "\r");
define('LF', "\n");
define('CRLF', CR.LF);

function normalizeToUnixLineBreaks($text, &$count_crlf=0, &$count_cr=0, &$count_lf=0) {
	$text = str_replace(CRLF, LF, $text, $count_crlf);
	$text = str_replace(CR, LF, $text, $count_cr);
	$count_lf = substr_count($text, LF) - $count_crlf - $count_cr;

	return $text;
}

function normalizeToWindowsLineBreaks($text, &$count_crlf=0, &$count_cr=0, &$count_lf=0) {
	$text = normalizeToUnixLineBreaks($text, $count_crlf, $count_cr, $count_lf);

	$text = str_replace(LF, CRLF, $text);

	return $text;
}

/* function normalizeToMacintoshLineBreaks($text, &$count_crlf=0, &$count_cr=0, &$count_lf=0) {
	$text = normalizeToUnixLineBreaks($text, $count_crlf, $count_cr, $count_lf);

	$text = str_replace(LF, CR, $text);

	return $text;
} */

function is_binary_data($inp) {
	for ($i=0; $i<=31; $i++) {
		if ((strpos($inp, chr($i)) !== false) && ($i != 9) && ($i != 10) && ($i != 13)) {
			return true;
		}
	}
	return false;
}

function dir_add_trailing_slash($directory) {
	if (substr($directory, strlen($directory)-1, 1) != '/') $directory .= '/';
	return $directory;
}

$my_dir = dir_add_trailing_slash($my_dir);

if (!is_dir($my_dir)) die("DOSCONV Fehler: Verzeichnis '$my_dir' existiert nicht!!\n");

$max_fsize = $max_fsize * 1024 * 1024;
$upd_count = 0;
$chk_count = 0;
$dir_count = 0;

$something_outputed = false;

function output($text) {
	global $something_outputed;
	echo $text."\n";
	$something_outputed = true;
}

function sucheCRLFUnkonformeDateien($verzeichnis, $do_fix = false) {
	global $allowed_ext, $exclude_ext, $max_fsize, $upd_count, $chk_count, $dir_count, $no_list;

	$handle = opendir($verzeichnis);
	while ($datei = readdir($handle))
	{
		unset($cont);
		if (($datei != '.') && ($datei != '..'))
		{
			$file = $verzeichnis.$datei;
			if (is_dir($file)) // Wenn Verzeichniseintrag ein Verzeichnis ist
			{
				// Erneuter Funktionsaufruf, um das aktuelle Verzeichnis auszulesen
				if (RECURSIVE_DIR) {
					sucheCRLFUnkonformeDateien($file.'/', $do_fix);
					$dir_count++;
				}
			} else {
				if (count($allowed_ext) > 0) {
					$ok = false;
					foreach ($allowed_ext as $pat) {
						if (fnmatch($pat, $datei)) {
							$ok = true;
							break;
						}
					}
					unset($pat);
					if (!$ok) continue;
				}

				if (count($exclude_ext) > 0) {
					$ok = true;
					foreach ($exclude_ext as $pat) {
						if (fnmatch($pat, $datei)) {
							$ok = false;
							break;
						}
					}
					unset($pat);
					if (!$ok) continue;
				}

				if (filesize($file) > $max_fsize) {
					output("DOSCONV Fehler: Datei '$file' übersprungen. Zu groß!");
					continue;
				}

				if (!is_readable($file)) {
					output("DOSCONV Fehler: Datei '$file' ist nicht lesbar!");
					continue;
				}

				$cont = file_get_contents($file);

				if (is_binary_data($cont)) {
					// output("DOSCONV Fehler: Datei '$file' ist binär!");
					continue;
				}

				$chk_count++;

				$c_crlf = 0;
				$c_cr = 0;
				$c_lf = 0;

				$cont = normalizeToWindowsLineBreaks($cont, $c_crlf, $c_cr, $c_lf);

				if ($c_cr + $c_lf > 0) {
					if ((SHOW_OUTPUT) && (!$no_list)) {
						output("$file (CRLF=$c_crlf; LF=$c_lf; CR=$c_cr)");
					}

					if ($do_fix) {
						if (!is_writable($file)) {
							output("DOSCONV Fehler: Datei '$file' kann nicht geschrieben werden!");
							continue;
						} else {
							$upd_count++;
							$h = fopen($file, 'w');
							fputs($h, $cont);
							fclose($h);
						}
					} else {
						$upd_count++;
					}
				}
			}
		}
	}
	closedir($handle);
}

sucheCRLFUnkonformeDateien($my_dir, $do_fix);

if (SHOW_OUTPUT) {
	if ($something_outputed) {
		output("--- --- --- --- --- --- ---");
	}
	output("DOSCONV checked $chk_count files in $dir_count directories.");
	$output = "DOSCONV ";
	if ($do_fix) {
		$output .= "fixed";
	} else {
		$output .= "found";
	}
	$output .= " $upd_count files with bare LF or CR inside!";
	output($output);
	unset($output);
	if ((!$do_fix) && ($upd_count > 0)) {
		output("Please add the argument '-fix' to fix these files.");
	}
}

?>
