/* Copyright (C) 1995-2020, The AROS Development Team. All rights reserved. Desc: Version CLI command */ /****************************************************************************** NAME Version [] [] [] [FILE] [FULL] [RES] SYNOPSIS NAME/M,MD5SUM/S,VERSION/N,REVISION/N,FILE/S,FULL/S,RES/S,ARCH/S LOCATION C: FUNCTION Prints or checks the version and revision information of a file, library or device. INPUTS NAME -- name of file, library or device to check. If not given it prints version and revision of Kickstart. MD5SUM -- message-digest computation VERSION -- checks for version and returns error code 5 (warn) if the version of the file is lower. REVISION -- checks for revision and returns error code 5 (warn) if the revision of the file is lower. FILE -- reads from file and ignores currently loaded libraries and devices FULL -- prints additional information RES -- gets version of resident commands ARCH -- displays architecture information about a file RESULT NOTES If the tag contains a trailing space and dollar sign, you may use the Unix command ident. EXAMPLE BUGS SEE ALSO INTERNALS HISTORY ******************************************************************************/ #define ELF_32BIT /* This is needed for correct .ARM.Attributes parsing */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__AROSPLATFORM_SMP__) #include #include #include #endif /*===[md5.h]==============================================================*/ /* Data structure for MD5 (Message-Digest) computation */ typedef struct { ULONG buf[4]; /* scratch buffer */ ULONG i[2]; /* number of _bits_ handled mod 2^64 */ unsigned char in[64]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX *mdContext); void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); void MD5Final(unsigned char digest[16], MD5_CTX *mdContext); /*==[md5.c]===============================================================*/ /* * Used in 'version' as is, just removed the #include "ambient.h" * * Harry Sintonen */ /* * Ambient - the ultimate desktop * ------------------------------ * (c) 2001-2003 by David Gerber * All Rights Reserved * * $Id$ */ //#include "ambient.h" //#include /* *********************************************************************** ** md5.c -- the source code for MD5 routines ** ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** ** Created: 2/17/90 RLR ** ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** *********************************************************************** */ /* * Edited 7 May 93 by CP to change the interface to match that * of the MD5 routines in RSAREF. Due to this alteration, this * code is "derived from the RSA Data Security, Inc. MD5 Message- * Digest Algorithm". (See below.) */ /* *********************************************************************** ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** ** ** ** License to copy and use this software is granted provided that ** ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** ** Digest Algorithm" in all material mentioning or referencing this ** ** software or this function. ** ** ** ** License is also granted to make and use derivative works ** ** provided that such works are identified as "derived from the RSA ** ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** ** material mentioning or referencing the derived work. ** ** ** ** RSA Data Security, Inc. makes no representations concerning ** ** either the merchantability of this software or the suitability ** ** of this software for any particular purpose. It is provided "as ** ** is" without express or implied warranty of any kind. ** ** ** ** These notices must be retained in any copies of any part of this ** ** documentation and/or software. ** *********************************************************************** */ //#include "md5.h" /* *********************************************************************** ** Message-digest routines: ** ** To form the message digest for a message M ** ** (1) Initialize a context buffer mdContext using MD5Init ** ** (2) Call MD5Update on mdContext and M ** ** (3) Call MD5Final on mdContext ** ** The message digest is now in the bugffer passed to MD5Final ** *********************************************************************** */ static unsigned char PADDING[64] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; /* F, G, H and I are basic MD5 functions */ #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits */ #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ /* Rotation is separate from addition to prevent recomputation */ #define FF(a, b, c, d, x, s, ac) \ {(a) += F ((b), (c), (d)) + (x) + (ULONG)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) \ {(a) += G ((b), (c), (d)) + (x) + (ULONG)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) \ {(a) += H ((b), (c), (d)) + (x) + (ULONG)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) \ {(a) += I ((b), (c), (d)) + (x) + (ULONG)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } static void Transform(register ULONG *buf,register ULONG *in); /* The routine MD5Init initializes the message-digest context mdContext. All fields are set to zero. */ void MD5Init ( MD5_CTX *mdContext) { mdContext->i[0] = mdContext->i[1] = (ULONG)0; /* Load magic initialization constants. */ mdContext->buf[0] = (ULONG)0x67452301L; mdContext->buf[1] = (ULONG)0xefcdab89L; mdContext->buf[2] = (ULONG)0x98badcfeL; mdContext->buf[3] = (ULONG)0x10325476L; } /* The routine MD5Update updates the message-digest context to account for the presence of each of the characters inBuf[0..inLen-1] in the message whose digest is being computed. */ void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) { register int i, ii; int mdi; ULONG in[16]; /* compute number of bytes mod 64 */ mdi = (int)((mdContext->i[0] >> 3) & 0x3F); /* update number of bits */ if ((mdContext->i[0] + ((ULONG)inLen << 3)) < mdContext->i[0]) mdContext->i[1]++; mdContext->i[0] += ((ULONG)inLen << 3); mdContext->i[1] += ((ULONG)inLen >> 29); while (inLen--) { /* add new character to buffer, increment mdi */ mdContext->in[mdi++] = *inBuf++; /* transform if necessary */ if (mdi == 0x40) { for (i = 0, ii = 0; i < 16; i++, ii += 4) in[i] = (((ULONG)mdContext->in[ii+3]) << 24) | (((ULONG)mdContext->in[ii+2]) << 16) | (((ULONG)mdContext->in[ii+1]) << 8) | ((ULONG)mdContext->in[ii]); Transform (mdContext->buf, in); mdi = 0; } } } /* The routine MD5Final terminates the message-digest computation and ends with the desired message digest in mdContext->digest[0...15]. */ void MD5Final (unsigned char digest[16], MD5_CTX *mdContext) { ULONG in[16]; int mdi; unsigned int i, ii; unsigned int padLen; /* save number of bits */ in[14] = mdContext->i[0]; in[15] = mdContext->i[1]; /* compute number of bytes mod 64 */ mdi = (int)((mdContext->i[0] >> 3) & 0x3F); /* pad out to 56 mod 64 */ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); MD5Update (mdContext, PADDING, padLen); /* append length in bits and transform */ for (i = 0, ii = 0; i < 14; i++, ii += 4) in[i] = (((ULONG)mdContext->in[ii+3]) << 24) | (((ULONG)mdContext->in[ii+2]) << 16) | (((ULONG)mdContext->in[ii+1]) << 8) | ((ULONG)mdContext->in[ii]); Transform (mdContext->buf, in); /* store buffer in digest */ for (i = 0, ii = 0; i < 4; i++, ii += 4) { digest[ii] = (unsigned char) (mdContext->buf[i] & 0xFF); digest[ii+1] = (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); digest[ii+2] = (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); digest[ii+3] = (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); } } /* Basic MD5 step. Transforms buf based on in. Note that if the Mysterious Constants are arranged backwards in little-endian order and decrypted with the DES they produce OCCULT MESSAGES! */ void Transform(register ULONG *buf,register ULONG *in) { register ULONG a = buf[0], b = buf[1], c = buf[2], d = buf[3]; /* Round 1 */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 FF ( a, b, c, d, in[ 0], S11, 0xD76AA478L); /* 1 */ FF ( d, a, b, c, in[ 1], S12, 0xE8C7B756L); /* 2 */ FF ( c, d, a, b, in[ 2], S13, 0x242070DBL); /* 3 */ FF ( b, c, d, a, in[ 3], S14, 0xC1BDCEEEL); /* 4 */ FF ( a, b, c, d, in[ 4], S11, 0xF57C0FAFL); /* 5 */ FF ( d, a, b, c, in[ 5], S12, 0x4787C62AL); /* 6 */ FF ( c, d, a, b, in[ 6], S13, 0xA8304613L); /* 7 */ FF ( b, c, d, a, in[ 7], S14, 0xFD469501L); /* 8 */ FF ( a, b, c, d, in[ 8], S11, 0x698098D8L); /* 9 */ FF ( d, a, b, c, in[ 9], S12, 0x8B44F7AFL); /* 10 */ FF ( c, d, a, b, in[10], S13, 0xFFFF5BB1L); /* 11 */ FF ( b, c, d, a, in[11], S14, 0x895CD7BEL); /* 12 */ FF ( a, b, c, d, in[12], S11, 0x6B901122L); /* 13 */ FF ( d, a, b, c, in[13], S12, 0xFD987193L); /* 14 */ FF ( c, d, a, b, in[14], S13, 0xA679438EL); /* 15 */ FF ( b, c, d, a, in[15], S14, 0x49B40821L); /* 16 */ /* Round 2 */ #define S21 5 #define S22 9 #define S23 14 #define S24 20 GG ( a, b, c, d, in[ 1], S21, 0xF61E2562L); /* 17 */ GG ( d, a, b, c, in[ 6], S22, 0xC040B340L); /* 18 */ GG ( c, d, a, b, in[11], S23, 0x265E5A51L); /* 19 */ GG ( b, c, d, a, in[ 0], S24, 0xE9B6C7AAL); /* 20 */ GG ( a, b, c, d, in[ 5], S21, 0xD62F105DL); /* 21 */ GG ( d, a, b, c, in[10], S22, 0x02441453L); /* 22 */ GG ( c, d, a, b, in[15], S23, 0xD8A1E681L); /* 23 */ GG ( b, c, d, a, in[ 4], S24, 0xE7D3FBC8L); /* 24 */ GG ( a, b, c, d, in[ 9], S21, 0x21E1CDE6L); /* 25 */ GG ( d, a, b, c, in[14], S22, 0xC33707D6L); /* 26 */ GG ( c, d, a, b, in[ 3], S23, 0xF4D50D87L); /* 27 */ GG ( b, c, d, a, in[ 8], S24, 0x455A14EDL); /* 28 */ GG ( a, b, c, d, in[13], S21, 0xA9E3E905L); /* 29 */ GG ( d, a, b, c, in[ 2], S22, 0xFCEFA3F8L); /* 30 */ GG ( c, d, a, b, in[ 7], S23, 0x676F02D9L); /* 31 */ GG ( b, c, d, a, in[12], S24, 0x8D2A4C8AL); /* 32 */ /* Round 3 */ #define S31 4 #define S32 11 #define S33 16 #define S34 23 HH ( a, b, c, d, in[ 5], S31, 0xFFFA3942L); /* 33 */ HH ( d, a, b, c, in[ 8], S32, 0x8771F681L); /* 34 */ HH ( c, d, a, b, in[11], S33, 0x6D9D6122L); /* 35 */ HH ( b, c, d, a, in[14], S34, 0xFDE5380CL); /* 36 */ HH ( a, b, c, d, in[ 1], S31, 0xA4BEEA44L); /* 37 */ HH ( d, a, b, c, in[ 4], S32, 0x4BDECFA9L); /* 38 */ HH ( c, d, a, b, in[ 7], S33, 0xF6BB4B60L); /* 39 */ HH ( b, c, d, a, in[10], S34, 0xBEBFBC70L); /* 40 */ HH ( a, b, c, d, in[13], S31, 0x289B7EC6L); /* 41 */ HH ( d, a, b, c, in[ 0], S32, 0xEAA127FAL); /* 42 */ HH ( c, d, a, b, in[ 3], S33, 0xD4EF3085L); /* 43 */ HH ( b, c, d, a, in[ 6], S34, 0x04881D05L); /* 44 */ HH ( a, b, c, d, in[ 9], S31, 0xD9D4D039L); /* 45 */ HH ( d, a, b, c, in[12], S32, 0xE6DB99E5L); /* 46 */ HH ( c, d, a, b, in[15], S33, 0x1FA27CF8L); /* 47 */ HH ( b, c, d, a, in[ 2], S34, 0xC4AC5665L); /* 48 */ /* Round 4 */ #define S41 6 #define S42 10 #define S43 15 #define S44 21 II ( a, b, c, d, in[ 0], S41, 0xF4292244L); /* 49 */ II ( d, a, b, c, in[ 7], S42, 0x432AFF97L); /* 50 */ II ( c, d, a, b, in[14], S43, 0xAB9423A7L); /* 51 */ II ( b, c, d, a, in[ 5], S44, 0xFC93A039L); /* 52 */ II ( a, b, c, d, in[12], S41, 0x655B59C3L); /* 53 */ II ( d, a, b, c, in[ 3], S42, 0x8F0CCC92L); /* 54 */ II ( c, d, a, b, in[10], S43, 0xFFEFF47DL); /* 55 */ II ( b, c, d, a, in[ 1], S44, 0x85845DD1L); /* 56 */ II ( a, b, c, d, in[ 8], S41, 0x6FA87E4FL); /* 57 */ II ( d, a, b, c, in[15], S42, 0xFE2CE6E0L); /* 58 */ II ( c, d, a, b, in[ 6], S43, 0xA3014314L); /* 59 */ II ( b, c, d, a, in[13], S44, 0x4E0811A1L); /* 60 */ II ( a, b, c, d, in[ 4], S41, 0xF7537E82L); /* 61 */ II ( d, a, b, c, in[11], S42, 0xBD3AF235L); /* 62 */ II ( c, d, a, b, in[ 2], S43, 0x2AD7D2BBL); /* 63 */ II ( b, c, d, a, in[ 9], S44, 0xEB86D391L); /* 64 */ buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } /*==[end md5.c]============================================================*/ const TEXT version[] = "$VER: Version 42.3 (20.11.2015)\n"; const TEXT ver[] = "$VER:"; static const char ERROR_HEADER[] = "Version"; #define TEMPLATE "NAME/M,MD5SUM/S,VERSION/N,REVISION/N,FILE/S,FULL/S,RES/S,ARCH/S" struct { CONST_STRPTR *arg_name; IPTR arg_md5sum; LONG *arg_version; LONG *arg_revision; IPTR arg_file; IPTR arg_full; IPTR arg_res; IPTR arg_arch; } args; LONG mversion, mrevision; struct { STRPTR pv_name; ULONG pv_flags; LONG pv_version; LONG pv_revision; UWORD pv_arch; UBYTE pv_arm_cpu; UBYTE pv_arm_fpu; //LONG pv_days; STRPTR pv_vername; STRPTR pv_revname; STRPTR pv_datestr; STRPTR pv_extralf; STRPTR pv_extrastr; UBYTE pv_md5sum[16]; } parsedver = { NULL, 0, 0, 0, 0, -1, 0, NULL, NULL, NULL, NULL, NULL, {0}}; #define PVF_MD5SUM (1 << 0) #define PVF_NOVERSION (1 << 1) static int makeverstring(CONST_STRPTR name); static void printverstring(void); static void freeverstring(void); static int makesysver(void); static int cmpargsparsed(void); /**************************** support functions ************************/ /* Duplicate string, by given length or -1 for full length */ static STRPTR dupstr(CONST_STRPTR buffer, LONG len) { STRPTR ret = NULL; if (buffer) { if (len == -1) len = strlen(buffer); ret = AllocVec(len + 1, MEMF_ANY); if (ret) { CopyMem((STRPTR) buffer, ret, len); ret[len] = '\0'; } } return ret; } static inline int myisspace(int c) { return (c == ' ' || c == '\t'); } /* Return a pointer to a string, stripped by all leading whitespace characters * (SPACE, TAB). */ static STRPTR skipwhites(CONST_STRPTR buffer) { for (;; buffer++) { if (buffer[0] == '\0' || !isspace(buffer[0])) { return (STRPTR) buffer; } } } /* Return a pointer to a string, stripped by all leading space characters * (SPACE). */ static STRPTR skipspaces(CONST_STRPTR buffer) { for (;; buffer++) { if (buffer[0] == '\0' || buffer[0] != ' ') { return (STRPTR) buffer; } } } /* Strip all whitespace-characters from the end of a string. Note that the * buffer passed in will be modified! */ static void stripwhites(STRPTR buffer) { int len = strlen(buffer); while (len > 0) { if (!isspace(buffer[len-1])) { buffer[len] = '\0'; return; } len--; } buffer[len] = '\0'; } /* Searches for a given string in a file and stores up to *lenptr characters * into the buffer beginning with the first character after the given string. */ static int findinfile(BPTR file, CONST_STRPTR string, STRPTR buffer, int *lenptr, unsigned char digest[16]) { int error = RETURN_OK; int buflen = *lenptr, len = 0, pos, stringlen; BOOL ready = FALSE; MD5_CTX md5ctx; STRPTR bufpos; STRPTR tmp; tmp = AllocMem(buflen, MEMF_PUBLIC); if (!tmp) { return RETURN_FAIL; } stringlen = strlen(string); *lenptr = -1; if (args.arg_md5sum) { MD5Init(&md5ctx); } bufpos = tmp; while ((len = Read(file, &tmp[len], buflen - len)) > 0) { pos = 0; if (args.arg_md5sum) { MD5Update(&md5ctx, bufpos, len); } if (ready) { /* If we get here we're scanning the rest of the file for md5sum. - Piru */ len = 0; } else { while ((len - pos) >= stringlen) { /* Compare the current buffer position with the supplied string. */ if (strncmp(&tmp[pos], string, stringlen) == 0) { /* It is equal! Now move the rest of the buffer to the top of * the buffer and fill it up. */ int findstrlen = len - pos; memcpy(buffer, &tmp[pos + stringlen], findstrlen); len = Read(file, &buffer[findstrlen], buflen - findstrlen); if (len >= 0) { if (args.arg_md5sum) { MD5Update(&md5ctx, &buffer[findstrlen], len); } *lenptr = findstrlen + len; } else { error = RETURN_FAIL; } ready = TRUE; break; } pos++; } /* Move the rest of the buffer that could not be compared (because it * is smaller than the string to compare) to the top of the buffer. */ if (!ready) { memmove(tmp, &tmp[len - stringlen], stringlen); } else { /* If we're not md5summing, stop file scanning now. - Piru */ if (!args.arg_md5sum) { break; } } len = stringlen; } bufpos = &tmp[len]; } FreeMem(tmp, buflen); if (len == -1) { error = RETURN_FAIL; } if (args.arg_md5sum) { SetMem(digest, 0, 16); MD5Final(digest, &md5ctx); } return error; } /*************************** parsing functions *************************/ /* Convert a date in the form DD.MM.YY or DD.MM.YYYY into a numerical * value. Return FALSE, if buffer doesn't contain a valid date. */ static BOOL makedatefromstring(CONST_STRPTR *bufptr) { CONST_STRPTR buffer = *bufptr; struct DateTime dt; CONST_STRPTR headerstart, end; STRPTR newbuf; LONG res; int len, i; UBYTE c; //if (isspace(buffer[0])) // buffer++; headerstart = buffer; buffer = strchr(buffer, '('); if (!buffer) { return FALSE; } buffer++; end = strchr(buffer, ')'); if (!end) { return FALSE; } len = (int)(end - buffer); newbuf = dupstr(buffer, len); if (!newbuf) { return FALSE; } for (i = 0; i < len; i++) { c = newbuf[i]; if (c == '.' || c == '/') newbuf[i] = '-'; else if (!isalnum(c)) { end = buffer + i; newbuf[i] = '\0'; break; } } D(Printf("date: \"%s\"\n", newbuf)); dt.dat_Format = FORMAT_CDN; dt.dat_Flags = 0; dt.dat_StrDay = NULL; dt.dat_StrDate = newbuf; dt.dat_StrTime = NULL; res = StrToDate(&dt); if (!res) { dt.dat_Format = FORMAT_DOS; res = StrToDate(&dt); if (!res) { //Printf("StrToDate failed!\n"); FreeVec(newbuf); return FALSE; } } FreeVec(newbuf); //parsedver.pv_days = dt.dat_Stamp.ds_Days; parsedver.pv_datestr = AllocVec(buffer - headerstart + LEN_DATSTRING + 2, MEMF_ANY); if (!parsedver.pv_datestr) { return FALSE; } dt.dat_Stamp.ds_Minute = 0; dt.dat_Stamp.ds_Tick = 0; dt.dat_StrDate = parsedver.pv_datestr + (buffer - headerstart); dt.dat_Format = FORMAT_DEF; if (!DateToStr(&dt)) { //Printf("DateToStr failed!\n"); return FALSE; } CopyMem((STRPTR) headerstart, parsedver.pv_datestr, buffer - headerstart); res = strlen(parsedver.pv_datestr); parsedver.pv_datestr[res++] = ')'; parsedver.pv_datestr[res] = '\0'; *bufptr = end + 1; return TRUE; } /* Check whether the given string contains a version in the form * . . If not return FALSE, otherwise fill in parsedver and * return TRUE. */ static BOOL makeversionfromstring(CONST_STRPTR *bufptr) { LONG pos, ver, rev; CONST_STRPTR buffer = *bufptr; CONST_STRPTR verstart, revstart; //Printf("makeversionfromstring: buffer \"%s\"\n", (LONG) buffer); /* Do version */ verstart = buffer; pos = StrToLong((STRPTR) buffer, &ver); if (pos == -1) { return FALSE; } parsedver.pv_version = ver; parsedver.pv_revision = -1; parsedver.pv_vername = dupstr(verstart, pos); if (!parsedver.pv_vername) { return FALSE; } /* Do revision */ buffer += pos; revstart = buffer; buffer = skipspaces(buffer); /* NOTE: skipspaces, not skipwhites! */ if (*buffer != '.') { *bufptr = buffer; return TRUE; } buffer++; pos = StrToLong((STRPTR) buffer, &rev); if (pos == -1) { *bufptr = buffer; return TRUE; } parsedver.pv_revision = rev; /* calc the revision string len */ pos = buffer + pos - revstart; parsedver.pv_revname = dupstr(revstart, pos); if (!parsedver.pv_revname) { return FALSE; } *bufptr = revstart + pos; return TRUE; } static const char *arm_cpus[] = { "ARM Pre-v4", "ARMv4", "ARMv4T", "ARMv5T", "ARMv5TE", "ARMv5TEJ", "ARMv6", "ARMv6KZ", "ARMv6T2", "ARMv6K", "ARMv7", "ARMv6-M", "ARMv6S-M", "ARMv7E-M" }; static const char *arm_fpus[] = { "VFP", "VFPv2", "VFPv3", "VFPv3 (D0 - D15)", "VFPv4", "VFPv4 (D0 - D15)" }; static void printverstring(void) { if (args.arg_md5sum) { if (parsedver.pv_flags & PVF_MD5SUM) { /* Endianess safe version */ Printf("%02lX%02lX%02lX%02lX" "%02lX%02lX%02lX%02lX" "%02lX%02lX%02lX%02lX" "%02lX%02lX%02lX%02lX ", parsedver.pv_md5sum[0], parsedver.pv_md5sum[1], parsedver.pv_md5sum[2], parsedver.pv_md5sum[3], parsedver.pv_md5sum[4], parsedver.pv_md5sum[5], parsedver.pv_md5sum[6], parsedver.pv_md5sum[7], parsedver.pv_md5sum[8], parsedver.pv_md5sum[9], parsedver.pv_md5sum[10], parsedver.pv_md5sum[11], parsedver.pv_md5sum[12], parsedver.pv_md5sum[13], parsedver.pv_md5sum[14], parsedver.pv_md5sum[15]); } else { /* "01234567012345670123456701234567: " */ PutStr(" "); } } if (parsedver.pv_flags & PVF_NOVERSION) { Printf("%s\n", (IPTR) parsedver.pv_name); } else { if (args.arg_full) { /* If md5sum output was there, avoid linefeed to allow parsing the output - Piru */ if (args.arg_md5sum) { parsedver.pv_extralf = " "; } Printf("%s%s%s%s%s%s%s\n", (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""), (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname, (IPTR) (parsedver.pv_datestr ? (IPTR)parsedver.pv_datestr : (IPTR)""), (IPTR) (parsedver.pv_extralf ? (IPTR)parsedver.pv_extralf : (IPTR)""), (IPTR) (parsedver.pv_extrastr ? (IPTR)parsedver.pv_extrastr : (IPTR)"")); } else { Printf("%s%s%s%s\n", (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""), (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname); } if (args.arg_arch) { const char *arch; if (parsedver.pv_arch == EM_ARM) { Printf("Architecture: "); if (parsedver.pv_arm_cpu == (UBYTE)-1) Printf("ARM (unspecified)"); else if (parsedver.pv_arm_cpu <= ELF_CPU_ARMv7EM) Printf(arm_cpus[parsedver.pv_arm_cpu]); else Printf("Unknown ARM (%d)", parsedver.pv_arm_cpu); if (parsedver.pv_arm_fpu > 6) Printf(" Unknown FPU (%d)", parsedver.pv_arm_fpu); else if (parsedver.pv_arm_fpu) Printf(" %s", arm_fpus[parsedver.pv_arm_fpu]); Printf("\n"); } else { switch (parsedver.pv_arch) { case 0: /* No information available */ arch = NULL; break; case EM_386: arch = "I386"; break; case EM_68K: arch = "M68k"; break; case EM_PPC: arch = "PowerPC"; break; case EM_X86_64: arch = "X86-64"; break; default: arch = "Unknown"; break; } if (arch) Printf("Architecture: %s\n", arch); } } } } static int makedata(CONST_STRPTR buffer, CONST_STRPTR ptr, int pos) { D(Printf("makedata: buffer \"%s\" ptr \"%s\"\n", buffer, ptr)); if (makeversionfromstring(&ptr)) { CONST_STRPTR endp; BOOL doskip; /* It is! */ /* Copy the program-name into a buffer. */ parsedver.pv_name = dupstr(buffer, pos); if (!parsedver.pv_name) { PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER); return RETURN_FAIL; } /* Now find the date */ D(Printf("makedata: ptr #1: \"%s\"\n", ptr)); doskip = strchr(ptr, '(') ? TRUE : FALSE; (void) makedatefromstring(&ptr); D(Printf("makedata: ptr #2: \"%s\"\n", ptr)); if (doskip) ptr = skipspaces(ptr); /* NOTE: not skipwhites! */ for (endp = ptr; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++) ; pos = endp - ptr; if (pos && ptr[pos-1] == '$') pos--; if (pos) { parsedver.pv_extrastr = dupstr(ptr, pos); if (!parsedver.pv_extrastr) { PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER); return RETURN_FAIL; } D(Printf("makedata: Extra string: %s\n", parsedver.pv_extrastr)); if (doskip) parsedver.pv_extralf = "\n"; } return 1; } return 0; } /* Retrieves version information from string. The data is stored in the * global struct parsedver.pv_ */ static int makedatafromstring(CONST_STRPTR buffer) { int error = RETURN_OK; int pos = 0; LONG add, dummy; while (buffer[pos] && buffer[pos] != '\r' && buffer[pos] != '\n') { /* NOTE: Not isspace()! - Piru */ if (myisspace(buffer[pos]) && (add = StrToLong((STRPTR) buffer + pos + 1, &dummy)) != -1) { CONST_STRPTR ptr; /* Found something, which looks like a version. Now check, if it * really is. */ D(Printf("makedatafromstring: buffer + %ld: \"%s\"\n", pos, buffer + pos)); ptr = buffer + pos + 1; if (makedata(buffer, ptr, pos)) { break; } } pos++; } if (!buffer[pos] || buffer[pos] == '\r' || buffer[pos] == '\n') { CONST_STRPTR endp; /* use the whatever is after ver tag as name */ for (endp = buffer; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++) ; pos = endp - buffer; parsedver.pv_name = dupstr(buffer, pos); if (!parsedver.pv_name) { PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER); return RETURN_FAIL; } } /* Strip any whitespaces from the tail of the program-name. */ if (parsedver.pv_name) { stripwhites(parsedver.pv_name); } return error; } /* Case-insensitive FindResident() */ static struct Resident *findresident(CONST_STRPTR name) { struct Resident **rp; struct Resident *resident; rp = (struct Resident **) SysBase->ResModules; while ((resident = *rp++)) { #ifdef __mc68000__ if (((LONG)resident) < 0) rp = (struct Resident **)((ULONG)resident & 0x7fffffff); #else if (((IPTR)resident) & 0x01) rp = (struct Resident **)((IPTR)resident & ~1); #endif else { if (!Stricmp(resident->rt_Name, (STRPTR) name)) break; } } return resident; } /* Case-insensitive FindName() */ static struct Node *findname(struct List *list, CONST_STRPTR name) { struct Node *node; ForeachNode(list, node) { if (!Stricmp(node->ln_Name, (STRPTR) name)) { return node; } } return NULL; } /* Retrieve information from resident modules. Returns 0 for success. */ static int createresidentver(struct Resident *MyResident) { STRPTR buffer = NULL; CONST_STRPTR name = NULL; STRPTR tmpbuffer; int pos = 0; int len = 0; BOOL foundver = FALSE; int error; if (MyResident->rt_IdString) { buffer = skipwhites(MyResident->rt_IdString); D(Printf("createresidentver: buffer \"%s\"\n", buffer)); /* Locate version part */ while (buffer[pos]) { LONG dummy; /* NOTE: Not isspace()! - Piru */ if (myisspace(buffer[pos]) && StrToLong(buffer + pos + 1, &dummy) != -1) { buffer += pos; foundver = TRUE; break; } pos++; } D(Printf("createresidentver: buffer: \"%s\"\n", buffer)); name = MyResident->rt_IdString; len = buffer - name; } /* If could not find any version info, use the resident rt_Name */ if (!foundver) buffer = ""; D(Printf("createresidentver: buffer: \"%s\"\n", buffer)); if ((!args.arg_full) || (!name)) { name = MyResident->rt_Name; len = strlen(MyResident->rt_Name); } tmpbuffer = AllocVec(len + strlen(buffer) + 1, MEMF_ANY); if (!tmpbuffer) { PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER); return RETURN_FAIL; } CopyMem(name, tmpbuffer, len); strcpy(tmpbuffer + len, buffer); D(Printf("createresidentver: tmpbuffer: \"%s\"\n", tmpbuffer)); error = makedatafromstring(tmpbuffer); FreeVec(tmpbuffer); return error; } /* Retrieve version information from library. Returns 0 for success. */ static int createlibraryver(struct Library *MyLibrary) { STRPTR buffer, tmpbuffer; int error, foundver = FALSE, pos; if (MyLibrary->lib_IdString) { //Printf("createlibraryver: lib_IdString \"%s\"\n", (LONG) MyLibrary->lib_IdString); buffer = skipwhites(MyLibrary->lib_IdString); //Printf("createlibraryver: buffer \"%s\"\n", (LONG) buffer); /* Find full 'ver.rev' version info */ pos = 0; while (buffer[pos]) { LONG dummy, add; /* NOTE: Not isspace()! - Piru */ if (myisspace(buffer[pos]) && (add = StrToLong(buffer + pos + 1, &dummy)) != -1 && buffer[pos + 1 + add] == '.') { buffer += pos; pos = 0; foundver = TRUE; break; } pos++; } /* If could not find 'ver.rev', find any numeric */ if (!foundver) { pos = 0; while (buffer[pos]) { LONG dummy; /* NOTE: Not isspace()! - Piru */ if (myisspace(buffer[pos]) && StrToLong(buffer + pos + 1, &dummy) != -1) { buffer += pos; pos = 0; foundver = TRUE; break; } pos++; } } } /* If could not find any version info, use the resident rt_Name */ if (!foundver) { buffer = ""; error = RETURN_WARN; } tmpbuffer = AllocVec(strlen(MyLibrary->lib_Node.ln_Name) + strlen(buffer) + 1, MEMF_ANY); if (!tmpbuffer) { PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER); return RETURN_FAIL; } IPTR lvparams[2]; lvparams[0] = (IPTR)MyLibrary->lib_Node.ln_Name; lvparams[1] = (IPTR)buffer; RawDoFmt("%s%s", (RAWARG)&lvparams, RAWFMTFUNC_STRING, tmpbuffer); pos = makedatafromstring(tmpbuffer); if (pos > error) { error = pos; } FreeVec(tmpbuffer); return error; } /* Create default strings */ static int createdefvers(CONST_STRPTR name) { FreeVec(parsedver.pv_revname); FreeVec(parsedver.pv_vername); FreeVec(parsedver.pv_name); parsedver.pv_name = dupstr(name, -1); parsedver.pv_vername = AllocVec(14, MEMF_ANY); parsedver.pv_revname = AllocVec(15, MEMF_ANY); if (parsedver.pv_name && parsedver.pv_vername && parsedver.pv_revname) { IPTR versparam; versparam = (IPTR)parsedver.pv_version; RawDoFmt("%ld", (RAWARG)&versparam, RAWFMTFUNC_STRING, parsedver.pv_vername); versparam = (IPTR)parsedver.pv_revision; RawDoFmt(".%ld", (RAWARG)&versparam, RAWFMTFUNC_STRING, parsedver.pv_revname); return RETURN_OK; } return RETURN_FAIL; } /* Create version info from named resident */ static int makeresidentver(CONST_STRPTR name) { struct Resident *MyResident; int error = -1; if ((MyResident = findresident(name))) { error = createresidentver(MyResident); if (error != RETURN_OK) { /* get values from residenttag */ parsedver.pv_version = MyResident->rt_Version; parsedver.pv_revision = -1; error = createdefvers(MyResident->rt_Name); } } return error; } /* Create version info from named list node */ static int makeexeclistver(struct List *list, CONST_STRPTR name) { struct Library *MyLibrary; int error = -1; #if defined(__AROSPLATFORM_SMP__) void *ExecLockBase = OpenResource("execlock.resource"); if (ExecLockBase) ObtainSystemLock(list, SPINLOCK_MODE_READ, LOCKF_FORBID); else Forbid(); #else Forbid(); #endif MyLibrary = (struct Library *) findname(list, name); if (MyLibrary) { /* get values from library */ ULONG ver = MyLibrary->lib_Version; ULONG rev = MyLibrary->lib_Revision; error = createlibraryver(MyLibrary); if (error != RETURN_OK || parsedver.pv_version != ver || parsedver.pv_revision != rev) { /* special case where createlibraryrev was successful, but * version or revision don't match. */ if (error == RETURN_OK) { /* If there is extrastr, make sure there's linefeed, too. */ if (parsedver.pv_extrastr) { parsedver.pv_extralf = "\n"; } } /* get values from library */ parsedver.pv_version = ver; parsedver.pv_revision = rev; error = createdefvers(MyLibrary->lib_Node.ln_Name); } } #if defined(__AROSPLATFORM_SMP__) if (ExecLockBase) ReleaseSystemLock(list, LOCKF_FORBID); else Permit(); #else Permit(); #endif return error; } /* Find resident from seglist, return NULL if none found */ static struct Resident *FindLibResident(BPTR Segment) { while (Segment) { IPTR *MySegment; UWORD *MyBuffer; UWORD *EndBuffer; MySegment = (IPTR*) BADDR(Segment); MyBuffer = (UWORD*) &MySegment[1]; EndBuffer = (UWORD*) &MySegment[(MySegment[-1] - sizeof(ULONG) * 4) / sizeof(ULONG)]; while (MyBuffer < EndBuffer) { struct Resident *MyResident; MyResident = (struct Resident*) MyBuffer; if (MyResident->rt_MatchWord == RTC_MATCHWORD && MyResident->rt_MatchTag == MyResident) { return MyResident; } MyBuffer++; } Segment =(BPTR) MySegment[0]; } SetIoErr(ERROR_OBJECT_NOT_FOUND); return NULL; } /* Find $VER: tag from seglist, return NULL if none found Returns dupstr()d string or NULL. */ static STRPTR FindSegmentVER(BPTR Segment) { while (Segment) { void *MySegment; CONST_STRPTR MyBuffer; ULONG BufferLen; CONST_STRPTR EndBuffer; CONST_STRPTR SegmentEnd; MySegment = BADDR(Segment); MyBuffer = (CONST_STRPTR) (MySegment + sizeof(BPTR)); BufferLen = *(ULONG *)(MySegment - sizeof(ULONG)); SegmentEnd = (CONST_STRPTR) (MySegment + (BufferLen - sizeof(BPTR))); EndBuffer = SegmentEnd - 5; while (MyBuffer < EndBuffer) { if (MyBuffer[0] == '$' && MyBuffer[1] == 'V' && MyBuffer[2] == 'E' && MyBuffer[3] == 'R' && MyBuffer[4] == ':') { CONST_STRPTR EndPtr; MyBuffer += 5; /* Required because some smartass could end his $VER: tag * without '\0' in the segment to save space. - Piru */ for (EndPtr = MyBuffer; EndPtr < SegmentEnd && *EndPtr; EndPtr++) ; if (EndPtr - MyBuffer) { return dupstr(MyBuffer, EndPtr - MyBuffer); } } MyBuffer++; } Segment =*(BPTR *)MySegment; } SetIoErr(ERROR_OBJECT_NOT_FOUND); return NULL; } /* Create version info from named device */ static int makedevicever(CONST_STRPTR name) { struct DevProc *MyDevProc; int error = -1; MyDevProc = GetDeviceProc((STRPTR) name, NULL); if (MyDevProc) { if (MyDevProc->dvp_DevNode->dol_Type == DLT_DEVICE) { BPTR SegList; SegList = MyDevProc->dvp_DevNode->dol_misc.dol_handler.dol_SegList; if (SegList) { struct Resident *MyResident; MyResident = FindLibResident(SegList); if (MyResident) { error = createresidentver(MyResident); } else error = RETURN_FAIL; } } FreeDeviceProc(MyDevProc); } if (error != RETURN_OK && error != -1) { Printf("Could not find version information for '%s'\n", name); } return error; } static int elf_read_block(BPTR file, ULONG offset, APTR buffer, ULONG size) { if (Seek(file, offset, OFFSET_BEGINNING) < 0) return 0; return Read(file, buffer, size); } static void *load_block(BPTR file, ULONG offset, ULONG size) { void *block = AllocMem(size, MEMF_ANY); if (block) { if (elf_read_block(file, offset, block, size) == size) return block; FreeMem(block, size); } return NULL; } static inline UWORD elf_read_word(UWORD data, struct elfheader *eh) { switch (eh->ident[EI_DATA]) { case ELFDATA2LSB: return AROS_LE2WORD(data); case ELFDATA2MSB: return AROS_BE2WORD(data); default: return 0; } } static inline ULONG elf_read_long(ULONG data, struct elfheader *eh) { switch (eh->ident[EI_DATA]) { case ELFDATA2LSB: return AROS_LE2LONG(data); case ELFDATA2MSB: return AROS_BE2LONG(data); default: return 0; } } static ULONG read_shnum(BPTR file, struct elfheader *eh) { ULONG shnum = elf_read_word(eh->shnum, eh); /* the ELF header only uses 16 bits to store the count of section headers, * so it can't handle more than 65535 headers. if the count is 0, and an * offset is defined, then the real count can be found in the first * section header (which always exists). * * similarly, if the string table index is SHN_XINDEX, then the actual * index is found in the first section header also. * * see the System V ABI 2001-04-24 draft for more details. */ if (shnum == 0) { struct sheader sh; ULONG shoff = elf_read_long(eh->shoff, eh); if (shoff == 0) return 0; if (elf_read_block(file, shoff, &sh, sizeof(sh)) != sizeof(sh)) return 0; /* wider section header count is in the size field */ shnum = elf_read_long(sh.size, eh); } return shnum; } static BOOL ARM_ParseAttrs(UBYTE *data, ULONG len, struct elfheader *eh) { struct attrs_section *attrs; if (data[0] != ATTR_VERSION_CURRENT) { D(Printf("Unknown attributes version: 0x%02\n", data[0])); return FALSE; } attrs = (void *)data + 1; while (len > 0) { ULONG attrs_size = elf_read_long(attrs->size, eh); if (!strcmp(attrs->vendor, "aeabi")) { struct attrs_subsection *aeabi_attrs = (void *)attrs->vendor + 6; ULONG aeabi_len = attrs_size - 10; D(Printf("Found aeabi attributes @ 0x%p (length %u)\n", aeabi_attrs, aeabi_len)); while (aeabi_len > 0) { ULONG aeabi_attrs_size = elf_read_long(aeabi_attrs->size, eh); if (aeabi_attrs->tag == Tag_File) { UBYTE *file_subsection = (void *)aeabi_attrs + sizeof(struct attrs_subsection); UBYTE file_len = aeabi_attrs_size - sizeof(struct attrs_subsection); D(Printf("Found file-wide attributes @ 0x%p (length %u)\n", file_subsection, file_len)); while (file_len > 0) { UBYTE tag, shift; ULONG val = 0; tag = *file_subsection++; file_len--; if (file_len == 0) { D(Printf("Mailformed attribute tag %d (no data)\n", tag)); return FALSE; } switch (tag) { case Tag_CPU_raw_name: case Tag_CPU_name: case Tag_compatibility: case Tag_also_compatible_with: case Tag_conformance: /* These two are NULL-terminated strings. Just skip. */ while (file_len) { file_len--; if (*file_subsection++ == 0) break; } break; default: /* Read ULEB128 value */ shift = 0; while (file_len) { UBYTE byte; byte = *file_subsection++; file_len--; val |= (byte & 0x7F) << shift; if (!(byte & 0x80)) break; shift += 7; } } switch (tag) { case Tag_CPU_arch: D(Printf("ARM CPU architecture set to %d\n", val)); parsedver.pv_arm_cpu = val; break; case Tag_FP_arch: D(Printf("ARM FPU architecture set to %d\n", val)); parsedver.pv_arm_fpu = val; break; } } return TRUE; } aeabi_attrs = (void *)aeabi_attrs + aeabi_attrs_size; aeabi_len -= aeabi_attrs_size; } return FALSE; } attrs = (void *)attrs + attrs_size; len -= attrs_size; } return FALSE; } static int arm_read_cpudata(BPTR file, struct elfheader *eh) { struct sheader *sh; ULONG int_shnum; ULONG shoff; UWORD shentsize; ULONG i; int_shnum = read_shnum(file, eh); if (!int_shnum) return 0; shoff = elf_read_long(eh->shoff, eh); shentsize = elf_read_word(eh->shentsize, eh); /* load section headers */ if (!(sh = load_block(file, shoff, int_shnum * shentsize))) return 0; for (i = 0; i < int_shnum; i++) { if (sh[i].type == SHT_ARM_ATTRIBUTES) { ULONG off = elf_read_long(sh[i].offset, eh); ULONG len = elf_read_long(sh[i].size, eh); void *data = load_block(file, off, len); D(Printf("ARM ATTRIBUTES section %d loaded at 0x%p\n", i, data)); if (data) { ARM_ParseAttrs(data, len, eh); FreeMem(data, len); } break; } } FreeMem(sh, int_shnum * shentsize); return 1; } /* Retrieve version information from file. Return 0 for success. */ #define BUFFERSIZE (16384 + 1) static int makefilever(CONST_STRPTR name) { BPTR file; int error; // = RETURN_OK; file = Open((STRPTR) name, MODE_OLDFILE); if (file) { UBYTE *buffer; buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC); if (buffer) { int len = BUFFERSIZE - 1; if (args.arg_arch) { ULONG len = Read(file, buffer, sizeof(struct elfheader)); if (len == sizeof(struct elfheader)) { if (buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F') { /* It's an ELF file, read machine ID */ struct elfheader *eh = (struct elfheader *)buffer; parsedver.pv_arch = elf_read_word(eh->machine, eh); if (parsedver.pv_arch == EM_ARM) arm_read_cpudata(file, eh); } } else if (len >= 4) { if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0x03 && buffer[3] == 0xF3) { /* It's AmigaOS hunk file. m68k obviously :) */ parsedver.pv_arch = EM_68K; } } /* Rewind the file */ Seek(file, 0, OFFSET_BEGINNING); } error = findinfile(file, ver, buffer, &len, parsedver.pv_md5sum); if (error == RETURN_OK) { parsedver.pv_flags |= PVF_MD5SUM; if (len >= 0) { STRPTR startbuffer; buffer[len] = '\0'; startbuffer = skipwhites(buffer); //Printf("startbuffer \"%s\"\n", startbuffer); error = makedatafromstring(startbuffer); } else { /* Try LoadSeg */ error = RETURN_ERROR; Close(file); file = LoadSeg((STRPTR) name); if (file) { struct Resident *MyResident; MyResident = FindLibResident(file); if (MyResident /*&& (MyResident->rt_Type == NT_LIBRARY || MyResident->rt_Type == NT_DEVICE)*/) { error = createresidentver(MyResident); } UnLoadSeg(file); } file = BNULL; if (error != RETURN_OK) { /* If user didn't ask for md5sum or we could not calculate it. */ if (!args.arg_md5sum || (!(parsedver.pv_flags & PVF_MD5SUM))) { Printf("Could not find version information for '%s'\n", (IPTR) name); } } } } else { PrintFault(IoErr(), (STRPTR) ERROR_HEADER); } FreeMem(buffer, BUFFERSIZE); } else { error = RETURN_FAIL; PrintFault(IoErr(), (STRPTR) ERROR_HEADER); } if (file) Close(file); } else { LONG ioerr = IoErr(); if (ioerr == ERROR_OBJECT_NOT_FOUND || ioerr == ERROR_OBJECT_WRONG_TYPE) { error = -1; } else { PrintFault(IoErr(), (STRPTR) ERROR_HEADER); error = RETURN_FAIL; } } return error; } static int makerescmdver(CONST_STRPTR name) { int error = -1; struct Segment *segment; Forbid(); segment = FindSegment((STRPTR) name, NULL, 0); if (!segment) { segment = FindSegment((STRPTR) name, NULL, 1); } if (segment) { if (segment->seg_UC == CMD_INTERNAL || segment->seg_UC == CMD_DISABLED) { Permit(); error = makeresidentver("shell"); Forbid(); } else { STRPTR buffer = FindSegmentVER(segment->seg_Seg); if (buffer) { STRPTR startbuffer; startbuffer = skipwhites(buffer); //Printf("startbuffer \"%s\"\n", (LONG) startbuffer); error = makedatafromstring(startbuffer); FreeVec(buffer); } } } Permit(); return error; } static int setvervar(CONST_STRPTR name, LONG ver, LONG rev) { IPTR versparams[2]; UBYTE buf[32]; versparams[0] = (IPTR)ver; versparams[1] = (IPTR)rev; RawDoFmt("%ld.%ld", (RAWARG)&versparams, RAWFMTFUNC_STRING, buf); return SetVar((STRPTR) name, buf, -1, GVF_LOCAL_ONLY | LV_VAR) ? RETURN_OK : -1; } static int makekickversion(void) { parsedver.pv_version = SysBase->LibNode.lib_Version; parsedver.pv_revision = SysBase->SoftVer; setvervar("Kickstart", parsedver.pv_version, parsedver.pv_revision); Printf("Kickstart %ld.%ld", (LONG) parsedver.pv_version, (LONG) parsedver.pv_revision); return RETURN_OK; } static int makewbversion(void) { int error = -1; struct Library *VersionBase; VersionBase = OpenLibrary("version.library", 0); if (VersionBase) { error = makeexeclistver(&SysBase->LibList, "version.library"); if (error == RETURN_OK) { STRPTR newname = dupstr("Workbench", -1); if (newname) { FreeVec(parsedver.pv_name); parsedver.pv_name = newname; } setvervar("Workbench", parsedver.pv_version, parsedver.pv_revision); } CloseLibrary(VersionBase); } return error; } static int makesysver(void) { int error; error = makekickversion(); if (error == RETURN_OK) { error = makewbversion(); if (error == RETURN_OK) { PutStr(", "); } else { /* prevent silly errormsg if no version.library */ PutStr("\n"); error = RETURN_WARN; } } return error; } /* Determine, by which means to get the version-string. */ static int makeverstring(CONST_STRPTR name) { int error; // = RETURN_OK; BOOL volume = name[strlen(name) - 1] == ':'; CONST_STRPTR filepart = FilePart(name); error = -1; if (!volume && !args.arg_file) { if (*filepart) { error = makeresidentver(filepart); if (error != RETURN_OK) { /* Try libraries */ error = makeexeclistver(&SysBase->LibList, filepart); if (error != RETURN_OK) { IPTR fnameparams[2]; STRPTR namebuf; ULONG namelen = strlen(filepart); /* 12 is "MOSSYS:LIBS/" */ if ((namebuf = AllocVec(12 + namelen + 4 + 1, MEMF_PUBLIC))) { fnameparams[0] = (IPTR)"LIBS:"; fnameparams[1] = (IPTR)filepart; RawDoFmt("%s%s", (RAWARG)&fnameparams, RAWFMTFUNC_STRING, namebuf); error = makefilever(namebuf); /* Try devices */ if (error != RETURN_OK) { error = makeexeclistver(&SysBase->DeviceList, filepart); if (error != RETURN_OK) { fnameparams[0] = (IPTR)"DEVS:"; RawDoFmt("%s%s", (RAWARG)&fnameparams, RAWFMTFUNC_STRING, namebuf); error = makefilever(namebuf); } } FreeVec(namebuf); } } } } } if (!args.arg_res && error == -1) { if (volume) { error = makedevicever(name); } else { if (*filepart) { error = makefilever(name); } } } if (!args.arg_file && error == -1) { error = makerescmdver(name); } if (error) { /* If user asked for md5sum, and we could calculate it, don't print error * but the md5sum + file. */ if (args.arg_md5sum && (parsedver.pv_flags & PVF_MD5SUM)) { parsedver.pv_name = dupstr(name, -1); parsedver.pv_flags |= PVF_NOVERSION; error = RETURN_OK; } } if (error == -1) { PrintFault(ERROR_OBJECT_NOT_FOUND, (STRPTR) ERROR_HEADER); error = RETURN_FAIL; } return error; } static void freeverstring(void) { parsedver.pv_flags = 0; parsedver.pv_version = 0; parsedver.pv_revision = 0; FreeVec(parsedver.pv_extrastr); parsedver.pv_extrastr = NULL; parsedver.pv_extralf = NULL; FreeVec(parsedver.pv_datestr); parsedver.pv_datestr = NULL; FreeVec(parsedver.pv_revname); parsedver.pv_revname = NULL; FreeVec(parsedver.pv_vername); parsedver.pv_vername = NULL; FreeVec(parsedver.pv_name); parsedver.pv_name = NULL; } /* Compare the version given as argument with the version from the object. * Return RETURN_WARN, if args-v>object-v, otherwise return RETURN_OK. */ static int cmpargsparsed(void) { if (args.arg_version) { if (*(args.arg_version) > parsedver.pv_version) { return RETURN_WARN; } else if (*(args.arg_version) == parsedver.pv_version && args.arg_revision) { if (*(args.arg_revision) > parsedver.pv_revision) { return RETURN_WARN; } } } else if (args.arg_revision) { if (*(args.arg_revision) > parsedver.pv_revision) { return RETURN_WARN; } } return RETURN_OK; } /******************************* main program ****************************/ int __nocommandline; int main (void) { LONG error = RETURN_FAIL; struct RDArgs *rda; rda = ReadArgs(TEMPLATE, (IPTR *) &args, NULL); if (rda) { if (!args.arg_name || !*args.arg_name) { /* No args, make system version */ error = makesysver(); if (error == RETURN_OK) { printverstring(); if (parsedver.pv_flags & PVF_NOVERSION) { error = RETURN_FAIL; } if (error == RETURN_OK) { error = cmpargsparsed(); } } freeverstring(); } else { CONST_STRPTR *name; BOOL multifile; #if 1 /* Workaround for: * version file ver * version file ver rev */ if (!args.arg_version && !args.arg_revision) { LONG narg = 1; while (args.arg_name[narg]) { narg++; } if (narg == 2 || narg == 3) { if (StrToLong(args.arg_name[1], &mversion) > 0) { args.arg_version = &mversion; args.arg_name[1] = args.arg_name[2]; if (narg == 3) { args.arg_name[2] = NULL; } if (narg == 3) { if (StrToLong(args.arg_name[1], &mrevision) > 0) { args.arg_revision = &mrevision; args.arg_name[1] = NULL; } } } } } #endif multifile = args.arg_name[1] != NULL; for (name = args.arg_name; *name; name++) { error = makeverstring(*name); if (error == RETURN_OK) { printverstring(); if (!multifile) { /* Single args, do compare stuff also */ if (parsedver.pv_flags & PVF_NOVERSION) { error = RETURN_FAIL; } if (error == RETURN_OK) { error = cmpargsparsed(); } } } freeverstring(); } } FreeArgs(rda); } else { PrintFault(IoErr(), (STRPTR) ERROR_HEADER); error = RETURN_FAIL; } return error; }