Hint added.
[AROS.git] / workbench / c / Version.c
blob27deb85ee7394e67ec5b607975f2d41223df43eb
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Version CLI command
6 Lang: english
7 */
9 /******************************************************************************
11 NAME
13 Version [<library|device|file>] [<version #>] [<revision #>] [FILE] [FULL] [RES]
15 SYNOPSIS
17 NAME/M,MD5SUM/S,VERSION/N,REVISION/N,FILE/S,FULL/S,RES/S,ARCH/S
19 LOCATION
23 FUNCTION
25 Prints or checks the version and revision information of a file, library or device.
27 INPUTS
29 NAME -- name of file, library or device to check. If not given it
30 prints version and revision of Kickstart.
31 MD5SUM -- message-digest computation
32 VERSION -- checks for version and returns error code 5 (warn) if the
33 version of the file is lower.
34 REVISION -- checks for revision and returns error code 5 (warn) if the
35 revision of the file is lower.
36 FILE -- reads from file and ignores currently loaded libraries and devices
37 FULL -- prints additional information
38 RES -- gets version of resident commands
39 ARCH -- displays architecture information about a file
41 RESULT
43 NOTES
44 If the tag contains a trailing space and dollar sign, you may use the Unix command ident.
46 EXAMPLE
48 BUGS
50 SEE ALSO
52 INTERNALS
54 HISTORY
56 ******************************************************************************/
58 #define ELF_32BIT /* This is needed for correct .ARM.Attributes parsing */
60 #include <aros/arosbase.h>
61 #include <aros/debug.h>
62 #include <aros/inquire.h>
63 #include <proto/aros.h>
65 #define ENABLE_RT 1
66 #include <aros/rt.h>
68 #include <string.h>
69 #include <ctype.h>
71 #include <proto/exec.h>
72 #include <exec/execbase.h>
73 #include <exec/libraries.h>
74 #include <exec/memory.h>
75 #include <exec/resident.h>
76 #include <proto/dos.h>
77 #include <proto/utility.h>
78 #include <proto/alib.h>
79 #include <dos/datetime.h>
80 #include <dos/dos.h>
81 #include <dos/dosextens.h>
82 #include <dos/elf.h>
84 /*===[md5.h]==============================================================*/
86 /* Data structure for MD5 (Message-Digest) computation */
87 typedef struct {
88 ULONG buf[4]; /* scratch buffer */
89 ULONG i[2]; /* number of _bits_ handled mod 2^64 */
90 unsigned char in[64]; /* input buffer */
91 } MD5_CTX;
93 void MD5Init(MD5_CTX *mdContext);
94 void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
95 void MD5Final(unsigned char digest[16], MD5_CTX *mdContext);
97 /*==[md5.c]===============================================================*/
100 * Used in 'version' as is, just removed the #include "ambient.h"
102 * Harry Sintonen <sintonen@iki.fi>
106 * Ambient - the ultimate desktop
107 * ------------------------------
108 * (c) 2001-2003 by David Gerber <zapek@meanmachine.ch>
109 * All Rights Reserved
111 * $Id$
114 //#include "ambient.h"
115 //#include <exec/types.h>
118 ***********************************************************************
119 ** md5.c -- the source code for MD5 routines **
120 ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
121 ** Created: 2/17/90 RLR **
122 ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
123 ***********************************************************************
127 * Edited 7 May 93 by CP to change the interface to match that
128 * of the MD5 routines in RSAREF. Due to this alteration, this
129 * code is "derived from the RSA Data Security, Inc. MD5 Message-
130 * Digest Algorithm". (See below.)
134 ***********************************************************************
135 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
136 ** **
137 ** License to copy and use this software is granted provided that **
138 ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
139 ** Digest Algorithm" in all material mentioning or referencing this **
140 ** software or this function. **
141 ** **
142 ** License is also granted to make and use derivative works **
143 ** provided that such works are identified as "derived from the RSA **
144 ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
145 ** material mentioning or referencing the derived work. **
146 ** **
147 ** RSA Data Security, Inc. makes no representations concerning **
148 ** either the merchantability of this software or the suitability **
149 ** of this software for any particular purpose. It is provided "as **
150 ** is" without express or implied warranty of any kind. **
151 ** **
152 ** These notices must be retained in any copies of any part of this **
153 ** documentation and/or software. **
154 ***********************************************************************
157 //#include "md5.h"
160 ***********************************************************************
161 ** Message-digest routines: **
162 ** To form the message digest for a message M **
163 ** (1) Initialize a context buffer mdContext using MD5Init **
164 ** (2) Call MD5Update on mdContext and M **
165 ** (3) Call MD5Final on mdContext **
166 ** The message digest is now in the bugffer passed to MD5Final **
167 ***********************************************************************
170 static unsigned char PADDING[64] = {
171 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
181 /* F, G, H and I are basic MD5 functions */
182 #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
183 #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
184 #define H(x, y, z) ((x) ^ (y) ^ (z))
185 #define I(x, y, z) ((y) ^ ((x) | (~z)))
187 /* ROTATE_LEFT rotates x left n bits */
188 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
190 /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
191 /* Rotation is separate from addition to prevent recomputation */
192 #define FF(a, b, c, d, x, s, ac) \
193 {(a) += F ((b), (c), (d)) + (x) + (ULONG)(ac); \
194 (a) = ROTATE_LEFT ((a), (s)); \
195 (a) += (b); \
197 #define GG(a, b, c, d, x, s, ac) \
198 {(a) += G ((b), (c), (d)) + (x) + (ULONG)(ac); \
199 (a) = ROTATE_LEFT ((a), (s)); \
200 (a) += (b); \
202 #define HH(a, b, c, d, x, s, ac) \
203 {(a) += H ((b), (c), (d)) + (x) + (ULONG)(ac); \
204 (a) = ROTATE_LEFT ((a), (s)); \
205 (a) += (b); \
207 #define II(a, b, c, d, x, s, ac) \
208 {(a) += I ((b), (c), (d)) + (x) + (ULONG)(ac); \
209 (a) = ROTATE_LEFT ((a), (s)); \
210 (a) += (b); \
213 static void Transform(register ULONG *buf,register ULONG *in);
215 /* The routine MD5Init initializes the message-digest context
216 mdContext. All fields are set to zero.
218 void MD5Init ( MD5_CTX *mdContext)
220 mdContext->i[0] = mdContext->i[1] = (ULONG)0;
222 /* Load magic initialization constants.
224 mdContext->buf[0] = (ULONG)0x67452301L;
225 mdContext->buf[1] = (ULONG)0xefcdab89L;
226 mdContext->buf[2] = (ULONG)0x98badcfeL;
227 mdContext->buf[3] = (ULONG)0x10325476L;
230 /* The routine MD5Update updates the message-digest context to
231 account for the presence of each of the characters inBuf[0..inLen-1]
232 in the message whose digest is being computed.
234 void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf,
235 unsigned int inLen)
237 register int i, ii;
238 int mdi;
239 ULONG in[16];
241 /* compute number of bytes mod 64 */
242 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
244 /* update number of bits */
245 if ((mdContext->i[0] + ((ULONG)inLen << 3)) < mdContext->i[0])
246 mdContext->i[1]++;
247 mdContext->i[0] += ((ULONG)inLen << 3);
248 mdContext->i[1] += ((ULONG)inLen >> 29);
250 while (inLen--)
252 /* add new character to buffer, increment mdi */
253 mdContext->in[mdi++] = *inBuf++;
255 /* transform if necessary */
256 if (mdi == 0x40)
258 for (i = 0, ii = 0; i < 16; i++, ii += 4)
259 in[i] = (((ULONG)mdContext->in[ii+3]) << 24) |
260 (((ULONG)mdContext->in[ii+2]) << 16) |
261 (((ULONG)mdContext->in[ii+1]) << 8) |
262 ((ULONG)mdContext->in[ii]);
263 Transform (mdContext->buf, in);
264 mdi = 0;
269 /* The routine MD5Final terminates the message-digest computation and
270 ends with the desired message digest in mdContext->digest[0...15].
272 void MD5Final (unsigned char digest[16], MD5_CTX *mdContext)
274 ULONG in[16];
275 int mdi;
276 unsigned int i, ii;
277 unsigned int padLen;
279 /* save number of bits */
280 in[14] = mdContext->i[0];
281 in[15] = mdContext->i[1];
283 /* compute number of bytes mod 64 */
284 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
286 /* pad out to 56 mod 64 */
287 padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
288 MD5Update (mdContext, PADDING, padLen);
290 /* append length in bits and transform */
291 for (i = 0, ii = 0; i < 14; i++, ii += 4)
292 in[i] = (((ULONG)mdContext->in[ii+3]) << 24) |
293 (((ULONG)mdContext->in[ii+2]) << 16) |
294 (((ULONG)mdContext->in[ii+1]) << 8) |
295 ((ULONG)mdContext->in[ii]);
296 Transform (mdContext->buf, in);
298 /* store buffer in digest */
299 for (i = 0, ii = 0; i < 4; i++, ii += 4)
301 digest[ii] = (unsigned char) (mdContext->buf[i] & 0xFF);
302 digest[ii+1] = (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
303 digest[ii+2] = (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
304 digest[ii+3] = (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
308 /* Basic MD5 step. Transforms buf based on in. Note that if the Mysterious
309 Constants are arranged backwards in little-endian order and decrypted with
310 the DES they produce OCCULT MESSAGES!
312 void Transform(register ULONG *buf,register ULONG *in)
314 register ULONG a = buf[0], b = buf[1], c = buf[2], d = buf[3];
316 /* Round 1 */
317 #define S11 7
318 #define S12 12
319 #define S13 17
320 #define S14 22
321 FF ( a, b, c, d, in[ 0], S11, 0xD76AA478L); /* 1 */
322 FF ( d, a, b, c, in[ 1], S12, 0xE8C7B756L); /* 2 */
323 FF ( c, d, a, b, in[ 2], S13, 0x242070DBL); /* 3 */
324 FF ( b, c, d, a, in[ 3], S14, 0xC1BDCEEEL); /* 4 */
325 FF ( a, b, c, d, in[ 4], S11, 0xF57C0FAFL); /* 5 */
326 FF ( d, a, b, c, in[ 5], S12, 0x4787C62AL); /* 6 */
327 FF ( c, d, a, b, in[ 6], S13, 0xA8304613L); /* 7 */
328 FF ( b, c, d, a, in[ 7], S14, 0xFD469501L); /* 8 */
329 FF ( a, b, c, d, in[ 8], S11, 0x698098D8L); /* 9 */
330 FF ( d, a, b, c, in[ 9], S12, 0x8B44F7AFL); /* 10 */
331 FF ( c, d, a, b, in[10], S13, 0xFFFF5BB1L); /* 11 */
332 FF ( b, c, d, a, in[11], S14, 0x895CD7BEL); /* 12 */
333 FF ( a, b, c, d, in[12], S11, 0x6B901122L); /* 13 */
334 FF ( d, a, b, c, in[13], S12, 0xFD987193L); /* 14 */
335 FF ( c, d, a, b, in[14], S13, 0xA679438EL); /* 15 */
336 FF ( b, c, d, a, in[15], S14, 0x49B40821L); /* 16 */
338 /* Round 2 */
339 #define S21 5
340 #define S22 9
341 #define S23 14
342 #define S24 20
343 GG ( a, b, c, d, in[ 1], S21, 0xF61E2562L); /* 17 */
344 GG ( d, a, b, c, in[ 6], S22, 0xC040B340L); /* 18 */
345 GG ( c, d, a, b, in[11], S23, 0x265E5A51L); /* 19 */
346 GG ( b, c, d, a, in[ 0], S24, 0xE9B6C7AAL); /* 20 */
347 GG ( a, b, c, d, in[ 5], S21, 0xD62F105DL); /* 21 */
348 GG ( d, a, b, c, in[10], S22, 0x02441453L); /* 22 */
349 GG ( c, d, a, b, in[15], S23, 0xD8A1E681L); /* 23 */
350 GG ( b, c, d, a, in[ 4], S24, 0xE7D3FBC8L); /* 24 */
351 GG ( a, b, c, d, in[ 9], S21, 0x21E1CDE6L); /* 25 */
352 GG ( d, a, b, c, in[14], S22, 0xC33707D6L); /* 26 */
353 GG ( c, d, a, b, in[ 3], S23, 0xF4D50D87L); /* 27 */
354 GG ( b, c, d, a, in[ 8], S24, 0x455A14EDL); /* 28 */
355 GG ( a, b, c, d, in[13], S21, 0xA9E3E905L); /* 29 */
356 GG ( d, a, b, c, in[ 2], S22, 0xFCEFA3F8L); /* 30 */
357 GG ( c, d, a, b, in[ 7], S23, 0x676F02D9L); /* 31 */
358 GG ( b, c, d, a, in[12], S24, 0x8D2A4C8AL); /* 32 */
360 /* Round 3 */
361 #define S31 4
362 #define S32 11
363 #define S33 16
364 #define S34 23
365 HH ( a, b, c, d, in[ 5], S31, 0xFFFA3942L); /* 33 */
366 HH ( d, a, b, c, in[ 8], S32, 0x8771F681L); /* 34 */
367 HH ( c, d, a, b, in[11], S33, 0x6D9D6122L); /* 35 */
368 HH ( b, c, d, a, in[14], S34, 0xFDE5380CL); /* 36 */
369 HH ( a, b, c, d, in[ 1], S31, 0xA4BEEA44L); /* 37 */
370 HH ( d, a, b, c, in[ 4], S32, 0x4BDECFA9L); /* 38 */
371 HH ( c, d, a, b, in[ 7], S33, 0xF6BB4B60L); /* 39 */
372 HH ( b, c, d, a, in[10], S34, 0xBEBFBC70L); /* 40 */
373 HH ( a, b, c, d, in[13], S31, 0x289B7EC6L); /* 41 */
374 HH ( d, a, b, c, in[ 0], S32, 0xEAA127FAL); /* 42 */
375 HH ( c, d, a, b, in[ 3], S33, 0xD4EF3085L); /* 43 */
376 HH ( b, c, d, a, in[ 6], S34, 0x04881D05L); /* 44 */
377 HH ( a, b, c, d, in[ 9], S31, 0xD9D4D039L); /* 45 */
378 HH ( d, a, b, c, in[12], S32, 0xE6DB99E5L); /* 46 */
379 HH ( c, d, a, b, in[15], S33, 0x1FA27CF8L); /* 47 */
380 HH ( b, c, d, a, in[ 2], S34, 0xC4AC5665L); /* 48 */
382 /* Round 4 */
383 #define S41 6
384 #define S42 10
385 #define S43 15
386 #define S44 21
387 II ( a, b, c, d, in[ 0], S41, 0xF4292244L); /* 49 */
388 II ( d, a, b, c, in[ 7], S42, 0x432AFF97L); /* 50 */
389 II ( c, d, a, b, in[14], S43, 0xAB9423A7L); /* 51 */
390 II ( b, c, d, a, in[ 5], S44, 0xFC93A039L); /* 52 */
391 II ( a, b, c, d, in[12], S41, 0x655B59C3L); /* 53 */
392 II ( d, a, b, c, in[ 3], S42, 0x8F0CCC92L); /* 54 */
393 II ( c, d, a, b, in[10], S43, 0xFFEFF47DL); /* 55 */
394 II ( b, c, d, a, in[ 1], S44, 0x85845DD1L); /* 56 */
395 II ( a, b, c, d, in[ 8], S41, 0x6FA87E4FL); /* 57 */
396 II ( d, a, b, c, in[15], S42, 0xFE2CE6E0L); /* 58 */
397 II ( c, d, a, b, in[ 6], S43, 0xA3014314L); /* 59 */
398 II ( b, c, d, a, in[13], S44, 0x4E0811A1L); /* 60 */
399 II ( a, b, c, d, in[ 4], S41, 0xF7537E82L); /* 61 */
400 II ( d, a, b, c, in[11], S42, 0xBD3AF235L); /* 62 */
401 II ( c, d, a, b, in[ 2], S43, 0x2AD7D2BBL); /* 63 */
402 II ( b, c, d, a, in[ 9], S44, 0xEB86D391L); /* 64 */
404 buf[0] += a;
405 buf[1] += b;
406 buf[2] += c;
407 buf[3] += d;
410 /*==[end md5.c]============================================================*/
413 const TEXT version[] = "$VER: Version 42.3 (20.11.2015)\n";
414 const TEXT ver[] = "$VER:";
416 static const char ERROR_HEADER[] = "Version";
418 #define TEMPLATE "NAME/M,MD5SUM/S,VERSION/N,REVISION/N,FILE/S,FULL/S,RES/S,ARCH/S"
419 struct
421 CONST_STRPTR *arg_name;
422 IPTR arg_md5sum;
423 LONG *arg_version;
424 LONG *arg_revision;
425 IPTR arg_file;
426 IPTR arg_full;
427 IPTR arg_res;
428 IPTR arg_arch;
430 args;
432 LONG mversion, mrevision;
434 struct
436 STRPTR pv_name;
437 ULONG pv_flags;
438 LONG pv_version;
439 LONG pv_revision;
440 UWORD pv_arch;
441 UBYTE pv_arm_cpu;
442 UBYTE pv_arm_fpu;
443 //LONG pv_days;
444 STRPTR pv_vername;
445 STRPTR pv_revname;
446 STRPTR pv_datestr;
447 STRPTR pv_extralf;
448 STRPTR pv_extrastr;
449 UBYTE pv_md5sum[16];
451 parsedver = { NULL, 0, 0, 0, 0, -1, 0, NULL, NULL, NULL, NULL, NULL, {0}};
453 #define PVF_MD5SUM (1 << 0)
454 #define PVF_NOVERSION (1 << 1)
456 static
457 int makeverstring(CONST_STRPTR name);
458 static
459 void printverstring(void);
460 static
461 void freeverstring(void);
462 static
463 int makesysver(void);
464 static
465 int cmpargsparsed(void);
468 /**************************** support functions ************************/
470 /* Duplicate string, by given length or -1 for full length
472 static
473 STRPTR dupstr(CONST_STRPTR buffer, LONG len)
475 STRPTR ret = NULL;
477 if (buffer)
479 if (len == -1)
480 len = strlen(buffer);
482 ret = AllocVec(len + 1, MEMF_ANY);
483 if (ret)
485 CopyMem((STRPTR) buffer, ret, len);
486 ret[len] = '\0';
490 return ret;
494 static
495 inline int myisspace(int c)
497 return (c == ' ' || c == '\t');
501 /* Return a pointer to a string, stripped by all leading whitespace characters
502 * (SPACE, TAB).
504 static
505 STRPTR skipwhites(CONST_STRPTR buffer)
507 for (;; buffer++)
509 if (buffer[0] == '\0' || !isspace(buffer[0]))
511 return (STRPTR) buffer;
517 /* Return a pointer to a string, stripped by all leading space characters
518 * (SPACE).
520 static
521 STRPTR skipspaces(CONST_STRPTR buffer)
523 for (;; buffer++)
525 if (buffer[0] == '\0' || buffer[0] != ' ')
527 return (STRPTR) buffer;
533 /* Strip all whitespace-characters from the end of a string. Note that the
534 * buffer passed in will be modified!
536 static
537 void stripwhites(STRPTR buffer)
539 int len = strlen(buffer);
541 while (len > 0)
543 if (!isspace(buffer[len-1]))
545 buffer[len] = '\0';
546 return;
548 len--;
550 buffer[len] = '\0';
554 /* Searches for a given string in a file and stores up to *lenptr characters
555 * into the buffer beginning with the first character after the given string.
557 static
558 int findinfile(BPTR file, CONST_STRPTR string, STRPTR buffer, int *lenptr, unsigned char digest[16])
560 int error = RETURN_OK;
561 int buflen = *lenptr, len = 0, pos, stringlen;
562 BOOL ready = FALSE;
563 MD5_CTX md5ctx;
564 STRPTR bufpos;
565 STRPTR tmp;
567 tmp = AllocMem(buflen, MEMF_PUBLIC);
568 if (!tmp)
570 return RETURN_FAIL;
573 stringlen = strlen(string);
574 *lenptr = -1;
576 if (args.arg_md5sum)
578 MD5Init(&md5ctx);
581 bufpos = tmp;
582 while ((len = Read(file, &tmp[len], buflen - len)) > 0)
584 pos = 0;
586 if (args.arg_md5sum)
588 MD5Update(&md5ctx, bufpos, len);
591 if (ready)
593 /* If we get here we're scanning the rest of the file for md5sum. - Piru */
594 len = 0;
596 else
598 while ((len - pos) >= stringlen)
600 /* Compare the current buffer position with the supplied string. */
601 if (strncmp(&tmp[pos], string, stringlen) == 0)
603 /* It is equal! Now move the rest of the buffer to the top of
604 * the buffer and fill it up.
606 int findstrlen = len - pos;
608 memcpy(buffer, &tmp[pos + stringlen], findstrlen);
610 len = Read(file, &buffer[findstrlen], buflen - findstrlen);
611 if (len >= 0)
613 if (args.arg_md5sum)
615 MD5Update(&md5ctx, &buffer[findstrlen], len);
618 *lenptr = findstrlen + len;
620 else
622 error = RETURN_FAIL;
624 ready = TRUE;
625 break;
627 pos++;
629 /* Move the rest of the buffer that could not be compared (because it
630 * is smaller than the string to compare) to the top of the buffer.
632 if (!ready)
634 memmove(tmp, &tmp[len - stringlen], stringlen);
636 else
638 /* If we're not md5summing, stop file scanning now. - Piru */
639 if (!args.arg_md5sum)
641 break;
644 len = stringlen;
647 bufpos = &tmp[len];
650 FreeMem(tmp, buflen);
652 if (len == -1)
654 error = RETURN_FAIL;
657 if (args.arg_md5sum)
659 memset(digest, 0, 16);
660 MD5Final(digest, &md5ctx);
663 return error;
667 /*************************** parsing functions *************************/
669 /* Convert a date in the form DD.MM.YY or DD.MM.YYYY into a numerical
670 * value. Return FALSE, if buffer doesn't contain a valid date.
672 static
673 BOOL makedatefromstring(CONST_STRPTR *bufptr)
675 CONST_STRPTR buffer = *bufptr;
676 struct DateTime dt;
677 CONST_STRPTR headerstart, end;
678 STRPTR newbuf;
679 LONG res;
680 int len, i;
681 UBYTE c;
683 //if (isspace(buffer[0]))
684 // buffer++;
686 headerstart = buffer;
688 buffer = strchr(buffer, '(');
689 if (!buffer)
691 return FALSE;
693 buffer++;
694 end = strchr(buffer, ')');
695 if (!end)
697 return FALSE;
699 len = (int)(end - buffer);
700 newbuf = dupstr(buffer, len);
701 if (!newbuf)
703 return FALSE;
705 for (i = 0; i < len; i++)
707 c = newbuf[i];
709 if (c == '.' || c == '/')
710 newbuf[i] = '-';
711 else if (!isalnum(c))
713 end = buffer + i;
714 newbuf[i] = '\0';
715 break;
719 D(Printf("date: \"%s\"\n", newbuf));
721 dt.dat_Format = FORMAT_CDN;
722 dt.dat_Flags = 0;
723 dt.dat_StrDay = NULL;
724 dt.dat_StrDate = newbuf;
725 dt.dat_StrTime = NULL;
726 res = StrToDate(&dt);
727 if (!res)
729 dt.dat_Format = FORMAT_DOS;
730 res = StrToDate(&dt);
731 if (!res)
733 //Printf("StrToDate failed!\n");
734 FreeVec(newbuf);
735 return FALSE;
738 FreeVec(newbuf);
740 //parsedver.pv_days = dt.dat_Stamp.ds_Days;
742 parsedver.pv_datestr = AllocVec(buffer - headerstart + LEN_DATSTRING + 2, MEMF_ANY);
743 if (!parsedver.pv_datestr)
745 return FALSE;
748 dt.dat_Stamp.ds_Minute = 0;
749 dt.dat_Stamp.ds_Tick = 0;
751 dt.dat_StrDate = parsedver.pv_datestr + (buffer - headerstart);
752 dt.dat_Format = FORMAT_DEF;
753 if (!DateToStr(&dt))
755 //Printf("DateToStr failed!\n");
756 return FALSE;
759 CopyMem((STRPTR) headerstart, parsedver.pv_datestr, buffer - headerstart);
760 res = strlen(parsedver.pv_datestr);
761 parsedver.pv_datestr[res++] = ')';
762 parsedver.pv_datestr[res] = '\0';
764 *bufptr = end + 1;
766 return TRUE;
770 /* Check whether the given string contains a version in the form
771 * <version>.<revision> . If not return FALSE, otherwise fill in parsedver and
772 * return TRUE.
774 static
775 BOOL makeversionfromstring(CONST_STRPTR *bufptr)
777 LONG pos, ver, rev;
778 CONST_STRPTR buffer = *bufptr;
779 CONST_STRPTR verstart, revstart;
781 //Printf("makeversionfromstring: buffer \"%s\"\n", (LONG) buffer);
783 /* Do version */
785 verstart = buffer;
786 pos = StrToLong((STRPTR) buffer, &ver);
787 if (pos == -1)
789 return FALSE;
791 parsedver.pv_version = ver;
792 parsedver.pv_revision = -1;
794 parsedver.pv_vername = dupstr(verstart, pos);
795 if (!parsedver.pv_vername)
797 return FALSE;
800 /* Do revision */
802 buffer += pos;
803 revstart = buffer;
804 buffer = skipspaces(buffer); /* NOTE: skipspaces, not skipwhites! */
805 if (*buffer != '.')
807 *bufptr = buffer;
808 return TRUE;
811 buffer++;
812 pos = StrToLong((STRPTR) buffer, &rev);
813 if (pos == -1)
815 *bufptr = buffer;
816 return TRUE;
819 parsedver.pv_revision = rev;
821 /* calc the revision string len */
822 pos = buffer + pos - revstart;
823 parsedver.pv_revname = dupstr(revstart, pos);
824 if (!parsedver.pv_revname)
826 return FALSE;
829 *bufptr = revstart + pos;
831 return TRUE;
834 static const char *arm_cpus[] =
836 "ARM Pre-v4",
837 "ARMv4",
838 "ARMv4T",
839 "ARMv5T",
840 "ARMv5TE",
841 "ARMv5TEJ",
842 "ARMv6",
843 "ARMv6KZ",
844 "ARMv6T2",
845 "ARMv6K",
846 "ARMv7",
847 "ARMv6-M",
848 "ARMv6S-M",
849 "ARMv7E-M"
852 static const char *arm_fpus[] =
854 "VFP",
855 "VFPv2",
856 "VFPv3",
857 "VFPv3 (D0 - D15)",
858 "VFPv4",
859 "VFPv4 (D0 - D15)"
862 static
863 void printverstring(void)
865 if (args.arg_md5sum)
867 if (parsedver.pv_flags & PVF_MD5SUM)
869 /* Endianess safe version */
870 Printf("%02lX%02lX%02lX%02lX"
871 "%02lX%02lX%02lX%02lX"
872 "%02lX%02lX%02lX%02lX"
873 "%02lX%02lX%02lX%02lX ",
874 parsedver.pv_md5sum[0],
875 parsedver.pv_md5sum[1],
876 parsedver.pv_md5sum[2],
877 parsedver.pv_md5sum[3],
878 parsedver.pv_md5sum[4],
879 parsedver.pv_md5sum[5],
880 parsedver.pv_md5sum[6],
881 parsedver.pv_md5sum[7],
882 parsedver.pv_md5sum[8],
883 parsedver.pv_md5sum[9],
884 parsedver.pv_md5sum[10],
885 parsedver.pv_md5sum[11],
886 parsedver.pv_md5sum[12],
887 parsedver.pv_md5sum[13],
888 parsedver.pv_md5sum[14],
889 parsedver.pv_md5sum[15]);
891 else
893 /* "01234567012345670123456701234567: " */
894 PutStr("<no md5sum available> ");
898 if (parsedver.pv_flags & PVF_NOVERSION)
900 Printf("%s\n", (IPTR) parsedver.pv_name);
902 else
904 if (args.arg_full)
906 /* If md5sum output was there, avoid linefeed to allow parsing the output - Piru */
907 if (args.arg_md5sum)
909 parsedver.pv_extralf = " ";
912 Printf("%s%s%s%s%s%s%s\n",
913 (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""),
914 (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname,
915 (IPTR) (parsedver.pv_datestr ? (IPTR)parsedver.pv_datestr : (IPTR)""),
916 (IPTR) (parsedver.pv_extralf ? (IPTR)parsedver.pv_extralf : (IPTR)""),
917 (IPTR) (parsedver.pv_extrastr ? (IPTR)parsedver.pv_extrastr : (IPTR)""));
919 else
921 Printf("%s%s%s%s\n",
922 (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""),
923 (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname);
926 if (args.arg_arch)
928 const char *arch;
930 if (parsedver.pv_arch == EM_ARM)
932 Printf("Architecture: ");
934 if (parsedver.pv_arm_cpu == (UBYTE)-1)
935 Printf("ARM (unspecified)");
936 else if (parsedver.pv_arm_cpu <= ELF_CPU_ARMv7EM)
937 Printf(arm_cpus[parsedver.pv_arm_cpu]);
938 else
939 Printf("Unknown ARM (%d)", parsedver.pv_arm_cpu);
941 if (parsedver.pv_arm_fpu > 6)
942 Printf(" Unknown FPU (%d)", parsedver.pv_arm_fpu);
943 else if (parsedver.pv_arm_fpu)
944 Printf(" %s", arm_fpus[parsedver.pv_arm_fpu]);
946 Printf("\n");
948 else
950 switch (parsedver.pv_arch)
952 case 0:
953 /* No information available */
954 arch = NULL;
955 break;
957 case EM_386:
958 arch = "I386";
959 break;
961 case EM_68K:
962 arch = "M68k";
963 break;
965 case EM_PPC:
966 arch = "PowerPC";
967 break;
969 case EM_X86_64:
970 arch = "X86-64";
971 break;
973 default:
974 arch = "Unknown";
975 break;
978 if (arch)
979 Printf("Architecture: %s\n", arch);
986 static
987 int makedata(CONST_STRPTR buffer, CONST_STRPTR ptr, int pos)
989 D(Printf("makedata: buffer \"%s\" ptr \"%s\"\n", buffer, ptr));
991 if (makeversionfromstring(&ptr))
993 CONST_STRPTR endp;
994 BOOL doskip;
996 /* It is! */
997 /* Copy the program-name into a buffer. */
998 parsedver.pv_name = dupstr(buffer, pos);
999 if (!parsedver.pv_name)
1001 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1002 return RETURN_FAIL;
1005 /* Now find the date */
1006 D(Printf("makedata: ptr #1: \"%s\"\n", ptr));
1007 doskip = strchr(ptr, '(') ? TRUE : FALSE;
1008 (void) makedatefromstring(&ptr);
1010 D(Printf("makedata: ptr #2: \"%s\"\n", ptr));
1011 if (doskip)
1012 ptr = skipspaces(ptr); /* NOTE: not skipwhites! */
1013 for (endp = ptr; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++)
1015 pos = endp - ptr;
1016 if (pos && ptr[pos-1] == '$')
1017 pos--;
1018 if (pos)
1020 parsedver.pv_extrastr = dupstr(ptr, pos);
1021 if (!parsedver.pv_extrastr)
1023 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1024 return RETURN_FAIL;
1027 D(Printf("makedata: Extra string: %s\n", parsedver.pv_extrastr));
1028 if (doskip)
1029 parsedver.pv_extralf = "\n";
1032 return 1;
1035 return 0;
1039 /* Retrieves version information from string. The data is stored in the
1040 * global struct parsedver.pv_
1043 static
1044 int makedatafromstring(CONST_STRPTR buffer)
1046 int error = RETURN_OK;
1047 int pos = 0;
1048 LONG add, dummy;
1050 while (buffer[pos] && buffer[pos] != '\r' && buffer[pos] != '\n')
1052 /* NOTE: Not isspace()! - Piru */
1053 if (myisspace(buffer[pos]) &&
1054 (add = StrToLong((STRPTR) buffer + pos + 1, &dummy)) != -1)
1056 CONST_STRPTR ptr;
1058 /* Found something, which looks like a version. Now check, if it
1059 * really is.
1062 D(Printf("makedatafromstring: buffer + %ld: \"%s\"\n", pos, buffer + pos));
1064 ptr = buffer + pos + 1;
1066 if (makedata(buffer, ptr, pos))
1068 break;
1071 pos++;
1074 if (!buffer[pos] || buffer[pos] == '\r' || buffer[pos] == '\n')
1076 CONST_STRPTR endp;
1078 /* use the whatever is after ver tag as name */
1079 for (endp = buffer; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++)
1081 pos = endp - buffer;
1083 parsedver.pv_name = dupstr(buffer, pos);
1084 if (!parsedver.pv_name)
1086 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1087 return RETURN_FAIL;
1091 /* Strip any whitespaces from the tail of the program-name.
1093 if (parsedver.pv_name)
1095 stripwhites(parsedver.pv_name);
1098 return error;
1102 /* Case-insensitive FindResident()
1104 static
1105 struct Resident *findresident(CONST_STRPTR name)
1107 struct Resident **rp;
1108 struct Resident *resident;
1110 rp = (struct Resident **) SysBase->ResModules;
1112 while ((resident = *rp++))
1114 #ifdef __mc68000__
1115 if (((LONG)resident) < 0)
1116 rp = (struct Resident **)((ULONG)resident & 0x7fffffff);
1117 #else
1118 if (((IPTR)resident) & 0x01)
1119 rp = (struct Resident **)((IPTR)resident & ~1);
1120 #endif
1121 else {
1122 if (!Stricmp(resident->rt_Name, (STRPTR) name))
1123 break;
1127 return resident;
1131 /* Case-insensitive FindName()
1133 static
1134 struct Node *findname(struct List *list, CONST_STRPTR name)
1136 struct Node *node;
1138 ForeachNode(list, node)
1140 if (!Stricmp(node->ln_Name, (STRPTR) name))
1142 return node;
1146 return NULL;
1150 /* Retrieve information from resident modules. Returns 0 for success.
1152 static
1153 int createresidentver(struct Resident *MyResident)
1155 STRPTR buffer = NULL;
1156 CONST_STRPTR name = NULL;
1157 STRPTR tmpbuffer;
1158 int pos = 0;
1159 int len = 0;
1160 BOOL foundver = FALSE;
1161 int error;
1163 if (MyResident->rt_IdString)
1165 buffer = skipwhites(MyResident->rt_IdString);
1166 D(Printf("createresidentver: buffer \"%s\"\n", buffer));
1168 /* Locate version part */
1169 while (buffer[pos])
1171 LONG dummy;
1173 /* NOTE: Not isspace()! - Piru */
1174 if (myisspace(buffer[pos]) &&
1175 StrToLong(buffer + pos + 1, &dummy) != -1)
1177 buffer += pos;
1178 foundver = TRUE;
1179 break;
1181 pos++;
1183 D(Printf("createresidentver: buffer: \"%s\"\n", buffer));
1185 name = MyResident->rt_IdString;
1186 len = buffer - name;
1189 /* If could not find any version info, use the resident rt_Name */
1190 if (!foundver)
1191 buffer = "";
1193 D(Printf("createresidentver: buffer: \"%s\"\n", buffer));
1195 if ((!args.arg_full) || (!name))
1197 name = MyResident->rt_Name;
1198 len = strlen(MyResident->rt_Name);
1201 tmpbuffer = AllocVec(len + strlen(buffer) + 1, MEMF_ANY);
1202 if (!tmpbuffer)
1204 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1205 return RETURN_FAIL;
1207 CopyMem(name, tmpbuffer, len);
1208 strcpy(tmpbuffer + len, buffer);
1209 D(Printf("createresidentver: tmpbuffer: \"%s\"\n", tmpbuffer));
1211 error = makedatafromstring(tmpbuffer);
1213 FreeVec(tmpbuffer);
1215 return error;
1219 /* Retrieve version information from library. Returns 0 for success.
1221 static
1222 int createlibraryver(struct Library *MyLibrary)
1224 STRPTR buffer, tmpbuffer;
1225 int error, foundver = FALSE, pos;
1227 if (MyLibrary->lib_IdString)
1229 //Printf("createlibraryver: lib_IdString \"%s\"\n", (LONG) MyLibrary->lib_IdString);
1230 buffer = skipwhites(MyLibrary->lib_IdString);
1232 //Printf("createlibraryver: buffer \"%s\"\n", (LONG) buffer);
1234 /* Find full 'ver.rev' version info
1236 pos = 0;
1237 while (buffer[pos])
1239 LONG dummy, add;
1241 /* NOTE: Not isspace()! - Piru */
1242 if (myisspace(buffer[pos]) &&
1243 (add = StrToLong(buffer + pos + 1, &dummy)) != -1 &&
1244 buffer[pos + 1 + add] == '.')
1246 buffer += pos;
1247 pos = 0;
1248 foundver = TRUE;
1249 break;
1251 pos++;
1254 /* If could not find 'ver.rev', find any numeric */
1255 if (!foundver)
1257 pos = 0;
1258 while (buffer[pos])
1260 LONG dummy;
1262 /* NOTE: Not isspace()! - Piru */
1263 if (myisspace(buffer[pos]) &&
1264 StrToLong(buffer + pos + 1, &dummy) != -1)
1266 buffer += pos;
1267 pos = 0;
1268 foundver = TRUE;
1269 break;
1271 pos++;
1276 /* If could not find any version info, use the resident rt_Name */
1277 if (!foundver)
1279 buffer = "";
1280 error = RETURN_WARN;
1283 tmpbuffer = AllocVec(strlen(MyLibrary->lib_Node.ln_Name) + strlen(buffer) + 1, MEMF_ANY);
1284 if (!tmpbuffer)
1286 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1287 return RETURN_FAIL;
1290 strcpy(tmpbuffer, MyLibrary->lib_Node.ln_Name);
1291 strcat(tmpbuffer, buffer);
1292 //Printf("createlibraryver: tmpbuffer: \"%s\"\n", (LONG) tmpbuffer);
1293 pos = makedatafromstring(tmpbuffer);
1294 if (pos > error)
1296 error = pos;
1299 FreeVec(tmpbuffer);
1301 return error;
1305 /* Create default strings
1307 static
1308 int createdefvers(CONST_STRPTR name)
1310 FreeVec(parsedver.pv_revname);
1311 FreeVec(parsedver.pv_vername);
1312 FreeVec(parsedver.pv_name);
1314 parsedver.pv_name = dupstr(name, -1);
1315 parsedver.pv_vername = AllocVec(14, MEMF_ANY);
1316 parsedver.pv_revname = AllocVec(15, MEMF_ANY);
1318 if (parsedver.pv_name &&
1319 parsedver.pv_vername &&
1320 parsedver.pv_revname)
1322 __sprintf(parsedver.pv_vername, "%ld", (long)parsedver.pv_version);
1323 __sprintf(parsedver.pv_revname, ".%ld", (long)parsedver.pv_revision);
1325 return RETURN_OK;
1328 return RETURN_FAIL;
1332 /* Create version info from named resident
1334 static
1335 int makeresidentver(CONST_STRPTR name)
1337 struct Resident *MyResident;
1338 int error = -1;
1340 if ((MyResident = findresident(name)))
1342 error = createresidentver(MyResident);
1343 if (error != RETURN_OK)
1345 /* get values from residenttag */
1346 parsedver.pv_version = MyResident->rt_Version;
1347 parsedver.pv_revision = -1;
1348 error = createdefvers(MyResident->rt_Name);
1352 return error;
1356 /* Create version info from named list node
1358 static
1359 int makeexeclistver(struct List *list, CONST_STRPTR name)
1361 struct Library *MyLibrary;
1362 int error = -1;
1364 Forbid();
1366 MyLibrary = (struct Library *) findname(list, name);
1367 if (MyLibrary)
1369 /* get values from library */
1370 ULONG ver = MyLibrary->lib_Version;
1371 ULONG rev = MyLibrary->lib_Revision;
1373 error = createlibraryver(MyLibrary);
1374 if (error != RETURN_OK ||
1375 parsedver.pv_version != ver ||
1376 parsedver.pv_revision != rev)
1378 /* special case where createlibraryrev was successful, but
1379 * version or revision don't match.
1381 if (error == RETURN_OK)
1383 /* If there is extrastr, make sure there's linefeed, too.
1385 if (parsedver.pv_extrastr)
1387 parsedver.pv_extralf = "\n";
1391 /* get values from library */
1392 parsedver.pv_version = ver;
1393 parsedver.pv_revision = rev;
1395 error = createdefvers(MyLibrary->lib_Node.ln_Name);
1399 Permit();
1401 return error;
1405 /* Find resident from seglist, return NULL if none found
1407 static
1408 struct Resident *FindLibResident(BPTR Segment)
1410 while (Segment)
1412 IPTR *MySegment;
1413 UWORD *MyBuffer;
1414 UWORD *EndBuffer;
1416 MySegment = (IPTR*) BADDR(Segment);
1417 MyBuffer = (UWORD*) &MySegment[1];
1418 EndBuffer = (UWORD*) &MySegment[(MySegment[-1] - sizeof(ULONG) * 4) / sizeof(ULONG)];
1420 while (MyBuffer < EndBuffer)
1422 struct Resident *MyResident;
1424 MyResident = (struct Resident*) MyBuffer;
1425 if (MyResident->rt_MatchWord == RTC_MATCHWORD &&
1426 MyResident->rt_MatchTag == MyResident)
1428 return MyResident;
1431 MyBuffer++;
1434 Segment =(BPTR) MySegment[0];
1437 SetIoErr(ERROR_OBJECT_NOT_FOUND);
1438 return NULL;
1442 /* Find $VER: tag from seglist, return NULL if none found
1443 Returns dupstr()d string or NULL.
1445 static
1446 STRPTR FindSegmentVER(BPTR Segment)
1448 while (Segment)
1450 void *MySegment;
1451 CONST_STRPTR MyBuffer;
1452 ULONG BufferLen;
1453 CONST_STRPTR EndBuffer;
1454 CONST_STRPTR SegmentEnd;
1456 MySegment = BADDR(Segment);
1457 MyBuffer = (CONST_STRPTR) (MySegment + sizeof(BPTR));
1458 BufferLen = *(ULONG *)(MySegment - sizeof(ULONG));
1459 SegmentEnd = (CONST_STRPTR) (MySegment + (BufferLen - sizeof(BPTR)));
1460 EndBuffer = SegmentEnd - 5;
1462 while (MyBuffer < EndBuffer)
1464 if (MyBuffer[0] == '$' &&
1465 MyBuffer[1] == 'V' &&
1466 MyBuffer[2] == 'E' &&
1467 MyBuffer[3] == 'R' &&
1468 MyBuffer[4] == ':')
1470 CONST_STRPTR EndPtr;
1472 MyBuffer += 5;
1473 /* Required because some smartass could end his $VER: tag
1474 * without '\0' in the segment to save space. - Piru
1476 for (EndPtr = MyBuffer; EndPtr < SegmentEnd && *EndPtr; EndPtr++)
1478 if (EndPtr - MyBuffer)
1480 return dupstr(MyBuffer, EndPtr - MyBuffer);
1484 MyBuffer++;
1487 Segment =*(BPTR *)MySegment;
1490 SetIoErr(ERROR_OBJECT_NOT_FOUND);
1491 return NULL;
1495 /* Create version info from named device
1497 static
1498 int makedevicever(CONST_STRPTR name)
1500 struct DevProc *MyDevProc;
1501 int error = -1;
1503 MyDevProc = GetDeviceProc((STRPTR) name, NULL);
1504 if (MyDevProc)
1506 if (MyDevProc->dvp_DevNode->dol_Type == DLT_DEVICE)
1508 BPTR SegList;
1510 SegList = MyDevProc->dvp_DevNode->dol_misc.dol_handler.dol_SegList;
1511 if (SegList)
1513 struct Resident *MyResident;
1515 MyResident = FindLibResident(SegList);
1516 if (MyResident)
1518 error = createresidentver(MyResident);
1520 else
1521 error = RETURN_FAIL;
1524 FreeDeviceProc(MyDevProc);
1527 if (error != RETURN_OK && error != -1)
1529 Printf("Could not find version information for '%s'\n", name);
1532 return error;
1535 static int elf_read_block(BPTR file, ULONG offset, APTR buffer, ULONG size)
1537 if (Seek(file, offset, OFFSET_BEGINNING) < 0)
1538 return 0;
1540 return Read(file, buffer, size);
1543 static void *load_block(BPTR file, ULONG offset, ULONG size)
1545 void *block = AllocMem(size, MEMF_ANY);
1547 if (block)
1549 if (elf_read_block(file, offset, block, size) == size)
1550 return block;
1552 FreeMem(block, size);
1555 return NULL;
1558 static inline UWORD elf_read_word(UWORD data, struct elfheader *eh)
1560 switch (eh->ident[EI_DATA])
1562 case ELFDATA2LSB:
1563 return AROS_LE2WORD(data);
1565 case ELFDATA2MSB:
1566 return AROS_BE2WORD(data);
1568 default:
1569 return 0;
1573 static inline ULONG elf_read_long(ULONG data, struct elfheader *eh)
1575 switch (eh->ident[EI_DATA])
1577 case ELFDATA2LSB:
1578 return AROS_LE2LONG(data);
1580 case ELFDATA2MSB:
1581 return AROS_BE2LONG(data);
1583 default:
1584 return 0;
1588 static ULONG read_shnum(BPTR file, struct elfheader *eh)
1590 ULONG shnum = elf_read_word(eh->shnum, eh);
1592 /* the ELF header only uses 16 bits to store the count of section headers,
1593 * so it can't handle more than 65535 headers. if the count is 0, and an
1594 * offset is defined, then the real count can be found in the first
1595 * section header (which always exists).
1597 * similarly, if the string table index is SHN_XINDEX, then the actual
1598 * index is found in the first section header also.
1600 * see the System V ABI 2001-04-24 draft for more details.
1602 if (shnum == 0)
1604 struct sheader sh;
1605 ULONG shoff = elf_read_long(eh->shoff, eh);
1607 if (shoff == 0)
1608 return 0;
1610 if (elf_read_block(file, shoff, &sh, sizeof(sh)) != sizeof(sh))
1611 return 0;
1613 /* wider section header count is in the size field */
1614 shnum = elf_read_long(sh.size, eh);
1617 return shnum;
1620 static BOOL ARM_ParseAttrs(UBYTE *data, ULONG len, struct elfheader *eh)
1622 struct attrs_section *attrs;
1624 if (data[0] != ATTR_VERSION_CURRENT)
1626 D(Printf("Unknown attributes version: 0x%02\n", data[0]));
1627 return FALSE;
1630 attrs = (void *)data + 1;
1631 while (len > 0)
1633 ULONG attrs_size = elf_read_long(attrs->size, eh);
1635 if (!strcmp(attrs->vendor, "aeabi"))
1637 struct attrs_subsection *aeabi_attrs = (void *)attrs->vendor + 6;
1638 ULONG aeabi_len = attrs_size - 10;
1640 D(Printf("Found aeabi attributes @ 0x%p (length %u)\n", aeabi_attrs, aeabi_len));
1642 while (aeabi_len > 0)
1644 ULONG aeabi_attrs_size = elf_read_long(aeabi_attrs->size, eh);
1646 if (aeabi_attrs->tag == Tag_File)
1648 UBYTE *file_subsection = (void *)aeabi_attrs + sizeof(struct attrs_subsection);
1649 UBYTE file_len = aeabi_attrs_size - sizeof(struct attrs_subsection);
1651 D(Printf("Found file-wide attributes @ 0x%p (length %u)\n", file_subsection, file_len));
1653 while (file_len > 0)
1655 UBYTE tag, shift;
1656 ULONG val = 0;
1658 tag = *file_subsection++;
1659 file_len--;
1661 if (file_len == 0)
1663 D(Printf("Mailformed attribute tag %d (no data)\n", tag));
1664 return FALSE;
1667 switch (tag)
1669 case Tag_CPU_raw_name:
1670 case Tag_CPU_name:
1671 case Tag_compatibility:
1672 case Tag_also_compatible_with:
1673 case Tag_conformance:
1674 /* These two are NULL-terminated strings. Just skip. */
1675 while (file_len)
1677 file_len--;
1678 if (*file_subsection++ == 0)
1679 break;
1681 break;
1683 default:
1684 /* Read ULEB128 value */
1685 shift = 0;
1686 while (file_len)
1688 UBYTE byte;
1690 byte = *file_subsection++;
1691 file_len--;
1693 val |= (byte & 0x7F) << shift;
1694 if (!(byte & 0x80))
1695 break;
1697 shift += 7;
1701 switch (tag)
1703 case Tag_CPU_arch:
1704 D(Printf("ARM CPU architecture set to %d\n", val));
1705 parsedver.pv_arm_cpu = val;
1706 break;
1708 case Tag_FP_arch:
1709 D(Printf("ARM FPU architecture set to %d\n", val));
1710 parsedver.pv_arm_fpu = val;
1711 break;
1715 return TRUE;
1717 aeabi_attrs = (void *)aeabi_attrs + aeabi_attrs_size;
1718 aeabi_len -= aeabi_attrs_size;
1721 return FALSE;
1723 attrs = (void *)attrs + attrs_size;
1724 len -= attrs_size;
1726 return FALSE;
1729 static int arm_read_cpudata(BPTR file, struct elfheader *eh)
1731 struct sheader *sh;
1732 ULONG int_shnum;
1733 ULONG shoff;
1734 UWORD shentsize;
1735 ULONG i;
1737 int_shnum = read_shnum(file, eh);
1738 if (!int_shnum)
1739 return 0;
1741 shoff = elf_read_long(eh->shoff, eh);
1742 shentsize = elf_read_word(eh->shentsize, eh);
1744 /* load section headers */
1745 if (!(sh = load_block(file, shoff, int_shnum * shentsize)))
1746 return 0;
1748 for (i = 0; i < int_shnum; i++)
1750 if (sh[i].type == SHT_ARM_ATTRIBUTES)
1752 ULONG off = elf_read_long(sh[i].offset, eh);
1753 ULONG len = elf_read_long(sh[i].size, eh);
1754 void *data = load_block(file, off, len);
1756 D(Printf("ARM ATTRIBUTES section %d loaded at 0x%p\n", i, data));
1758 if (data)
1760 ARM_ParseAttrs(data, len, eh);
1761 FreeMem(data, len);
1763 break;
1767 FreeMem(sh, int_shnum * shentsize);
1769 return 1;
1773 /* Retrieve version information from file. Return 0 for success.
1775 #define BUFFERSIZE (16384 + 1)
1776 static
1777 int makefilever(CONST_STRPTR name)
1779 BPTR file;
1780 int error; // = RETURN_OK;
1782 file = Open((STRPTR) name, MODE_OLDFILE);
1783 if (file)
1785 UBYTE *buffer;
1787 buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1788 if (buffer)
1790 int len = BUFFERSIZE - 1;
1792 if (args.arg_arch)
1794 ULONG len = Read(file, buffer, sizeof(struct elfheader));
1796 if (len == sizeof(struct elfheader))
1798 if (buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F')
1800 /* It's an ELF file, read machine ID */
1801 struct elfheader *eh = (struct elfheader *)buffer;
1803 parsedver.pv_arch = elf_read_word(eh->machine, eh);
1804 if (parsedver.pv_arch == EM_ARM)
1805 arm_read_cpudata(file, eh);
1808 else if (len >= 4)
1810 if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0x03 && buffer[3] == 0xF3)
1812 /* It's AmigaOS hunk file. m68k obviously :) */
1813 parsedver.pv_arch = EM_68K;
1816 /* Rewind the file */
1817 Seek(file, 0, OFFSET_BEGINNING);
1820 error = findinfile(file, ver, buffer, &len, parsedver.pv_md5sum);
1821 if (error == RETURN_OK)
1823 parsedver.pv_flags |= PVF_MD5SUM;
1825 if (len >= 0)
1827 STRPTR startbuffer;
1829 buffer[len] = '\0';
1830 startbuffer = skipwhites(buffer);
1832 //Printf("startbuffer \"%s\"\n", startbuffer);
1833 error = makedatafromstring(startbuffer);
1835 else
1837 /* Try LoadSeg
1839 error = RETURN_ERROR;
1841 Close(file);
1843 file = LoadSeg((STRPTR) name);
1844 if (file)
1846 struct Resident *MyResident;
1848 MyResident = FindLibResident(file);
1849 if (MyResident /*&&
1850 (MyResident->rt_Type == NT_LIBRARY ||
1851 MyResident->rt_Type == NT_DEVICE)*/)
1853 error = createresidentver(MyResident);
1856 UnLoadSeg(file);
1858 file = BNULL;
1860 if (error != RETURN_OK)
1862 /* If user didn't ask for md5sum or we could not calculate it.
1864 if (!args.arg_md5sum || (!(parsedver.pv_flags & PVF_MD5SUM)))
1866 Printf("Could not find version information for '%s'\n", (IPTR) name);
1871 else
1873 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1876 FreeMem(buffer, BUFFERSIZE);
1878 else
1880 error = RETURN_FAIL;
1881 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1884 if (file)
1885 Close(file);
1887 else
1889 LONG ioerr = IoErr();
1891 if (ioerr == ERROR_OBJECT_NOT_FOUND ||
1892 ioerr == ERROR_OBJECT_WRONG_TYPE)
1894 error = -1;
1896 else
1898 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1899 error = RETURN_FAIL;
1903 return error;
1907 static
1908 int makerescmdver(CONST_STRPTR name)
1910 int error = -1;
1911 struct Segment *segment;
1913 Forbid();
1915 segment = FindSegment((STRPTR) name, NULL, 0);
1916 if (!segment)
1918 segment = FindSegment((STRPTR) name, NULL, 1);
1921 if (segment)
1923 if (segment->seg_UC == CMD_INTERNAL ||
1924 segment->seg_UC == CMD_DISABLED)
1926 Permit();
1927 error = makeresidentver("shell");
1928 Forbid();
1930 else
1932 STRPTR buffer = FindSegmentVER(segment->seg_Seg);
1933 if (buffer)
1935 STRPTR startbuffer;
1937 startbuffer = skipwhites(buffer);
1939 //Printf("startbuffer \"%s\"\n", (LONG) startbuffer);
1940 error = makedatafromstring(startbuffer);
1942 FreeVec(buffer);
1947 Permit();
1949 return error;
1952 static
1953 int setvervar(CONST_STRPTR name, LONG ver, LONG rev)
1955 UBYTE buf[32];
1957 __sprintf(buf, "%ld.%ld", (long) ver, (long) rev);
1959 return SetVar((STRPTR) name, buf, -1, GVF_LOCAL_ONLY | LV_VAR) ? RETURN_OK : -1;
1963 static
1964 int makekickversion(void)
1966 parsedver.pv_version = SysBase->LibNode.lib_Version;
1967 parsedver.pv_revision = SysBase->SoftVer;
1969 setvervar("Kickstart",
1970 parsedver.pv_version,
1971 parsedver.pv_revision);
1973 Printf("Kickstart %ld.%ld",
1974 (LONG) parsedver.pv_version, (LONG) parsedver.pv_revision);
1976 return RETURN_OK;
1980 static
1981 int makewbversion(void)
1983 int error = -1;
1984 struct Library *VersionBase;
1986 VersionBase = OpenLibrary("version.library", 0);
1987 if (VersionBase)
1989 error = makeexeclistver(&SysBase->LibList, "version.library");
1991 if (error == RETURN_OK)
1993 STRPTR newname = dupstr("Workbench", -1);
1994 if (newname)
1996 FreeVec(parsedver.pv_name);
1997 parsedver.pv_name = newname;
1999 setvervar("Workbench", parsedver.pv_version, parsedver.pv_revision);
2002 CloseLibrary(VersionBase);
2005 return error;
2009 static
2010 int makesysver(void)
2012 int error;
2014 error = makekickversion();
2015 if (error == RETURN_OK)
2017 error = makewbversion();
2019 if (error == RETURN_OK)
2021 PutStr(", ");
2023 else
2025 /* prevent silly errormsg if no version.library */
2026 PutStr("\n");
2027 error = RETURN_WARN;
2031 return error;
2035 /* Determine, by which means to get the version-string.
2037 static
2038 int makeverstring(CONST_STRPTR name)
2040 int error; // = RETURN_OK;
2041 BOOL volume = name[strlen(name) - 1] == ':';
2042 CONST_STRPTR filepart = FilePart(name);
2044 error = -1;
2046 if (!volume && !args.arg_file)
2048 if (*filepart)
2050 error = makeresidentver(filepart);
2051 if (error != RETURN_OK)
2054 /* Try libraries
2056 error = makeexeclistver(&SysBase->LibList, filepart);
2057 if (error != RETURN_OK)
2059 STRPTR namebuf;
2060 ULONG namelen = strlen(filepart);
2062 /* 12 is "MOSSYS:LIBS/" */
2063 if ((namebuf = AllocVec(12 + namelen + 4 + 1, MEMF_PUBLIC)))
2065 strcpy(namebuf, "LIBS:");
2066 strcat(namebuf, filepart);
2067 error = makefilever(namebuf);
2069 /* Try devices
2071 if (error != RETURN_OK)
2073 error = makeexeclistver(&SysBase->DeviceList, filepart);
2074 if (error != RETURN_OK)
2076 strcpy(namebuf, "DEVS:");
2077 strcat(namebuf, filepart);
2078 error = makefilever(namebuf);
2081 FreeVec(namebuf);
2088 if (!args.arg_res && error == -1)
2090 if (volume)
2092 error = makedevicever(name);
2094 else
2096 if (*filepart)
2098 error = makefilever(name);
2103 if (!args.arg_file && error == -1)
2105 error = makerescmdver(name);
2108 if (error)
2110 /* If user asked for md5sum, and we could calculate it, don't print error
2111 * but the md5sum + file.
2113 if (args.arg_md5sum && (parsedver.pv_flags & PVF_MD5SUM))
2115 parsedver.pv_name = dupstr(name, -1);
2116 parsedver.pv_flags |= PVF_NOVERSION;
2117 error = RETURN_OK;
2121 if (error == -1)
2123 PrintFault(ERROR_OBJECT_NOT_FOUND, (STRPTR) ERROR_HEADER);
2124 error = RETURN_FAIL;
2127 return error;
2131 static
2132 void freeverstring(void)
2134 parsedver.pv_flags = 0;
2135 parsedver.pv_version = 0;
2136 parsedver.pv_revision = 0;
2138 FreeVec(parsedver.pv_extrastr);
2139 parsedver.pv_extrastr = NULL;
2140 parsedver.pv_extralf = NULL;
2141 FreeVec(parsedver.pv_datestr);
2142 parsedver.pv_datestr = NULL;
2143 FreeVec(parsedver.pv_revname);
2144 parsedver.pv_revname = NULL;
2145 FreeVec(parsedver.pv_vername);
2146 parsedver.pv_vername = NULL;
2147 FreeVec(parsedver.pv_name);
2148 parsedver.pv_name = NULL;
2151 /* Compare the version given as argument with the version from the object.
2152 * Return RETURN_WARN, if args-v>object-v, otherwise return RETURN_OK.
2154 static
2155 int cmpargsparsed(void)
2157 if (args.arg_version)
2159 if (*(args.arg_version) > parsedver.pv_version)
2161 return RETURN_WARN;
2163 else if (*(args.arg_version) == parsedver.pv_version && args.arg_revision)
2165 if (*(args.arg_revision) > parsedver.pv_revision)
2167 return RETURN_WARN;
2171 else if (args.arg_revision)
2173 if (*(args.arg_revision) > parsedver.pv_revision)
2175 return RETURN_WARN;
2178 return RETURN_OK;
2181 /******************************* main program ****************************/
2183 int __nocommandline;
2185 int main (void)
2187 LONG error = RETURN_FAIL;
2189 struct RDArgs *rda;
2191 rda = ReadArgs(TEMPLATE, (IPTR *) &args, NULL);
2192 if (rda)
2194 if (!args.arg_name || !*args.arg_name)
2196 /* No args, make system version */
2197 error = makesysver();
2198 if (error == RETURN_OK)
2200 printverstring();
2201 if (parsedver.pv_flags & PVF_NOVERSION)
2203 error = RETURN_FAIL;
2205 if (error == RETURN_OK)
2207 error = cmpargsparsed();
2210 freeverstring();
2212 else
2214 CONST_STRPTR *name;
2215 BOOL multifile;
2216 #if 1
2217 /* Workaround for:
2218 * version file ver
2219 * version file ver rev
2221 if (!args.arg_version && !args.arg_revision)
2223 LONG narg = 1;
2224 while (args.arg_name[narg]) { narg++; }
2225 if (narg == 2 || narg == 3)
2227 if (StrToLong(args.arg_name[1], &mversion) > 0)
2229 args.arg_version = &mversion;
2230 args.arg_name[1] = args.arg_name[2];
2231 if (narg == 3)
2233 args.arg_name[2] = NULL;
2235 if (narg == 3)
2237 if (StrToLong(args.arg_name[1], &mrevision) > 0)
2239 args.arg_revision = &mrevision;
2240 args.arg_name[1] = NULL;
2246 #endif
2247 multifile = args.arg_name[1] != NULL;
2249 for (name = args.arg_name; *name; name++)
2251 error = makeverstring(*name);
2252 if (error == RETURN_OK)
2254 printverstring();
2256 if (!multifile)
2258 /* Single args, do compare stuff also */
2259 if (parsedver.pv_flags & PVF_NOVERSION)
2261 error = RETURN_FAIL;
2263 if (error == RETURN_OK)
2265 error = cmpargsparsed();
2269 freeverstring();
2274 FreeArgs(rda);
2276 else
2278 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
2279 error = RETURN_FAIL;
2282 RT_Exit();
2284 return error;