Windows: permit code signing without timestamps
[openafs.git] / src / config / mkvers.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* Make the AFS_component_version_number.c file. Do it in C since there's no
11  * guarantee of perl on an NT platform.
12  *
13  */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <time.h>
22
23 #define VERINFO_STRING_CHARS_MAX  950   /* max chars in verinfo string */
24 #define CFILE_STRING_CHARS_MAX    2000  /* max chars in C file string */
25
26 #define DEPTH_MAX  6            /* maximum depth of src dir search (>= 3) */
27
28 #define CMLDIR_DFLT   "../../src/CML"   /* default starting point for search */
29 #define CMLDIR_BUFSZ  ((DEPTH_MAX * (sizeof("../") - 1)) + sizeof("src/CML"))
30 static char cmldir[CMLDIR_BUFSZ];
31
32 #define STATE "state"
33 #define STAMPS "stamps"
34
35 #define CML_STRING "cml_version_number[]=\"@(#)"
36 #define CML_VER_DECL "char " CML_STRING
37 char *cml_string = CML_VER_DECL;
38
39 #define VERINFO_BUILD_STRING  "#define AFS_VERINFO_BUILD "
40
41 #define AFS_STRING "char* AFSVersion = \""
42 #define VERS_FILE "AFS_component_version_number"
43
44 #define MAXDELTAS 128
45 typedef struct {
46     char name[512];
47     char type;
48 } deltaNames;
49
50 deltaNames stateDeltas[MAXDELTAS], stampDeltas[MAXDELTAS];
51 FILE *fpStamps;
52 FILE *fpState;
53 FILE *fpVers;
54 int nStates = 0;
55 int nStamps = 0;
56
57 enum {
58     CF_DEFAULT,
59     CF_VERINFO,
60     CF_TEXT,
61     CF_XML
62 } cfgFormat = CF_DEFAULT;
63
64 char *programName;
65
66 void PrintStamps(void);
67
68 void
69 Usage(void)
70 {
71     printf
72         ("Usage: %s [-d directory] [-o output file name] [-c component] [-v | -t | -x]\n",
73          programName);
74     printf("%s creates the AFS_component_version_number.c file.\n",
75            programName);
76     printf("Options:\n");
77     printf("-d directory - path of the CML directory, default is %s.\n",
78            CMLDIR_DFLT);
79     printf("-o file name - alternate output file name.\n");
80     printf
81         ("-c component - if not \"afs\" prefix for cml_version_number variable.\n");
82     printf("-v generate NT versioninfo style declarations.\n");
83     printf("-t generate text file style information.\n");
84     printf("-x generate XML revision information.\n");
85     exit(1);
86 }
87
88 int
89 main(int argc, char **argv)
90 {
91     char stampsFile[1024];
92     char stateFile[1024];
93     char s[1024];
94     int i;
95     char *baseDir;
96     int argDir = 0;
97     char *outputFile = NULL;
98     char outputFileBuf[sizeof(VERS_FILE) + 2];
99     struct stat sbuf;
100     time_t versTime;
101     int reBuild = 0;
102     int code;
103     char *cml_prefix = NULL;
104
105     /* initialize cmldir buffer and set default starting directory */
106     for (i = 0; i < DEPTH_MAX; i++) {
107         strcat(cmldir, "../");
108     }
109     strcat(cmldir, "src/CML");
110
111     baseDir = strstr(cmldir, CMLDIR_DFLT);
112
113     programName = argv[0];
114
115     for (i = 1; i < argc; i++) {
116         if (!strcmp("-d", argv[i])) {
117             i++;
118             if (i >= argc || *argv[i] == '-') {
119                 printf("Missing directory name for -d option.\n");
120                 Usage();
121             }
122             baseDir = argv[i];
123             argDir = 1;
124         } else if (!strcmp("-o", argv[i])) {
125             i++;
126             if (i >= argc || *argv[i] == '-') {
127                 printf("Missing output file name for -o option.\n");
128                 Usage();
129             }
130             outputFile = argv[i];
131         } else if (!strcmp("-c", argv[i])) {
132             i++;
133             if (i >= argc || *argv[i] == '-') {
134                 printf("Missing component for -c option.\n");
135                 Usage();
136             }
137             cml_prefix = argv[i];
138         } else if (!strcmp("-v", argv[i])) {
139             if (cfgFormat == CF_DEFAULT || cfgFormat == CF_VERINFO) {
140                 cfgFormat = CF_VERINFO;
141             } else {
142                 printf("Specify only one alternative output format\n");
143                 Usage();
144             }
145         } else if (!strcmp("-t", argv[i])) {
146             if (cfgFormat == CF_DEFAULT || cfgFormat == CF_TEXT) {
147                 cfgFormat = CF_TEXT;
148             } else {
149                 printf("Specify only one alternative output format\n");
150                 Usage();
151             }
152         } else if (!strcmp("-x", argv[i])) {
153             if (cfgFormat == CF_DEFAULT || cfgFormat == CF_XML) {
154                 cfgFormat = CF_XML;
155             } else {
156                 printf("Specify only one alternative output format\n");
157                 Usage();
158             }
159         } else {
160             printf("%s: Unknown argument.\n", argv[i]);
161             Usage();
162         }
163     }
164
165     /* set outputFile if not specified */
166
167     if (outputFile == NULL) {
168         strcpy(outputFileBuf, VERS_FILE);
169         if (cfgFormat == CF_VERINFO) {
170             strcat(outputFileBuf, ".h");
171         } else if (cfgFormat == CF_TEXT) {
172             strcat(outputFileBuf, ".txt");
173         } else if (cfgFormat == CF_XML) {
174             strcat(outputFileBuf, ".xml");
175         } else {
176             strcat(outputFileBuf, ".c");
177         }
178         outputFile = outputFileBuf;
179     }
180
181     /* Determine if we need to create the output file. */
182
183     if ((code = stat(outputFile, &sbuf)) < 0) {
184         reBuild = 1;
185         versTime = (time_t) 0;  /* indicates no output file. */
186     } else {
187         versTime = sbuf.st_mtime;
188     }
189
190     sprintf(stampsFile, "%s/%s", baseDir, STAMPS);
191     code = stat(stampsFile, &sbuf);
192
193     while (code < 0 && errno == ENOENT && !argDir && baseDir > cmldir) {
194         /* Try path at next level of depth. */
195         baseDir -= sizeof("../") - 1;
196         sprintf(stampsFile, "%s/%s", baseDir, STAMPS);
197         code = stat(stampsFile, &sbuf);
198     }
199     if (code == 0 && versTime <= sbuf.st_mtime) {
200         reBuild = 1;
201     }
202
203     sprintf(stateFile, "%s/%s", baseDir, STATE);
204
205     if (!reBuild) {
206         code = stat(stateFile, &sbuf);
207         /* dont' check alternate base dir, since it would be reset above */
208         if (code == 0 && versTime <= sbuf.st_mtime)
209             reBuild = 1;
210     }
211
212     if (!reBuild) {
213         printf("Not rebuilding %s since it is up to date.\n", outputFile);
214         exit(0);
215     }
216
217     if (cml_prefix) {
218         cml_string =
219             (char *)malloc(strlen("char ") + strlen(cml_prefix) + strlen(CML_STRING) +
220                    1);
221         if (!cml_string) {
222             printf("No space to use prefix in cml string, ignoring it.\n");
223             cml_string = CML_VER_DECL;
224         } else {
225             (void)sprintf(cml_string, "%s%s%s", "char ", cml_prefix,
226                           CML_STRING);
227         }
228     }
229
230     fpState = fopen(stateFile, "r");
231     fpStamps = fopen(stampsFile, "r");
232     fpVers = fopen(outputFile, "w");
233
234     if (fpStamps == NULL || fpState == NULL || fpVers == NULL) {
235         if (fpVers) {
236             if (cfgFormat == CF_VERINFO) {
237                 fprintf(fpVers,
238                         "%s \"No configuration information available\"\n",
239                         VERINFO_BUILD_STRING);
240             } else if (cfgFormat == CF_TEXT) {
241                 fprintf(fpVers, "No configuration information available.\n");
242             } else {
243                 fprintf(fpVers,
244                         "%sNo configuration information available\";\n",
245                         cml_string);
246                 fprintf(fpVers, "%safs??\";\n", AFS_STRING);
247             }
248             fclose(fpVers);
249         } else {
250             fprintf(stderr, "Can't write version information to %s.\n",
251                     outputFile);
252         }
253         fprintf(stderr,
254                 "No configuration information available, continuing...\n");
255         exit(1);
256     }
257
258
259     nStates = 0;
260     while (fgets(s, sizeof(s), fpState)) {
261         if (*s == 'I' || *s == 'N' || *s == 'C' || *s == 'O') {
262             stateDeltas[nStates].type = *s;
263             s[strlen(s) - 1] = '\0';
264             (void)strcpy(stateDeltas[nStates].name, s + 2);
265             nStates++;
266         }
267
268     }
269     fclose(fpState);
270
271     PrintStamps();
272     fclose(fpVers);
273
274     return 0;
275 }
276
277
278 void
279 PrintStamps(void)
280 {
281     char *s;
282     char *c = NULL;
283     int i;
284     size_t outMax, outCount = 0;
285     time_t t = time(NULL);
286
287     if (cfgFormat == CF_VERINFO) {
288         outMax = VERINFO_STRING_CHARS_MAX;
289     } else if (cfgFormat == CF_TEXT) {
290         outMax = 0;             /* signifies that there is no maximum */
291     } else {
292         outMax = CFILE_STRING_CHARS_MAX;
293     }
294
295     for (i = 0; i < nStates; i++) {
296         if (stateDeltas[i].type == 'C') {
297             if (cfgFormat == CF_VERINFO) {
298                 fprintf(fpVers, "%s \"Base configuration %s",
299                         VERINFO_BUILD_STRING, stateDeltas[i].name);
300             } else if (cfgFormat == CF_TEXT) {
301                 fprintf(fpVers, "Base configuration %s\n",
302                         stateDeltas[i].name);
303             } else if (cfgFormat == CF_XML) {
304                 fprintf(fpVers,
305                         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
306                         "<revision>\n"
307                         "<revnumber>\n"
308                         "Base configuration %s\n"
309                         "</revnumber>\n"
310                         "<date>\n"
311                         "%s"
312                         "</date>\n"
313                         "</revision>\n",
314                         stateDeltas[i].name, ctime(&t));
315             } else {
316                 fprintf(fpVers, "%sBase configuration %s", cml_string,
317                         stateDeltas[i].name);
318             }
319             c = stateDeltas[i].name;
320             break;
321         }
322     }
323
324     for (i = 0; i < nStates; i++) {
325         if (stateDeltas[i].type == 'I' || stateDeltas[i].type == 'N') {
326             outCount += strlen(stateDeltas[i].name) + 2;
327             if (outMax && outCount > outMax) {
328                 break;
329             }
330             if (cfgFormat == CF_TEXT) {
331                 fprintf(fpVers, "%c%s\n", stateDeltas[i].type,
332                         stateDeltas[i].name);
333             } else if (cfgFormat == CF_XML) {
334                 fprintf(fpVers,
335                         "<revremark>\n"
336                         ";%c%s"
337                          "</revremark>\n",
338                         stateDeltas[i].type,
339                         stateDeltas[i].name);
340             } else {
341                 fprintf(fpVers, ";%c%s", stateDeltas[i].type,
342                         stateDeltas[i].name);
343             }
344         }
345     }
346
347     for (i = 0; i < nStates; i++) {
348         if (stateDeltas[i].type == 'O') {
349             outCount += strlen(stateDeltas[i].name) + 2;
350             if (outMax && outCount > outMax) {
351                 break;
352             }
353             if (cfgFormat == CF_TEXT) {
354                 fprintf(fpVers, "%c%s\n", stateDeltas[i].type,
355                         stateDeltas[i].name);
356             } else if (cfgFormat == CF_XML) {
357                 fprintf(fpVers,
358                         "<revremark>\n"
359                         ";%c%s"
360                          "</revremark>\n",
361                         stateDeltas[i].type,
362                         stateDeltas[i].name);
363             } else {
364                 fprintf(fpVers, ";%c%s", stateDeltas[i].type,
365                         stateDeltas[i].name);
366             }
367         }
368     }
369
370     if (outMax && outCount > outMax) {
371         fprintf(fpVers, ";[LIST TRUNCATED]");
372     }
373
374     if (cfgFormat == CF_VERINFO) {
375         fprintf(fpVers, "\"\n");
376     } else if (cfgFormat == CF_DEFAULT) {
377         fprintf(fpVers, "\";\n");
378
379         if (c)
380             c += 3;
381         s = (char *)strchr(c, ' ');
382         if (s)
383             *s = '\0';
384         fprintf(fpVers, "%s%s\";\n", AFS_STRING, c ? c : "Unknown");
385     } else if (cfgFormat == CF_XML) {
386         fprintf(fpVers, "</revision>\n");
387     }
388 }