use m4_normalize on the output strings to remove trailing spaces/tabs and consolidate...
[AROS.git] / workbench / c / Version.c
blobac0a036b7bfac3d0866d54b27469010839c0367e
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 #if defined(__AROSPLATFORM_SMP__)
85 #include <aros/types/spinlock_s.h>
86 #include <proto/execlock.h>
87 #include <resources/execlock.h>
88 #endif
90 /*===[md5.h]==============================================================*/
92 /* Data structure for MD5 (Message-Digest) computation */
93 typedef struct {
94 ULONG buf[4]; /* scratch buffer */
95 ULONG i[2]; /* number of _bits_ handled mod 2^64 */
96 unsigned char in[64]; /* input buffer */
97 } MD5_CTX;
99 void MD5Init(MD5_CTX *mdContext);
100 void MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
101 void MD5Final(unsigned char digest[16], MD5_CTX *mdContext);
103 /*==[md5.c]===============================================================*/
106 * Used in 'version' as is, just removed the #include "ambient.h"
108 * Harry Sintonen <sintonen@iki.fi>
112 * Ambient - the ultimate desktop
113 * ------------------------------
114 * (c) 2001-2003 by David Gerber <zapek@meanmachine.ch>
115 * All Rights Reserved
117 * $Id$
120 //#include "ambient.h"
121 //#include <exec/types.h>
124 ***********************************************************************
125 ** md5.c -- the source code for MD5 routines **
126 ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
127 ** Created: 2/17/90 RLR **
128 ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
129 ***********************************************************************
133 * Edited 7 May 93 by CP to change the interface to match that
134 * of the MD5 routines in RSAREF. Due to this alteration, this
135 * code is "derived from the RSA Data Security, Inc. MD5 Message-
136 * Digest Algorithm". (See below.)
140 ***********************************************************************
141 ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
142 ** **
143 ** License to copy and use this software is granted provided that **
144 ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
145 ** Digest Algorithm" in all material mentioning or referencing this **
146 ** software or this function. **
147 ** **
148 ** License is also granted to make and use derivative works **
149 ** provided that such works are identified as "derived from the RSA **
150 ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
151 ** material mentioning or referencing the derived work. **
152 ** **
153 ** RSA Data Security, Inc. makes no representations concerning **
154 ** either the merchantability of this software or the suitability **
155 ** of this software for any particular purpose. It is provided "as **
156 ** is" without express or implied warranty of any kind. **
157 ** **
158 ** These notices must be retained in any copies of any part of this **
159 ** documentation and/or software. **
160 ***********************************************************************
163 //#include "md5.h"
166 ***********************************************************************
167 ** Message-digest routines: **
168 ** To form the message digest for a message M **
169 ** (1) Initialize a context buffer mdContext using MD5Init **
170 ** (2) Call MD5Update on mdContext and M **
171 ** (3) Call MD5Final on mdContext **
172 ** The message digest is now in the bugffer passed to MD5Final **
173 ***********************************************************************
176 static unsigned char PADDING[64] = {
177 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
187 /* F, G, H and I are basic MD5 functions */
188 #define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
189 #define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
190 #define H(x, y, z) ((x) ^ (y) ^ (z))
191 #define I(x, y, z) ((y) ^ ((x) | (~z)))
193 /* ROTATE_LEFT rotates x left n bits */
194 #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
196 /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
197 /* Rotation is separate from addition to prevent recomputation */
198 #define FF(a, b, c, d, x, s, ac) \
199 {(a) += F ((b), (c), (d)) + (x) + (ULONG)(ac); \
200 (a) = ROTATE_LEFT ((a), (s)); \
201 (a) += (b); \
203 #define GG(a, b, c, d, x, s, ac) \
204 {(a) += G ((b), (c), (d)) + (x) + (ULONG)(ac); \
205 (a) = ROTATE_LEFT ((a), (s)); \
206 (a) += (b); \
208 #define HH(a, b, c, d, x, s, ac) \
209 {(a) += H ((b), (c), (d)) + (x) + (ULONG)(ac); \
210 (a) = ROTATE_LEFT ((a), (s)); \
211 (a) += (b); \
213 #define II(a, b, c, d, x, s, ac) \
214 {(a) += I ((b), (c), (d)) + (x) + (ULONG)(ac); \
215 (a) = ROTATE_LEFT ((a), (s)); \
216 (a) += (b); \
219 static void Transform(register ULONG *buf,register ULONG *in);
221 /* The routine MD5Init initializes the message-digest context
222 mdContext. All fields are set to zero.
224 void MD5Init ( MD5_CTX *mdContext)
226 mdContext->i[0] = mdContext->i[1] = (ULONG)0;
228 /* Load magic initialization constants.
230 mdContext->buf[0] = (ULONG)0x67452301L;
231 mdContext->buf[1] = (ULONG)0xefcdab89L;
232 mdContext->buf[2] = (ULONG)0x98badcfeL;
233 mdContext->buf[3] = (ULONG)0x10325476L;
236 /* The routine MD5Update updates the message-digest context to
237 account for the presence of each of the characters inBuf[0..inLen-1]
238 in the message whose digest is being computed.
240 void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf,
241 unsigned int inLen)
243 register int i, ii;
244 int mdi;
245 ULONG in[16];
247 /* compute number of bytes mod 64 */
248 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
250 /* update number of bits */
251 if ((mdContext->i[0] + ((ULONG)inLen << 3)) < mdContext->i[0])
252 mdContext->i[1]++;
253 mdContext->i[0] += ((ULONG)inLen << 3);
254 mdContext->i[1] += ((ULONG)inLen >> 29);
256 while (inLen--)
258 /* add new character to buffer, increment mdi */
259 mdContext->in[mdi++] = *inBuf++;
261 /* transform if necessary */
262 if (mdi == 0x40)
264 for (i = 0, ii = 0; i < 16; i++, ii += 4)
265 in[i] = (((ULONG)mdContext->in[ii+3]) << 24) |
266 (((ULONG)mdContext->in[ii+2]) << 16) |
267 (((ULONG)mdContext->in[ii+1]) << 8) |
268 ((ULONG)mdContext->in[ii]);
269 Transform (mdContext->buf, in);
270 mdi = 0;
275 /* The routine MD5Final terminates the message-digest computation and
276 ends with the desired message digest in mdContext->digest[0...15].
278 void MD5Final (unsigned char digest[16], MD5_CTX *mdContext)
280 ULONG in[16];
281 int mdi;
282 unsigned int i, ii;
283 unsigned int padLen;
285 /* save number of bits */
286 in[14] = mdContext->i[0];
287 in[15] = mdContext->i[1];
289 /* compute number of bytes mod 64 */
290 mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
292 /* pad out to 56 mod 64 */
293 padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
294 MD5Update (mdContext, PADDING, padLen);
296 /* append length in bits and transform */
297 for (i = 0, ii = 0; i < 14; i++, ii += 4)
298 in[i] = (((ULONG)mdContext->in[ii+3]) << 24) |
299 (((ULONG)mdContext->in[ii+2]) << 16) |
300 (((ULONG)mdContext->in[ii+1]) << 8) |
301 ((ULONG)mdContext->in[ii]);
302 Transform (mdContext->buf, in);
304 /* store buffer in digest */
305 for (i = 0, ii = 0; i < 4; i++, ii += 4)
307 digest[ii] = (unsigned char) (mdContext->buf[i] & 0xFF);
308 digest[ii+1] = (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
309 digest[ii+2] = (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
310 digest[ii+3] = (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
314 /* Basic MD5 step. Transforms buf based on in. Note that if the Mysterious
315 Constants are arranged backwards in little-endian order and decrypted with
316 the DES they produce OCCULT MESSAGES!
318 void Transform(register ULONG *buf,register ULONG *in)
320 register ULONG a = buf[0], b = buf[1], c = buf[2], d = buf[3];
322 /* Round 1 */
323 #define S11 7
324 #define S12 12
325 #define S13 17
326 #define S14 22
327 FF ( a, b, c, d, in[ 0], S11, 0xD76AA478L); /* 1 */
328 FF ( d, a, b, c, in[ 1], S12, 0xE8C7B756L); /* 2 */
329 FF ( c, d, a, b, in[ 2], S13, 0x242070DBL); /* 3 */
330 FF ( b, c, d, a, in[ 3], S14, 0xC1BDCEEEL); /* 4 */
331 FF ( a, b, c, d, in[ 4], S11, 0xF57C0FAFL); /* 5 */
332 FF ( d, a, b, c, in[ 5], S12, 0x4787C62AL); /* 6 */
333 FF ( c, d, a, b, in[ 6], S13, 0xA8304613L); /* 7 */
334 FF ( b, c, d, a, in[ 7], S14, 0xFD469501L); /* 8 */
335 FF ( a, b, c, d, in[ 8], S11, 0x698098D8L); /* 9 */
336 FF ( d, a, b, c, in[ 9], S12, 0x8B44F7AFL); /* 10 */
337 FF ( c, d, a, b, in[10], S13, 0xFFFF5BB1L); /* 11 */
338 FF ( b, c, d, a, in[11], S14, 0x895CD7BEL); /* 12 */
339 FF ( a, b, c, d, in[12], S11, 0x6B901122L); /* 13 */
340 FF ( d, a, b, c, in[13], S12, 0xFD987193L); /* 14 */
341 FF ( c, d, a, b, in[14], S13, 0xA679438EL); /* 15 */
342 FF ( b, c, d, a, in[15], S14, 0x49B40821L); /* 16 */
344 /* Round 2 */
345 #define S21 5
346 #define S22 9
347 #define S23 14
348 #define S24 20
349 GG ( a, b, c, d, in[ 1], S21, 0xF61E2562L); /* 17 */
350 GG ( d, a, b, c, in[ 6], S22, 0xC040B340L); /* 18 */
351 GG ( c, d, a, b, in[11], S23, 0x265E5A51L); /* 19 */
352 GG ( b, c, d, a, in[ 0], S24, 0xE9B6C7AAL); /* 20 */
353 GG ( a, b, c, d, in[ 5], S21, 0xD62F105DL); /* 21 */
354 GG ( d, a, b, c, in[10], S22, 0x02441453L); /* 22 */
355 GG ( c, d, a, b, in[15], S23, 0xD8A1E681L); /* 23 */
356 GG ( b, c, d, a, in[ 4], S24, 0xE7D3FBC8L); /* 24 */
357 GG ( a, b, c, d, in[ 9], S21, 0x21E1CDE6L); /* 25 */
358 GG ( d, a, b, c, in[14], S22, 0xC33707D6L); /* 26 */
359 GG ( c, d, a, b, in[ 3], S23, 0xF4D50D87L); /* 27 */
360 GG ( b, c, d, a, in[ 8], S24, 0x455A14EDL); /* 28 */
361 GG ( a, b, c, d, in[13], S21, 0xA9E3E905L); /* 29 */
362 GG ( d, a, b, c, in[ 2], S22, 0xFCEFA3F8L); /* 30 */
363 GG ( c, d, a, b, in[ 7], S23, 0x676F02D9L); /* 31 */
364 GG ( b, c, d, a, in[12], S24, 0x8D2A4C8AL); /* 32 */
366 /* Round 3 */
367 #define S31 4
368 #define S32 11
369 #define S33 16
370 #define S34 23
371 HH ( a, b, c, d, in[ 5], S31, 0xFFFA3942L); /* 33 */
372 HH ( d, a, b, c, in[ 8], S32, 0x8771F681L); /* 34 */
373 HH ( c, d, a, b, in[11], S33, 0x6D9D6122L); /* 35 */
374 HH ( b, c, d, a, in[14], S34, 0xFDE5380CL); /* 36 */
375 HH ( a, b, c, d, in[ 1], S31, 0xA4BEEA44L); /* 37 */
376 HH ( d, a, b, c, in[ 4], S32, 0x4BDECFA9L); /* 38 */
377 HH ( c, d, a, b, in[ 7], S33, 0xF6BB4B60L); /* 39 */
378 HH ( b, c, d, a, in[10], S34, 0xBEBFBC70L); /* 40 */
379 HH ( a, b, c, d, in[13], S31, 0x289B7EC6L); /* 41 */
380 HH ( d, a, b, c, in[ 0], S32, 0xEAA127FAL); /* 42 */
381 HH ( c, d, a, b, in[ 3], S33, 0xD4EF3085L); /* 43 */
382 HH ( b, c, d, a, in[ 6], S34, 0x04881D05L); /* 44 */
383 HH ( a, b, c, d, in[ 9], S31, 0xD9D4D039L); /* 45 */
384 HH ( d, a, b, c, in[12], S32, 0xE6DB99E5L); /* 46 */
385 HH ( c, d, a, b, in[15], S33, 0x1FA27CF8L); /* 47 */
386 HH ( b, c, d, a, in[ 2], S34, 0xC4AC5665L); /* 48 */
388 /* Round 4 */
389 #define S41 6
390 #define S42 10
391 #define S43 15
392 #define S44 21
393 II ( a, b, c, d, in[ 0], S41, 0xF4292244L); /* 49 */
394 II ( d, a, b, c, in[ 7], S42, 0x432AFF97L); /* 50 */
395 II ( c, d, a, b, in[14], S43, 0xAB9423A7L); /* 51 */
396 II ( b, c, d, a, in[ 5], S44, 0xFC93A039L); /* 52 */
397 II ( a, b, c, d, in[12], S41, 0x655B59C3L); /* 53 */
398 II ( d, a, b, c, in[ 3], S42, 0x8F0CCC92L); /* 54 */
399 II ( c, d, a, b, in[10], S43, 0xFFEFF47DL); /* 55 */
400 II ( b, c, d, a, in[ 1], S44, 0x85845DD1L); /* 56 */
401 II ( a, b, c, d, in[ 8], S41, 0x6FA87E4FL); /* 57 */
402 II ( d, a, b, c, in[15], S42, 0xFE2CE6E0L); /* 58 */
403 II ( c, d, a, b, in[ 6], S43, 0xA3014314L); /* 59 */
404 II ( b, c, d, a, in[13], S44, 0x4E0811A1L); /* 60 */
405 II ( a, b, c, d, in[ 4], S41, 0xF7537E82L); /* 61 */
406 II ( d, a, b, c, in[11], S42, 0xBD3AF235L); /* 62 */
407 II ( c, d, a, b, in[ 2], S43, 0x2AD7D2BBL); /* 63 */
408 II ( b, c, d, a, in[ 9], S44, 0xEB86D391L); /* 64 */
410 buf[0] += a;
411 buf[1] += b;
412 buf[2] += c;
413 buf[3] += d;
416 /*==[end md5.c]============================================================*/
419 const TEXT version[] = "$VER: Version 42.3 (20.11.2015)\n";
420 const TEXT ver[] = "$VER:";
422 static const char ERROR_HEADER[] = "Version";
424 #define TEMPLATE "NAME/M,MD5SUM/S,VERSION/N,REVISION/N,FILE/S,FULL/S,RES/S,ARCH/S"
425 struct
427 CONST_STRPTR *arg_name;
428 IPTR arg_md5sum;
429 LONG *arg_version;
430 LONG *arg_revision;
431 IPTR arg_file;
432 IPTR arg_full;
433 IPTR arg_res;
434 IPTR arg_arch;
436 args;
438 LONG mversion, mrevision;
440 struct
442 STRPTR pv_name;
443 ULONG pv_flags;
444 LONG pv_version;
445 LONG pv_revision;
446 UWORD pv_arch;
447 UBYTE pv_arm_cpu;
448 UBYTE pv_arm_fpu;
449 //LONG pv_days;
450 STRPTR pv_vername;
451 STRPTR pv_revname;
452 STRPTR pv_datestr;
453 STRPTR pv_extralf;
454 STRPTR pv_extrastr;
455 UBYTE pv_md5sum[16];
457 parsedver = { NULL, 0, 0, 0, 0, -1, 0, NULL, NULL, NULL, NULL, NULL, {0}};
459 #define PVF_MD5SUM (1 << 0)
460 #define PVF_NOVERSION (1 << 1)
462 static
463 int makeverstring(CONST_STRPTR name);
464 static
465 void printverstring(void);
466 static
467 void freeverstring(void);
468 static
469 int makesysver(void);
470 static
471 int cmpargsparsed(void);
474 /**************************** support functions ************************/
476 /* Duplicate string, by given length or -1 for full length
478 static
479 STRPTR dupstr(CONST_STRPTR buffer, LONG len)
481 STRPTR ret = NULL;
483 if (buffer)
485 if (len == -1)
486 len = strlen(buffer);
488 ret = AllocVec(len + 1, MEMF_ANY);
489 if (ret)
491 CopyMem((STRPTR) buffer, ret, len);
492 ret[len] = '\0';
496 return ret;
500 static
501 inline int myisspace(int c)
503 return (c == ' ' || c == '\t');
507 /* Return a pointer to a string, stripped by all leading whitespace characters
508 * (SPACE, TAB).
510 static
511 STRPTR skipwhites(CONST_STRPTR buffer)
513 for (;; buffer++)
515 if (buffer[0] == '\0' || !isspace(buffer[0]))
517 return (STRPTR) buffer;
523 /* Return a pointer to a string, stripped by all leading space characters
524 * (SPACE).
526 static
527 STRPTR skipspaces(CONST_STRPTR buffer)
529 for (;; buffer++)
531 if (buffer[0] == '\0' || buffer[0] != ' ')
533 return (STRPTR) buffer;
539 /* Strip all whitespace-characters from the end of a string. Note that the
540 * buffer passed in will be modified!
542 static
543 void stripwhites(STRPTR buffer)
545 int len = strlen(buffer);
547 while (len > 0)
549 if (!isspace(buffer[len-1]))
551 buffer[len] = '\0';
552 return;
554 len--;
556 buffer[len] = '\0';
560 /* Searches for a given string in a file and stores up to *lenptr characters
561 * into the buffer beginning with the first character after the given string.
563 static
564 int findinfile(BPTR file, CONST_STRPTR string, STRPTR buffer, int *lenptr, unsigned char digest[16])
566 int error = RETURN_OK;
567 int buflen = *lenptr, len = 0, pos, stringlen;
568 BOOL ready = FALSE;
569 MD5_CTX md5ctx;
570 STRPTR bufpos;
571 STRPTR tmp;
573 tmp = AllocMem(buflen, MEMF_PUBLIC);
574 if (!tmp)
576 return RETURN_FAIL;
579 stringlen = strlen(string);
580 *lenptr = -1;
582 if (args.arg_md5sum)
584 MD5Init(&md5ctx);
587 bufpos = tmp;
588 while ((len = Read(file, &tmp[len], buflen - len)) > 0)
590 pos = 0;
592 if (args.arg_md5sum)
594 MD5Update(&md5ctx, bufpos, len);
597 if (ready)
599 /* If we get here we're scanning the rest of the file for md5sum. - Piru */
600 len = 0;
602 else
604 while ((len - pos) >= stringlen)
606 /* Compare the current buffer position with the supplied string. */
607 if (strncmp(&tmp[pos], string, stringlen) == 0)
609 /* It is equal! Now move the rest of the buffer to the top of
610 * the buffer and fill it up.
612 int findstrlen = len - pos;
614 memcpy(buffer, &tmp[pos + stringlen], findstrlen);
616 len = Read(file, &buffer[findstrlen], buflen - findstrlen);
617 if (len >= 0)
619 if (args.arg_md5sum)
621 MD5Update(&md5ctx, &buffer[findstrlen], len);
624 *lenptr = findstrlen + len;
626 else
628 error = RETURN_FAIL;
630 ready = TRUE;
631 break;
633 pos++;
635 /* Move the rest of the buffer that could not be compared (because it
636 * is smaller than the string to compare) to the top of the buffer.
638 if (!ready)
640 memmove(tmp, &tmp[len - stringlen], stringlen);
642 else
644 /* If we're not md5summing, stop file scanning now. - Piru */
645 if (!args.arg_md5sum)
647 break;
650 len = stringlen;
653 bufpos = &tmp[len];
656 FreeMem(tmp, buflen);
658 if (len == -1)
660 error = RETURN_FAIL;
663 if (args.arg_md5sum)
665 memset(digest, 0, 16);
666 MD5Final(digest, &md5ctx);
669 return error;
673 /*************************** parsing functions *************************/
675 /* Convert a date in the form DD.MM.YY or DD.MM.YYYY into a numerical
676 * value. Return FALSE, if buffer doesn't contain a valid date.
678 static
679 BOOL makedatefromstring(CONST_STRPTR *bufptr)
681 CONST_STRPTR buffer = *bufptr;
682 struct DateTime dt;
683 CONST_STRPTR headerstart, end;
684 STRPTR newbuf;
685 LONG res;
686 int len, i;
687 UBYTE c;
689 //if (isspace(buffer[0]))
690 // buffer++;
692 headerstart = buffer;
694 buffer = strchr(buffer, '(');
695 if (!buffer)
697 return FALSE;
699 buffer++;
700 end = strchr(buffer, ')');
701 if (!end)
703 return FALSE;
705 len = (int)(end - buffer);
706 newbuf = dupstr(buffer, len);
707 if (!newbuf)
709 return FALSE;
711 for (i = 0; i < len; i++)
713 c = newbuf[i];
715 if (c == '.' || c == '/')
716 newbuf[i] = '-';
717 else if (!isalnum(c))
719 end = buffer + i;
720 newbuf[i] = '\0';
721 break;
725 D(Printf("date: \"%s\"\n", newbuf));
727 dt.dat_Format = FORMAT_CDN;
728 dt.dat_Flags = 0;
729 dt.dat_StrDay = NULL;
730 dt.dat_StrDate = newbuf;
731 dt.dat_StrTime = NULL;
732 res = StrToDate(&dt);
733 if (!res)
735 dt.dat_Format = FORMAT_DOS;
736 res = StrToDate(&dt);
737 if (!res)
739 //Printf("StrToDate failed!\n");
740 FreeVec(newbuf);
741 return FALSE;
744 FreeVec(newbuf);
746 //parsedver.pv_days = dt.dat_Stamp.ds_Days;
748 parsedver.pv_datestr = AllocVec(buffer - headerstart + LEN_DATSTRING + 2, MEMF_ANY);
749 if (!parsedver.pv_datestr)
751 return FALSE;
754 dt.dat_Stamp.ds_Minute = 0;
755 dt.dat_Stamp.ds_Tick = 0;
757 dt.dat_StrDate = parsedver.pv_datestr + (buffer - headerstart);
758 dt.dat_Format = FORMAT_DEF;
759 if (!DateToStr(&dt))
761 //Printf("DateToStr failed!\n");
762 return FALSE;
765 CopyMem((STRPTR) headerstart, parsedver.pv_datestr, buffer - headerstart);
766 res = strlen(parsedver.pv_datestr);
767 parsedver.pv_datestr[res++] = ')';
768 parsedver.pv_datestr[res] = '\0';
770 *bufptr = end + 1;
772 return TRUE;
776 /* Check whether the given string contains a version in the form
777 * <version>.<revision> . If not return FALSE, otherwise fill in parsedver and
778 * return TRUE.
780 static
781 BOOL makeversionfromstring(CONST_STRPTR *bufptr)
783 LONG pos, ver, rev;
784 CONST_STRPTR buffer = *bufptr;
785 CONST_STRPTR verstart, revstart;
787 //Printf("makeversionfromstring: buffer \"%s\"\n", (LONG) buffer);
789 /* Do version */
791 verstart = buffer;
792 pos = StrToLong((STRPTR) buffer, &ver);
793 if (pos == -1)
795 return FALSE;
797 parsedver.pv_version = ver;
798 parsedver.pv_revision = -1;
800 parsedver.pv_vername = dupstr(verstart, pos);
801 if (!parsedver.pv_vername)
803 return FALSE;
806 /* Do revision */
808 buffer += pos;
809 revstart = buffer;
810 buffer = skipspaces(buffer); /* NOTE: skipspaces, not skipwhites! */
811 if (*buffer != '.')
813 *bufptr = buffer;
814 return TRUE;
817 buffer++;
818 pos = StrToLong((STRPTR) buffer, &rev);
819 if (pos == -1)
821 *bufptr = buffer;
822 return TRUE;
825 parsedver.pv_revision = rev;
827 /* calc the revision string len */
828 pos = buffer + pos - revstart;
829 parsedver.pv_revname = dupstr(revstart, pos);
830 if (!parsedver.pv_revname)
832 return FALSE;
835 *bufptr = revstart + pos;
837 return TRUE;
840 static const char *arm_cpus[] =
842 "ARM Pre-v4",
843 "ARMv4",
844 "ARMv4T",
845 "ARMv5T",
846 "ARMv5TE",
847 "ARMv5TEJ",
848 "ARMv6",
849 "ARMv6KZ",
850 "ARMv6T2",
851 "ARMv6K",
852 "ARMv7",
853 "ARMv6-M",
854 "ARMv6S-M",
855 "ARMv7E-M"
858 static const char *arm_fpus[] =
860 "VFP",
861 "VFPv2",
862 "VFPv3",
863 "VFPv3 (D0 - D15)",
864 "VFPv4",
865 "VFPv4 (D0 - D15)"
868 static
869 void printverstring(void)
871 if (args.arg_md5sum)
873 if (parsedver.pv_flags & PVF_MD5SUM)
875 /* Endianess safe version */
876 Printf("%02lX%02lX%02lX%02lX"
877 "%02lX%02lX%02lX%02lX"
878 "%02lX%02lX%02lX%02lX"
879 "%02lX%02lX%02lX%02lX ",
880 parsedver.pv_md5sum[0],
881 parsedver.pv_md5sum[1],
882 parsedver.pv_md5sum[2],
883 parsedver.pv_md5sum[3],
884 parsedver.pv_md5sum[4],
885 parsedver.pv_md5sum[5],
886 parsedver.pv_md5sum[6],
887 parsedver.pv_md5sum[7],
888 parsedver.pv_md5sum[8],
889 parsedver.pv_md5sum[9],
890 parsedver.pv_md5sum[10],
891 parsedver.pv_md5sum[11],
892 parsedver.pv_md5sum[12],
893 parsedver.pv_md5sum[13],
894 parsedver.pv_md5sum[14],
895 parsedver.pv_md5sum[15]);
897 else
899 /* "01234567012345670123456701234567: " */
900 PutStr("<no md5sum available> ");
904 if (parsedver.pv_flags & PVF_NOVERSION)
906 Printf("%s\n", (IPTR) parsedver.pv_name);
908 else
910 if (args.arg_full)
912 /* If md5sum output was there, avoid linefeed to allow parsing the output - Piru */
913 if (args.arg_md5sum)
915 parsedver.pv_extralf = " ";
918 Printf("%s%s%s%s%s%s%s\n",
919 (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""),
920 (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname,
921 (IPTR) (parsedver.pv_datestr ? (IPTR)parsedver.pv_datestr : (IPTR)""),
922 (IPTR) (parsedver.pv_extralf ? (IPTR)parsedver.pv_extralf : (IPTR)""),
923 (IPTR) (parsedver.pv_extrastr ? (IPTR)parsedver.pv_extrastr : (IPTR)""));
925 else
927 Printf("%s%s%s%s\n",
928 (IPTR) parsedver.pv_name, (IPTR) (*parsedver.pv_name ? " " : ""),
929 (IPTR) parsedver.pv_vername, (IPTR) parsedver.pv_revname);
932 if (args.arg_arch)
934 const char *arch;
936 if (parsedver.pv_arch == EM_ARM)
938 Printf("Architecture: ");
940 if (parsedver.pv_arm_cpu == (UBYTE)-1)
941 Printf("ARM (unspecified)");
942 else if (parsedver.pv_arm_cpu <= ELF_CPU_ARMv7EM)
943 Printf(arm_cpus[parsedver.pv_arm_cpu]);
944 else
945 Printf("Unknown ARM (%d)", parsedver.pv_arm_cpu);
947 if (parsedver.pv_arm_fpu > 6)
948 Printf(" Unknown FPU (%d)", parsedver.pv_arm_fpu);
949 else if (parsedver.pv_arm_fpu)
950 Printf(" %s", arm_fpus[parsedver.pv_arm_fpu]);
952 Printf("\n");
954 else
956 switch (parsedver.pv_arch)
958 case 0:
959 /* No information available */
960 arch = NULL;
961 break;
963 case EM_386:
964 arch = "I386";
965 break;
967 case EM_68K:
968 arch = "M68k";
969 break;
971 case EM_PPC:
972 arch = "PowerPC";
973 break;
975 case EM_X86_64:
976 arch = "X86-64";
977 break;
979 default:
980 arch = "Unknown";
981 break;
984 if (arch)
985 Printf("Architecture: %s\n", arch);
992 static
993 int makedata(CONST_STRPTR buffer, CONST_STRPTR ptr, int pos)
995 D(Printf("makedata: buffer \"%s\" ptr \"%s\"\n", buffer, ptr));
997 if (makeversionfromstring(&ptr))
999 CONST_STRPTR endp;
1000 BOOL doskip;
1002 /* It is! */
1003 /* Copy the program-name into a buffer. */
1004 parsedver.pv_name = dupstr(buffer, pos);
1005 if (!parsedver.pv_name)
1007 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1008 return RETURN_FAIL;
1011 /* Now find the date */
1012 D(Printf("makedata: ptr #1: \"%s\"\n", ptr));
1013 doskip = strchr(ptr, '(') ? TRUE : FALSE;
1014 (void) makedatefromstring(&ptr);
1016 D(Printf("makedata: ptr #2: \"%s\"\n", ptr));
1017 if (doskip)
1018 ptr = skipspaces(ptr); /* NOTE: not skipwhites! */
1019 for (endp = ptr; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++)
1021 pos = endp - ptr;
1022 if (pos && ptr[pos-1] == '$')
1023 pos--;
1024 if (pos)
1026 parsedver.pv_extrastr = dupstr(ptr, pos);
1027 if (!parsedver.pv_extrastr)
1029 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1030 return RETURN_FAIL;
1033 D(Printf("makedata: Extra string: %s\n", parsedver.pv_extrastr));
1034 if (doskip)
1035 parsedver.pv_extralf = "\n";
1038 return 1;
1041 return 0;
1045 /* Retrieves version information from string. The data is stored in the
1046 * global struct parsedver.pv_
1049 static
1050 int makedatafromstring(CONST_STRPTR buffer)
1052 int error = RETURN_OK;
1053 int pos = 0;
1054 LONG add, dummy;
1056 while (buffer[pos] && buffer[pos] != '\r' && buffer[pos] != '\n')
1058 /* NOTE: Not isspace()! - Piru */
1059 if (myisspace(buffer[pos]) &&
1060 (add = StrToLong((STRPTR) buffer + pos + 1, &dummy)) != -1)
1062 CONST_STRPTR ptr;
1064 /* Found something, which looks like a version. Now check, if it
1065 * really is.
1068 D(Printf("makedatafromstring: buffer + %ld: \"%s\"\n", pos, buffer + pos));
1070 ptr = buffer + pos + 1;
1072 if (makedata(buffer, ptr, pos))
1074 break;
1077 pos++;
1080 if (!buffer[pos] || buffer[pos] == '\r' || buffer[pos] == '\n')
1082 CONST_STRPTR endp;
1084 /* use the whatever is after ver tag as name */
1085 for (endp = buffer; *endp != '\0' && *endp != '\r' && *endp != '\n'; endp++)
1087 pos = endp - buffer;
1089 parsedver.pv_name = dupstr(buffer, pos);
1090 if (!parsedver.pv_name)
1092 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1093 return RETURN_FAIL;
1097 /* Strip any whitespaces from the tail of the program-name.
1099 if (parsedver.pv_name)
1101 stripwhites(parsedver.pv_name);
1104 return error;
1108 /* Case-insensitive FindResident()
1110 static
1111 struct Resident *findresident(CONST_STRPTR name)
1113 struct Resident **rp;
1114 struct Resident *resident;
1116 rp = (struct Resident **) SysBase->ResModules;
1118 while ((resident = *rp++))
1120 #ifdef __mc68000__
1121 if (((LONG)resident) < 0)
1122 rp = (struct Resident **)((ULONG)resident & 0x7fffffff);
1123 #else
1124 if (((IPTR)resident) & 0x01)
1125 rp = (struct Resident **)((IPTR)resident & ~1);
1126 #endif
1127 else {
1128 if (!Stricmp(resident->rt_Name, (STRPTR) name))
1129 break;
1133 return resident;
1137 /* Case-insensitive FindName()
1139 static
1140 struct Node *findname(struct List *list, CONST_STRPTR name)
1142 struct Node *node;
1144 ForeachNode(list, node)
1146 if (!Stricmp(node->ln_Name, (STRPTR) name))
1148 return node;
1152 return NULL;
1156 /* Retrieve information from resident modules. Returns 0 for success.
1158 static
1159 int createresidentver(struct Resident *MyResident)
1161 STRPTR buffer = NULL;
1162 CONST_STRPTR name = NULL;
1163 STRPTR tmpbuffer;
1164 int pos = 0;
1165 int len = 0;
1166 BOOL foundver = FALSE;
1167 int error;
1169 if (MyResident->rt_IdString)
1171 buffer = skipwhites(MyResident->rt_IdString);
1172 D(Printf("createresidentver: buffer \"%s\"\n", buffer));
1174 /* Locate version part */
1175 while (buffer[pos])
1177 LONG dummy;
1179 /* NOTE: Not isspace()! - Piru */
1180 if (myisspace(buffer[pos]) &&
1181 StrToLong(buffer + pos + 1, &dummy) != -1)
1183 buffer += pos;
1184 foundver = TRUE;
1185 break;
1187 pos++;
1189 D(Printf("createresidentver: buffer: \"%s\"\n", buffer));
1191 name = MyResident->rt_IdString;
1192 len = buffer - name;
1195 /* If could not find any version info, use the resident rt_Name */
1196 if (!foundver)
1197 buffer = "";
1199 D(Printf("createresidentver: buffer: \"%s\"\n", buffer));
1201 if ((!args.arg_full) || (!name))
1203 name = MyResident->rt_Name;
1204 len = strlen(MyResident->rt_Name);
1207 tmpbuffer = AllocVec(len + strlen(buffer) + 1, MEMF_ANY);
1208 if (!tmpbuffer)
1210 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1211 return RETURN_FAIL;
1213 CopyMem(name, tmpbuffer, len);
1214 strcpy(tmpbuffer + len, buffer);
1215 D(Printf("createresidentver: tmpbuffer: \"%s\"\n", tmpbuffer));
1217 error = makedatafromstring(tmpbuffer);
1219 FreeVec(tmpbuffer);
1221 return error;
1225 /* Retrieve version information from library. Returns 0 for success.
1227 static
1228 int createlibraryver(struct Library *MyLibrary)
1230 STRPTR buffer, tmpbuffer;
1231 int error, foundver = FALSE, pos;
1233 if (MyLibrary->lib_IdString)
1235 //Printf("createlibraryver: lib_IdString \"%s\"\n", (LONG) MyLibrary->lib_IdString);
1236 buffer = skipwhites(MyLibrary->lib_IdString);
1238 //Printf("createlibraryver: buffer \"%s\"\n", (LONG) buffer);
1240 /* Find full 'ver.rev' version info
1242 pos = 0;
1243 while (buffer[pos])
1245 LONG dummy, add;
1247 /* NOTE: Not isspace()! - Piru */
1248 if (myisspace(buffer[pos]) &&
1249 (add = StrToLong(buffer + pos + 1, &dummy)) != -1 &&
1250 buffer[pos + 1 + add] == '.')
1252 buffer += pos;
1253 pos = 0;
1254 foundver = TRUE;
1255 break;
1257 pos++;
1260 /* If could not find 'ver.rev', find any numeric */
1261 if (!foundver)
1263 pos = 0;
1264 while (buffer[pos])
1266 LONG dummy;
1268 /* NOTE: Not isspace()! - Piru */
1269 if (myisspace(buffer[pos]) &&
1270 StrToLong(buffer + pos + 1, &dummy) != -1)
1272 buffer += pos;
1273 pos = 0;
1274 foundver = TRUE;
1275 break;
1277 pos++;
1282 /* If could not find any version info, use the resident rt_Name */
1283 if (!foundver)
1285 buffer = "";
1286 error = RETURN_WARN;
1289 tmpbuffer = AllocVec(strlen(MyLibrary->lib_Node.ln_Name) + strlen(buffer) + 1, MEMF_ANY);
1290 if (!tmpbuffer)
1292 PrintFault(ERROR_NO_FREE_STORE, (STRPTR) ERROR_HEADER);
1293 return RETURN_FAIL;
1296 strcpy(tmpbuffer, MyLibrary->lib_Node.ln_Name);
1297 strcat(tmpbuffer, buffer);
1298 //Printf("createlibraryver: tmpbuffer: \"%s\"\n", (LONG) tmpbuffer);
1299 pos = makedatafromstring(tmpbuffer);
1300 if (pos > error)
1302 error = pos;
1305 FreeVec(tmpbuffer);
1307 return error;
1311 /* Create default strings
1313 static
1314 int createdefvers(CONST_STRPTR name)
1316 FreeVec(parsedver.pv_revname);
1317 FreeVec(parsedver.pv_vername);
1318 FreeVec(parsedver.pv_name);
1320 parsedver.pv_name = dupstr(name, -1);
1321 parsedver.pv_vername = AllocVec(14, MEMF_ANY);
1322 parsedver.pv_revname = AllocVec(15, MEMF_ANY);
1324 if (parsedver.pv_name &&
1325 parsedver.pv_vername &&
1326 parsedver.pv_revname)
1328 __sprintf(parsedver.pv_vername, "%ld", (long)parsedver.pv_version);
1329 __sprintf(parsedver.pv_revname, ".%ld", (long)parsedver.pv_revision);
1331 return RETURN_OK;
1334 return RETURN_FAIL;
1338 /* Create version info from named resident
1340 static
1341 int makeresidentver(CONST_STRPTR name)
1343 struct Resident *MyResident;
1344 int error = -1;
1346 if ((MyResident = findresident(name)))
1348 error = createresidentver(MyResident);
1349 if (error != RETURN_OK)
1351 /* get values from residenttag */
1352 parsedver.pv_version = MyResident->rt_Version;
1353 parsedver.pv_revision = -1;
1354 error = createdefvers(MyResident->rt_Name);
1358 return error;
1362 /* Create version info from named list node
1364 static
1365 int makeexeclistver(struct List *list, CONST_STRPTR name)
1367 struct Library *MyLibrary;
1368 int error = -1;
1369 #if defined(__AROSPLATFORM_SMP__)
1370 void *ExecLockBase = OpenResource("execlock.resource");
1372 if (ExecLockBase)
1373 ObtainSystemLock(list, SPINLOCK_MODE_READ, LOCKF_FORBID);
1374 else
1375 Forbid();
1376 #else
1377 Forbid();
1378 #endif
1379 MyLibrary = (struct Library *) findname(list, name);
1380 if (MyLibrary)
1382 /* get values from library */
1383 ULONG ver = MyLibrary->lib_Version;
1384 ULONG rev = MyLibrary->lib_Revision;
1386 error = createlibraryver(MyLibrary);
1387 if (error != RETURN_OK ||
1388 parsedver.pv_version != ver ||
1389 parsedver.pv_revision != rev)
1391 /* special case where createlibraryrev was successful, but
1392 * version or revision don't match.
1394 if (error == RETURN_OK)
1396 /* If there is extrastr, make sure there's linefeed, too.
1398 if (parsedver.pv_extrastr)
1400 parsedver.pv_extralf = "\n";
1404 /* get values from library */
1405 parsedver.pv_version = ver;
1406 parsedver.pv_revision = rev;
1408 error = createdefvers(MyLibrary->lib_Node.ln_Name);
1412 #if defined(__AROSPLATFORM_SMP__)
1413 if (ExecLockBase)
1414 ReleaseSystemLock(list, LOCKF_FORBID);
1415 else
1416 Permit();
1417 #else
1418 Permit();
1419 #endif
1421 return error;
1425 /* Find resident from seglist, return NULL if none found
1427 static
1428 struct Resident *FindLibResident(BPTR Segment)
1430 while (Segment)
1432 IPTR *MySegment;
1433 UWORD *MyBuffer;
1434 UWORD *EndBuffer;
1436 MySegment = (IPTR*) BADDR(Segment);
1437 MyBuffer = (UWORD*) &MySegment[1];
1438 EndBuffer = (UWORD*) &MySegment[(MySegment[-1] - sizeof(ULONG) * 4) / sizeof(ULONG)];
1440 while (MyBuffer < EndBuffer)
1442 struct Resident *MyResident;
1444 MyResident = (struct Resident*) MyBuffer;
1445 if (MyResident->rt_MatchWord == RTC_MATCHWORD &&
1446 MyResident->rt_MatchTag == MyResident)
1448 return MyResident;
1451 MyBuffer++;
1454 Segment =(BPTR) MySegment[0];
1457 SetIoErr(ERROR_OBJECT_NOT_FOUND);
1458 return NULL;
1462 /* Find $VER: tag from seglist, return NULL if none found
1463 Returns dupstr()d string or NULL.
1465 static
1466 STRPTR FindSegmentVER(BPTR Segment)
1468 while (Segment)
1470 void *MySegment;
1471 CONST_STRPTR MyBuffer;
1472 ULONG BufferLen;
1473 CONST_STRPTR EndBuffer;
1474 CONST_STRPTR SegmentEnd;
1476 MySegment = BADDR(Segment);
1477 MyBuffer = (CONST_STRPTR) (MySegment + sizeof(BPTR));
1478 BufferLen = *(ULONG *)(MySegment - sizeof(ULONG));
1479 SegmentEnd = (CONST_STRPTR) (MySegment + (BufferLen - sizeof(BPTR)));
1480 EndBuffer = SegmentEnd - 5;
1482 while (MyBuffer < EndBuffer)
1484 if (MyBuffer[0] == '$' &&
1485 MyBuffer[1] == 'V' &&
1486 MyBuffer[2] == 'E' &&
1487 MyBuffer[3] == 'R' &&
1488 MyBuffer[4] == ':')
1490 CONST_STRPTR EndPtr;
1492 MyBuffer += 5;
1493 /* Required because some smartass could end his $VER: tag
1494 * without '\0' in the segment to save space. - Piru
1496 for (EndPtr = MyBuffer; EndPtr < SegmentEnd && *EndPtr; EndPtr++)
1498 if (EndPtr - MyBuffer)
1500 return dupstr(MyBuffer, EndPtr - MyBuffer);
1504 MyBuffer++;
1507 Segment =*(BPTR *)MySegment;
1510 SetIoErr(ERROR_OBJECT_NOT_FOUND);
1511 return NULL;
1515 /* Create version info from named device
1517 static
1518 int makedevicever(CONST_STRPTR name)
1520 struct DevProc *MyDevProc;
1521 int error = -1;
1523 MyDevProc = GetDeviceProc((STRPTR) name, NULL);
1524 if (MyDevProc)
1526 if (MyDevProc->dvp_DevNode->dol_Type == DLT_DEVICE)
1528 BPTR SegList;
1530 SegList = MyDevProc->dvp_DevNode->dol_misc.dol_handler.dol_SegList;
1531 if (SegList)
1533 struct Resident *MyResident;
1535 MyResident = FindLibResident(SegList);
1536 if (MyResident)
1538 error = createresidentver(MyResident);
1540 else
1541 error = RETURN_FAIL;
1544 FreeDeviceProc(MyDevProc);
1547 if (error != RETURN_OK && error != -1)
1549 Printf("Could not find version information for '%s'\n", name);
1552 return error;
1555 static int elf_read_block(BPTR file, ULONG offset, APTR buffer, ULONG size)
1557 if (Seek(file, offset, OFFSET_BEGINNING) < 0)
1558 return 0;
1560 return Read(file, buffer, size);
1563 static void *load_block(BPTR file, ULONG offset, ULONG size)
1565 void *block = AllocMem(size, MEMF_ANY);
1567 if (block)
1569 if (elf_read_block(file, offset, block, size) == size)
1570 return block;
1572 FreeMem(block, size);
1575 return NULL;
1578 static inline UWORD elf_read_word(UWORD data, struct elfheader *eh)
1580 switch (eh->ident[EI_DATA])
1582 case ELFDATA2LSB:
1583 return AROS_LE2WORD(data);
1585 case ELFDATA2MSB:
1586 return AROS_BE2WORD(data);
1588 default:
1589 return 0;
1593 static inline ULONG elf_read_long(ULONG data, struct elfheader *eh)
1595 switch (eh->ident[EI_DATA])
1597 case ELFDATA2LSB:
1598 return AROS_LE2LONG(data);
1600 case ELFDATA2MSB:
1601 return AROS_BE2LONG(data);
1603 default:
1604 return 0;
1608 static ULONG read_shnum(BPTR file, struct elfheader *eh)
1610 ULONG shnum = elf_read_word(eh->shnum, eh);
1612 /* the ELF header only uses 16 bits to store the count of section headers,
1613 * so it can't handle more than 65535 headers. if the count is 0, and an
1614 * offset is defined, then the real count can be found in the first
1615 * section header (which always exists).
1617 * similarly, if the string table index is SHN_XINDEX, then the actual
1618 * index is found in the first section header also.
1620 * see the System V ABI 2001-04-24 draft for more details.
1622 if (shnum == 0)
1624 struct sheader sh;
1625 ULONG shoff = elf_read_long(eh->shoff, eh);
1627 if (shoff == 0)
1628 return 0;
1630 if (elf_read_block(file, shoff, &sh, sizeof(sh)) != sizeof(sh))
1631 return 0;
1633 /* wider section header count is in the size field */
1634 shnum = elf_read_long(sh.size, eh);
1637 return shnum;
1640 static BOOL ARM_ParseAttrs(UBYTE *data, ULONG len, struct elfheader *eh)
1642 struct attrs_section *attrs;
1644 if (data[0] != ATTR_VERSION_CURRENT)
1646 D(Printf("Unknown attributes version: 0x%02\n", data[0]));
1647 return FALSE;
1650 attrs = (void *)data + 1;
1651 while (len > 0)
1653 ULONG attrs_size = elf_read_long(attrs->size, eh);
1655 if (!strcmp(attrs->vendor, "aeabi"))
1657 struct attrs_subsection *aeabi_attrs = (void *)attrs->vendor + 6;
1658 ULONG aeabi_len = attrs_size - 10;
1660 D(Printf("Found aeabi attributes @ 0x%p (length %u)\n", aeabi_attrs, aeabi_len));
1662 while (aeabi_len > 0)
1664 ULONG aeabi_attrs_size = elf_read_long(aeabi_attrs->size, eh);
1666 if (aeabi_attrs->tag == Tag_File)
1668 UBYTE *file_subsection = (void *)aeabi_attrs + sizeof(struct attrs_subsection);
1669 UBYTE file_len = aeabi_attrs_size - sizeof(struct attrs_subsection);
1671 D(Printf("Found file-wide attributes @ 0x%p (length %u)\n", file_subsection, file_len));
1673 while (file_len > 0)
1675 UBYTE tag, shift;
1676 ULONG val = 0;
1678 tag = *file_subsection++;
1679 file_len--;
1681 if (file_len == 0)
1683 D(Printf("Mailformed attribute tag %d (no data)\n", tag));
1684 return FALSE;
1687 switch (tag)
1689 case Tag_CPU_raw_name:
1690 case Tag_CPU_name:
1691 case Tag_compatibility:
1692 case Tag_also_compatible_with:
1693 case Tag_conformance:
1694 /* These two are NULL-terminated strings. Just skip. */
1695 while (file_len)
1697 file_len--;
1698 if (*file_subsection++ == 0)
1699 break;
1701 break;
1703 default:
1704 /* Read ULEB128 value */
1705 shift = 0;
1706 while (file_len)
1708 UBYTE byte;
1710 byte = *file_subsection++;
1711 file_len--;
1713 val |= (byte & 0x7F) << shift;
1714 if (!(byte & 0x80))
1715 break;
1717 shift += 7;
1721 switch (tag)
1723 case Tag_CPU_arch:
1724 D(Printf("ARM CPU architecture set to %d\n", val));
1725 parsedver.pv_arm_cpu = val;
1726 break;
1728 case Tag_FP_arch:
1729 D(Printf("ARM FPU architecture set to %d\n", val));
1730 parsedver.pv_arm_fpu = val;
1731 break;
1735 return TRUE;
1737 aeabi_attrs = (void *)aeabi_attrs + aeabi_attrs_size;
1738 aeabi_len -= aeabi_attrs_size;
1741 return FALSE;
1743 attrs = (void *)attrs + attrs_size;
1744 len -= attrs_size;
1746 return FALSE;
1749 static int arm_read_cpudata(BPTR file, struct elfheader *eh)
1751 struct sheader *sh;
1752 ULONG int_shnum;
1753 ULONG shoff;
1754 UWORD shentsize;
1755 ULONG i;
1757 int_shnum = read_shnum(file, eh);
1758 if (!int_shnum)
1759 return 0;
1761 shoff = elf_read_long(eh->shoff, eh);
1762 shentsize = elf_read_word(eh->shentsize, eh);
1764 /* load section headers */
1765 if (!(sh = load_block(file, shoff, int_shnum * shentsize)))
1766 return 0;
1768 for (i = 0; i < int_shnum; i++)
1770 if (sh[i].type == SHT_ARM_ATTRIBUTES)
1772 ULONG off = elf_read_long(sh[i].offset, eh);
1773 ULONG len = elf_read_long(sh[i].size, eh);
1774 void *data = load_block(file, off, len);
1776 D(Printf("ARM ATTRIBUTES section %d loaded at 0x%p\n", i, data));
1778 if (data)
1780 ARM_ParseAttrs(data, len, eh);
1781 FreeMem(data, len);
1783 break;
1787 FreeMem(sh, int_shnum * shentsize);
1789 return 1;
1793 /* Retrieve version information from file. Return 0 for success.
1795 #define BUFFERSIZE (16384 + 1)
1796 static
1797 int makefilever(CONST_STRPTR name)
1799 BPTR file;
1800 int error; // = RETURN_OK;
1802 file = Open((STRPTR) name, MODE_OLDFILE);
1803 if (file)
1805 UBYTE *buffer;
1807 buffer = AllocMem(BUFFERSIZE, MEMF_PUBLIC);
1808 if (buffer)
1810 int len = BUFFERSIZE - 1;
1812 if (args.arg_arch)
1814 ULONG len = Read(file, buffer, sizeof(struct elfheader));
1816 if (len == sizeof(struct elfheader))
1818 if (buffer[0] == 0x7f && buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F')
1820 /* It's an ELF file, read machine ID */
1821 struct elfheader *eh = (struct elfheader *)buffer;
1823 parsedver.pv_arch = elf_read_word(eh->machine, eh);
1824 if (parsedver.pv_arch == EM_ARM)
1825 arm_read_cpudata(file, eh);
1828 else if (len >= 4)
1830 if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0x03 && buffer[3] == 0xF3)
1832 /* It's AmigaOS hunk file. m68k obviously :) */
1833 parsedver.pv_arch = EM_68K;
1836 /* Rewind the file */
1837 Seek(file, 0, OFFSET_BEGINNING);
1840 error = findinfile(file, ver, buffer, &len, parsedver.pv_md5sum);
1841 if (error == RETURN_OK)
1843 parsedver.pv_flags |= PVF_MD5SUM;
1845 if (len >= 0)
1847 STRPTR startbuffer;
1849 buffer[len] = '\0';
1850 startbuffer = skipwhites(buffer);
1852 //Printf("startbuffer \"%s\"\n", startbuffer);
1853 error = makedatafromstring(startbuffer);
1855 else
1857 /* Try LoadSeg
1859 error = RETURN_ERROR;
1861 Close(file);
1863 file = LoadSeg((STRPTR) name);
1864 if (file)
1866 struct Resident *MyResident;
1868 MyResident = FindLibResident(file);
1869 if (MyResident /*&&
1870 (MyResident->rt_Type == NT_LIBRARY ||
1871 MyResident->rt_Type == NT_DEVICE)*/)
1873 error = createresidentver(MyResident);
1876 UnLoadSeg(file);
1878 file = BNULL;
1880 if (error != RETURN_OK)
1882 /* If user didn't ask for md5sum or we could not calculate it.
1884 if (!args.arg_md5sum || (!(parsedver.pv_flags & PVF_MD5SUM)))
1886 Printf("Could not find version information for '%s'\n", (IPTR) name);
1891 else
1893 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1896 FreeMem(buffer, BUFFERSIZE);
1898 else
1900 error = RETURN_FAIL;
1901 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1904 if (file)
1905 Close(file);
1907 else
1909 LONG ioerr = IoErr();
1911 if (ioerr == ERROR_OBJECT_NOT_FOUND ||
1912 ioerr == ERROR_OBJECT_WRONG_TYPE)
1914 error = -1;
1916 else
1918 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
1919 error = RETURN_FAIL;
1923 return error;
1927 static
1928 int makerescmdver(CONST_STRPTR name)
1930 int error = -1;
1931 struct Segment *segment;
1933 Forbid();
1935 segment = FindSegment((STRPTR) name, NULL, 0);
1936 if (!segment)
1938 segment = FindSegment((STRPTR) name, NULL, 1);
1941 if (segment)
1943 if (segment->seg_UC == CMD_INTERNAL ||
1944 segment->seg_UC == CMD_DISABLED)
1946 Permit();
1947 error = makeresidentver("shell");
1948 Forbid();
1950 else
1952 STRPTR buffer = FindSegmentVER(segment->seg_Seg);
1953 if (buffer)
1955 STRPTR startbuffer;
1957 startbuffer = skipwhites(buffer);
1959 //Printf("startbuffer \"%s\"\n", (LONG) startbuffer);
1960 error = makedatafromstring(startbuffer);
1962 FreeVec(buffer);
1967 Permit();
1969 return error;
1972 static
1973 int setvervar(CONST_STRPTR name, LONG ver, LONG rev)
1975 UBYTE buf[32];
1977 __sprintf(buf, "%ld.%ld", (long) ver, (long) rev);
1979 return SetVar((STRPTR) name, buf, -1, GVF_LOCAL_ONLY | LV_VAR) ? RETURN_OK : -1;
1983 static
1984 int makekickversion(void)
1986 parsedver.pv_version = SysBase->LibNode.lib_Version;
1987 parsedver.pv_revision = SysBase->SoftVer;
1989 setvervar("Kickstart",
1990 parsedver.pv_version,
1991 parsedver.pv_revision);
1993 Printf("Kickstart %ld.%ld",
1994 (LONG) parsedver.pv_version, (LONG) parsedver.pv_revision);
1996 return RETURN_OK;
2000 static
2001 int makewbversion(void)
2003 int error = -1;
2004 struct Library *VersionBase;
2006 VersionBase = OpenLibrary("version.library", 0);
2007 if (VersionBase)
2009 error = makeexeclistver(&SysBase->LibList, "version.library");
2011 if (error == RETURN_OK)
2013 STRPTR newname = dupstr("Workbench", -1);
2014 if (newname)
2016 FreeVec(parsedver.pv_name);
2017 parsedver.pv_name = newname;
2019 setvervar("Workbench", parsedver.pv_version, parsedver.pv_revision);
2022 CloseLibrary(VersionBase);
2025 return error;
2029 static
2030 int makesysver(void)
2032 int error;
2034 error = makekickversion();
2035 if (error == RETURN_OK)
2037 error = makewbversion();
2039 if (error == RETURN_OK)
2041 PutStr(", ");
2043 else
2045 /* prevent silly errormsg if no version.library */
2046 PutStr("\n");
2047 error = RETURN_WARN;
2051 return error;
2055 /* Determine, by which means to get the version-string.
2057 static
2058 int makeverstring(CONST_STRPTR name)
2060 int error; // = RETURN_OK;
2061 BOOL volume = name[strlen(name) - 1] == ':';
2062 CONST_STRPTR filepart = FilePart(name);
2064 error = -1;
2066 if (!volume && !args.arg_file)
2068 if (*filepart)
2070 error = makeresidentver(filepart);
2071 if (error != RETURN_OK)
2074 /* Try libraries
2076 error = makeexeclistver(&SysBase->LibList, filepart);
2077 if (error != RETURN_OK)
2079 STRPTR namebuf;
2080 ULONG namelen = strlen(filepart);
2082 /* 12 is "MOSSYS:LIBS/" */
2083 if ((namebuf = AllocVec(12 + namelen + 4 + 1, MEMF_PUBLIC)))
2085 strcpy(namebuf, "LIBS:");
2086 strcat(namebuf, filepart);
2087 error = makefilever(namebuf);
2089 /* Try devices
2091 if (error != RETURN_OK)
2093 error = makeexeclistver(&SysBase->DeviceList, filepart);
2094 if (error != RETURN_OK)
2096 strcpy(namebuf, "DEVS:");
2097 strcat(namebuf, filepart);
2098 error = makefilever(namebuf);
2101 FreeVec(namebuf);
2108 if (!args.arg_res && error == -1)
2110 if (volume)
2112 error = makedevicever(name);
2114 else
2116 if (*filepart)
2118 error = makefilever(name);
2123 if (!args.arg_file && error == -1)
2125 error = makerescmdver(name);
2128 if (error)
2130 /* If user asked for md5sum, and we could calculate it, don't print error
2131 * but the md5sum + file.
2133 if (args.arg_md5sum && (parsedver.pv_flags & PVF_MD5SUM))
2135 parsedver.pv_name = dupstr(name, -1);
2136 parsedver.pv_flags |= PVF_NOVERSION;
2137 error = RETURN_OK;
2141 if (error == -1)
2143 PrintFault(ERROR_OBJECT_NOT_FOUND, (STRPTR) ERROR_HEADER);
2144 error = RETURN_FAIL;
2147 return error;
2151 static
2152 void freeverstring(void)
2154 parsedver.pv_flags = 0;
2155 parsedver.pv_version = 0;
2156 parsedver.pv_revision = 0;
2158 FreeVec(parsedver.pv_extrastr);
2159 parsedver.pv_extrastr = NULL;
2160 parsedver.pv_extralf = NULL;
2161 FreeVec(parsedver.pv_datestr);
2162 parsedver.pv_datestr = NULL;
2163 FreeVec(parsedver.pv_revname);
2164 parsedver.pv_revname = NULL;
2165 FreeVec(parsedver.pv_vername);
2166 parsedver.pv_vername = NULL;
2167 FreeVec(parsedver.pv_name);
2168 parsedver.pv_name = NULL;
2171 /* Compare the version given as argument with the version from the object.
2172 * Return RETURN_WARN, if args-v>object-v, otherwise return RETURN_OK.
2174 static
2175 int cmpargsparsed(void)
2177 if (args.arg_version)
2179 if (*(args.arg_version) > parsedver.pv_version)
2181 return RETURN_WARN;
2183 else if (*(args.arg_version) == parsedver.pv_version && args.arg_revision)
2185 if (*(args.arg_revision) > parsedver.pv_revision)
2187 return RETURN_WARN;
2191 else if (args.arg_revision)
2193 if (*(args.arg_revision) > parsedver.pv_revision)
2195 return RETURN_WARN;
2198 return RETURN_OK;
2201 /******************************* main program ****************************/
2203 int __nocommandline;
2205 int main (void)
2207 LONG error = RETURN_FAIL;
2209 struct RDArgs *rda;
2211 rda = ReadArgs(TEMPLATE, (IPTR *) &args, NULL);
2212 if (rda)
2214 if (!args.arg_name || !*args.arg_name)
2216 /* No args, make system version */
2217 error = makesysver();
2218 if (error == RETURN_OK)
2220 printverstring();
2221 if (parsedver.pv_flags & PVF_NOVERSION)
2223 error = RETURN_FAIL;
2225 if (error == RETURN_OK)
2227 error = cmpargsparsed();
2230 freeverstring();
2232 else
2234 CONST_STRPTR *name;
2235 BOOL multifile;
2236 #if 1
2237 /* Workaround for:
2238 * version file ver
2239 * version file ver rev
2241 if (!args.arg_version && !args.arg_revision)
2243 LONG narg = 1;
2244 while (args.arg_name[narg]) { narg++; }
2245 if (narg == 2 || narg == 3)
2247 if (StrToLong(args.arg_name[1], &mversion) > 0)
2249 args.arg_version = &mversion;
2250 args.arg_name[1] = args.arg_name[2];
2251 if (narg == 3)
2253 args.arg_name[2] = NULL;
2255 if (narg == 3)
2257 if (StrToLong(args.arg_name[1], &mrevision) > 0)
2259 args.arg_revision = &mrevision;
2260 args.arg_name[1] = NULL;
2266 #endif
2267 multifile = args.arg_name[1] != NULL;
2269 for (name = args.arg_name; *name; name++)
2271 error = makeverstring(*name);
2272 if (error == RETURN_OK)
2274 printverstring();
2276 if (!multifile)
2278 /* Single args, do compare stuff also */
2279 if (parsedver.pv_flags & PVF_NOVERSION)
2281 error = RETURN_FAIL;
2283 if (error == RETURN_OK)
2285 error = cmpargsparsed();
2289 freeverstring();
2294 FreeArgs(rda);
2296 else
2298 PrintFault(IoErr(), (STRPTR) ERROR_HEADER);
2299 error = RETURN_FAIL;
2302 RT_Exit();
2304 return error;