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