#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shlwapi.h>
#include <shellapi.h>
#define elemof(x) (sizeof(x)/sizeof(*(x)))
#define T TEXT
#define nobreak
typedef const BYTE *LPCBYTE;
HANDLE stdin,stdout,stderr;
int icp,ocp; // Eingabe- und Ausgabe-Codeseite
bool no_marker; // kein Marker an Ausgabedatei (mittels vorangestelltem Fragezeichen)
HANDLE fin,fout;
int exitcode; // 1 wenn Warnung aufgetreten
LPCTSTR DeleteOnError; // Dateiname zum Löschen
#ifdef UNICODE
# define CommandLineToArgv CommandLineToArgvW
# define STR "%S"
#else
# define CommandLineToArgv CommandLineToArgvA
# define STR "%s"
static LPSTR*CommandLineToArgvA(LPCSTR CmdLine,int*argc) {
// Vereinfachte Version für begrenzte Argument-Anzahl
struct _ret_t{
LPSTR argv[10];
CHAR buf[1];
} *ret;
LPSTR d;
int i=0;
bool inquote=false;
bool instring=false;
ret=(_ret_t*)LocalAlloc(LPTR,sizeof(*ret)+lstrlen(CmdLine));
for (d=ret->buf;;) {
CHAR c=*CmdLine++;
switch (c) {
case '"': {
inquote=!inquote;
if (!instring) {
ret->argv[i++]=d; // Stringanfang trotzdem setzen
instring=true;
}
}break;
case ' ':
case '\t': if (inquote) goto def;
case 0: if (instring) {
*d++=0; // Stringende setzen
instring=false;
}break;
default: def:{
if (!instring) {
ret->argv[i++]=d; // Stringanfang setzen
instring=true;
}
*d++=c;
}
}
if (!c) break;
}
if (argc) *argc=i;
return ret->argv;
}
#endif
// Wandelt Zeilenenden von "\n" nach "\r\n"
// Liefert Zeiger auf Ende der Ergebnis-Zeichenkette
LPSTR Unix2DosA(LPCSTR s, LPSTR d, UINT l) {
CHAR c;
if (!l) return NULL;
for(;;){
c=*s++;
if (!c) break;
if (c=='\n') {
if (!--l) break;
*d++='\r';
}
if (!--l) break;
*d++=c;
};
*d=0; // Nullterminierung Windows-mäßig sicherstellen
return d;
}
static void vfprintf(HANDLE f, LPCSTR p, va_list va) {
CHAR buf1[1024];
CHAR buf2[1024];
if (!HIWORD(p)) {
LoadStringA(0,(UINT)p,buf2,elemof(buf2));
p=buf2;
}
wvnsprintfA(buf1,elemof(buf1),p,va);
CharToOemA(buf1,buf2);
Unix2DosA(buf2,buf1,elemof(buf1));
DWORD bw;
WriteFile(f,buf1,lstrlenA(buf1),&bw,NULL);
}
static void crlf(HANDLE f) {
vfprintf(f,"\n",NULL);
}
static void _cdecl warn(PCSTR p, ...) {
vfprintf(stderr,p,(va_list)(&p+1));
crlf(stderr);
exitcode=1;
}
static void _declspec(noreturn) _cdecl err(PCSTR p, ...) {
vfprintf(stderr,p,(va_list)(&p+1));
crlf(stderr);
if (DeleteOnError) {
CloseHandle(fout);
DeleteFile(DeleteOnError);
}
ExitProcess(2);
}
static LPCBYTE MemChr(LPCBYTE s, DWORD len, BYTE c) {
if (len) do{
if (*s==c) return s;
s++;
}while(--len);
return NULL;
}
// Länge in Bytes, geradzahlig
static void SwapBytes(PBYTE s, DWORD len) {
if (len && (len&1)==0) do {
BYTE t=*s;
*s=s[1];
++*s++=t;
}while (len-=2);
}
static void DecodeCodepage(LPCTSTR s, int&cp) {
if (StrToIntEx(s,STIF_SUPPORT_HEX,&cp));
else if (!lstrcmpi(s,T("utf-8"))) cp=CP_UTF8;
else if (!lstrcmpi(s,T("utf8"))) cp=CP_UTF8;
else if (!lstrcmpi(s,T("utf-7"))) cp=CP_UTF7;
else if (!lstrcmpi(s,T("utf7"))) cp=CP_UTF7;
else if (!lstrcmpi(s,T("ansi"))) cp=CP_ACP;
else if (!lstrcmpi(s,T("mac"))) cp=CP_MACCP;
else if (!lstrcmpi(s,T("oem"))) cp=CP_OEMCP;
else if (!lstrcmpi(s,T("symbol"))) cp=CP_SYMBOL;
else if (!lstrcmpi(s,T("utf-16"))) cp=1200;
else if (!lstrcmpi(s,T("utf16"))) cp=1200;
else if (!lstrcmpi(s,T("gb"))) cp=936; // Vereinfachtes Chinesisch
else if (!lstrcmpi(s,T("big5"))) cp=950; // Traditionelles Chinesisch
else if (!lstrcmpi(s,T("sjis"))) cp=932; // Japanisch
else if (!lstrcmpi(s,T("koi-8r"))) cp=20866; // Russisch (weder DOS noch Windows)
else if (!lstrcmpi(s,T("koi8r"))) cp=20866; // Russisch (weder DOS noch Windows)
else err("Unbekannte Kodierungsangabe \"" STR "\"!",s);
}
extern "C" void CALLBACK mainCRTStartup(void) {
stdin=GetStdHandle(STD_INPUT_HANDLE);
stdout=GetStdHandle(STD_OUTPUT_HANDLE);
stderr=GetStdHandle(STD_ERROR_HANDLE);
int argc;
LPTSTR *argv=CommandLineToArgv(GetCommandLine(),&argc);
icp=-1; // Auto-Detect!
if (argc>=2) {
LPTSTR p=StrChr(argv[1],':');
if (p) {
*p=0;
DecodeCodepage(argv[1],icp);
argv[1]=p+1;
}
if (argv[1][0]=='?') {
argv[1]++;
no_marker=true;
}
DecodeCodepage(argv[1],ocp);
}else err("Die Ausgabe-Codeseite muss als erstes Argument angegeben werden.\n"
"Möglich sind numerische Angaben (auch hex), \"utf-8\", \"utf-16\", \"?utf-8\", \"?utf-16\",\n"
"\"oem\", \"ansi\", \"utf-7\", \"mac\", \"symbol\", \"gb\", \"big5\", \"sjis\", \"koi-8r\"\n"
"Vorangestellte Kodeseite mit \":\" abgetrennt ist Eingabe-Kodeseite, sonst Auto-Detect.\n"
"Für eine komplette Liste aller Nummern durchsuche MSDN nach \"sjis\"!\n"
"Bis zu zwei weitere Argumente sind Ein- und Ausgabedateiname.\n"
"h#s, 05/08");
if (argc>=3) {
if (argc>=4 && !lstrcmpi(argv[2],argv[3]))
fout=fin=CreateFile(argv[2],GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,0);
else
fin=CreateFile(argv[2],GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,0);
if (fin==INVALID_HANDLE_VALUE) err("Kann Eingabedatei " STR " nicht öffnen, Fehlerkode %d!",argv[2],GetLastError());
}else fin=stdin;
if (argc>=4) {
if (!fout) {
fout=CreateFile(argv[3],GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,0);
if (fout==INVALID_HANDLE_VALUE) err("Kann Ausgabedatei " STR " nicht anlegen, Fehlerkode %d!",argv[3],GetLastError());
DeleteOnError=argv[3]; // folgendes err() löscht diese Datei
}
}else fout=stdout;
if (argc>=5) warn("Zu viele Argumente (argc == %d)!",argc);
// Eingabedatei einlesen
DWORD fsize=GetFileSize(fin,NULL);
if (!fsize || fsize==INVALID_FILE_SIZE)
err("Länge der Eingabedatei liefert %d (Eingabedatenströme gehen nicht, Fehlerkode %d)",fsize,GetLastError());
LPBYTE pin=(LPBYTE)GlobalAlloc(GMEM_FIXED,fsize);
if (!pin) err("Speicheranforderung von %u Bytes fehlgeschlagen!",fsize);
DWORD br;
ReadFile(fin,pin,fsize,&br,NULL);
if (br!=fsize) err("Konnte nicht die volle Dateilänge (%u) lesen, nur %u Bytes!",fsize,br);
if (fin!=stdin && fin!=fout) CloseHandle(fin);
int skip=0;
// Autodetect der Eingabecodeseite sowie Entfernen von Windows-Notepad-Präfixen
if (icp==-1) {
if ((fsize&1)==0) { // gerade Gesamt-Byte-Anzahl
if (pin[0]==0xFF && pin[1]==0xFE) { // Windows' Notepad-Präfix (Byte Order Mark)
icp=1200; // UTF-16 Little Endian
skip=2;
goto found;
}
if (pin[0]==0xFE && pin[1]==0xFF) {
icp=1201; // UTF-16 Big Endian
skip=2;
goto found;
}
LPCBYTE p=MemChr(pin,fsize,0); // Null-Byte suchen
if (p) {
icp=(p-pin)&1?1200:1201; // UTF-16, Endian je nach Position der ersten Null
goto found;
}
}
if (fsize>=3 && pin[0]==0xEF && pin[1]==0xBB && pin[2]==0xBF) {
icp=CP_UTF8;
skip=3;
goto found;
}
if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,(LPCSTR)pin,fsize,NULL,0)) { // probieren: UTF-8
icp=CP_UTF8;
goto found;
}
warn("Codeseite der Eingabedatei kann nicht erraten werden, \"ansi\" wird angenommen.");
icp=CP_ACP;
}else{
// nur Entfernen von Windows-Notepad-Präfixen (BOM)
if (icp==CP_UTF8 && fsize>=3 && pin[0]==0xEF && pin[1]==0xBB && pin[2]==0xBF) skip=3;
else if (fsize>=2 && (fsize&1)==0
&& (icp==1200 && pin[0]==0xFF && pin[1]==0xFE || icp==1201 && pin[0]==0xFE && pin[1]==0xFF)) skip=2;
}
found:
LPBYTE pin2=pin+skip;
fsize-=skip;
if (icp!=ocp) { // gleiche Kodeseiten eigentlich nur zum Setzen/Entfernen von Präfixen
// Transkodierung in UCS-16, wenn Eingabedatei nicht in diesem Format
switch (icp) {
case 1200: break; // nicht transkodieren
case 1201: SwapBytes(pin2,fsize); break; // Byte-Order tauschen
default: {
LPBYTE pin3=(LPBYTE)GlobalAlloc(GMEM_FIXED,fsize*2); // bläht maximal Faktor 2 auf
fsize=MultiByteToWideChar(icp,0,(LPCSTR)pin2,fsize,(LPWSTR)pin3,fsize)<<1;
if (!fsize) err("Eingabetranskodiervorgang fehlgeschlagen, Fehlerkode %d!",GetLastError());
GlobalFree(pin);
pin=pin2=pin3; // pin: zum Freigeben, pin2: zum Transkodieren
}
}
// Transkodierung zur Ziel-Kodeseitem wenn Ausgabedatei nicht in UTF-16
switch (ocp) {
case 1200: break;
case 1201: SwapBytes(pin2,fsize); break;
default: {
DWORD allocsize=fsize+(fsize>>1); // bläht maximal aufs 1,5fache auf
LPBYTE pin3=(LPBYTE)GlobalAlloc(GMEM_FIXED,allocsize);
fsize=WideCharToMultiByte(ocp,0,(LPCWSTR)pin2,fsize>>1,(LPSTR)pin3,allocsize,NULL,NULL);
if (!fsize) err("Ausgabetranskodiervorgang fehlgeschlagen, Fehlerkode %d!",GetLastError());
GlobalFree(pin);
pin=pin2=pin3;
}
}
}
// Datei-Ausgabe
if (fout==fin) SetFilePointer(fout,0,NULL,FILE_BEGIN);
DWORD bw;
if (!no_marker) switch (ocp) {
case CP_UTF8: WriteFile(fout,"\xEF\xBB\xBF",3,&bw,NULL); break;
case 1200: WriteFile(fout,"\xFF\xFE",2,&bw,NULL); break;
case 1201: WriteFile(fout,"\xFE\xFF",2,&bw,NULL); break;
}
// DeleteOnError=NULL; // Bei diesen Fehlern Dateibruchstück stehen lassen
if (!WriteFile(fout,pin2,fsize,&bw,NULL) || bw!=fsize)
err("Fehler beim Schreiben der Ausgabedatei! Platte voll?");
if (fin==fout) WriteFile(fout,pin2,0,&bw,NULL); // abschneiden
if (fout!=stdout && !CloseHandle(fout))
err("Fehler beim Schließen der Ausgabedatei! Platte voll?");
ExitProcess(exitcode);
}
Detected encoding: ANSI (CP1252) | 4
|
|