OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / tools / qtlibspatcher / binpatch.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include <cstdio>
34 #include <cstring>
35 #include <locale>
36
37 #include <qglobal.h>
38
39 #include "binpatch.h"
40
41 #ifdef Q_OS_WIN
42 #       define strcasecmp _stricmp
43 #       define strncasecmp _strnicmp
44 #endif
45
46 // returns positive value if it finds a null termination inside the buffer
47 long BinPatch::getBufferStringLength(char *data, char *end)
48 {
49     long size = 0;
50     while (data < end) {
51         if (*data == '\0')
52             return size;
53         ++data;
54         ++size;
55     }
56
57     return -1;
58 }
59
60 // returns true if data ends with one of the tokens. (Sep. with ;)
61 bool BinPatch::endsWithTokens(const char *data)
62 {
63     if(strlen(endTokens) > 0) {
64         char endstmp[1024];
65         ulong tlen = ulong(strlen(data));
66
67         if(strlen(endTokens) >= sizeof(endstmp))
68             return false;
69
70         strcpy(endstmp, endTokens);
71
72         char *token = strtok(endstmp, ";");
73
74         while(token != NULL) {
75             // check if it ends with the token
76             if ((tlen >= strlen(token))
77                 && (strcasecmp((data+tlen)-strlen(token), token) == 0))
78                 return true;
79             token = strtok(NULL, ";");
80         }
81     } else {
82         return true; //true if no tokens
83     }
84
85     return false; //no matching tokens
86 }
87
88 bool BinPatch::patchHelper(char *inbuffer, const char *oldstr, const char *newstr, size_t len, long *rw)
89 {
90     char nc1 = *oldstr;
91     char nc2 = 0;
92     char *inend = inbuffer + len;
93     size_t oldlen = strlen(oldstr);
94     size_t newlen = strlen(newstr);
95     char *instart = inbuffer;
96     *rw = 0;
97     bool write = true;
98
99     isupper(nc1) ? nc2 = tolower(nc1) : nc2 = toupper(nc1);
100
101     while(inbuffer < inend) {
102         if ((*inbuffer == nc1) || (*inbuffer == nc2)) {
103             if (inbuffer > (inend-oldlen) || inbuffer > (inend-newlen)) {
104                 *rw = (long)(inend-inbuffer); //rewind, not enough to make a compare
105                 break;
106             }
107
108             if (strncasecmp(inbuffer, oldstr, oldlen) == 0) {
109                 if (useLength && (instart == inbuffer)) {
110                     *rw = (long)(len+1); //we don't have access to the length byte, rewind all + 1!
111                     write = false;
112                     break;
113                 }
114
115                 long oldsize = -1;
116                 if (useLength) { //VC60
117                     oldsize = (unsigned char)(*(inbuffer-1));
118
119                     // vc60 pdb files sometimes uses 0A, then it should be null terminated
120                     if (oldsize < (long)oldlen) {
121                         if (oldsize != 0x0A) { //strange case... skip
122                             inbuffer+=oldlen;
123                             continue;
124                         }
125
126                         oldsize = getBufferStringLength(inbuffer, inend);
127
128                         if (oldsize < 0) {
129                             *rw = (long)(inend-inbuffer); //rewind, entire string not in buffer
130                             break;
131                         }
132                     }
133
134                     if (inbuffer > (inend-oldsize)) {
135                         *rw = (long)(inend-inbuffer); //rewind, entire string not in buffer
136                         break;
137                     }
138                 } else { //VC7x
139                     oldsize = getBufferStringLength(inbuffer, inend);
140                     if (oldsize < 0) {
141                         *rw = (long)(inend-inbuffer); //rewind, entire string not in buffer
142                         break;
143                     }
144                 }
145
146                 char oldPath[1024];
147                 if (oldsize > (long)sizeof(oldPath)) {
148                     //at least don't crash
149                     inbuffer+=oldsize;
150                     continue;
151                 }
152                 memset(oldPath, '\0', sizeof(oldPath));
153                 strncpy(oldPath, newstr, newlen);
154
155                 if (insertReplace)
156                     strncat(oldPath, inbuffer+oldlen, oldsize-oldlen);
157
158                 // just replace if it ends with a token in endTokens
159                 if (endsWithTokens(oldPath)) {
160                     if (oldsize < (long)strlen(oldPath))
161                         oldsize = (long)strlen(oldPath);
162
163                     memcpy(inbuffer, oldPath, oldsize);
164                 }
165
166                 inbuffer+=oldsize;
167                 continue;
168             }
169         }
170         ++inbuffer;
171     }
172
173     return write;
174 }
175
176 bool BinPatch::patch(const char *oldstr, const char *newstr)
177 {
178     size_t oldlen = strlen(oldstr);
179     size_t newlen = strlen(newstr);
180
181     if ((!fileName || strlen(fileName) < 1)
182         || (!oldstr || oldlen < 1)
183         || (!newstr || newlen < 1))
184         return false;
185
186     FILE *input;
187
188     if (!(input = fopen(fileName, "r+b")))
189     {
190         fprintf(stderr, "Warning: Could not open file %s\n", fileName);
191         return false;
192     }
193
194     char data[60000];
195     long rw = 0;
196     long offset = 0;
197
198     size_t len;
199     len = fread(data, sizeof(char), sizeof(data), input);
200
201     do {
202         if (patchHelper(data, oldstr, newstr, len, &rw)) {
203             fseek(input, offset, SEEK_SET); //overwrite
204             fwrite(data, sizeof(char), len, input);
205         }
206
207         offset += (long)((-rw) + len);
208         if (fseek(input, offset, SEEK_SET) != 0)
209             break;
210         len = fread(data, sizeof(char), sizeof(data), input);
211     } while(!(feof(input) && (len <= oldlen || len <= newlen)));
212
213     fclose(input);
214
215     return true;
216 }