Copyright (c) 2012-2016 Hyperion Entertainment and contributors.

Programming AmigaOS 4: Utility - Little Helpers

From AmigaOS Documentation Wiki
Jump to: navigation, search

This article was adapted from Amiga Future magazine's series on developing for AmigaOS....

The inconspicuous AmigaOS 4 utility.library has been extended with some useful functions. So it's about time we took a closer look at it.

We have sub-divided the overall description into different sections. So, let's get started with part one, the very flexible taglists.

Tag list improvements

Extending function-calls and their parameters can be a complicated issue, without updating all the old sources. By using a tag list, you can include new tags very flexibly, so unknown tags will be ignored. So if you are running the program on an older system, these new options are not available. That means, tag lists will become more and more important. Many of these new functions are about creating, searching and filtering the tagslists. A complete overview about all the new functions can be found in the final section of this article.

  struct TagItem *taglist;
 
  /* allocates memory for four pairs of tag-entries */
  /* the fifth entry only includes the identification for the list-ending */
  if((taglist = IUtility->AllocateTagItems(5)))
  {
    /* Fill the taglist */
    taglist[0].ti_Tag  = TAG_IGNORE;
    taglist[0].ti_Data = 1;
    ...
    taglist[4].ti_Tag  = TAG_END;
    taglist[4].ti_Data = 0;
 
    /* do something wiht the taglist */
    ...
 
    /*at the end the taglist memory can be deallocated*/
    IUtility->FreeTagItems(taglist);
  }

Check-digits

The utility.library provides a list of interesting features for check-digits. There are a great number of different check-digits and algorithms (i.e. CR32, MD5). The Amiga allows you to calculate a 160 bit (= 20 byte) check-digit via the SHA-1 algorithm. This comes in handy, if you want to check over huge data sections, without having an identical number. In comparison to check-digits with only 2 respectively 4 bytes, this can become a real problem. In detail, there are 3 steps to be made.

First of all, you have to initialise the data structure by using the IUtility->MessageDigest_SHA_Unit() command. Second comes the actual calculation via the IUtility->MessageDigest_SHA_Update() command, whereas the data and the length must be given to create the check-digit.

Lastly we type in the IUtility->MessageDigest_SHA_Final() command. After completing all three steps, it's possible to get the calculated check-digit through mdsha_Code. The example below shows you how:

  struct MessageDigest_SHA mdsha = { 0 };
  STRPTR testdata = "This is just a test";
 
  IUtility->MessageDigest_SHA_Init(&mdsha);
  IUtility->MessageDigest_SHA_Update(&mdsha, testdata, IUtility->Strlen(somedata));
  IUtility->MessageDigest_SHA_Final(&mdsha);
 
  IDOS->Printf(" Checksum is: %.20s\n",mdsha.mdsha_Code);

The test value will be output bit by bit, so it's not very clever to use the IDOS->Printf() command, because control-characters can be included there as well. The value can be compared with the reference value thru the IUtility->StriCmp() command.

/*
 * CalcChecksum.c
 *
 * gcc CalcChecksum.c -o CalcChecksum -l auto
 */
 
/******************************* INCLUDES *************************************/
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
 
/******************************* DEFINES **************************************/
 
#define BUFFER_SIZE 65536  /* 64 kByte Block */
 
/************************ VARIABLEN DEKLARATIONEN *****************************/
 
const UBYTE *gb_ArgTemplate = "FILENAME/A";
enum { ARG_filename, ARG_MAX };
ULONG gb_Args[ARG_MAX] = { 0 };
 
/******************************************************************************/
 
BOOL CalculateChecksum(CONST_STRPTR filename, UBYTE *checksum)
{
  BPTR fh;
  struct MessageDigest_SHA mdsha = { 0 };
 
  /* Preparing checksum calculation */
  IUtility->MessageDigest_SHA_Init(&mdsha);
 
  if((fh = IDOS->Open((STRPTR)filename, MODE_OLDFILE)))
  {
    LONG character;
    UBYTE *buffer;
 
    if((buffer = (UBYTE *) IExec->AllocVecTags(BUFFER_SIZE, TAG_END)))
    {
      while((character = IDOS->Read(fh, buffer, BUFFER_SIZE)) > 0)
      {
        IUtility->MessageDigest_SHA_Update(&mdsha, buffer, character);
      }
 
      IExec->FreeVec(buffer);
    }
 
    IDOS->Close(fh);
  }
 
  IUtility->MessageDigest_SHA_Final(&mdsha);
  IUtility->Strlcpy(checksum, mdsha.mdsha_Code, 20);
  checksum[20] = '\0';
 
  return fh ? TRUE : FALSE;
}
 
/******************************************************************************/
 
int main()
{
  struct RDArgs *rda;
 
  if((rda = IDOS->ReadArgs((STRPTR)gb_ArgTemplate,(LONG*)gb_Args,NULL)))
  {
    UBYTE keybuffer[21]; /* 20 Bytes Checksum + \0 */
 
    if(BerechneChecksumme((STRPTR)gb_Args[ARG_filename], keybuffer))
    {
      /*
      ** The checksum is a 160-bit data stream which should
      ** not strictly be output like a string. We do so here
      ** for convenience only.
      */
      IDOS->Printf("Checksum for file '%s' is %s\n",
        gb_Args[ARG_filename], keybuffer);
    }
 
    IDOS->FreeArgs(rda);
  }
  else IDOS->PrintFault(IDOS->IoErr(),"CalcChecksum");
 
  return 0;
}
AF111 CalcChecksum.png

Standard-C-Functions

If you don't like to include the additonal C-libraries (newlib or clib2), it's possible to get similar functions from the utility.libary. In the case of string-functions, there are IUtility->Strlcpy() and IUtility->Strlcat() functions, which seem to have a slightly strange name, but work in a similar way as strncpy and strncat from the C-libaries. Since version 53.4 there is also a function called IUtility->Strlen() which determines the length of a string. With AmigaOS 4.1 Final Edition new functions were added for handling UTF-8 coded strings.

Like the memset function, there is a function called IUtility->SetMem(), which is able to set the memory-area. The required arguments are the starting-address in the memory, the value to be set (usually 0 deletes the memory-area) and the number of bytes that have to be amended. In general the order of the arguments are the same as in the memset function. A shortcut can be taken, if you just use the IUtility->ClearMem() function. This one only needs the starting-address and the number of bytes that should be cleared to zero. One other function, that works with memory-areas, is called IUtility->MoveMem(). As the name implies, the memory-area will be moved. Well the source material stays unchanged, so it's more like copying the areas. You can compare this function to the IExec->CopyMem() function from the exec.libary, but with the difference of allowing overlapping memory-areas. Make sure that your destination has enough free space for the data and, of course, it has to be reserved in your own program as well, because without reserving it, the OS will shutdown your program, if "aggravated" memory protection is enabled.

/*
 * MemoryFunctions.c
 *
 * gcc MemoryFunctions.c -o MemoryFunctions -l auto
 */
 
/******************************* INCLUDES *************************************/
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
 
/******************************************************************************/
 
int main()
{
  UBYTE *mem;
 
  /* Request memory */
  if((mem = (UBYTE *) IExec->AllocVecTags(512, TAG_END)))
  {
    IDOS->Printf("memory allocated\n");
 
    /* Clear memory area */
    IUtility->ClearMem(mem,512);
 
    /* Set memory area */    
    IUtility->SetMem(mem,'@',128);
 
    /* Move memory area */
    IUtility->MoveMem(mem,mem + 128,256);
 
    /* Free memory area */
    IExec->FreeVec(mem);
  }
  else IDOS->Printf("Not enough memory\n");
 
  return( 0 );
}

Randomizing

If you ever liked to create your own lottery numbers, the new IUtility->Random() function will allow you to do just that. All you need is a number, so you can just pick the actual time or a random value from the stack. The function will return a number ranging from 1 up to 2147483647, which can be cut down via the modulo operation. Next up we see an example of how to create your own lottery numbers. The same number can be chosen again, so there is room for improvement.

  struct RandomState state;
  ULONG i, number;
  IDOS->Printf("6 out of 49:\n");
  for(i=1; i<7; i++)
  {
    /* determines a number between 1 and 49 */
    number = (IUtility->Random(&state) % 48) + 1;
    IDOS->Printf("%ld. Number %2ld\n",i,number);
  }
 
  number = (IUtility->Random(&state) % 48) + 1;
  IDOS->Printf("Additional number %2ld\n",number);
/* LottoGenerator.c
 *
 * gcc LottoGenerator.c -o LottoGenerator -l auto
 */
 
/******************************* INCLUDES *************************************/
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <time.h>
 
/******************************************************************************/
 
int main()
{
  struct RandomState state;
  uint32 i, number;
 
  /*
  ** Initialize the start values so that a different
  ** series of random numbers are generated each time.
  */
  state.rs_High = time(NULL);
  state.rs_Low  = time(NULL);
 
  IDOS->Printf("Lotto 6 out of 49:\n");
  for(i=1; i<7; i++)
  {
    /*
    ** Determine a number from 1 to 49.
    */
    number = (IUtility->Random(&state) % 48) + 1;
    IDOS->Printf("%ld. Number is %2ld\n",i,number);
  }
 
  number = (IUtility->Random(&state) % 48) + 1;
  IDOS->Printf("The bonus number is %2ld\n",number);
 
  return 0;
}
AF111 LottoGenerator.png

Miscellaneous functions

Another helpful function is called IUtility->GetUniqueID(), which determines a unique number. This comes in handy by creating temporary files using multiple processes. As long as the system isn't shutdown, this unique number will be available. By restarting the system, the numbers will be created from scratch. Here is an easy example of how to create files with unique numbers:

  ULONG id = IUtility->GetUniqueID();
  BPTR fh;
  TEXT filename[30];
  IUtility->SNPrintf(filename,sizeof(filename),"t:tmp.%ld",id);
  if(IDOS->Open(filename,MODE_NEWFILE))
  {
    ...
    IDOS->CLose(fh);
  }

New utility.library functions

Taglist functions

Allocates the memory for the number of given tags.

struct TagItem *AllocateTagItems(ULONG numtags);

Changes the tag-data-values in the changelist based on the values from the list. Unknown tags in the list will be ignored.

VOID ApplyTagChanges(struct TagItem *list, struct TagItem *changelist);

Creating a taglist copy; has to be deallocated via FreeTagItems().

struct TagItem *CloneTagItems(struct TagItem *orglist);

Deallocates the memory for the taglist.

VOID FreeTagItems(struct TagItem *taglist);

Creates a skip-tag-list; has to be deallocated via DeleteSkipList().

struct SkipList * CreateSkipList(struct Hook *hook, LONG max_levels);
VOID DeleteSkipList(struct SkipList *list);

Searches the changelist and deletes all entries, which are identical to the entries in the orglist. In the case of different values, the keyword "apply" defines if the value should be assumed from the orglist.

VOID FilterTagChanges(struct TagItem *changelist, struct TagItem *orglist, ULONG apply);

Deletes the tags from the changelist, if they occur in the filter array.

ULONG FilterTagItems(struct TagItem *changelist, Tag *filterarray ,ULONG logic);

Searches the taglist for the right entry and returns the matching items.

struct TagItem *FindTagItem(Tag tag,struct TagItem *taglist);

Determines the data value of the declared tag from the list.

ULONG GetTagData(Tag tag, ULONG default, struct TagItem *taglist);

Transfers the maplist values into the corresponding fields of the taglist. Type pin-points what happens to all entries, which only occur in the taglist.

VOID MapTags(struct TagItem *taglist, struct TagItem *maplist, ULONG type);

Splay-Tree-Functions

Creating a Splay-Tree-Datastructure; has to be deallocated via DeleteSplayTree()

struct SplayTree * CreateSplayTree(struct Hook *hook);
VOID DeleteSplayTree(struct SplayTree *tree);

Searches the tree for a specific key and returns the matching node

struct SplayNode * FindSplayNode(struct SplayTree *tree, APTR key);

Creates a new SplayNode and adds it to the tree

struct SplayNode * InsertSplayNode(struct SplayTree *tree, APTR key, ULONG data_size);

Removes the SplayNode from the tree, if a matching entry for the key was found.

BOOL RemoveSplayNode(struct SplayTree *tree, APTR key);

Skip-List-Functions

Searches the list for a specific key and returns the matching node.

struct SkipNode * FindSkipNode(struct SkipList *list, APTR key);

Determines the first node in the list

struct SkipNode * GetFirstSkipNode(struct SkipList *list);

Determines the next node after the previous node in the list.

struct SkipNode * GetNextSkipNode(struct SkipList *list, struct SkipNode *previousnode);

Creates a new skipnode and adds it to the list.

struct SkipNode * InsertSkipNode(struct SkipList *list, APTR key, ULONG total_size);

Removes the SkipNode from the list, if a matching entry for the key was found.

BOOL RemoveSkipNode(struct SkipList *list, APTR key);

Miscellaneous functions

Calculates a 160 bit checksum

VOID MessageDigest_SHA_Init(struct MessageDigest_SHA *mdsha);
VOID MessageDigest_SHA_Update(struct MessageDigest_SHA *mdsha, APTR data, LONG num_bytes);
VOID MessageDigest_SHA_Final(struct MessageDigest_SHA *mdsha);

A hook-function for an object to send a message

ULONG CallHookPkt(struct Hook *hook, APTR object, APTR message);

Provides a unique number (as long as the computer is running)

ULONG GetUniqueID(void);

A random number between 1 ... 2147483647 will be returned

LONG Random(struct RandomState *state);

Adds a value to the memory-area

APTR SetMem(APTR destination, UBYTE value, LONG length);

Deletes a memory-area (it will be filled with 0 bytes):

VOID ClearMem(APTR destination, uint32 size);

Moves parts of the memory-area (even if they are overlapping):

VOID MoveMem(APTR source, APTR destination, uint32 size);

Adds the source string to the destination, where the max length will be considered.

LONG Strlcat(STRPTR destination, CONST_STRPTR source, LONG destination_size);

Copies the string from its source to its destination, where the max length will be considered.

LONG Strlcpy(STRPTR destination, CONST_STRPTR source, LONG destination_size);

Determines the length of a string uint32 Strlen(CONST_STRPTR string);

Searches a node based on its name. Lower and Upper case will be ignored.

struct Node *node = FindNameNC(struct Node *start, STRPTR name);

Creates a string based on a specific format and the required args, (the returned string has to be deallocated via FreeVec)

STRPTR VASPrintf(CONST_STRPTR format, APTR args);
STRPTR ASPrintf(CONST_STRPTR format, ...);

Creates a Format-String, where the result is stored in the buffer.

LONG VSNPrintf(STRPTR buffer, LONG buffer_size, CONST_STRPTR format, APTR args);
LONG SNPrintf(STRPTR buffer, LONG buffer_size, CONST_STRPTR format, ...);

Authors

Written by Michael Christoph
Translation by Florian Hanel
Copyright (c) 2013 Michael Christoph