/* ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 1, or (at your option) ** any later version. ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Author : Alexandre Parenteau --- December 1997 */ /* * TextBinary.cpp --- utilities to ckeck if files are binary or text */ #include "stdafx.h" #ifdef WIN32 # include # include # include #endif /* WIN32 */ #ifdef macintosh # include # include # include # include # include "MacMisc.h" #endif /* macintosh */ #ifdef HAVE_UNISTD_H # include #endif #include #include #include #include #include "TextBinary.h" #include "FileTraversal.h" #include "AppConsole.h" #include "CPStr.h" #include "CvsPrefs.h" #if !defined(S_ISLNK) && defined(S_IFLNK) # if defined(S_IFMT) # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) # else # define S_ISLNK(m) ((m) & S_IFLNK) # endif #endif #define MAX_TEST_SIZE 100000 #ifdef WIN32 # ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; # endif #endif /* WIN32 */ static CStaticAllocT sBuf; static int sBufSize; static kTextBinTYPE FillBuffer(const char *arg, const char *dir, const UFSSpec * spec, bool bin) { sBuf.AdjustSize(MAX_TEST_SIZE); if(chdir(dir) != 0) { cvs_err("Unable to chdir to ""%s"" (error %d)\n", dir, errno); return kFileMissing; } FILE *samp = fopen(arg, bin ? "rb" : "r"); if(samp == 0L) { return kFileMissing; } sBufSize = fread(sBuf, sizeof(char), MAX_TEST_SIZE, samp); if(sBufSize < 0) { cvs_err("Unable to read ""%s"" (error %d)\n", arg, errno); return kFileMissing; } fclose(samp); return kFileIsOK; } static kTextBinTYPE TestBuffer(const char *arg, const char *dir, const UFSSpec * spec, kFileType expectType) { if(sBufSize == 0) return kFileIsOK; bool isUnicode = false; #ifdef WIN32 isUnicode = gCvsPrefs.WhichVersion() == kWin32 ? false : IsTextUnicode(sBuf, sBufSize, NULL); #endif /* WIN32 */ // Windows : fread skips the \r, so we don't care. // Mac : MSL exchanges when reading the \r and \n, // so we look for \n // Others : default to \n const unsigned char nativeNL = '\n'; const unsigned char nonnativeNL = '\r'; float percent = 0; int numBinChars = 0; int numTextChars = 0; if( !isUnicode ) { // figure if it is binary int i; unsigned char c; char *tmp; for(tmp = sBuf, i = 0; i < sBufSize; i++) { c = (unsigned char)*tmp++; if((c < 0x20 && c != nativeNL && c != nonnativeNL && c != '\t') || (c >= 0x80 #ifdef macintosh && c != (unsigned char)'Ñ' && c != (unsigned char)'¥' /* powerplant */ #endif /* macintosh */ )) numBinChars++; else numTextChars++; } percent = (float)numBinChars / (float)sBufSize; } // more than 5% binary makes binary bool isbin = percent >= 0.05; switch( expectType ) { case kFileTypeText: if( isUnicode ) { return kTextIsUnicode; } else if( isbin ) { return kTextIsBinary; } break; case kFileTypeBin: if( isUnicode ) { return kBinIsUnicode; } else if( !isbin ) { return kBinIsText; } break; case kFileTypeUnicode: if( !isUnicode ) { return isbin ? kUnicodeIsBinary : kUnicodeIsText; } break; default: #ifdef WIN32 ASSERT(FALSE); // uknown type #endif break; } // for the text files, check if the new lines are good if(kFileTypeText == expectType) { int i; unsigned char c; char *tmp; int numNonNative = 0, numNative = 0; for(tmp = sBuf, i = 0; i < sBufSize; i++) { c = (unsigned char)*tmp++; if(c == nonnativeNL) numNonNative++; else if(c == nativeNL) numNative++; } if(numNative == 0) { if(numNonNative > 0) return kTextWrongLF; } else { percent = (float)numNonNative / (float)numNative; if(percent >= 0.1) return kTextWrongLF; } if(numBinChars > 0) return kTextEscapeChar; } return kFileIsOK; } #if TARGET_RT_MAC_CFM static kTextBinTYPE GetMacSig(const char *arg, const char *dir, const UFSSpec * spec, bool expectbin) { CInfoPBRec info; info.dirInfo.ioVRefNum = spec->vRefNum; info.dirInfo.ioDrDirID = spec->parID; info.dirInfo.ioNamePtr = (StringPtr)spec->name; info.dirInfo.ioFDirIndex = 0; info.dirInfo.ioACUser = 0; OSErr err = PBGetCatInfoSync(&info); if(err != noErr || (info.dirInfo.ioFlAttrib & 0x10)/*folder*/) { cvs_err("Unable to get info on ""%s"" (error %d)\n", arg, err); return kFileMissing; } if(info.hFileInfo.ioFlFndrInfo.fdFlags & (1 << 15)) { return kFileIsAlias; } if(expectbin) { if(info.hFileInfo.ioFlFndrInfo.fdType == 'TEXT') return kBinWrongSig; } else { if(info.hFileInfo.ioFlRLgLen > 0 && info.hFileInfo.ioFlLgLen == 0) return kTextIsBinary; if(info.hFileInfo.ioFlFndrInfo.fdType != 'TEXT') return kTextWrongSig; } return kFileIsOK; } #endif /* TARGET_RT_MAC_CFM */ #if TARGET_RT_MAC_MACHO static kTextBinTYPE GetMacSig(const char *arg, const char *dir, const UFSSpec * spec, bool expectbin) { FSCatalogInfo info; FInfo finfo; OSErr err = FSGetCatalogInfo(spec, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoDataSizes | kFSCatInfoRsrcSizes, &info, NULL, NULL, NULL); if(err != noErr || (info.nodeFlags & kFSNodeIsDirectoryMask)) { cvs_err("Unable to get info on ""%s"" (error %d)\n", arg, err); return kFileMissing; } memcpy(&finfo, &info.finderInfo, 2 * sizeof(OSType)); if(expectbin) { if(finfo.fdType == 'TEXT') return kBinWrongSig; } else { if(info.rsrcPhysicalSize > 0 && info.dataPhysicalSize == 0) return kTextIsBinary; if(finfo.fdType != 'TEXT' && finfo.fdType != ' ') return kTextWrongSig; } return kFileIsOK; } #endif /* TARGET_RT_MAC_MACHO */ static kTextBinTYPE TestValidName(const char *filename) { kTextBinTYPE result = kFileIsOK; if(strchr(filename, '/') != 0L) result = kFileInvalidName; if(strchr(filename, '\n') != 0L) result = kFileInvalidName; #ifdef macintosh if(strchr(filename, '\r') != 0L) result = kFileInvalidName; if(strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) result = kFileInvalidName; if(strcmp(filename, "CVS") != 0 && stricmp(filename, "cvs") == 0) result = kFileInvalidName; #endif /* !macintosh */ return result; } static kTextBinTYPE TestFileIsAlias(const char *arg, const char *dir, const UFSSpec * spec) { kTextBinTYPE result = kFileIsOK; #ifdef S_ISLNK CPStr fullname; struct stat sb; fullname = dir; if(!fullname.endsWith(kPathDelimiter)) fullname << kPathDelimiter; fullname << arg; if (stat(fullname, &sb) == -1) result = kFileMissing; else if(S_ISLNK(sb.st_mode)) result = kFileIsAlias; #endif /* S_ISLNK */ return result; } kTextBinTYPE FileIsText(const char *arg, const char *dir, const UFSSpec * spec) { kTextBinTYPE state; #ifdef macintosh state = GetMacSig(arg, dir, spec, false); if(state != kFileIsOK) return state; #endif /* macintosh */ state = FillBuffer(arg, dir, spec, false); if(state != kFileIsOK) return state; if(sBufSize == 0) return kFileIsOK; state = TestBuffer(arg, dir, spec, kFileTypeText); if(state != kFileIsOK) return state; state = TestValidName(arg); if(state != kFileIsOK) return state; state = TestFileIsAlias(arg, dir, spec); if(state != kFileIsOK) return state; return kFileIsOK; } kTextBinTYPE FileIsBinary(const char *arg, const char *dir, const UFSSpec * spec) { kTextBinTYPE state; state = FillBuffer(arg, dir, spec, true); if(state != kFileIsOK) return state; state = TestBuffer(arg, dir, spec, kFileTypeBin); #ifdef macintosh kTextBinTYPE sigstate = GetMacSig(arg, dir, spec, true); if(sigstate != kFileIsOK) return state; if(state != kFileIsOK && state != kBinIsText) return state; #else /* !macintosh */ if(state != kFileIsOK) return state; #endif /* !macintosh */ state = TestValidName(arg); if(state != kFileIsOK) return state; state = TestFileIsAlias(arg, dir, spec); if(state != kFileIsOK) return state; return kFileIsOK; } kTextBinTYPE FileIsUnicode(const char *arg, const char *dir, const UFSSpec * spec /*= 0L*/) { kTextBinTYPE state; state = FillBuffer(arg, dir, spec, true); if(state != kFileIsOK) return state; state = TestBuffer(arg, dir, spec, kFileTypeUnicode); #ifdef macintosh kTextBinTYPE sigstate = GetMacSig(arg, dir, spec, true); if(sigstate != kFileIsOK) return state; if(state != kFileIsOK && state != kBinIsText) return state; #else /* !macintosh */ if(state != kFileIsOK) return state; #endif /* !macintosh */ state = TestValidName(arg); if(state != kFileIsOK) return state; state = TestFileIsAlias(arg, dir, spec); if(state != kFileIsOK) return state; return kFileIsOK; } bool SplitPath(const char *dir, CStr & uppath, CStr & folder) { char newdir[512]; char olddir[512]; char newfile[256]; int lendir = strlen(dir); strcpy(olddir, dir); if(dir[lendir - 1] == kPathDelimiter) { olddir[lendir - 1] = '\0'; --lendir; } const char *tmp = olddir, *prev = olddir; while((tmp = strchr(tmp, kPathDelimiter)) != 0L) prev = ++tmp; strcpy(newdir, olddir); newdir[prev - olddir] = '\0'; strcpy(newfile, prev); uppath = newdir; folder = newfile; return true; } void GetExtension(const char *file, CStr & base, CStr & ext) { char *tmp = strrchr(file, '.'); if(tmp == 0L) { ext = ""; base = file; } else { ext = tmp + 1; base.set(file, tmp - file - 1); } } bool MakeTmpFile(CStr & file, const char *prefix, const char *extension /*= 0L*/, bool create /*= false*/) { static int count = 0; char filename[256]; CStr tmpPath; CStr ext; CStr prf; if(extension != 0L && extension[0] != '\0') { if(extension[0] != '.') ext = "."; ext << extension; } if(prefix != 0L && prefix[0] != '\0') { prf = prefix; } else prf = "cvsgui"; #ifdef WIN32 char *ptr; ptr = getenv(_T("TMP")); if (ptr != 0L && _access( ptr, 0 ) != -1 ) tmpPath = ptr; else tmpPath = "."; #endif /* !WIN32 */ #ifdef macintosh UFSSpec theFolder; OSErr err; if((err = MacGetTempFolder(theFolder, tmpPath)) != noErr) { cvs_err("Unable to locate the preferences folder (error %d) !\n", err); return 0L; } for (;;) { sprintf(filename, "%s%d%s", (const char *)prf, count, (const char *)ext); if (::strlen (filename) <= 31) break; if (prf.length () == 0) { cvs_err("Unable to truncate temp file !\n"); return 0L; } // if prf.set (prf, prf.length () - 1); } // for #endif /* !macintosh */ #if qUnix tmpPath = "/tmp"; #endif struct stat sb; if (stat(tmpPath, &sb) == -1 || !S_ISDIR(sb.st_mode)) { cvs_err("Unable to access '%s' (error %d) !\n", (const char *)tmpPath, errno); return false; } if(!tmpPath.endsWith(kPathDelimiter)) tmpPath << kPathDelimiter; int maxtry = 10000; while(1) { sprintf(filename, "%s%d%s", (const char *)prf, count++, (const char *)ext); file = tmpPath; file << filename; if (stat(file, &sb) == -1 && errno == ENOENT) { if( create ) { FILE* stream = fopen(file, "w"); if( stream ) { fclose(stream); } } return true; } if(maxtry-- == 0) { cvs_err("Time out to access '%s' !\n", (const char *)tmpPath); break; } } return false; } bool GetEnvValue(const char *cmd, const char *key, UStr & value) { const char *tmp = strstr(cmd, key); if(tmp == 0L) return false; tmp = strchr(tmp, '='); if(tmp == 0L) return false; tmp++; const char *tmp2 = strchr(tmp, '&'); if(tmp2 == 0L) value = tmp; else value.set(tmp, tmp2 - tmp); return true; } int StringToArgv(const char *cmdLine, char **argv) { int argc = 0; char Quote = '\0'; /* Contains the quote character if we are inside "" or '', or \0 otherwise */ const char* walker1 = cmdLine; /* walker1 traverses the original command line */ char* walker2; /* walker2 traverses the parsed copy of the command line */ char theChar; /* theChar is the character we are currently parsing */ char arguments [MAX_CMD_LEN]; /* will contain a parsed copy of the command line */ walker2 = arguments; /* copy into arguments */ while ((theChar = *walker1++) != '\0') { /* parse command line to \0 */ if (theChar == ' ') /* skip leading space before an argument */ continue; argv [argc++] = walker2; /* set argv to point to the space where we copy the next argument */ if (argc > MAX_ARGS) { /* if we filled argv, skip the rest */ argc--; break; } do { /* parse an argument */ if (theChar == '\\' && *walker1++) /* skip \s */ theChar = *walker1++; else if ((theChar == '"') || (theChar == '\'')) { /* when we find a quote... */ if (Quote == '\0') { /* if we are not inside quotes already, */ Quote = theChar; /* remember we are inside quotes now */ continue; /* go to the next character */ } if (theChar == Quote) { /* if we are already inside that kind of quotes */ Quote = '\0'; /* close quotes */ continue; /* go to the next character */ } } *walker2++ = theChar; /* copy the character into args */ /* repeat until we reach the end of arguments or we reach a space that is not inside quotes */ } while ((*walker1 != '\0') && (((theChar = *walker1++) != ' ') || (Quote != '\0'))); *walker2++ = 0; /* null-terminate the last argument */ } return argc; }