Fixed out-by-one error in previous commit.
[AROS.git] / workbench / c / Version.c
blob03b1e7373396c62d6c93e8d19f8c8b822a4a7794
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 -- #FIXME what is that?
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
45 EXAMPLE
47 BUGS
49 SEE ALSO
51 INTERNALS
53 HISTORY
55 ******************************************************************************/
57 #define ELF_32BIT /* This is needed for correct .ARM.Attributes parsing */
59 #include <aros/arosbase.h>
60 #include <aros/debug.h>
61 #include <aros/inquire.h>
62 #include <proto/aros.h>
64 #define ENABLE_RT 1
65 #include <aros/rt.h>
67 #include <string.h>
68 #include <ctype.h>
70 #include <proto/exec.h>
71 #include <exec/execbase.h>
72 #include <exec/libraries.h>
73 #include <exec/memory.h>
74 #include <exec/resident.h>
75 #include <proto/dos.h>
76 #include <proto/utility.h>
77 #include <proto/alib.h>
78 #include <dos/datetime.h>
79 #include <dos/dos.h>
80 #include <dos/dosextens.h>
81 #include <dos/elf.h>
83 /*===[md5.h]==============================================================*/
85 /* Data structure for MD5 (Message-Digest) computation */
86 typedef struct {
87 ULONG buf[4]; /* scratch buffer */
88 ULONG i[2]; /* number of _bits_ handled mod 2^64 */
89 unsigned char in[64]; /* input buffer */
90 } MD5_CTX;
92 void MD5Init(MD5_CTX *mdContext);
93 void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
94 void MD5Final(unsigned char digest[16], MD5_CTX *mdContext);
96 /*==[md5.c]===============================================================*/
99 * Used in 'version' as is, just removed the #include "ambient.h"
101 * Harry Sintonen <sintonen@iki.fi>
105 * Ambient - the ultimate desktop
106 * ------------------------------
107 * (c) 2001-2003 by David Gerber <zapek@meanmachine.ch>
108 * All Rights Reserved
110 * $Id$
113 //#include "ambient.h"
114 //#include <exec/types.h>
117 ***********************************************************************
118 ** md5.c -- the source code for MD5 routines **
119 ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
120 ** Created: 2/17/90 RLR **
121 ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
122 ***********************************************************************
126 * Edited 7 May 93 by CP to change the interface to match that
127 * of the MD5 routines in RSAREF. Due to this alteration, this
128 * code is "derived from the RSA Data Security, Inc. MD5 Message-
129 * Digest Algorithm". (See below.)
133 ***********************************************************************
134 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
135 ** **
136 ** License to copy and use this software is granted provided that **
137 ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
138 ** Digest Algorithm" in all material mentioning or referencing this **
139 ** software or this function. **
140 ** **
141 ** License is also granted to make and use derivative works **
142 ** provided that such works are identified as "derived from the RSA **
143 ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
144 ** material mentioning or referencing the derived work. **
145 ** **
146 ** RSA Data Security, Inc. makes no representations concerning **
147 ** either the merchantability of this software or the suitability **
148 ** of this software for any particular purpose. It is provided "as **
149 ** is" without express or implied warranty of any kind. **
150 ** **
151 ** These notices must be retained in any copies of any part of this **
152 ** documentation and/or software. **
153 ***********************************************************************
156 //#include "md5.h"
159 ***********************************************************************
160 ** Message-digest routines: **
161 ** To form the message digest for a message M **
162 ** (1) Initialize a context buffer mdContext using MD5Init **
163 ** (2) Call MD5Update on mdContext and M **
164 ** (3) Call MD5Final on mdContext **
165 ** The message digest is now in the bugffer passed to MD5Final **
166 ***********************************************************************
169 static unsigned char PADDING[64] = {
170 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 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
180 /* F, G, H and I are basic MD5 functions */
181 #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
182 #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
183 #define H(x, y, z) ((x) ^ (y) ^ (z))
184 #define I(x, y, z) ((y) ^ ((x) | (~z)))
186 /* ROTATE_LEFT rotates x left n bits */
187 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
189 /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
190 /* Rotation is separate from addition to prevent recomputation */
191 #define FF(a, b, c, d, x, s, ac) \
192 {(a) += F ((b), (c), (d)) + (x) + (ULONG)(ac); \
193 (a) = ROTATE_LEFT ((a), (s)); \
194 (a) += (b); \
196 #define GG(a, b, c, d, x, s, ac) \
197 {(a) += G ((b), (c), (d)) + (x) + (ULONG)(ac); \
198 (a) = ROTATE_LEFT ((a), (s)); \
199 (a) += (b); \
201 #define HH(a, b, c, d, x, s, ac) \
202 {(a) += H ((b), (c), (d)) + (x) + (ULONG)(ac); \
203 (a) = ROTATE_LEFT ((a), (s)); \
204 (a) += (b); \
206 #define II(a, b, c, d, x, s, ac) \
207 {(a) += I ((b), (c), (d)) + (x) + (ULONG)(ac); \
208 (a) = ROTATE_LEFT ((a), (s)); \
209 (a) += (b); \
212 static void Transform(register ULONG *buf,register ULONG *in);
214 /* The routine MD5Init initializes the message-digest context
215 mdContext. All fields are set to zero.
217 void MD5Init ( MD5_CTX *mdContext)
219 mdContext->i[0] = mdContext->i[1] = (ULONG)0;
221 /* Load magic initialization constants.
223 mdContext->buf[0] = (ULONG)0x67452301L;
224 mdContext->buf[1] = (ULONG)0xefcdab89L;
225 mdContext->buf[2] = (ULONG)0x98badcfeL;
226 mdContext->buf[3] = (ULONG)0x10325476L;
229 /* The routine MD5Update updates the message-digest context to
230 account for the presence of each of the characters inBuf[0..inLen-1]
231 in the message whose digest is being computed.
233 void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf,
234 unsigned int inLen)
236 register int i, ii;
237 int mdi;
238 ULONG in[16];
240 /* compute number of bytes mod 64 */
241 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
243 /* update number of bits */
244 if ((mdContext->i[0] + ((ULONG)inLen << 3)) < mdContext->i[0])
245 mdContext->i[1]++;
246 mdContext->i[0] += ((ULONG)inLen << 3);
247 mdContext->i[1] += ((ULONG)inLen >> 29);
249 while (inLen--)
251 /* add new character to buffer, increment mdi */
252 mdContext->in[mdi++] = *inBuf++;
254 /* transform if necessary */
255 if (mdi == 0x40)
257 for (i = 0, ii = 0; i < 16; i++, ii += 4)
258 in[i] = (((ULONG)mdContext->in[ii+3]) << 24) |
259 (((ULONG)mdContext->in[ii+2]) << 16) |
260 (((ULONG)mdContext->in[ii+1]) << 8) |
261 ((ULONG)mdContext->in[ii]);
262 Transform (mdContext->buf, in);
263 mdi = 0;
268 /* The routine MD5Final terminates the message-digest computation and
269 ends with the desired message digest in mdContext->digest[0...15].
271 void MD5Final (unsigned char digest[16], MD5_CTX *mdContext)
273 ULONG in[16];
274 int mdi;
275 unsigned int i, ii;
276 unsigned int padLen;
278 /* save number of bits */
279 in[14] = mdContext->i[0];
280 in[15] = mdContext->i[1];
282 /* compute number of bytes mod 64 */
283 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
285 /* pad out to 56 mod 64 */
286 padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
287 MD5Update (mdContext, PADDING, padLen);
289 /* append length in bits and transform */
290 for (i = 0, ii = 0; i < 14; i++, ii += 4)
291 in[i] = (((ULONG)mdContext->in[ii+3]) << 24) |
292 (((ULONG)mdContext->in[ii+2]) << 16) |
293 (((ULONG)mdContext->in[ii+1]) << 8) |
294 ((ULONG)mdContext->in[ii]);
295 Transform (mdContext->buf, in);
297 /* store buffer in digest */
298 for (i = 0, ii = 0; i < 4; i++, ii += 4)
300 digest[ii] = (unsigned char) (mdContext->buf[i] & 0xFF);
301 digest[ii+1] = (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
302 digest[ii+2] = (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
303 digest[ii+3] = (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
307 /* Basic MD5 step. Transforms buf based on in. Note that if the Mysterious
308 Constants are arranged backwards in little-endian order and decrypted with
309 the DES they produce OCCULT MESSAGES!
311 void Transform(register ULONG *buf,register ULONG *in)
313 register ULONG a = buf[0], b = buf[1], c = buf[2], d = buf[3];
315 /* Round 1 */
316 #define S11 7
317 #define S12 12
318 #define S13 17
319 #define S14 22
320 FF ( a, b, c, d, in[ 0], S11, 0xD76AA478L); /* 1 */
321 FF ( d, a, b, c, in[ 1], S12, 0xE8C7B756L); /* 2 */
322 FF ( c, d, a, b, in[ 2], S13, 0x242070DBL); /* 3 */
323 FF ( b, c, d, a, in[ 3], S14, 0xC1BDCEEEL); /* 4 */
324 FF ( a, b, c, d, in[ 4], S11, 0xF57C0FAFL); /* 5 */
325 FF ( d, a, b, c, in[ 5], S12, 0x4787C62AL); /* 6 */
326 FF ( c, d, a, b, in[ 6], S13, 0xA8304613L); /* 7 */
327 FF ( b, c, d, a, in[ 7], S14, 0xFD469501L); /* 8 */
328 FF ( a, b, c, d, in[ 8], S11, 0x698098D8L); /* 9 */
329 FF ( d, a, b, c, in[ 9], S12, 0x8B44F7AFL); /* 10 */
330 FF ( c, d, a, b, in[10], S13, 0xFFFF5BB1L); /* 11 */
331 FF ( b, c, d, a, in[11], S14, 0x895CD7BEL); /* 12 */
332 FF ( a, b, c, d, in[12], S11, 0x6B901122L); /* 13 */
333 FF ( d, a, b, c, in[13], S12, 0xFD987193L); /* 14 */
334 FF ( c, d, a, b, in[14], S13, 0xA679438EL); /* 15 */
335 FF ( b, c, d, a, in[15], S14, 0x49B40821L); /* 16 */
337 /* Round 2 */
338 #define S21 5
339 #define S22 9
340 #define S23 14
341 #define S24 20
342 GG ( a, b, c, d, in[ 1], S21, 0xF61E2562L); /* 17 */
343 GG ( d, a, b, c, in[ 6], S22, 0xC040B340L); /* 18 */
344 GG ( c, d, a, b, in[11], S23, 0x265E5A51L); /* 19 */
345 GG ( b, c, d, a, in[ 0], S24, 0xE9B6C7AAL); /* 20 */
346 GG ( a, b, c, d, in[ 5], S21, 0xD62F105DL); /* 21 */
347 GG ( d, a, b, c, in[10], S22, 0x02441453L); /* 22 */
348 GG ( c, d, a, b, in[15], S23, 0xD8A1E681L); /* 23 */
349 GG ( b, c, d, a, in[ 4], S24, 0xE7D3FBC8L); /* 24 */
350 GG ( a, b, c, d, in[ 9], S21, 0x21E1CDE6L); /* 25 */
351 GG ( d, a, b, c, in[14], S22, 0xC33707D6L); /* 26 */
352 GG ( c, d, a, b, in[ 3], S23, 0xF4D50D87L); /* 27 */
353 GG ( b, c, d, a, in[ 8], S24, 0x455A14EDL); /* 28 */
354 GG ( a, b, c, d, in[13], S21, 0xA9E3E905L); /* 29 */
355 GG ( d, a, b, c, in[ 2], S22, 0xFCEFA3F8L); /* 30 */
356 GG ( c, d, a, b, in[ 7], S23, 0x676F02D9L); /* 31 */
357 GG ( b, c, d, a, in[12], S24, 0x8D2A4C8AL); /* 32 */
359 /* Round 3 */
360 #define S31 4
361 #define S32 11
362 #define S33 16
363 #define S34 23
364 HH ( a, b, c, d, in[ 5], S31, 0xFFFA3942L); /* 33 */
365 HH ( d, a, b, c, in[ 8], S32, 0x8771F681L); /* 34 */
366 HH ( c, d, a, b, in[11], S33, 0x6D9D6122L); /* 35 */
367 HH ( b, c, d, a, in[14], S34, 0xFDE5380CL); /* 36 */
368 HH ( a, b, c, d, in[ 1], S31, 0xA4BEEA44L); /* 37 */
369 HH ( d, a, b, c, in[ 4], S32, 0x4BDECFA9L); /* 38 */
370 HH ( c, d, a, b, in[ 7], S33, 0xF6BB4B60L); /* 39 */
371 HH ( b, c, d, a, in[10], S34, 0xBEBFBC70L); /* 40 */
372 HH ( a, b, c, d, in[13], S31, 0x289B7EC6L); /* 41 */
373 HH ( d, a, b, c, in[ 0], S32, 0xEAA127FAL); /* 42 */
374 HH ( c, d, a, b, in[ 3], S33, 0xD4EF3085L); /* 43 */
375 HH ( b, c, d, a, in[ 6], S34, 0x04881D05L); /* 44 */
376 HH ( a, b, c, d, in[ 9], S31, 0xD9D4D039L); /* 45 */
377 HH ( d, a, b, c, in[12], S32, 0xE6DB99E5L); /* 46 */
378 HH ( c, d, a, b, in[15], S33, 0x1FA27CF8L); /* 47 */
379 HH ( b, c, d, a, in[ 2], S34, 0xC4AC5665L); /* 48 */
381 /* Round 4 */
382 #define S41 6
383 #define S42 10
384 #define S43 15
385 #define S44 21
386 II ( a, b, c, d, in[ 0], S41, 0xF4292244L); /* 49 */
387 II ( d, a, b, c, in[ 7], S42, 0x432AFF97L); /* 50 */
388 II ( c, d, a, b, in[14], S43, 0xAB9423A7L); /* 51 */
389 II ( b, c, d, a, in[ 5], S44, 0xFC93A039L); /* 52 */
390 II ( a, b, c, d, in[12], S41, 0x655B59C3L); /* 53 */
391 II ( d, a, b, c, in[ 3], S42, 0x8F0CCC92L); /* 54 */
392 II ( c, d, a, b, in[10], S43, 0xFFEFF47DL); /* 55 */
393 II ( b, c, d, a, in[ 1], S44, 0x85845DD1L); /* 56 */
394 II ( a, b, c, d, in[ 8], S41, 0x6FA87E4FL); /* 57 */
395 II ( d, a, b, c, in[15], S42, 0xFE2CE6E0L); /* 58 */
396 II ( c, d, a, b, in[ 6], S43, 0xA3014314L); /* 59 */
397 II ( b, c, d, a, in[13], S44, 0x4E0811A1L); /* 60 */
398 II ( a, b, c, d, in[ 4], S41, 0xF7537E82L); /* 61 */
399 II ( d, a, b, c, in[11], S42, 0xBD3AF235L); /* 62 */
400 II ( c, d, a, b, in[ 2], S43, 0x2AD7D2BBL); /* 63 */
401 II ( b, c, d, a, in[ 9], S44, 0xEB86D391L); /* 64 */
403 buf[0] += a;
404 buf[1] += b;
405 buf[2] += c;
406 buf[3] += d;
409 /*==[end md5.c]============================================================*/
412 const TEXT version[] = "$VER: Version 42.2 (22.05.2011)\n";
414 static const char ERROR_HEADER[] = "Version";
416 #define TEMPLATE "NAME/M,MD5SUM/S,VERSION/N,REVISION/N,FILE/S,FULL/S,RES/S,ARCH/S"
417 struct
419 CONST_STRPTR *arg_name;
420 IPTR arg_md5sum;
421 LONG *arg_version;
422 LONG *arg_revision;
423 IPTR arg_file;
424 IPTR arg_full;
425 IPTR arg_res;
426 IPTR arg_arch;
428 args;
430 LONG mversion, mrevision;
432 struct
434 STRPTR pv_name;
435 ULONG pv_flags;
436 LONG pv_version;
437 LONG pv_revision;
438 UWORD pv_arch;
439 UBYTE pv_arm_cpu;
440 UBYTE pv_arm_fpu;
441 //LONG pv_days;
442 STRPTR pv_vername;
443 STRPTR pv_revname;
444 STRPTR pv_datestr;
445 STRPTR pv_extralf;
446 STRPTR pv_extrastr;
447 UBYTE pv_md5sum[16];
449 parsedver = { NULL, 0, 0, 0, 0, -1, 0, NULL, NULL, NULL, NULL, NULL, {0}};
451 #define PVF_MD5SUM (1 << 0)
452 #define PVF_NOVERSION (1 << 1)
454 static
455 int makeverstring(CONST_STRPTR name);
456 static
457 void printverstring(void);
458 static
459 void freeverstring(void);
460 static
461 int makesysver(void);
462 static
463 int cmpargsparsed(void);
466 /**************************** support functions ************************/
468 /* Duplicate string, by given length or -1 for full length
470 static
471 STRPTR dupstr(CONST_STRPTR buffer, LONG len)
473 STRPTR ret = NULL;
475 if (buffer)
477 if (len == -1)
478 len = strlen(buffer);
480 ret = AllocVec(len + 1, MEMF_ANY);
481 if (ret)
483 CopyMem((STRPTR) buffer, ret, len);
484 ret[len] = '\0';
488 return ret;
492 static
493 inline int myisspace(int c)
495 return (c == ' ' || c == '\t');
499 /* Return a pointer to a string, stripped by all leading whitespace characters
500 * (SPACE, TAB).
502 static
503 STRPTR skipwhites(CONST_STRPTR buffer)
505 for (;; buffer++)
507 if (buffer[0] == '\0' || !isspace(buffer[0]))
509 return (STRPTR) buffer;
515 /* Return a pointer to a string, stripped by all leading space characters
516 * (SPACE).
518 static
519 STRPTR skipspaces(CONST_STRPTR buffer)
521 for (;; buffer++)
523 if (buffer[0] == '\0' || buffer[0] != ' ')
525 return (STRPTR) buffer;
531 /* Strip all whitespace-characters from the end of a string. Note that the
532 * buffer passed in will be modified!
534 static
535 void stripwhites(STRPTR buffer)
537 int len = strlen(buffer);
539 while (len > 0)
541 if (!isspace(buffer[len-1]))
543 buffer[len] = '\0';
544 return;
546 len--;
548 buffer[len] = '\0';
552 /* Searches for a given string in a file and stores up to *lenptr characters
553 * into the buffer beginning with the first character after the given string.
555 static
556 int findinfile(BPTR file, CONST_STRPTR string, STRPTR buffer, int *lenptr, unsigned char digest[16])
558 int error = RETURN_OK;
559 int buflen = *lenptr, len = 0, pos, stringlen;
560 BOOL ready = FALSE;
561 MD5_CTX md5ctx;
562 STRPTR bufpos;
563 STRPTR tmp;
565 tmp = AllocMem(buflen, MEMF_PUBLIC);
566 if (!tmp)
568 return RETURN_FAIL;
571 stringlen = strlen(string);
572 *lenptr = -1;
574 if (args.arg_md5sum)
576 MD5Init(&md5ctx);
579 bufpos = tmp;
580 while ((len = Read(file, &tmp[len], buflen - len)) > 0)
582 pos = 0;
584 if (args.arg_md5sum)
586 MD5Update(&md5ctx, bufpos, len);
589 if (ready)
591 /* If we get here we're scanning the rest of the file for md5sum. - Piru */
592 len = 0;
594 else
596 while ((len - pos) >= stringlen)
598 /* Compare the current buffer position with the supplied string. */
599 if (strncmp(&tmp[pos], string, stringlen) == 0)
601 /* It is equal! Now move the rest of the buffer to the top of
602 * the buffer and fill it up.
604 int findstrlen = len - pos;
606 memcpy(buffer, &tmp[pos + stringlen], findstrlen);
608 len = Read(file, &buffer[findstrlen], buflen - findstrlen);
609 if (len >= 0)
611 if (args.arg_md5sum)
613 MD5Update(&md5ctx, &buffer[findstrlen], len);
616 *lenptr = findstrlen + len;
618 else
620 error = RETURN_FAIL;
622 ready = TRUE;
623 break;
625 pos++;
627 /* Move the rest of the buffer that could not be compared (because it
628 * is smaller than the string to compare) to the top of the buffer.
630 if (!ready)
632 memmove(tmp, &tmp[len - stringlen], stringlen);
634 else
636 /* If we're not md5summing, stop file scanning now. - Piru */
637 if (!args.arg_md5sum)
639 break;
642 len = stringlen;
645 bufpos = &tmp[len];
648 FreeMem(tmp, buflen);
650 if (len == -1)
652 error = RETURN_FAIL;
655 if (args.arg_md5sum)
657 memset(digest, 0, 16);
658 MD5Final(digest, &md5ctx);
661 return error;
665 /*************************** parsing functions *************************/
667 /* Convert a date in the form DD.MM.YY or DD.MM.YYYY into a numerical
668 * value. Return FALSE, if buffer doesn't contain a valid date.
670 static
671 BOOL makedatefromstring(CONST_STRPTR *bufptr)
673 CONST_STRPTR buffer = *bufptr;
674 struct DateTime dt;
675 CONST_STRPTR headerstart, end;
676 STRPTR newbuf;
677 LONG res;
678 int len, i;
679 UBYTE c;
681 //if (isspace(buffer[0]))
682 // buffer++;
684 headerstart = buffer;
686 buffer = strchr(buffer, '(');
687 if (!buffer)
689 return FALSE;
691 buffer++;
692 end = strchr(buffer, ')');
693 if (!end)
695 return FALSE;
697 len = (int)(end - buffer);
698 newbuf = dupstr(buffer, len);
699 if (!newbuf)
701 return FALSE;
703 for (i = 0; i < len; i++)
705 c = newbuf[i];
707 if (c == '.' || c == '/')
708 newbuf[i] = '-';
709 else if (!isalnum(c))
711 end = buffer + i;
712 newbuf[i] = '\0';
713 break;
717 D(Printf("date: \"%s\"\n", newbuf));
719 dt.dat_Format = FORMAT_CDN;
720 dt.dat_Flags = 0;
721 dt.dat_StrDay = NULL;
722 dt.dat_StrDate = newbuf;
723 dt.dat_StrTime = NULL;
724 res = StrToDate(&dt);
725 if (!res)
727 dt.dat_Format = FORMAT_DOS;
728 res = StrToDate(&dt);
729 if (!res)
731 //Printf("StrToDate failed!\n");
732 FreeVec(newbuf);
733 return FALSE;
736 FreeVec(newbuf);
738 //parsedver.pv_days = dt.dat_Stamp.ds_Days;
740 parsedver.pv_datestr = AllocVec(buffer - headerstart + LEN_DATSTRING + 2, MEMF_ANY);
741 if (!parsedver.pv_datestr)
743 return FALSE;
746 dt.dat_Stamp.ds_Minute = 0;
747 dt.dat_Stamp.ds_Tick = 0;
749 dt.dat_StrDate = parsedver.pv_datestr + (buffer - headerstart);
750 dt.dat_Format = FORMAT_DEF;
751 if (!DateToStr(&dt))
753 //Printf("DateToStr failed!\n");
754 return FALSE;
757 CopyMem((STRPTR) headerstart, parsedver.pv_datestr, buffer - headerstart);
758 res = strlen(parsedver.pv_datestr);
759 parsedver.pv_datestr[res++] = ')';
760 parsedver.pv_datestr[res] = '\0';
762 *bufptr = end + 1;
764 return TRUE;
768 /* Check whether the given string contains a version in the form
769 * <version>.<revision> . If not return FALSE, otherwise fill in parsedver and
770 * return TRUE.
772 static
773 BOOL makeversionfromstring(CONST_STRPTR *bufptr)
775 LONG pos, ver, rev;
776 CONST_STRPTR buffer = *bufptr;
777 CONST_STRPTR verstart, revstart;
779 //Printf("makeversionfromstring: buffer \"%s\"\n", (LONG) buffer);
781 /* Do version */
783 verstart = buffer;
784 pos = StrToLong((STRPTR) buffer, &ver);
785 if (pos == -1)
787 return FALSE;
789 parsedver.pv_version = ver;
790 parsedver.pv_revision = -1;
792 parsedver.pv_vername = dupstr(verstart, pos);
793 if (!parsedver.pv_vername)
795 return FALSE;
798 /* Do revision */
800 buffer += pos;
801 revstart = buffer;
802 buffer = skipspaces(buffer); /* NOTE: skipspaces, not skipwhites! */
803 if (*buffer != '.')
805 *bufptr = buffer;
806 return TRUE;
809 buffer++;
810 pos = StrToLong((STRPTR) buffer, &rev);
811 if (pos == -1)
813 *bufptr = buffer;
814 return TRUE;
817 parsedver.pv_revision = rev;
819 /* calc the revision string len */
820 pos = buffer + pos - revstart;
821 parsedver.pv_revname = dupstr(revstart, pos);
822 if (!parsedver.pv_revname)
824 return FALSE;
827 *bufptr = revstart + pos;
829 return TRUE;
832 static const char *arm_cpus[] =
834 "ARM Pre-v4",
835 "ARMv4",
836 "ARMv4T",
837 "ARMv5T",
838 "ARMv5TE",
839 "ARMv5TEJ",
840 "ARMv6",
841 "ARMv6KZ",
842 "ARMv6T2",
843 "ARMv6K",
844 "ARMv7",
845 "ARMv6-M",
846 "ARMv6S-M",
847 "ARMv7E-M"
850 static const char *arm_fpus[] =
852 "VFP",
853 "VFPv2",
854 "VFPv3",
855 "VFPv3 (D0 - D15)",
856 "VFPv4",
857 "VFPv4 (D0 - D15)"
860 static
861 void printverstring(void)
863 if (args.arg_md5sum)
865 if (parsedver.pv_flags & PVF_MD5SUM)
867 /* Endianess safe version */
868 Printf("%02lX%02lX%02lX%02lX"
869 "%02lX%02lX%02lX%02lX"
870 "%02lX%02lX%02lX%02lX"
871 "%02lX%02lX%02lX%02lX ",
872 parsedver.pv_md5sum[0],
873 parsedver.pv_md5sum[1],
874 parsedver.pv_md5sum[2],
875 parsedver.pv_md5sum[3],
876 parsedver.pv_md5sum[4],
877 parsedver.pv_md5sum[5],
878 parsedver.pv_md5sum[6],
879 parsedver.pv_md5sum[7],
880 parsedver.pv_md5sum[8],
881 parsedver.pv_md5sum[9],
882 parsedver.pv_md5sum[10],
883 parsedver.pv_md5sum[11],
884 parsedver.pv_md5sum[12],
885 parsedver.pv_md5sum[13],
886 parsedver.pv_md5sum[14],
887 parsedver.pv_md5sum[15]);
889 else
891 /* "01234567012345670123456701234567: " */
892 PutStr("<no md5sum available> ");
896 if (parsedver.pv_flags & PVF_NOVERSION)
898 Printf("%s\n", (IPTR) parsedver.pv_name);
900 else
902 if (args.arg_full)
904 /* If md5sum output was there, avoid linefeed to allow parsing the output - Piru */
905 if (args.arg_md5sum)
907 parsedver.pv_extralf = " ";
910 Printf("%s%s%s%s%s%s%s\n",
911 (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""),
912 (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname,
913 (IPTR) (parsedver.pv_datestr ? (IPTR)parsedver.pv_datestr : (IPTR)""),
914 (IPTR) (parsedver.pv_extralf ? (IPTR)parsedver.pv_extralf : (IPTR)""),
915 (IPTR) (parsedver.pv_extrastr ? (IPTR)parsedver.pv_extrastr : (IPTR)""));
917 else
919 Printf("%s%s%s%s\n",
920 (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""),
921 (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname);
924 if (args.arg_arch)
926 const char *arch;
928 if (parsedver.pv_arch == EM_ARM)
930 Printf("Architecture: ");
932 if (parsedver.pv_arm_cpu == (UBYTE)-1)
933 Printf("ARM (unspecified)");
934 else if (parsedver.pv_arm_cpu <= ELF_CPU_ARMv7EM)
935 Printf(arm_cpus[parsedver.pv_arm_cpu]);
936 else
937 Printf("Unknown ARM (%d)", parsedver.pv_arm_cpu);
939 if (parsedver.pv_arm_fpu > 6)
940 Printf(" Unknown FPU (%d)", parsedver.pv_arm_fpu);
941 else if (parsedver.pv_arm_fpu)
942 Printf(" %s", arm_fpus[parsedver.pv_arm_fpu]);
944 Printf("\n");
946 else
948 switch (parsedver.pv_arch)
950 case 0:
951 /* No information available */
952 arch = NULL;
953 break;
955 case EM_386:
956 arch = "I386";
957 break;
959 case EM_68K:
960 arch = "M68k";
961 break;
963 case EM_PPC:
964 arch = "PowerPC";
965 break;
967 case EM_X86_64:
968 arch = "X86-64";
969 break;
971 default:
972 arch = "Unknown";
973 break;
976 if (arch)
977 Printf("Architecture: %s\n", arch);
984 static
985 int makedata(CONST_STRPTR buffer, CONST_STRPTR ptr, int pos)
987 D(Printf("makedata: buffer \"%s\" ptr \"%s\"\n", buffer, ptr));
989 if (makeversionfromstring(&ptr))
991 CONST_STRPTR endp;
992 BOOL doskip;
994 /* It is! */
995 /* Copy the program-name into a buffer. */
996 parsedver.pv_name = dupstr(buffer, pos);
997 if (!parsedver.pv_name)
999 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1000 return RETURN_FAIL;
1003 /* Now find the date */
1004 D(Printf("makedata: ptr #1: \"%s\"\n", ptr));
1005 doskip = strchr(ptr, '(') ? TRUE : FALSE;
1006 (void) makedatefromstring(&ptr);
1008 D(Printf("makedata: ptr #2: \"%s\"\n", ptr));
1009 if (doskip)
1010 ptr = skipspaces(ptr); /* NOTE: not skipwhites! */
1011 for (endp = ptr; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++)
1013 pos = endp - ptr;
1014 if (pos)
1016 parsedver.pv_extrastr = dupstr(ptr, pos);
1017 if (!parsedver.pv_extrastr)
1019 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1020 return RETURN_FAIL;
1023 D(Printf("makedata: Extra string: %s\n", parsedver.pv_extrastr));
1024 if (doskip)
1025 parsedver.pv_extralf = "\n";
1028 return 1;
1031 return 0;
1035 /* Retrieves version information from string. The data is stored in the
1036 * global struct parsedver.pv_
1039 static
1040 int makedatafromstring(CONST_STRPTR buffer)
1042 int error = RETURN_OK;
1043 int pos = 0;
1044 LONG add, dummy;
1046 while (buffer[pos] && buffer[pos] != '\r' && buffer[pos] != '\n')
1048 /* NOTE: Not isspace()! - Piru */
1049 if (myisspace(buffer[pos]) &&
1050 (add = StrToLong((STRPTR) buffer + pos + 1, &dummy)) != -1)
1052 CONST_STRPTR ptr;
1054 /* Found something, which looks like a version. Now check, if it
1055 * really is.
1058 D(Printf("makedatafromstring: buffer + %ld: \"%s\"\n", pos, buffer + pos));
1060 ptr = buffer + pos + 1;
1062 if (makedata(buffer, ptr, pos))
1064 break;
1067 pos++;
1070 if (!buffer[pos] || buffer[pos] == '\r' || buffer[pos] == '\n')
1072 CONST_STRPTR endp;
1074 /* use the whatever is after ver tag as name */
1075 for (endp = buffer; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++)
1077 pos = endp - buffer;
1079 parsedver.pv_name = dupstr(buffer, pos);
1080 if (!parsedver.pv_name)
1082 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1083 return RETURN_FAIL;
1087 /* Strip any whitespaces from the tail of the program-name.
1089 if (parsedver.pv_name)
1091 stripwhites(parsedver.pv_name);
1094 return error;
1098 /* Case-insensitive FindResident()
1100 static
1101 struct Resident *findresident(CONST_STRPTR name)
1103 struct Resident **rp;
1104 struct Resident *resident;
1106 rp = (struct Resident **) SysBase->ResModules;
1108 while ((resident = *rp++))
1110 #ifdef __mc68000__
1111 if (((LONG)resident) < 0)
1112 rp = (struct Resident **)((ULONG)resident & 0x7fffffff);
1113 #else
1114 if (((IPTR)resident) & 0x01)
1115 rp = (struct Resident **)((IPTR)resident & ~1);
1116 #endif
1117 else {
1118 if (!Stricmp(resident->rt_Name, (STRPTR) name))
1119 break;
1123 return resident;
1127 /* Case-insensitive FindName()
1129 static
1130 struct Node *findname(struct List *list, CONST_STRPTR name)
1132 struct Node *node;
1134 ForeachNode(list, node)
1136 if (!Stricmp(node->ln_Name, (STRPTR) name))
1138 return node;
1142 return NULL;
1146 /* Retrieve information from resident modules. Returns 0 for success.
1148 static
1149 int createresidentver(struct Resident *MyResident)
1151 STRPTR buffer = NULL;
1152 CONST_STRPTR name = NULL;
1153 STRPTR tmpbuffer;
1154 int pos = 0;
1155 int len = 0;
1156 BOOL foundver = FALSE;
1157 int error;
1159 if (MyResident->rt_IdString)
1161 buffer = skipwhites(MyResident->rt_IdString);
1162 D(Printf("createresidentver: buffer \"%s\"\n", buffer));
1164 /* Locate version part */
1165 while (buffer[pos])
1167 LONG dummy;
1169 /* NOTE: Not isspace()! - Piru */
1170 if (myisspace(buffer[pos]) &&
1171 StrToLong(buffer + pos + 1, &dummy) != -1)
1173 buffer += pos;
1174 foundver = TRUE;
1175 break;
1177 pos++;
1179 D(Printf("createresidentver: buffer: \"%s\"\n", buffer));
1181 name = MyResident->rt_IdString;
1182 len = buffer - name;
1185 /* If could not find any version info, use the resident rt_Name */
1186 if (!foundver)
1187 buffer = "";
1189 D(Printf("createresidentver: buffer: \"%s\"\n", buffer));
1191 if ((!args.arg_full) || (!name))
1193 name = MyResident->rt_Name;
1194 len = strlen(MyResident->rt_Name);
1197 tmpbuffer = AllocVec(len + strlen(buffer) + 1, MEMF_ANY);
1198 if (!tmpbuffer)
1200 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1201 return RETURN_FAIL;
1203 CopyMem(name, tmpbuffer, len);
1204 strcpy(tmpbuffer + len, buffer);
1205 D(Printf("createresidentver: tmpbuffer: \"%s\"\n", tmpbuffer));
1207 error = makedatafromstring(tmpbuffer);
1209 FreeVec(tmpbuffer);
1211 return error;
1215 /* Retrieve version information from library. Returns 0 for success.
1217 static
1218 int createlibraryver(struct Library *MyLibrary)
1220 STRPTR buffer, tmpbuffer;
1221 int error, foundver = FALSE, pos;
1223 if (MyLibrary->lib_IdString)
1225 //Printf("createlibraryver: lib_IdString \"%s\"\n", (LONG) MyLibrary->lib_IdString);
1226 buffer = skipwhites(MyLibrary->lib_IdString);
1228 //Printf("createlibraryver: buffer \"%s\"\n", (LONG) buffer);
1230 /* Find full 'ver.rev' version info
1232 pos = 0;
1233 while (buffer[pos])
1235 LONG dummy, add;
1237 /* NOTE: Not isspace()! - Piru */
1238 if (myisspace(buffer[pos]) &&
1239 (add = StrToLong(buffer + pos + 1, &dummy)) != -1 &&
1240 buffer[pos + 1 + add] == '.')
1242 buffer += pos;
1243 pos = 0;
1244 foundver = TRUE;
1245 break;
1247 pos++;
1250 /* If could not find 'ver.rev', find any numeric */
1251 if (!foundver)
1253 pos = 0;
1254 while (buffer[pos])
1256 LONG dummy;
1258 /* NOTE: Not isspace()! - Piru */
1259 if (myisspace(buffer[pos]) &&
1260 StrToLong(buffer + pos + 1, &dummy) != -1)
1262 buffer += pos;
1263 pos = 0;
1264 foundver = TRUE;
1265 break;
1267 pos++;
1272 /* If could not find any version info, use the resident rt_Name */
1273 if (!foundver)
1275 buffer = "";
1276 error = RETURN_WARN;
1279 tmpbuffer = AllocVec(strlen(MyLibrary->lib_Node.ln_Name) + strlen(buffer) + 1, MEMF_ANY);
1280 if (!tmpbuffer)
1282 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1283 return RETURN_FAIL;
1286 strcpy(tmpbuffer, MyLibrary->lib_Node.ln_Name);
1287 strcat(tmpbuffer, buffer);
1288 //Printf("createlibraryver: tmpbuffer: \"%s\"\n", (LONG) tmpbuffer);
1289 pos = makedatafromstring(tmpbuffer);
1290 if (pos > error)
1292 error = pos;
1295 FreeVec(tmpbuffer);
1297 return error;
1301 /* Create default strings
1303 static
1304 int createdefvers(CONST_STRPTR name)
1306 FreeVec(parsedver.pv_revname);
1307 FreeVec(parsedver.pv_vername);
1308 FreeVec(parsedver.pv_name);
1310 parsedver.pv_name = dupstr(name, -1);
1311 parsedver.pv_vername = AllocVec(14, MEMF_ANY);
1312 parsedver.pv_revname = AllocVec(15, MEMF_ANY);
1314 if (parsedver.pv_name &&
1315 parsedver.pv_vername &&
1316 parsedver.pv_revname)
1318 __sprintf(parsedver.pv_vername, "%ld", (long)parsedver.pv_version);
1319 __sprintf(parsedver.pv_revname, ".%ld", (long)parsedver.pv_revision);
1321 return RETURN_OK;
1324 return RETURN_FAIL;
1328 /* Create version info from named resident
1330 static
1331 int makeresidentver(CONST_STRPTR name)
1333 struct Resident *MyResident;
1334 int error = -1;
1336 if ((MyResident = findresident(name)))
1338 error = createresidentver(MyResident);
1339 if (error != RETURN_OK)
1341 /* get values from residenttag */
1342 parsedver.pv_version = MyResident->rt_Version;
1343 parsedver.pv_revision = -1;
1344 error = createdefvers(MyResident->rt_Name);
1348 return error;
1352 /* Create version info from named list node
1354 static
1355 int makeexeclistver(struct List *list, CONST_STRPTR name)
1357 struct Library *MyLibrary;
1358 int error = -1;
1360 Forbid();
1362 MyLibrary = (struct Library *) findname(list, name);
1363 if (MyLibrary)
1365 /* get values from library */
1366 ULONG ver = MyLibrary->lib_Version;
1367 ULONG rev = MyLibrary->lib_Revision;
1369 error = createlibraryver(MyLibrary);
1370 if (error != RETURN_OK ||
1371 parsedver.pv_version != ver ||
1372 parsedver.pv_revision != rev)
1374 /* special case where createlibraryrev was successful, but
1375 * version or revision don't match.
1377 if (error == RETURN_OK)
1379 /* If there is extrastr, make sure there's linefeed, too.
1381 if (parsedver.pv_extrastr)
1383 parsedver.pv_extralf = "\n";
1387 /* get values from library */
1388 parsedver.pv_version = ver;
1389 parsedver.pv_revision = rev;
1391 error = createdefvers(MyLibrary->lib_Node.ln_Name);
1395 Permit();
1397 return error;
1401 /* Find resident from seglist, return NULL if none found
1403 static
1404 struct Resident *FindLibResident(BPTR Segment)
1406 while (Segment)
1408 IPTR *MySegment;
1409 UWORD *MyBuffer;
1410 UWORD *EndBuffer;
1412 MySegment = (IPTR*) BADDR(Segment);
1413 MyBuffer = (UWORD*) &MySegment[1];
1414 EndBuffer = (UWORD*) &MySegment[(MySegment[-1] - sizeof(ULONG) * 4) / sizeof(ULONG)];
1416 while (MyBuffer < EndBuffer)
1418 struct Resident *MyResident;
1420 MyResident = (struct Resident*) MyBuffer;
1421 if (MyResident->rt_MatchWord == RTC_MATCHWORD &&
1422 MyResident->rt_MatchTag == MyResident)
1424 return MyResident;
1427 MyBuffer++;
1430 Segment =(BPTR) MySegment[0];
1433 SetIoErr(ERROR_OBJECT_NOT_FOUND);
1434 return NULL;
1438 /* Find $VER: tag from seglist, return NULL if none found
1439 Returns dupstr()d string or NULL.
1441 static
1442 STRPTR FindSegmentVER(BPTR Segment)
1444 while (Segment)
1446 void *MySegment;
1447 CONST_STRPTR MyBuffer;
1448 ULONG BufferLen;
1449 CONST_STRPTR EndBuffer;
1450 CONST_STRPTR SegmentEnd;
1452 MySegment = BADDR(Segment);
1453 MyBuffer = (CONST_STRPTR) (MySegment + sizeof(BPTR));
1454 BufferLen = *(ULONG *)(MySegment - sizeof(ULONG));
1455 SegmentEnd = (CONST_STRPTR) (MySegment + (BufferLen - sizeof(BPTR)));
1456 EndBuffer = SegmentEnd - 5;
1458 while (MyBuffer < EndBuffer)
1460 if (MyBuffer[0] == '$' &&
1461 MyBuffer[1] == 'V' &&
1462 MyBuffer[2] == 'E' &&
1463 MyBuffer[3] == 'R' &&
1464 MyBuffer[4] == ':')
1466 CONST_STRPTR EndPtr;
1468 MyBuffer += 5;
1469 /* Required because some smartass could end his $VER: tag
1470 * without '\0' in the segment to save space. - Piru
1472 for (EndPtr = MyBuffer; EndPtr < SegmentEnd && *EndPtr; EndPtr++)
1474 if (EndPtr - MyBuffer)
1476 return dupstr(MyBuffer, EndPtr - MyBuffer);
1480 MyBuffer++;
1483 Segment =*(BPTR *)MySegment;
1486 SetIoErr(ERROR_OBJECT_NOT_FOUND);
1487 return NULL;
1491 /* Create version info from named device
1493 static
1494 int makedevicever(CONST_STRPTR name)
1496 struct DevProc *MyDevProc;
1497 int error = -1;
1499 MyDevProc = GetDeviceProc((STRPTR) name, NULL);
1500 if (MyDevProc)
1502 if (MyDevProc->dvp_DevNode->dol_Type == DLT_DEVICE)
1504 BPTR SegList;
1506 SegList = MyDevProc->dvp_DevNode->dol_misc.dol_handler.dol_SegList;
1507 if (SegList)
1509 struct Resident *MyResident;
1511 MyResident = FindLibResident(SegList);
1512 if (MyResident)
1514 error = createresidentver(MyResident);
1516 else
1517 error = RETURN_FAIL;
1520 FreeDeviceProc(MyDevProc);
1523 if (error != RETURN_OK && error != -1)
1525 Printf("Could not find version information for '%s'\n", name);
1528 return error;
1531 static int elf_read_block(BPTR file, ULONG offset, APTR buffer, ULONG size)
1533 if (Seek(file, offset, OFFSET_BEGINNING) < 0)
1534 return 0;
1536 return Read(file, buffer, size);
1539 static void *load_block(BPTR file, ULONG offset, ULONG size)
1541 void *block = AllocMem(size, MEMF_ANY);
1543 if (block)
1545 if (elf_read_block(file, offset, block, size) == size)
1546 return block;
1548 FreeMem(block, size);
1551 return NULL;
1554 static inline UWORD elf_read_word(UWORD data, struct elfheader *eh)
1556 switch (eh->ident[EI_DATA])
1558 case ELFDATA2LSB:
1559 return AROS_LE2WORD(data);
1561 case ELFDATA2MSB:
1562 return AROS_BE2WORD(data);
1564 default:
1565 return 0;
1569 static inline ULONG elf_read_long(ULONG data, struct elfheader *eh)
1571 switch (eh->ident[EI_DATA])
1573 case ELFDATA2LSB:
1574 return AROS_LE2LONG(data);
1576 case ELFDATA2MSB:
1577 return AROS_BE2LONG(data);
1579 default:
1580 return 0;
1584 static ULONG read_shnum(BPTR file, struct elfheader *eh)
1586 ULONG shnum = elf_read_word(eh->shnum, eh);
1588 /* the ELF header only uses 16 bits to store the count of section headers,
1589 * so it can't handle more than 65535 headers. if the count is 0, and an
1590 * offset is defined, then the real count can be found in the first
1591 * section header (which always exists).
1593 * similarly, if the string table index is SHN_XINDEX, then the actual
1594 * index is found in the first section header also.
1596 * see the System V ABI 2001-04-24 draft for more details.
1598 if (shnum == 0)
1600 struct sheader sh;
1601 ULONG shoff = elf_read_long(eh->shoff, eh);
1603 if (shoff == 0)
1604 return 0;
1606 if (elf_read_block(file, shoff, &sh, sizeof(sh)) != sizeof(sh))
1607 return 0;
1609 /* wider section header count is in the size field */
1610 shnum = elf_read_long(sh.size, eh);
1613 return shnum;
1616 static BOOL ARM_ParseAttrs(UBYTE *data, ULONG len, struct elfheader *eh)
1618 struct attrs_section *attrs;
1620 if (data[0] != ATTR_VERSION_CURRENT)
1622 D(Printf("Unknown attributes version: 0x%02\n", data[0]));
1623 return FALSE;
1626 attrs = (void *)data + 1;
1627 while (len > 0)
1629 ULONG attrs_size = elf_read_long(attrs->size, eh);
1631 if (!strcmp(attrs->vendor, "aeabi"))
1633 struct attrs_subsection *aeabi_attrs = (void *)attrs->vendor + 6;
1634 ULONG aeabi_len = attrs_size - 10;
1636 D(Printf("Found aeabi attributes @ 0x%p (length %u)\n", aeabi_attrs, aeabi_len));
1638 while (aeabi_len > 0)
1640 ULONG aeabi_attrs_size = elf_read_long(aeabi_attrs->size, eh);
1642 if (aeabi_attrs->tag == Tag_File)
1644 UBYTE *file_subsection = (void *)aeabi_attrs + sizeof(struct attrs_subsection);
1645 UBYTE file_len = aeabi_attrs_size - sizeof(struct attrs_subsection);
1647 D(Printf("Found file-wide attributes @ 0x%p (length %u)\n", file_subsection, file_len));
1649 while (file_len > 0)
1651 UBYTE tag, shift;
1652 ULONG val = 0;
1654 tag = *file_subsection++;
1655 file_len--;
1657 if (file_len == 0)
1659 D(Printf("Mailformed attribute tag %d (no data)\n", tag));
1660 return FALSE;
1663 switch (tag)
1665 case Tag_CPU_raw_name:
1666 case Tag_CPU_name:
1667 case Tag_compatibility:
1668 case Tag_also_compatible_with:
1669 case Tag_conformance:
1670 /* These two are NULL-terminated strings. Just skip. */
1671 while (file_len)
1673 file_len--;
1674 if (*file_subsection++ == 0)
1675 break;
1677 break;
1679 default:
1680 /* Read ULEB128 value */
1681 shift = 0;
1682 while (file_len)
1684 UBYTE byte;
1686 byte = *file_subsection++;
1687 file_len--;
1689 val |= (byte & 0x7F) << shift;
1690 if (!(byte & 0x80))
1691 break;
1693 shift += 7;
1697 switch (tag)
1699 case Tag_CPU_arch:
1700 D(Printf("ARM CPU architecture set to %d\n", val));
1701 parsedver.pv_arm_cpu = val;
1702 break;
1704 case Tag_FP_arch:
1705 D(Printf("ARM FPU architecture set to %d\n", val));
1706 parsedver.pv_arm_fpu = val;
1707 break;
1711 return TRUE;
1713 aeabi_attrs = (void *)aeabi_attrs + aeabi_attrs_size;
1714 aeabi_len -= aeabi_attrs_size;
1717 return FALSE;
1719 attrs = (void *)attrs + attrs_size;
1720 len -= attrs_size;
1722 return FALSE;
1725 static int arm_read_cpudata(BPTR file, struct elfheader *eh)
1727 struct sheader *sh;
1728 ULONG int_shnum;
1729 ULONG shoff;
1730 UWORD shentsize;
1731 ULONG i;
1733 int_shnum = read_shnum(file, eh);
1734 if (!int_shnum)
1735 return 0;
1737 shoff = elf_read_long(eh->shoff, eh);
1738 shentsize = elf_read_word(eh->shentsize, eh);
1740 /* load section headers */
1741 if (!(sh = load_block(file, shoff, int_shnum * shentsize)))
1742 return 0;
1744 for (i = 0; i < int_shnum; i++)
1746 if (sh[i].type == SHT_ARM_ATTRIBUTES)
1748 ULONG off = elf_read_long(sh[i].offset, eh);
1749 ULONG len = elf_read_long(sh[i].size, eh);
1750 void *data = load_block(file, off, len);
1752 D(Printf("ARM ATTRIBUTES section %d loaded at 0x%p\n", i, data));
1754 if (data)
1756 ARM_ParseAttrs(data, len, eh);
1757 FreeMem(data, len);
1759 break;
1763 FreeMem(sh, int_shnum * shentsize);
1765 return 1;
1769 /* Retrieve version information from file. Return 0 for success.
1771 #define BUFFERSIZE (16384 + 1)
1772 static
1773 int makefilever(CONST_STRPTR name)
1775 BPTR file;
1776 int error; // = RETURN_OK;
1778 file = Open((STRPTR) name, MODE_OLDFILE);
1779 if (file)
1781 UBYTE *buffer;
1783 buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1784 if (buffer)
1786 int len = BUFFERSIZE - 1;
1788 if (args.arg_arch)
1790 ULONG len = Read(file, buffer, sizeof(struct elfheader));
1792 if (len == sizeof(struct elfheader))
1794 if (buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F')
1796 /* It's a ELF file, read machine ID */
1797 struct elfheader *eh = (struct elfheader *)buffer;
1799 parsedver.pv_arch = elf_read_word(eh->machine, eh);
1800 if (parsedver.pv_arch == EM_ARM)
1801 arm_read_cpudata(file, eh);
1804 else if (len >= 4)
1806 if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0x03 && buffer[3] == 0xF3)
1808 /* It's AmigaOS hunk file. m68k obviously :) */
1809 parsedver.pv_arch = EM_68K;
1812 /* Rewind the file */
1813 Seek(file, 0, OFFSET_BEGINNING);
1816 error = findinfile(file, "$VER:", buffer, &len, parsedver.pv_md5sum);
1817 if (error == RETURN_OK)
1819 parsedver.pv_flags |= PVF_MD5SUM;
1821 if (len >= 0)
1823 STRPTR startbuffer;
1825 buffer[len] = '\0';
1826 startbuffer = skipwhites(buffer);
1828 //Printf("startbuffer \"%s\"\n", startbuffer);
1829 error = makedatafromstring(startbuffer);
1831 else
1833 /* Try LoadSeg
1835 error = RETURN_ERROR;
1837 Close(file);
1839 file = LoadSeg((STRPTR) name);
1840 if (file)
1842 struct Resident *MyResident;
1844 MyResident = FindLibResident(file);
1845 if (MyResident /*&&
1846 (MyResident->rt_Type == NT_LIBRARY ||
1847 MyResident->rt_Type == NT_DEVICE)*/)
1849 error = createresidentver(MyResident);
1852 UnLoadSeg(file);
1854 file = BNULL;
1856 if (error != RETURN_OK)
1858 /* If user didn't ask for md5sum or we could not calculate it.
1860 if (!args.arg_md5sum || (!(parsedver.pv_flags & PVF_MD5SUM)))
1862 Printf("Could not find version information for '%s'\n", (IPTR) name);
1867 else
1869 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1872 FreeMem(buffer, BUFFERSIZE);
1874 else
1876 error = RETURN_FAIL;
1877 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1880 if (file)
1881 Close(file);
1883 else
1885 LONG ioerr = IoErr();
1887 if (ioerr == ERROR_OBJECT_NOT_FOUND ||
1888 ioerr == ERROR_OBJECT_WRONG_TYPE)
1890 error = -1;
1892 else
1894 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1895 error = RETURN_FAIL;
1899 return error;
1903 static
1904 int makerescmdver(CONST_STRPTR name)
1906 int error = -1;
1907 struct Segment *segment;
1909 Forbid();
1911 segment = FindSegment((STRPTR) name, NULL, 0);
1912 if (!segment)
1914 segment = FindSegment((STRPTR) name, NULL, 1);
1917 if (segment)
1919 if (segment->seg_UC == CMD_INTERNAL ||
1920 segment->seg_UC == CMD_DISABLED)
1922 Permit();
1923 error = makeresidentver("shell");
1924 Forbid();
1926 else
1928 STRPTR buffer = FindSegmentVER(segment->seg_Seg);
1929 if (buffer)
1931 STRPTR startbuffer;
1933 startbuffer = skipwhites(buffer);
1935 //Printf("startbuffer \"%s\"\n", (LONG) startbuffer);
1936 error = makedatafromstring(startbuffer);
1938 FreeVec(buffer);
1943 Permit();
1945 return error;
1948 static
1949 int setvervar(CONST_STRPTR name, LONG ver, LONG rev)
1951 UBYTE buf[32];
1953 __sprintf(buf, "%ld.%ld", (long) ver, (long) rev);
1955 return SetVar((STRPTR) name, buf, -1, GVF_LOCAL_ONLY | LV_VAR) ? RETURN_OK : -1;
1959 static
1960 int makekickversion(void)
1962 parsedver.pv_version = SysBase->LibNode.lib_Version;
1963 parsedver.pv_revision = SysBase->SoftVer;
1965 setvervar("Kickstart",
1966 parsedver.pv_version,
1967 parsedver.pv_revision);
1969 Printf("Kickstart %ld.%ld",
1970 (LONG) parsedver.pv_version, (LONG) parsedver.pv_revision);
1972 return RETURN_OK;
1976 static
1977 int makewbversion(void)
1979 int error = -1;
1980 struct Library *VersionBase;
1982 VersionBase = OpenLibrary("version.library", 0);
1983 if (VersionBase)
1985 error = makeexeclistver(&SysBase->LibList, "version.library");
1987 if (error == RETURN_OK)
1989 STRPTR newname = dupstr("Workbench", -1);
1990 if (newname)
1992 FreeVec(parsedver.pv_name);
1993 parsedver.pv_name = newname;
1995 setvervar("Workbench", parsedver.pv_version, parsedver.pv_revision);
1998 CloseLibrary(VersionBase);
2001 return error;
2005 static
2006 int makesysver(void)
2008 int error;
2010 error = makekickversion();
2011 if (error == RETURN_OK)
2013 error = makewbversion();
2015 if (error == RETURN_OK)
2017 PutStr(", ");
2019 else
2021 /* prevent silly errormsg if no version.library */
2022 PutStr("\n");
2023 error = RETURN_WARN;
2027 return error;
2031 /* Determine, by which means to get the version-string.
2033 static
2034 int makeverstring(CONST_STRPTR name)
2036 int error; // = RETURN_OK;
2037 BOOL volume = name[strlen(name) - 1] == ':';
2038 CONST_STRPTR filepart = FilePart(name);
2040 error = -1;
2042 if (!volume && !args.arg_file)
2044 if (*filepart)
2046 error = makeresidentver(filepart);
2047 if (error != RETURN_OK)
2050 /* Try libraries
2052 error = makeexeclistver(&SysBase->LibList, filepart);
2053 if (error != RETURN_OK)
2055 STRPTR namebuf;
2056 ULONG namelen = strlen(filepart);
2058 /* 12 is "MOSSYS:LIBS/" */
2059 if ((namebuf = AllocVec(12 + namelen + 4 + 1, MEMF_PUBLIC)))
2061 strcpy(namebuf, "LIBS:");
2062 strcat(namebuf, filepart);
2063 error = makefilever(namebuf);
2065 /* Try devices
2067 if (error != RETURN_OK)
2069 error = makeexeclistver(&SysBase->DeviceList, filepart);
2070 if (error != RETURN_OK)
2072 strcpy(namebuf, "DEVS:");
2073 strcat(namebuf, filepart);
2074 error = makefilever(namebuf);
2077 FreeVec(namebuf);
2084 if (!args.arg_res && error == -1)
2086 if (volume)
2088 error = makedevicever(name);
2090 else
2092 if (*filepart)
2094 error = makefilever(name);
2099 if (!args.arg_file && error == -1)
2101 error = makerescmdver(name);
2104 if (error)
2106 /* If user asked for md5sum, and we could calculate it, don't print error
2107 * but the md5sum + file.
2109 if (args.arg_md5sum && (parsedver.pv_flags & PVF_MD5SUM))
2111 parsedver.pv_name = dupstr(name, -1);
2112 parsedver.pv_flags |= PVF_NOVERSION;
2113 error = RETURN_OK;
2117 if (error == -1)
2119 PrintFault(ERROR_OBJECT_NOT_FOUND, (STRPTR) ERROR_HEADER);
2120 error = RETURN_FAIL;
2123 return error;
2127 static
2128 void freeverstring(void)
2130 parsedver.pv_flags = 0;
2131 parsedver.pv_version = 0;
2132 parsedver.pv_revision = 0;
2134 FreeVec(parsedver.pv_extrastr);
2135 parsedver.pv_extrastr = NULL;
2136 parsedver.pv_extralf = NULL;
2137 FreeVec(parsedver.pv_datestr);
2138 parsedver.pv_datestr = NULL;
2139 FreeVec(parsedver.pv_revname);
2140 parsedver.pv_revname = NULL;
2141 FreeVec(parsedver.pv_vername);
2142 parsedver.pv_vername = NULL;
2143 FreeVec(parsedver.pv_name);
2144 parsedver.pv_name = NULL;
2147 /* Compare the version given as argument with the version from the object.
2148 * Return RETURN_WARN, if args-v>object-v, otherwise return RETURN_OK.
2150 static
2151 int cmpargsparsed(void)
2153 if (args.arg_version)
2155 if (*(args.arg_version) > parsedver.pv_version)
2157 return RETURN_WARN;
2159 else if (*(args.arg_version) == parsedver.pv_version && args.arg_revision)
2161 if (*(args.arg_revision) > parsedver.pv_revision)
2163 return RETURN_WARN;
2167 else if (args.arg_revision)
2169 if (*(args.arg_revision) > parsedver.pv_revision)
2171 return RETURN_WARN;
2174 return RETURN_OK;
2177 /******************************* main program ****************************/
2179 int __nocommandline;
2181 int main (void)
2183 LONG error = RETURN_FAIL;
2185 struct RDArgs *rda;
2187 rda = ReadArgs(TEMPLATE, (IPTR *) &args, NULL);
2188 if (rda)
2190 if (!args.arg_name || !*args.arg_name)
2192 /* No args, make system version */
2193 error = makesysver();
2194 if (error == RETURN_OK)
2196 printverstring();
2197 if (parsedver.pv_flags & PVF_NOVERSION)
2199 error = RETURN_FAIL;
2201 if (error == RETURN_OK)
2203 error = cmpargsparsed();
2206 freeverstring();
2208 else
2210 CONST_STRPTR *name;
2211 BOOL multifile;
2212 #if 1
2213 /* Workaround for:
2214 * version file ver
2215 * version file ver rev
2217 if (!args.arg_version && !args.arg_revision)
2219 LONG narg = 1;
2220 while (args.arg_name[narg]) { narg++; }
2221 if (narg == 2 || narg == 3)
2223 if (StrToLong(args.arg_name[1], &mversion) > 0)
2225 args.arg_version = &mversion;
2226 args.arg_name[1] = args.arg_name[2];
2227 if (narg == 3)
2229 args.arg_name[2] = NULL;
2231 if (narg == 3)
2233 if (StrToLong(args.arg_name[1], &mrevision) > 0)
2235 args.arg_revision = &mrevision;
2236 args.arg_name[1] = NULL;
2242 #endif
2243 multifile = args.arg_name[1] != NULL;
2245 for (name = args.arg_name; *name; name++)
2247 error = makeverstring(*name);
2248 if (error == RETURN_OK)
2250 printverstring();
2252 if (!multifile)
2254 /* Single args, do compare stuff also */
2255 if (parsedver.pv_flags & PVF_NOVERSION)
2257 error = RETURN_FAIL;
2259 if (error == RETURN_OK)
2261 error = cmpargsparsed();
2265 freeverstring();
2270 FreeArgs(rda);
2272 else
2274 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
2275 error = RETURN_FAIL;
2278 RT_Exit();
2280 return(error);