Modified utility usage and version display.
[people/mcb30/edk2.git] / edk2 / Tools / CCode / Source / SetStamp / SetStamp.c
1 /*++\r
2 \r
3 Copyright (c) 2004 - 2007, Intel Corporation                                                         \r
4 All rights reserved. This program and the accompanying materials                          \r
5 are licensed and made available under the terms and conditions of the BSD License         \r
6 which accompanies this distribution.  The full text of the license may be found at        \r
7 http://opensource.org/licenses/bsd-license.php                                            \r
8                                                                                           \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
11 \r
12 Module Name:\r
13   SetStamp.c\r
14 \r
15 Abstract:\r
16   Set Date/Time Stamp of Portable Executable (PE) format file\r
17 \r
18 --*/\r
19 \r
20 #include <stdio.h>\r
21 #include <string.h>\r
22 #include <time.h>\r
23 \r
24 #define LINE_MAXLEN 80\r
25 \r
26 //\r
27 // Utility Name\r
28 //\r
29 #define UTILITY_NAME  "SetStamp"\r
30 \r
31 //\r
32 // Utility version information\r
33 //\r
34 #define UTILITY_MAJOR_VERSION 0\r
35 #define UTILITY_MINOR_VERSION 1\r
36 \r
37 void\r
38 Version (\r
39   void\r
40   )\r
41 /*++\r
42 \r
43 Routine Description:\r
44 \r
45   Displays the standard utility information to SDTOUT\r
46 \r
47 Arguments:\r
48 \r
49   None\r
50 \r
51 Returns:\r
52 \r
53   None\r
54 \r
55 --*/\r
56 {\r
57   printf ("%s v%d.%d -Utility to set Date/Time Stamp for Portable Executable (PE) format file.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION);\r
58   printf ("Copyright (c) 1999-2007 Intel Corporation. All rights reserved.\n");\r
59 }\r
60 \r
61 void\r
62 Usage (\r
63   void\r
64   )\r
65 /*++\r
66 Routine Description:\r
67   print usage of setstamp command\r
68 \r
69 Arguments:\r
70   void\r
71 \r
72 Returns:\r
73   None\r
74 --*/\r
75 {\r
76   Version();\r
77   \r
78   //\r
79   // print usage of command\r
80   //\r
81   printf ("\nUsage: SetStamp <PE-File> <TIME-File>\n");\r
82 }\r
83 \r
84 int\r
85 GetDateTime (\r
86   FILE      *fp,\r
87   time_t    *ltime\r
88   )\r
89 /*++\r
90 Routine Description:\r
91   Read the date and time from TIME file. If the date/time string is\r
92 "NOW NOW", write the current date and time to TIME file and set it to\r
93 ltime. Else, set the date and time of TIME file to ltime.\r
94 \r
95 Arguments:\r
96   fp              - The pointer of TIME file\r
97   ltime           - Date and time\r
98 \r
99 Returns:\r
100   =  0            - Success\r
101   = -1            - Failed\r
102 --*/\r
103 {\r
104   char      buffer[LINE_MAXLEN];\r
105   struct tm stime;\r
106   struct tm *now;\r
107 \r
108   if (fgets (buffer, LINE_MAXLEN, fp) == NULL) {\r
109     printf ("Error: Cannot read TIME file.\n");\r
110     return -1;\r
111   }\r
112   //\r
113   // compare the value with "NOW NOW", write TIME file if equal\r
114   //\r
115   if (strncmp (buffer, "NOW NOW", 7) == 0) {\r
116     //\r
117     // get system current time and date\r
118     //\r
119     time (ltime);\r
120 \r
121     now = localtime (ltime);\r
122     if (now == NULL) {\r
123       printf ("Error: Cannot get local time.\n");\r
124       return -1;\r
125     }\r
126 \r
127     if (strftime (buffer, LINE_MAXLEN, "%Y-%m-%d %H:%M:%S", now) == 0) {\r
128       printf ("Error: Cannot format time string.\n");\r
129       return -1;\r
130     }\r
131     //\r
132     // write TIME file\r
133     //\r
134     if (fseek (fp, 0, SEEK_SET) != 0) {\r
135       printf ("Error: Cannot move location of TIME file.\n");\r
136       return -1;\r
137     }\r
138 \r
139     if (fputs (buffer, fp) == EOF) {\r
140       printf ("Error: Cannot write time string to TIME file.\n");\r
141       return -1;\r
142     }\r
143     //\r
144     // ltime has been set as current time and date, return\r
145     //\r
146     return 0;\r
147   }\r
148   //\r
149   // get the date and time from buffer\r
150   //\r
151   if (6 != sscanf (\r
152             buffer,\r
153             "%d-%d-%d %d:%d:%d",\r
154             &stime.tm_year,\r
155             &stime.tm_mon,\r
156             &stime.tm_mday,\r
157             &stime.tm_hour,\r
158             &stime.tm_min,\r
159             &stime.tm_sec\r
160             )) {\r
161     printf ("Error: Invaild date or time!\n");\r
162     return -1;\r
163   }\r
164   //\r
165   // in struct, Month (0 - 11; Jan = 0). So decrease 1 from it\r
166   //\r
167   stime.tm_mon -= 1;\r
168 \r
169   //\r
170   // in struct, Year (current year minus 1900)\r
171   // and only the dates can be handled from Jan 1, 1970 to Jan 18, 2038\r
172   //\r
173   //\r
174   // convert 0 -> 100 (2000), 1 -> 101 (2001), ..., 38 -> 138 (2038)\r
175   //\r
176   if (stime.tm_year <= 38) {\r
177     stime.tm_year += 100;\r
178   }\r
179   //\r
180   // convert 1970 -> 70, 2000 -> 100, ...\r
181   //\r
182   else if (stime.tm_year >= 1970) {\r
183     stime.tm_year -= 1900;\r
184   }\r
185   //\r
186   // convert the date and time to time_t format\r
187   //\r
188   *ltime = mktime (&stime);\r
189   if (*ltime == (time_t) - 1) {\r
190     printf ("Error: Invalid date or time!\n");\r
191     return -1;\r
192   }\r
193 \r
194   return 0;\r
195 }\r
196 \r
197 int\r
198 ReadFromFile (\r
199   FILE      *fp,\r
200   long      offset,\r
201   void      *buffer,\r
202   int       size\r
203   )\r
204 /*++\r
205 Routine Description:\r
206   read data from a specified location of file\r
207 \r
208 Arguments:\r
209   fp              - file pointer\r
210   offset          - number of bytes from beginning of file\r
211   buffer          - buffer used to store data\r
212   size            - size of buffer\r
213 \r
214 Returns:\r
215   =  0            - Success\r
216   = -1            - Failed\r
217 --*/\r
218 {\r
219   //\r
220   // set file pointer to the specified location of file\r
221   //\r
222   if (fseek (fp, offset, SEEK_SET) != 0) {\r
223     printf ("Error: Cannot move the current location of the file.\n");\r
224     return -1;\r
225   }\r
226   //\r
227   // read data from the file\r
228   //\r
229   if (fread (buffer, size, 1, fp) != 1) {\r
230     printf ("Error: Cannot read data from the file.\n");\r
231     return -1;\r
232   }\r
233 \r
234   return 0;\r
235 }\r
236 \r
237 int\r
238 WriteToFile (\r
239   FILE      *fp,\r
240   long      offset,\r
241   void      *buffer,\r
242   int       size\r
243   )\r
244 /*++\r
245 Routine Description:\r
246   write data to a specified location of file\r
247 \r
248 Arguments:\r
249   fp              - file pointer\r
250   offset          - number of bytes from beginning of file\r
251   buffer          - buffer used to store data\r
252   size            - size of buffer\r
253 \r
254 Returns:\r
255   =  0            - Success\r
256   = -1            - Failed\r
257 --*/\r
258 {\r
259   //\r
260   // set file pointer to the specified location of file\r
261   //\r
262   if (fseek (fp, offset, SEEK_SET) != 0) {\r
263     printf ("Error: Cannot move the current location of the file.\n");\r
264     return -1;\r
265   }\r
266   //\r
267   // write data to the file\r
268   //\r
269   if (fwrite (buffer, size, 1, fp) != 1) {\r
270     perror ("Error: Cannot write data to the file.\n");\r
271     return -1;\r
272   }\r
273 \r
274   return 0;\r
275 }\r
276 \r
277 int\r
278 SetStamp (\r
279   FILE      *fp,\r
280   time_t    ltime\r
281   )\r
282 /*++\r
283 Routine Description:\r
284   set Date/Time Stamp of the file\r
285 \r
286 Arguments:\r
287   fp              - file pointer\r
288   ltime           - time and date\r
289 \r
290 Returns:\r
291   =  0            - Success\r
292   = -1            - Failed\r
293 --*/\r
294 {\r
295   unsigned char header[4];\r
296   unsigned long offset;\r
297   unsigned long NumberOfRvaAndSizes;\r
298   unsigned int  nvalue;\r
299   unsigned long lvalue;\r
300 \r
301   //\r
302   // read the header of file\r
303   //\r
304   if (ReadFromFile (fp, 0, header, 2) != 0) {\r
305     return -1;\r
306   }\r
307   //\r
308   // "MZ" -- the header of image file (PE)\r
309   //\r
310   if (strncmp ((char *) header, "MZ", 2) != 0) {\r
311     printf ("Error: Invalid Image file.\n");\r
312     return -1;\r
313   }\r
314   //\r
315   // At location 0x3C, the stub has the file offset to the\r
316   // PE signature.\r
317   //\r
318   if (ReadFromFile (fp, 0x3C, &offset, 4) != 0) {\r
319     return -1;\r
320   }\r
321   //\r
322   // read the header of optional\r
323   //\r
324   if (ReadFromFile (fp, offset, header, 4) != 0) {\r
325     return -1;\r
326   }\r
327   //\r
328   // "PE\0\0" -- the signature of optional header\r
329   //\r
330   if (strncmp ((char *) header, "PE\0\0", 4) != 0) {\r
331     printf ("Error: Invalid PE format file.\n");\r
332     return -1;\r
333   }\r
334   //\r
335   // Add 8 to skip PE signature (4-byte), Machine (2-byte) and\r
336   // NumberOfSection (2-byte)\r
337   //\r
338   offset += 8;\r
339 \r
340   if (WriteToFile (fp, offset, &ltime, 4) != 0) {\r
341     return -1;\r
342   }\r
343   //\r
344   // Add 16 to skip COFF file header, and get to optional header.\r
345   //\r
346   offset += 16;\r
347 \r
348   //\r
349   // Check the magic field, 0x10B for PE32 and 0x20B for PE32+\r
350   //\r
351   if (ReadFromFile (fp, offset, &nvalue, 2) != 0) {\r
352     return -1;\r
353   }\r
354   //\r
355   // If this is PE32 image file, offset of NumberOfRvaAndSizes is 92.\r
356   // Else it is 108.\r
357   //\r
358   switch (nvalue & 0xFFFF) {\r
359   case 0x10B:\r
360     offset += 92;\r
361     break;\r
362 \r
363   case 0x20B:\r
364     offset += 108;\r
365     break;\r
366 \r
367   default:\r
368     printf ("Error: Sorry! The Magic value is unknown.\n");\r
369     return -1;\r
370   }\r
371   //\r
372   // get the value of NumberOfRvaAndSizes\r
373   //\r
374   if (ReadFromFile (fp, offset, &NumberOfRvaAndSizes, 4) != 0) {\r
375     return -1;\r
376   }\r
377   //\r
378   // Date/time stamp exists in Export Table, Import Table, Resource Table,\r
379   // Debug Table and Delay Import Table. And in Import Table and Delay Import\r
380   // Table, it will be set when bound. So here only set the date/time stamp\r
381   // of Export Table, Resource Table and Debug Table.\r
382   //\r
383   //\r
384   // change date/time stamp of Export Table, the offset of Export Table\r
385   // is 4 + 0 * 8 = 4. And the offset of stamp is 4.\r
386   //\r
387   if (NumberOfRvaAndSizes >= 1) {\r
388     if (ReadFromFile (fp, offset + 4, &lvalue, 4) != 0) {\r
389       return -1;\r
390     }\r
391 \r
392     if (lvalue != 0) {\r
393       if (WriteToFile (fp, lvalue + 4, &ltime, 4) != 0) {\r
394         return -1;\r
395       }\r
396     }\r
397   }\r
398   //\r
399   // change date/time stamp of Resource Table, the offset of Resource Table\r
400   // is 4 + 2 * 8 = 20. And the offset of stamp is 4.\r
401   //\r
402   if (NumberOfRvaAndSizes >= 3) {\r
403     if (ReadFromFile (fp, offset + 20, &lvalue, 4) != 0) {\r
404       return -1;\r
405     }\r
406 \r
407     if (lvalue != 0) {\r
408       if (WriteToFile (fp, lvalue + 4, &ltime, 4) != 0) {\r
409         return -1;\r
410       }\r
411     }\r
412   }\r
413   //\r
414   // change date/time stamp of Debug Table, offset of Debug Table\r
415   // is 4 + 6 * 8 = 52. And the offset of stamp is 4.\r
416   //\r
417   if (NumberOfRvaAndSizes >= 7) {\r
418     if (ReadFromFile (fp, offset + 52, &lvalue, 4) != 0) {\r
419       return -1;\r
420     }\r
421 \r
422     if (lvalue != 0) {\r
423       if (WriteToFile (fp, lvalue + 4, &ltime, 4) != 0) {\r
424         return -1;\r
425       }\r
426     }\r
427     //\r
428     // change the date/time stamp of Debug Data\r
429     //\r
430     if (ReadFromFile (fp, lvalue + 24, &lvalue, 4) != 0) {\r
431       return -1;\r
432     }\r
433     //\r
434     // get the signature of debug data\r
435     //\r
436     if (ReadFromFile (fp, lvalue, header, 2) != 0) {\r
437       return -1;\r
438     }\r
439     //\r
440     // "NB" - the signature of Debug Data\r
441     // Need Review: (From Spec. is "NB05", From .dll is "NB10")\r
442     //\r
443     if (strncmp ((char *) header, "NB", 2) == 0) {\r
444       if (WriteToFile (fp, lvalue + 8, &ltime, 4) != 0) {\r
445         return -1;\r
446       }\r
447     }\r
448   }\r
449 \r
450   return 0;\r
451 }\r
452 \r
453 int\r
454 main (\r
455   int       argc,\r
456   char      *argv[]\r
457   )\r
458 {\r
459   FILE    *fp;\r
460   time_t  ltime;\r
461 \r
462   if (argc == 1) {\r
463     Usage();\r
464     return -1;\r
465   }\r
466     \r
467   if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0) ||\r
468       (strcmp(argv[1], "-?") == 0) || (strcmp(argv[1], "/?") == 0)) {\r
469     Usage();\r
470     return -1;\r
471   }\r
472   \r
473   if ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0)) {\r
474     Version();\r
475     return -1;\r
476   }\r
477   //\r
478   // check the number of parameters\r
479   //\r
480   if (argc != 3) {\r
481     Usage ();\r
482     return -1;\r
483   }\r
484   //\r
485   // open the TIME file, if not exists, return\r
486   //\r
487   fp = fopen (argv[2], "r+");\r
488   if (fp == NULL) {\r
489     return 0;\r
490   }\r
491   //\r
492   // get time and date from file\r
493   //\r
494   if (GetDateTime (fp, &ltime) != 0) {\r
495     fclose (fp);\r
496     return -1;\r
497   }\r
498   //\r
499   // close the TIME file\r
500   //\r
501   fclose (fp);\r
502 \r
503   //\r
504   // open the PE file\r
505   //\r
506   fp = fopen (argv[1], "r+b");\r
507   if (fp == NULL) {\r
508     printf ("Error: Cannot open the PE file!\n");\r
509     return -1;\r
510   }\r
511   //\r
512   // set time and date stamp to the PE file\r
513   //\r
514   if (SetStamp (fp, ltime) != 0) {\r
515     fclose (fp);\r
516     return -1;\r
517   }\r
518 \r
519   printf ("Set Date/Time Stamp to %s", ctime (&ltime));\r
520 \r
521   //\r
522   // close the PE file\r
523   //\r
524   fclose (fp);\r
525 \r
526   return 0;\r
527 }\r