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