forked from Mirrors/freeswitch
382 lines
13 KiB
C
382 lines
13 KiB
C
/* SvnRev
|
||
*
|
||
* This utility retrieves the highest number that follows the "$Id: $" keyword
|
||
* or a combination of the $Rev: $ and $Date: $ keywords. The Subversion
|
||
* version control system expands these keywords and keeps them up to date.
|
||
* For an example of the tag, see the end of this comment.
|
||
*
|
||
* Details on the usage and the operation of this utility is available on-line
|
||
* at http://www.compuphase.com/svnrev.htm.
|
||
*
|
||
*
|
||
* Acknowledgements
|
||
*
|
||
* The support for .java files is contributed by Tom McCann (tommc@spoken.com).
|
||
* The option for prefixing and/or suffixing the build number (in the string
|
||
* constant SVN_REVSTR) was suggested by Robert Nitzel.
|
||
*
|
||
*
|
||
* License
|
||
*
|
||
* Copyright (c) 2005-2009, ITB CompuPhase (www.compuphase.com).
|
||
*
|
||
* This software is provided "as-is", without any express or implied warranty.
|
||
* In no event will the authors be held liable for any damages arising from
|
||
* the use of this software.
|
||
*
|
||
* Permission is granted to anyone to use this software for any purpose,
|
||
* including commercial applications, and to alter it and redistribute it
|
||
* freely, subject to the following restrictions:
|
||
*
|
||
* 1. The origin of this software must not be misrepresented; you must not
|
||
* claim that you wrote the original software. If you use this software in
|
||
* a product, an acknowledgment in the product documentation would be
|
||
* appreciated but is not required.
|
||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||
* misrepresented as being the original software.
|
||
* 3. This notice may not be removed or altered from any source distribution.
|
||
*
|
||
* Version: $Id: svnrev.c 1497 2010-02-12 17:20:21Z achaloyan $
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include <apr_pools.h>
|
||
#include <apr_file_io.h>
|
||
|
||
|
||
#if defined __WIN32__ || defined _Win32 || defined _WIN32
|
||
#define DIRSEP '\\'
|
||
#elif defined macintosh
|
||
#define DIRSEP ':'
|
||
#else
|
||
/* assume Linux/Unix */
|
||
#define DIRSEP '/'
|
||
#endif
|
||
|
||
#define MAX_LINELENGTH 512
|
||
#define MAX_SYMBOLLENGTH 32
|
||
|
||
static void about(void)
|
||
{
|
||
printf("svnrev 1.7.\n\n");
|
||
printf("Usage: svnrev [options] <input> [input [...]]\n\n"
|
||
"Options:\n"
|
||
"-ofilename\tOutput filename for the file with the build number. When no\n"
|
||
"\t\tfilename follows \"-o\", the result is written to stdout. The\n"
|
||
"\t\tdefault filename is \"svnrev.h\" for C/C++ and \"VersionInfo.java\"\n"
|
||
"\t\tfor Java.\n\n"
|
||
"-fpattern\tFormat: Adds text before or after the build number in the\n"
|
||
"\t\tconstant SVN_REVSTR. The pattern has the form \"text#text\"\n"
|
||
"\t\t(without the quotes) where \"text\" is arbitrary text and \"#\"\n"
|
||
"\t\twill be replaced by the build number.\n\n"
|
||
"-i\t\tIncremental: this option should be used when the list of input\n"
|
||
"\t\tfiles is a subset of all files in the project. When -i is\n"
|
||
"\t\tpresent, svnrev also scans the output file that was generated\n"
|
||
"\t\ton a previous run.\n\n"
|
||
"-jname\t\tJava: this option writes a java package file instead of a C/C++\n"
|
||
"\t\theader file. The name of the Java package must follow the\n"
|
||
"\t\toption (this is not the filename).\n\n"
|
||
"-v\t\tVerbose: prints the names of files that are modified since the\n"
|
||
"\t\tlast commit (into version control) to stderr.\n");
|
||
exit(1);
|
||
}
|
||
|
||
static void processfile(const char *name, int failsilent,
|
||
int *max_build, int *accum_build,
|
||
int *max_year, int *max_month, int *max_day,
|
||
int *ismodified)
|
||
|
||
{
|
||
char str[MAX_LINELENGTH], str_base[MAX_LINELENGTH];
|
||
char name_base[MAX_LINELENGTH];
|
||
char *p1;
|
||
FILE *fp, *fp_base;
|
||
int build, maj_build;
|
||
int year, month, day;
|
||
int cnt;
|
||
char modchar;
|
||
|
||
/* since we also want to verify whether the file is modified in version
|
||
* control, get the path to the working copy name
|
||
* for every source file "<path>\<filename>, the "working copy" base can
|
||
* be found in "<path>\.svn\text-base\<filename>.svn-base"
|
||
*/
|
||
if ((p1 = strrchr(name, DIRSEP)) != NULL) {
|
||
++p1; /* skip directory separator character ('\' in Windows, '/' in Linux) */
|
||
strncpy(name_base, name, (int)(p1 - name));
|
||
name_base[(int)(p1 - name)] = '\0';
|
||
} else {
|
||
name_base[0] = '\0';
|
||
p1 = (char*)name;
|
||
} /* if */
|
||
sprintf(name_base + strlen(name_base), ".svn%ctext-base%c%s.svn-base",
|
||
DIRSEP, DIRSEP, p1);
|
||
|
||
/* first extract the revision keywords */
|
||
fp = fopen(name, "r");
|
||
if (fp == NULL) {
|
||
if (!failsilent)
|
||
fprintf(stderr, "Failed to open input file '%s'\n", name);
|
||
return;
|
||
} /* if */
|
||
fp_base = fopen(name_base, "r"); /* fail silently */
|
||
build = 0;
|
||
maj_build = 0; /* RCS / CVS */
|
||
year = month = day = 0;
|
||
|
||
while (fgets(str, sizeof str, fp) != NULL) {
|
||
if (fp_base == NULL || fgets(str_base, sizeof str_base, fp_base) == NULL)
|
||
str_base[0] = '\0';
|
||
if ((p1 = strstr(str, "$Id:")) != NULL && strchr(p1+1, '$') != NULL) {
|
||
if (sscanf(p1, "$Id: %*s %d %d-%d-%d", &build, &year, &month, &day) < 4
|
||
&& sscanf(p1, "$Id: %*s %d %d/%d/%d", &build, &year, &month, &day) < 4)
|
||
if (sscanf(p1, "$Id: %*s %d.%d %d-%d-%d", &maj_build, &build, &year, &month, &day) < 5)
|
||
sscanf(p1, "$Id: %*s %d.%d %d/%d/%d", &maj_build, &build, &year, &month, &day);
|
||
} else if ((p1 = strstr(str, "$Rev:")) != NULL && strchr(p1+1, '$') != NULL) {
|
||
if (sscanf(p1, "$Rev: %d.%d", &maj_build, &build) < 2) {
|
||
sscanf(p1, "$Rev: %d", &build);
|
||
maj_build = 0;
|
||
} /* if */
|
||
} else if ((p1 = strstr(str, "$Revision:")) != NULL && strchr(p1+1, '$') != NULL) {
|
||
if (sscanf(p1, "$Revision: %d.%d", &maj_build, &build) < 2) {
|
||
/* SvnRev also writes this keyword in its own generated file; read it
|
||
* back for partial updates
|
||
*/
|
||
cnt = sscanf(p1, "$Revision: %d%c", &build, &modchar);
|
||
if (cnt == 2 && modchar == 'M' && ismodified != NULL)
|
||
*ismodified = 1;
|
||
maj_build = 0;
|
||
} /* if */
|
||
} else if ((p1 = strstr(str, "$Date:")) != NULL && strchr(p1+1, '$') != NULL) {
|
||
if (sscanf(p1, "$Date: %d-%d-%d", &year, &month, &day) < 3)
|
||
sscanf(p1, "$Date: %d/%d/%d", &year, &month, &day);
|
||
} else if (ismodified != NULL && *ismodified == 0 && fp_base != NULL) {
|
||
/* no keyword present, compare the lines for equivalence */
|
||
*ismodified = strcmp(str, str_base) != 0;
|
||
} /* if */
|
||
|
||
if (maj_build)
|
||
*accum_build += build; /* RCS / CVS */
|
||
else if (build > *max_build)
|
||
*max_build = build; /* Subversion */
|
||
if (year > *max_year
|
||
|| (year == *max_year && month > *max_month)
|
||
|| (year == *max_year && month == *max_month && day > *max_day))
|
||
{
|
||
*max_year = year;
|
||
*max_month = month;
|
||
*max_day = day;
|
||
} /* if */
|
||
if (build > 0 && year > 0 && (fp_base == NULL || ismodified == NULL || *ismodified != 0))
|
||
break; /* both build # and date found, not comparing or modification
|
||
* already found => no need to search further */
|
||
|
||
} /* while */
|
||
fclose(fp);
|
||
if (fp_base != NULL)
|
||
fclose(fp_base);
|
||
}
|
||
|
||
int main(int argc, char *argv[])
|
||
{
|
||
char *outname = NULL;
|
||
FILE *fp;
|
||
FILE *input_file;
|
||
char *input_file_name = NULL;
|
||
char *path_prefix = NULL;
|
||
int index;
|
||
int process_self = 0;
|
||
int verbose = 0;
|
||
int max_build, accum_build;
|
||
int max_year, max_month, max_day;
|
||
int ismodified, filemodified;
|
||
char prefix[MAX_SYMBOLLENGTH], suffix[MAX_SYMBOLLENGTH];
|
||
char modified_suffix[2];
|
||
int write_java = 0; /* flag for Java output, 0=.h output, 1=.java output */
|
||
/* java package to put revision info in.
|
||
* REVIEW - I assume if you want Java output you will specify a package. */
|
||
char *java_package = NULL;
|
||
|
||
if (argc <= 1)
|
||
about();
|
||
|
||
/* collect the options */
|
||
prefix[0] = '\0';
|
||
suffix[0] = '\0';
|
||
|
||
for (index = 1; index < argc; index++) {
|
||
/* check for options */
|
||
if (argv[index][0] == '-'
|
||
#if defined __WIN32__ || defined _Win32 || defined _WIN32
|
||
|| argv[index][0] == '/'
|
||
#endif
|
||
)
|
||
{
|
||
switch (argv[index][1]) {
|
||
case 'f': {
|
||
int len;
|
||
char *ptr = strchr(&argv[index][2], '#');
|
||
len = (ptr != NULL) ? (int)(ptr - &argv[index][2]) : (int)strlen(&argv[index][2]);
|
||
if (len >= MAX_SYMBOLLENGTH)
|
||
len = MAX_SYMBOLLENGTH - 1;
|
||
strncpy(prefix, &argv[index][2], len);
|
||
prefix[len] = '\0';
|
||
ptr = (ptr != NULL) ? ptr + 1 : strchr(argv[index], '\0');
|
||
len = (int)strlen(ptr);
|
||
if (len >= MAX_SYMBOLLENGTH)
|
||
len = MAX_SYMBOLLENGTH - 1;
|
||
strncpy(suffix, ptr, len);
|
||
suffix[len] = '\0';
|
||
break;
|
||
} /* case */
|
||
case 'i':
|
||
process_self = 1;
|
||
break;
|
||
case 'j':
|
||
write_java=1;
|
||
java_package = &argv[index][2];
|
||
break;
|
||
case 'o':
|
||
outname = &argv[index][2];
|
||
break;
|
||
case 'r':
|
||
input_file_name = &argv[index][2];
|
||
break;
|
||
case 'p':
|
||
path_prefix = &argv[index][2];
|
||
break;
|
||
case 'v':
|
||
verbose = 1;
|
||
break;
|
||
default:
|
||
fprintf(stderr, "Invalid option '%s'\n", argv[index]);
|
||
about();
|
||
} /* switch */
|
||
} /* if */
|
||
} /* for */
|
||
|
||
if (outname == NULL)
|
||
outname = write_java ? "SvnRevision.java" : "uni_revision.h";
|
||
if (!process_self && *outname != '\0')
|
||
remove(outname);
|
||
|
||
/* phase 1: scan through all files and get the highest build number */
|
||
|
||
max_build = 0;
|
||
accum_build = 0; /* for RCS / CVS */
|
||
max_year = max_month = max_day = 0;
|
||
ismodified = 0;
|
||
|
||
if(input_file_name) {
|
||
input_file = fopen(input_file_name, "r");
|
||
if (input_file != NULL) {
|
||
apr_dir_t *dir;
|
||
apr_finfo_t finfo;
|
||
apr_status_t rv;
|
||
apr_pool_t *pool;
|
||
char *file_path;
|
||
char dir_path[256]; /* line */
|
||
int offset = 0;
|
||
if(path_prefix)
|
||
offset = sprintf(dir_path, "%s", path_prefix);
|
||
else
|
||
offset = sprintf(dir_path, "../../");
|
||
|
||
apr_initialize();
|
||
apr_pool_create(&pool,NULL);
|
||
while (fgets(dir_path + offset, sizeof(dir_path) - offset, input_file) != NULL ) { /* read a line */
|
||
size_t len = strlen(dir_path)-1;
|
||
if(dir_path[len] == '\n')
|
||
dir_path[len] = 0;
|
||
rv = apr_dir_open(&dir,dir_path,pool);
|
||
if(rv == APR_SUCCESS) {
|
||
while (apr_dir_read(&finfo, APR_FINFO_NAME, dir) == APR_SUCCESS) { /* get next file */
|
||
if(finfo.filetype != APR_REG) continue;
|
||
|
||
apr_filepath_merge(&file_path,dir_path,finfo.name,0,pool);
|
||
|
||
filemodified = 0;
|
||
if (strcasecmp(file_path, outname)!=0)
|
||
processfile(file_path, 0, &max_build, &accum_build, &max_year, &max_month, &max_day, &filemodified);
|
||
if (filemodified && verbose)
|
||
fprintf(stderr, "\tNotice: modified file '%s'\n", file_path);
|
||
ismodified = ismodified || filemodified;
|
||
}
|
||
apr_dir_close(dir);
|
||
}
|
||
else {
|
||
fprintf(stderr, "No such directory '%s'\n", dir_path);
|
||
}
|
||
}
|
||
fclose (input_file);
|
||
apr_pool_destroy(pool);
|
||
apr_terminate();
|
||
}
|
||
else {
|
||
fprintf(stderr, "No such input file '%s'\n", input_file_name);
|
||
}
|
||
}
|
||
else {
|
||
for (index = 1; index < argc; index++) {
|
||
/* skip the options (already handled) */
|
||
if (argv[index][0] == '-'
|
||
#if defined __WIN32__ || defined _Win32 || defined _WIN32
|
||
|| argv[index][0] == '/'
|
||
#endif
|
||
)
|
||
continue;
|
||
|
||
filemodified = 0;
|
||
if (strcasecmp(argv[index], outname)!=0)
|
||
processfile(argv[index], 0, &max_build, &accum_build, &max_year, &max_month, &max_day, &filemodified);
|
||
if (filemodified && verbose)
|
||
fprintf(stderr, "\tNotice: modified file '%s'\n", argv[index]);
|
||
ismodified = ismodified || filemodified;
|
||
} /* for */
|
||
}
|
||
|
||
/* also run over the existing header file, if any */
|
||
if (process_self && *outname != '\0')
|
||
processfile(outname, 1, &max_build, &accum_build, &max_year, &max_month, &max_day, NULL/*&ismodified*/);
|
||
|
||
if (accum_build > max_build)
|
||
max_build = accum_build;
|
||
modified_suffix[0] = ismodified ? 'M' : '\0';
|
||
modified_suffix[1] = '\0';
|
||
|
||
/* phase 2: write a file with this highest build number */
|
||
if (*outname == '\0') {
|
||
fp = stdout;
|
||
} else if ((fp = fopen(outname, "w")) == NULL) {
|
||
fprintf(stderr, "Failed to create output file '%s'\n", outname);
|
||
return 2;
|
||
} /* if */
|
||
if (*outname != '\0') {
|
||
/* don't print the comments to stdout */
|
||
fprintf(fp, "/* This file was generated by the \"svnrev\" utility\n"
|
||
" * (http://www.compuphase.com/svnrev.htm).\n"
|
||
" * You should not modify it manually, as it may be re-generated.\n"
|
||
" *\n"
|
||
" * $Revision: %d%s$\n"
|
||
" * $Date: %04d-%02d-%02d$\n"
|
||
" */\n\n", max_build, modified_suffix, max_year, max_month, max_day);
|
||
} /* if */
|
||
|
||
fprintf(fp, "#ifndef UNI_REVISION_H\n");
|
||
fprintf(fp, "#define UNI_REVISION_H\n\n");
|
||
fprintf(fp, "#define UNI_REVISION\t\t%d\n", max_build);
|
||
fprintf(fp, "#define UNI_REVISION_STRING\t\"%s%d%s%s\"\n", prefix, max_build, modified_suffix, suffix);
|
||
fprintf(fp, "#define UNI_REVISION_DATE\t\"%04d-%02d-%02d\"\n", max_year, max_month, max_day);
|
||
fprintf(fp, "#define UNI_REVISION_STAMP\t%04d%02d%02dL\n", max_year, max_month, max_day);
|
||
fprintf(fp, "#define UNI_REVISION_MODIFIED\t%d\n", ismodified);
|
||
fprintf(fp, "\n#endif /* UNI_REVISION_H */\n");
|
||
|
||
if (*outname != '\0')
|
||
fclose(fp);
|
||
|
||
return 0;
|
||
}
|