1 krisbash 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** CtrlMd under the Apache CtrlM, Version 2.0 (the "CtrlM"); you may not
9 ** use this file except in compliance with the CtrlM. You may obtain a copy
10 ** of the CtrlM at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 CtrlM for the specific language governing permissions
20 ** and limitations under the CtrlM.
21 **
22 krisbash 1.1 **==============================================================================
23 */
24
25 /*
26 **==============================================================================
27 **
28 ** fixfiles:
29 **
30 ** This program performs three tasks:
31 **
32 ** (1) 'Fixes' text files (files made of ASCII 7-bit characters). This
33 ** involves two changes:
34 **
35 ** (*) Converting "\r\n" sequences to "\n"
36 ** (*) Appending "\n" to files that are missing this ending.
37 **
38 ** (2) Removes Windows internal files, such as:
39 **
40 ** sources
41 ** sources.dep
42 ** sources.inc
43 krisbash 1.1 ** dirs
44 ** buildchk.dbb
45 ** buildchk.err
46 ** buildchk.evt
47 ** buildchk.log
48 ** buildchk.prf
49 ** buildchk.rec
50 ** buildchk.trc
51 ** buildchk.wrn
52 ** buildfre.dbb
53 ** buildfre.err
54 ** buildfre.evt
55 ** buildfre.log
56 ** buildfre.prf
57 ** buildfre.rec
58 ** buildfre.trc
59 ** buildfre.wrn
60 ** makefile.cmn
61 ** project.mk
62 ** *.cmd
63 ** *.rc
64 krisbash 1.1 **
65 ** (3) Makes script files executable (files beginning with "#!" sequences).
66 **
67 ** This fixdist script compiles this program and runs it on the fly.
68 **
69 ** Note that this program examines all files in the distribution and runs
70 ** in under 2 seconds on Linux. It is slower on older Unix platforms but
71 ** quite fast.
72 **
73 **==============================================================================
74 */
75
76 #include <sys/types.h>
77 #include <dirent.h>
78 #include <sys/types.h>
79 #include <sys/stat.h>
80 #include <unistd.h>
81 #include <stdlib.h>
82 #include <stdio.h>
83 #include <string.h>
84 #include <errno.h>
85 krisbash 1.1 #include <ctype.h>
86 #include <limits.h>
87
88 #define FIXDIST_MAX_PATH 1024
89
90 const char* arg0;
91
92 typedef enum _Boolean
93 {
94 BOOLEAN_FALSE = 0,
95 BOOLEAN_TRUE = 1,
96 }
97 Boolean;
98
99 static int LoadFile(
100 const char* path,
101 size_t extraBytes,
102 char** dataOut,
103 size_t* sizeOut,
104 unsigned int* mode)
105 {
106 krisbash 1.1 char* data;
107 size_t size;
108 FILE* is;
109
110 /* Clear output parameters */
111 *dataOut = NULL;
112 *sizeOut = 0;
113
114 /* Stat this file (to get the size) */
115 {
116 struct stat st;
117
118 if (stat(path, &st) != 0)
119 return -1;
120
121 size = (size_t)st.st_size;
122 *mode = (unsigned int)st.st_mode;
123 }
124
125 /* Allocate the buffer */
126 if (!(data = (char*)malloc(size + extraBytes)))
127 krisbash 1.1 return -1;
128
129 /* Open the file */
130 if (!(is = fopen(path, "rb")))
131 {
132 free(data);
133 return -1;
134 }
135
136 /* Read the file into the buffer */
137 {
138 size_t n;
139 char buf[BUFSIZ];
140 char* end = data;
141
142 while ((n = fread(buf, 1, sizeof(buf), is)) > 0)
143 {
144 size_t remaining = size - (end - data);
145
146 /* No room in output buffer? (file grew since stat() called) */
147 if (n > remaining)
148 krisbash 1.1 {
149 fclose(is);
150 free(data);
151 return -1;
152 }
153
154 memcpy(end, buf, n);
155 end += n;
156 }
157
158 if (ferror(is) != 0)
159 {
160 fclose(is);
161 free(data);
162 return -1;
163 }
164 }
165
166 /* Close the file */
167 fclose(is);
168
169 krisbash 1.1 /* Set output parameters */
170 *dataOut = data;
171 *sizeOut = size;
172
173 return 0;
174 }
175
176 static int WriteFile(
177 const char* path,
178 const char* data,
179 size_t size)
180 {
181 FILE* os;
182
183 /* Open file */
184 if (!(os = fopen(path, "wb")))
185 return -1;
186
187 /* Write file */
188 {
189 size_t n;
190 krisbash 1.1
191 while ((n = fwrite(data, 1, size, os)) > 0)
192 {
193 data += n;
194 size -= n;
195 }
196
197 if (ferror(os) != 0)
198 {
199 fclose(os);
200 return -1;
201 }
202 }
203
204 fclose(os);
205
206 return size == 0 ? 0 : -1;
207 }
208
209 static Boolean HasTextExtension(const char* path)
210 {
211 krisbash 1.1 static const char* extensions[] =
212 {
213 ".c",
214 ".h",
215 ".cpp",
216 ".inc",
217 ".mof",
218 };
219 size_t numExtensions = sizeof(extensions) / sizeof(extensions[0]);
220 size_t i = 0;
221 const char* p;
222
223 if (!(p = strrchr(path, '.')))
224 {
225 return BOOLEAN_FALSE;
226 }
227
228 for (i = 0; i < numExtensions; i++)
229 {
230 if (strcmp(p, extensions[i]) == 0)
231 return BOOLEAN_TRUE;
232 krisbash 1.1 }
233
234 return BOOLEAN_FALSE;
235 }
236
237 static Boolean IsText(
238 const char* data,
239 size_t size,
240 size_t* line,
241 char* ch)
242 {
243 const char* p = data;
244 const char* end = data + size;
245
246 *line = 1;
247 *ch = '\0';
248
249 while (p != end)
250 {
251 char c = *p++;
252
253 krisbash 1.1 if (c == '\n')
254 (*line)++;
255
256 if (!isprint(c) && c != '\n' && c != '\r' && c != '\f' && c != '\t')
257 {
258 *ch = c;
259 return BOOLEAN_FALSE;
260 }
261 }
262
263 return BOOLEAN_TRUE;
264 }
265
266 static int FixFile(const char* path)
267 {
268 char* data;
269 size_t size;
270 int modified = 0;
271 const size_t extraBytes = 2;
272 unsigned int mode;
273 size_t line;
274 krisbash 1.1 char ch;
275
276 /* Load the file (allocate extra bytes for '\0' terminator and '\n' */
277 if (LoadFile(path, extraBytes, &data, &size, &mode) != 0)
278 {
279 fprintf(stderr, "%s: failed to read file: %s\n", arg0, path);
280 exit(1);
281 }
282
283 /* Skip non-ASCII files */
284 if (!IsText(data, size, &line, &ch))
285 {
286 if (HasTextExtension(path))
287 {
288 fprintf(
289 stderr,
290 "%s(%u): warning: non-standard character: %u\n",
291 path,
292 (unsigned int)line,
293 (unsigned char)ch);
294 }
295 krisbash 1.1 return 0;
296 }
297
298 /* Null terminate the buffer */
299 data[size] = '\0';
300
301 /* Replace "\r\n" sequences with "\n" */
302 {
303 const char* p = data;
304 char* q = data;
305 const char* end = data + size;
306
307 while (p != end)
308 {
309 if (p[0] == '\r' && p[1] == '\n')
310 {
311 p++;
312 size--;
313 modified = 1;
314 }
315
316 krisbash 1.1 *q++ = *p++;
317 }
318 }
319
320 /* Add '\n' to the end if missing */
321 if (size > 0 && data[size-1] != '\n')
322 {
323 data[size++] = '\n';
324 data[size] = '\0';
325 modified = 1;
326 }
327
328 /* If file was modified above */
329 if (modified)
330 {
331 /* Remove the original file */
332 if (unlink(path) != 0)
333 {
334 fprintf(stderr, "%s: failed to remove file: %s\n", arg0, path);
335 exit(1);
336 }
337 krisbash 1.1
338 /* Write the new file */
339 if (WriteFile(path, data, size) != 0)
340 {
341 fprintf(stderr, "%s: failed to write file: %s\n", arg0, path);
342 exit(1);
343 }
344
345 #if 0
346 printf("Modified %s\n", path);
347 #endif
348 }
349
350 /* Make script file executable */
351
352 if (data[0] == '#' && data[1] == '!')
353 {
354 chmod(path, mode | S_IXUSR);
355 }
356
357 free(data);
358 krisbash 1.1 return 0;
359 }
360
361 static Boolean IsWindowsFile(const char* path)
362 {
363 const char* names[] =
364 {
365 "sources",
366 "sources.dep",
367 "sources.inc",
368 "dirs",
369 "buildchk.dbb",
370 "buildchk.err",
371 "buildchk.evt",
372 "buildchk.log",
373 "buildchk.prf",
374 "buildchk.rec",
375 "buildchk.trc",
376 "buildchk.wrn",
377 "buildfre.dbb",
378 "buildfre.err",
379 krisbash 1.1 "buildfre.evt",
380 "buildfre.log",
381 "buildfre.prf",
382 "buildfre.rec",
383 "buildfre.trc",
384 "buildfre.wrn",
385 "makefile.cmn",
386 "project.mk"
387 };
388 const size_t numNames = sizeof(names) / sizeof(names[0]);
389 size_t i;
390
391 for (i = 0; i < numNames; i++)
392 {
393 const char* p = strrchr(path, '/');
394
395 if (p)
396 p++;
397 else
398 p = path;
399
400 krisbash 1.1 if (strcmp(p, names[i]) == 0)
401 {
402 return BOOLEAN_TRUE;
403 }
404
405 /* Check for files with the '.cmd' extension */
406 if ((p = strrchr(path, '.')) && strcmp(p, ".cmd") == 0)
407 {
408 return BOOLEAN_TRUE;
409 }
410
411 /* Check for files with the '.rc' extension */
412 if ((p = strrchr(path, '.')) && strcmp(p, ".rc") == 0)
413 {
414 return BOOLEAN_TRUE;
415 }
416 }
417
418 /* Not a windows file */
419 return BOOLEAN_FALSE;
420 }
421 krisbash 1.1
422 static void FixDir(const char* path)
423 {
424 DIR* dir;
425 struct dirent* ent;
426 char** dirs;
427 size_t dirsSize = 0;
428 size_t dirsCap = 64;
429 size_t i;
430
431 /* Allocate dynamic array to hold directories */
432 if (!(dirs = (char**)malloc(dirsCap * sizeof(char*))))
433 {
434 fprintf(stderr, "%s: malloc failed\n", arg0);
435 exit(1);
436 }
437
438 #if 0
439 printf("path{%s}\n", path);
440 #endif
441
442 krisbash 1.1 if (!(dir = opendir(path)))
443 {
444 fprintf(stderr, "%s: failed to open directory: %s\n", arg0, path);
445 exit(1);
446 }
447
448 while ((ent = readdir(dir)))
449 {
450 char buf[FIXDIST_MAX_PATH];
451 struct stat st;
452
453 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
454 continue;
455
456 *buf = '\0';
457 strncat(buf, path, sizeof(buf) - 1);
458 strncat(buf, "/", sizeof(buf) - 1 - strlen(buf));
459 strncat(buf, ent->d_name, sizeof(buf) - 1 - strlen(buf));
460
461 if (stat(buf, &st) != 0)
462 {
463 krisbash 1.1 fprintf(stderr, "%s: failed to stat: %s\n", arg0, buf);
464 exit(1);
465 }
466
467 if (st.st_mode & S_IFDIR)
468 {
469 char* p = strdup(buf);
470
471 if (!p)
472 {
473 fprintf(stderr, "%s: out of memory\n", arg0);
474 exit(1);
475 }
476
477 if (dirsSize == dirsCap)
478 {
479 dirsCap *= 2;
480
481 if (!(dirs = (char**)realloc(dirs, dirsCap * sizeof(char*))))
482 {
483 fprintf(stderr, "%s: realloc failed\n", arg0);
484 krisbash 1.1 exit(1);
485 }
486 }
487
488 dirs[dirsSize++] = p;
489 }
490 else
491 {
492 if (IsWindowsFile(buf))
493 {
494 unlink(buf);
495 #if 0
496 printf("Removed %s\n", buf);
497 #endif
498 }
499 else
500 {
501 FixFile(buf);
502 }
503 }
504 }
505 krisbash 1.1
506 /* Close the directory */
507 closedir(dir);
508
509 /* Process subdirectories */
510 for (i = 0; i < dirsSize; i++)
511 {
512 FixDir(dirs[i]);
513 free(dirs[i]);
514 }
515
516 free(dirs);
517 }
518
519 int main(int argc, char** argv)
520 {
521 arg0 = argv[0];
522
523 if (argc != 1)
524 {
525 fprintf(stderr, "Usage: %s\n", arg0);
526 krisbash 1.1 exit(1);
527 }
528
529 /* Fix all files in this directory and beneath it */
530 FixDir(".");
531
532 return 0;
533 }
|