[77] in Kerberos_V5_Development
Re: version tags
raeburn@ATHENA.MIT.EDU (raeburn@ATHENA.MIT.EDU)
Wed Jan 17 23:20:14 1990
Okay, here's my proposal, off the top of my head:
/* contents of this structure not known to appl programmer */
typedef struct {
char krb5_version_tag_txt[8];
} krb5_version_tag;
/* function/macro; returns 0 on success, non-zero on failure */
extern int krb5_version_tag_check (krb5_version_tag *,
krb5_version_tag *);
Version tags should be used for most structures, and should be the
first component of those structures. The only possible exceptions I
see would be:
krb5_data
any belonging solely to a cryptosystem
(i.e., not part of the interface)
All data structures, entry-point vectors, operations tables, etc,
should have version tags. (Do you really want me to go through and
defined them all? It would seem just as easy left up to the
implementors of those routines.)
Version tag values should be defined (with related symbolic names) in
the library files. The eight-character sequences need not be
null-terminated.
The possibility of krb5_version_tag_check being a macro implies that
it should not be invoked with arguments that have side effects. I
would recommend making a function by this name even if there is a
macro that does the same thing. (A macro isn't addressable, or
callable via a pointer. Seems like a good policy to provide functions
to back up macros just in case....)
If there is a library function which will be needed for absolutely any
manipulation of an object type, the current tag may be defined in the
same file as that function; otherwise, it should be in a separate
file.
Library functions which return a data structure (or array of
structures) which the user is expected to examine must take the
version tag as an argument; functions which fill in an allocated
structure should require that the tag of the structure be filled in
before the routine is called. An error code should be allocated
(perhaps one for the entire set of libraries would do) which says
"incorrect version number specified" or "version not supported".
Outdated version tags must still be defined until such time as we have
determined that the library is free of any occurrence of the older
version. After we release the code, outdated tags must remain until
we are incapable of supporting those versions of the structures.
I would suggest a simple naming scheme be followed consistently, at
least for starters. Something like:
struct _krb5_ccache {
krb5_version_tag tag;
/* ... */
};
extern krb5_version_tag krb5_ccache_version_1; /* old */
extern krb5_version_tag krb5_ccache_version_2;
/* in the .c file */
krb5_version_tag krb5_ccache_version_1 = { "ccache01" };
krb5_version_tag krb5_ccache_version_2 = { "ccache02" };
The actual text is immaterial, as long as it is fairly distinct and
helps to identify the structure.
++++++++++++++++
An initial implementation of krb5_version_tag_check:
/* foo.h */
typedef struct {
char krb5_version_tag_txt[8];
} krb5_version_tag;
extern int krb5_version_tag_check (krb5_version_tag *,
krb5_version_tag *);
#define krb5_version_tag_check(T1,T2) \
(bcmp (T1,T2,sizeof(krb5_version_tag)))
/* foo.c */
#include "foo.h"
#undef krb5_version_tag_check
int krb5_version_tag_check (tag1, tag2)
krb5_version_tag *tag1, *tag2;
{
return bcmp (tag1, tag2, sizeof (krb5_version_tag));
}
I doubt an actual character-by-character comparison will be needed for
many systems. If the bcmp version proves to be too slow, we could try
something like:
#define krb5_version_tag_check(T1,T2) \
(((long *) T1)[0] == ((long *) T2)[0]
&& ((long *) T1) [1] == ((long *) T2) [1])
... but this would have to be system-specific. For particularly
intelligent compilers, we could also make use of a built-in bcmp or
memcmp, if there is one. For gcc on a vax, we could probably use
casts to "long long" and only one comparison. Some systems might
permit use of "double", but that's very risky. (Don't want version
checks to generate FP faults. :)