Etusivulle

ScrollingStringGadget.js

// An object to scroll some text inside a string gadget
function ScrollingStringGadget(gad, length, str, delay, indent) {
		// Copyright (C) Mikko Koivunalho 1999
		// gad, the string gadget in which to do the scrolling
		// length, the length of the string gadget (which was given in the HTML source)
		//    Unfortunately the length seems to vary. Sometimes it's exactly what you 
		//    specified in the source, sometimes it's that + 1, and when the client uses proportional fonts, oh dear!
		//    This object does not modify the value you give.
		// str, the whole string which to scroll
		// delay, the scrolling delay (speed) in milliseconds
		// indent, the amount of space left of string in first round
	this.gadget = gad;
	this.gadgetLength = length;
	this.string = new String(str);
	this.scrollDelay = delay;
	this.firstTimeIndent = indent;
	this.thisObjectName = new String(""); // !! Use must give this property a value
		// The value is the string representation of the object you have created
		// like this: scroller = new ScrollingStringGadget(abc,0,"",0,0);
		//            scroller.thisObjectName = "scroller";
		// Unfortunately this seems to be necessary for window.setTimeout().
		// Damn inconvenient, though!

		// Don't change below this point -- For internal use only!!
	this.left = 0; // How much of the string has already disappeared to the left
	this.firstTime = 0; // The amount of space on the left of string during the first round
	this.tempString = new String(""); // a temporary string to make the gadget contents into
	this.timeoutId = null;
}
new ScrollingStringGadget(null, 0, "", 0, 0); // Needed to create the prototypes
function ScrollingStringGadget_doScroll() {
	if((this.left + this.gadgetLength) <= this.string.length) {
			// The string has not yet reached the end
		if(this.firstTime > 0) {
				// This is the first time doing the scroll round
				// meaning we must add space before the string
			this.tempString = "";
			for(var i = 1; i <= this.firstTime; i++) {
				this.tempString = this.tempString + " ";
			}
			this.tempString = this.tempString + this.string.substring(0, 
					(this.gadgetLength - this.firstTime));
			this.firstTime--;
		}
		else {
			this.tempString = this.string.substring(this.left, (this.left + this.gadgetLength));
			this.left++;
		}
	}
	else {
			// The string has reached the end and now we have to attach 
			// part of the beginning to the end
		if(this.left <= this.string.length - 1) {
				// Atleast one character left in the end (beginning of the string gadget
				// temp variables could be used here, but I optimized instead: 
				// var do_scrolling_takefromend = this.string.length - this.left;
				// var do_scrolling_takefrombegin = this.gadgetLength - do_scrolling_takefromend;
			this.tempString = this.string.substring(this.left, this.string.length);
			this.tempString = this.tempString + this.string.substring(0, 
				this.gadgetLength - (this.string.length - this.left)); //or do_scrolling_takefrombegin
			this.left++;
		}
		else {
				// Here the whole string has gone to the left of the gadget!
			this.left = 0;
			this.tempString = this.string.substring(this.left, (this.left + this.gadgetLength));
			this.left++;
		}
	}
	this.gadget.value = this.tempString;
	this.timeoutId = window.setTimeout(this.thisObjectName + ".doScroll()", this.scrollDelay); // Repeat this function
	return 0;
}
function ScrollingStringGadget_begin() {
	this.firstTime = this.firstTimeIndent;
	this.doScroll();
}
function ScrollingStringGadget_stop() {
	window.clearTimeout(this.timeoutId);
}
function ScrollingStringGadget_resume() {
	this.doScroll();
}
ScrollingStringGadget.prototype.doScroll = ScrollingStringGadget_doScroll;
// Public functions below!!
ScrollingStringGadget.prototype.begin = ScrollingStringGadget_begin;
// Begin scrolling
ScrollingStringGadget.prototype.stop = ScrollingStringGadget_stop;
// Stop scrolling
ScrollingStringGadget.prototype.resume = ScrollingStringGadget_resume;
// Resume scrolling

PingPongStringGadget.js

// An object to pingpong scroll some text inside a string gadget
// i.e. make the string go from left to right and vice versa and
// "bounce" from the "walls" (gadget edges).
function PingpongStringGadget(gad, length, str, delay) {
		// Copyright (C) 1999 Mikko Koivunalho
		// gad, the string gadget in which to do the scrolling
		// length, the length of the string gadget (which was given in the HTML source)
		//    Unfortunately the length seems to vary. Sometimes it's exactly what you 
		//    specified in the source, sometimes it's that + 1, and when the client uses proportional fonts, oh dear!
		//    This object does not modify the value you give.
		// str, the whole string which to scroll
		// delay, the scrolling delay (speed) in milliseconds
	this.gadget = gad;
	this.gadgetLength = length;
	this.string = new String(str);
	this.scrollDelay = delay;
	this.thisObjectName = new String(""); // !! Use must give this property a value!!
		// The value is the string representation of the object you have created
		// like this: scroller = new ScrollingStringGadget(abc,0,"",0,0);
		//            scroller.thisObjectName = "scroller";
		// Unfortunately this seems to be necessary for window.setTimeout().
		// Damn inconvenient, though!

		// Don't change below this point -- For internal use only!!
	this.whiteSpaces = 0; // The amount of space on the left side of string
	this.dirToLeft = new Boolean(false); // Is the string going leftwards? User preferences!
	this.tempString = new String(""); // a temporary string to make the gadget contents into
	this.timeoutId = null;
}
new PingpongStringGadget(null, 0, "", 0); // Needed to create the prototypes
function PingpongStringGadget_doScroll() {
	this.tempString = "";
	if(this.dirToLeft == true) {
		this.whiteSpaces--;
		if(this.whiteSpaces <= 0) {
			this.dirToLeft = false;
		}
	}
	else {  // this.dirToLeft == false -> "dirToRight"
		this.whiteSpaces++;
		if((this.whiteSpaces + this.string.length) >= this.gadgetLength) {
			this.dirToLeft = true;
		}
	}
	if(this.whiteSpaces >= 0) { // Normal situation!! ;-)
		for(var i = 1; i <= this.whiteSpaces; i++) {
			this.tempString = this.tempString + " ";
		}
		this.tempString = this.tempString + this.string;
	}
	else { // The string is entering through the left edge; therefore special arrangements.
		var showChars = this.whiteSpaces + this.string.length;
		this.tempString = this.string.substring(this.string.length - showChars, 
				this.string.length); // We cut the end part of the string
	}
	this.gadget.value = this.tempString;
	this.timeoutId = window.setTimeout(this.thisObjectName + ".doScroll()", this.scrollDelay); // Repeat this function
}
// A function to set the place and scroll direction of the string 
//  before starting to pingpong scroll it.
function PingpongStringGadget_startPlace(place,dir) {
		// place, either a number or a string; users choices: 
		//   users choices: center, left, right, enter_left, enter_right
		// dir, direction to which scroll
	if(typeof place == "string") {
			// place can be center, left, right, enter_left or enter_right
		if(place == "center") {
			this.whiteSpaces = Math.floor((this.gadgetLength - this.string.length) / 2);
			if(typeof dir == "string") {
				if(dir == "left") {
					this.dirToLeft = true;
				}
				if(dir == "right") {
					this.dirToLeft = false;
				}
			}
		}
		if(place == "left") {
			this.whiteSpaces = -1;
					// - 1 because in doScroll() the string gets moved one step forward before showing it
			this.dirToLeft = false;
		}
		if(place == "right") { // Places the string in the right edge
			this.whiteSpaces = (this.gadgetLength - this.string.length) + 1;
					// + 1 because in doScroll() the string gets moved one step backward before showing it
			this.dirToLeft = true;
			/*alert("string.length: " + this.string.length + " whiteSpaces: " + this.whiteSpaces +
				"\nstring: " + this.string + "\ngadgetlength: " + this.gadgetLength);*/
		}
		if(place == "enter_left") { // The string enters from the left edge
			this.whiteSpaces = -this.string.length - 1;
			this.dirToLeft = false;
		}
		if(place == "enter_right") { // The string enters from the right edge
			this.whiteSpaces = this.gadgetLength + 1;
			this.dirToLeft = true;
		}
	}
	else { // place == "number"
		this.whiteSpaces = place;
		if(typeof dir == "string") {
			if(dir == "left") {
				this.dirToLeft = true;
			}
			if(dir == "right") {
				this.dirToLeft = false;
			}
		}
	}
}
function PingpongStringGadget_begin() {
	this.doScroll();
}
function PingpongStringGadget_stop() {
	window.clearTimeout(this.timeoutId);
}
function PingpongStringGadget_resume() {
	this.doScroll();
}
PingpongStringGadget.prototype.doScroll = PingpongStringGadget_doScroll;
		// Public functions below!!
PingpongStringGadget.prototype.startPlace = PingpongStringGadget_startPlace;
		// From where to start the scrolling
PingpongStringGadget.prototype.begin = PingpongStringGadget_begin;
		// Begin pingponging
PingpongStringGadget.prototype.stop = PingpongStringGadget_stop;
		// stop pingponging
PingpongStringGadget.prototype.resume = PingpongStringGadget_resume;
		// resume pingponging

Viitenumero.e

class VIITENUMERO
creation
	make
feature -- Version
	versionstring: STRING is "\0$VER: Viitenumero 1.0 (12.09.02) Copyright (C) 2002 Mikko Koivunalho"
		-- Amiga specific version string.
feature -- Creation
	make is
		-- Muodosta viitenumero siitä luvusta, joka on annettu argumenttina
		-- komennolla ja tulosta se.
	local
		version_of_program: STRING -- This is needed to include the version string into the code.
		tarkisteluku, summa: INTEGER
		i, kertoluvut_index: INTEGER
		kertoluvut: ARRAY[INTEGER]
		valmis: STRING
	do
		!!version_of_program.make_from_string(versionstring) -- This is needed to include the version string into the code.
		if argument_count = 1
		then
			debug 
				io.put_string("Argumentti on %"")
				io.put_string(argument(1))
				io.put_string("%".%N")
			end
			if not argument(1).is_empty and then is_viitenumero(argument(1))
			then
				from
					kertoluvut := << 7, 3, 1 >>
					kertoluvut_index := 1
					summa := 0
					i := argument(1).upper
				invariant
					argument(1).lower - 1 <= i and
					i <= argument(1).upper
				variant
					argument(1).lower - 1 + i
				until i = argument(1).lower - 1
				loop
					summa := argument(1).item(i).decimal_value * kertoluvut.item(kertoluvut_index) + summa
					if kertoluvut_index = 3
					then 
						kertoluvut_index := 1
					else
						kertoluvut_index := kertoluvut_index + 1
					end
					i := i - 1
					debug
						io.put_string("Summa on %"")
						io.put_string(summa.to_string)
						io.put_string("%".%N")
					end
				end

				if summa \\ 10 = 0
				then
					tarkisteluku := 0
					debug
						io.put_string("Tarkisteluku on %"0%".%N")
					end
				else
					tarkisteluku := ((summa / 10).floor + 1) * 10 - summa
					debug
						io.put_string("Tarkisteluku on %"")
						io.put_string(tarkisteluku.to_string)
						io.put_string("%".%N")
					end
				end
				check tarkisteluku.in_range(0, 9) end

				-- jaetaan viiden numeron sarjoihin oikealta luettuna.
				debug
					io.put_string("Seuraavaksi koostetaan valmis viitenumero 5 numeron sarjoihin.%N")
				end
				from
					!!valmis.make_empty
					i := argument(1).count
					valmis.append(tarkisteluku.to_string)
					debug
						io.put_string("Valmiina on %"")
						io.put_string(valmis)
						io.put_string("%", i on %"")
						io.put_string(i.to_string)
						io.put_string("%".%N")
					end
				invariant
					i.in_range(0, argument(1).count)
				until
					i = 0
				loop
					if valmis.count = 5 or valmis.count = 11 or valmis.count = 17
					then
						valmis.insert_character(' ', 1)
					else
						valmis.insert_character(argument(1).item(i), 1)
						i := i - 1
					end
					debug
						io.put_string("Valmiina on %"")
						io.put_string(valmis)
						io.put_string("%", i on %"")
						io.put_string(i.to_string)
						io.put_string("%".%N")
					end
				end

				io.put_string(valmis)
				io.put_string("%N")
			else -- Komentoriviargumentti ei ole kunnollinen viitenumero
				io.put_string("ERROR: %"")
				io.put_string(argument(1))
				io.put_string("%" ei ole viitenumero.%N")
			end
		else
				io.put_string("ERROR: Vain yksi argumentti: viitenumero.%N")			
		end
	end

feature -- Helping routines

	is_viitenumero(ehdotelma_s: STRING): BOOLEAN is
		-- Tarkista onko ehdotelma_s kelvollinen viitenumeroksi.
	require
		not_empty: not ehdotelma_s.is_empty
	local
		i: INTEGER
	do
		if ehdotelma_s.count <= 19
		then
			from
				i := ehdotelma_s.lower
			invariant
				ehdotelma_s.lower <= i and
				i <= ehdotelma_s.upper + 1
			variant
				ehdotelma_s.upper + 1 - i
			until
				i = ehdotelma_s.upper + 1 or else not ehdotelma_s.item(i).is_digit
			loop
				i := i + 1
			end
			Result := (i = ehdotelma_s.upper + 1)
		else
			Result := False
		end
	ensure
	end

invariant -- Class invariant

end -- class VIITENUMERO

Viite-Handler.c

/*
** This file is free software according to the GNU Public license
** Viite-Handler Copyright (C) 2002 Mikko Koivunalho
*/

#define __USE_SYSBASE

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "viitenumero.h"

/*
** Program Version
*/
static char VersionTag[] = "\0$VER: Viite-Handler 1.0 (12.09.02) Copyright (C) 2002 Mikko Koivunalho";


extern struct ExecBase *__far AbsExecBase;

struct ExecBase *SysBase;
struct DosLibrary *DosBase;

static void replypkt1(struct DosPacket *, LONG);
static void replypkt2(struct DosPacket *, LONG, LONG);

#define MAX_VIITENUMERO_PITUUS 24 /* 20 numbers + 3 spaces + '\0' */
#define MAX_VIITENUMERO_EHDOTUS_PITUUS 20 /* 19 numbers + '\0' */

struct OpenFile {
	char of_FileContents[MAX_VIITENUMERO_PITUUS]; 		/* viitenumero (20 chars) + '\0' */
	char *of_PointerInFile;					/* The position of read pointer, a POINTER pointing somewhere in of_FileContents */
};

struct OpenFileNode {
	struct MinNode ofn_MinNode;
	struct OpenFile ofn_OpenFile;
};



void __saveds start(void)
{
	struct MsgPort *mp;
	struct DosPacket *dp;
	struct DeviceNode *dn;
	struct Message *mn;
	BOOL error, remove_handler, nosplit;
	char file_name[256] = "", merkki2;
	char *merkki3_p, *file_name_p;
	struct FileHandle *fh;
	struct MinList *openfilelist;
	struct OpenFileNode *workopenfilenode;

#ifdef DEBUG
	DPutStr("Just started the handler.\n");
#endif

	SysBase = AbsExecBase;

	DosBase = (struct DosLibrary *) OpenLibrary("dos.library", 37);
	if(DosBase != NULL)
	{
		mp = &((struct Process *)FindTask(NULL))->pr_MsgPort;
		(void)WaitPort(mp);
		dp = (struct DosPacket *)GetMsg(mp)->mn_Node.ln_Name; /*Startup msg*/
		error = TRUE;

		dn = BADDR(dp->dp_Arg3);
		dn->dn_Task = mp;
		replypkt1(dp, DOSTRUE); /*Answer Startup msg*/

#ifdef DEBUG
		DPutStr("Startup packet done with.\n");
#endif

		if(openfilelist = AllocMem(sizeof(struct MinList), MEMF_CLEAR))
		{
			NewList((struct List *) openfilelist);        /* Important: prepare header for use */

			error = FALSE;
			remove_handler = FALSE;
			
			do
			{
#ifdef DEBUG
				DPutStr("Now entering the main loop.\n");
#endif
				(void)WaitPort(mp);
				while((mn = GetMsg(mp)) != NULL)
				{
					dp = (struct DosPacket *)mn->mn_Node.ln_Name;
					switch(dp->dp_Type)
					{


						case ACTION_FINDINPUT: /*Open file for reading read-only*/
#ifdef DEBUG
							DPutStr("Received packet ACTION_FINDINPUT.\n");
#endif
							merkki3_p = BADDR(dp->dp_Arg3);
#ifdef DEBUG
							DPutChar(merkki3_p[1]);
#endif

							merkki3_p = BADDR(dp->dp_Arg3);
							merkki2 = merkki3_p[0]; /* The length of the BSTR */
							merkki3_p = BADDR(dp->dp_Arg3);
							merkki3_p = &merkki3_p[1];

							file_name[0] = '\0';
							strncat(file_name, merkki3_p, (size_t) merkki2);
#ifdef DEBUG
							DPutStr("Tiedoston nimi polkuineen: ");
							DPutStr(file_name);
							DPutStr("\n");
#endif
							if(strchr(file_name, ':'))
							{
								file_name_p = strchr(file_name, ':') + 1;
								/* Now we find out if the filename has a /NOSPLIT suffix. */
								if(strchr(file_name_p, '/') && stricmp(strchr(file_name_p, '/'), "/NOSPLIT") == 0 )
								{
									nosplit = TRUE;
									*strchr(file_name_p, '/') = '\0';
								}
								else { nosplit = FALSE; }
#ifdef DEBUG
								DPutStr("ViiteTiedoston nimi: ");
								DPutStr(file_name_p);
								DPutStr("\n");
#endif
								if(is_good_for_viitenumero(file_name_p))
								{
#ifdef DEBUG
									DPutStr("ViiteTiedoston nimi on OK viitenumeron muodostamiseen!\n");
#endif
									if(workopenfilenode = AllocMem(sizeof(struct OpenFileNode), MEMF_CLEAR))
									{
										viitenumero(file_name_p, workopenfilenode->ofn_OpenFile.of_FileContents);
										if(!nosplit) split_into_series(workopenfilenode->ofn_OpenFile.of_FileContents, 5);
										workopenfilenode->ofn_OpenFile.of_PointerInFile = workopenfilenode->ofn_OpenFile.of_FileContents;
										AddTail((struct List *) openfilelist, (struct Node *) workopenfilenode);
#ifdef DEBUG
										DPutStr("Tiedosto lisätty listaan.\n");
#endif
#ifdef DEBUG
										DPutStr("workopenfilenode->ofn_OpenFile.of_FileContents: ");
										DPutStr(workopenfilenode->ofn_OpenFile.of_FileContents);
										DPutStr("\n");
#endif
										fh = BADDR(dp->dp_Arg1);
										fh->fh_Arg1 = (LONG) workopenfilenode; /* Pointer to a handlers internal reference system. */
										replypkt1(dp, DOSTRUE);
									}
									else
									{
										replypkt2(dp, DOSFALSE, ERROR_NO_FREE_STORE);
									}
								}
								else
								{
#ifdef DEBUG
									DPutStr("ViiteTiedoston nimi ei ole OK viitenumeron muodostamiseen!\n");
#endif
									replypkt2(dp, DOSFALSE, ERROR_OBJECT_NOT_FOUND);
								}
							}
							else
							{
								replypkt2(dp, DOSFALSE, ERROR_OBJECT_NOT_FOUND);
							}

							break;


						case ACTION_READ: /*read file*/
#ifdef DEBUG
							DPutStr("Received packet ACTION_READ.\n");
#endif
							workopenfilenode = (struct OpenFileNode *) dp->dp_Arg1;
#ifdef DEBUG
							DPutStr("workopenfilenode->ofn_OpenFile.of_FileContents:n sisältö on ");
							DPutStr(workopenfilenode->ofn_OpenFile.of_FileContents);
							DPutStr("\n");
#endif
							/* workopenfilenode->ofn_OpenFile.of_PointerInFile is a pointer !!! */
							if(strlen(workopenfilenode->ofn_OpenFile.of_PointerInFile) > 0)
							{	/* There is something yet in workopenfilenode->ofn_OpenFile.of_FileContents */
#ifdef DEBUG
								DPutStr("There is something yet in workopenfilenode->ofn_OpenFile.of_FileContents.\n");
#endif
								if(strlen(workopenfilenode->ofn_OpenFile.of_PointerInFile) > dp->dp_Arg3)
								{	/* We only read as much as requested. */
#ifdef DEBUG
									DPutStr("We only read as much as requested..\n");
#endif
									CopyMem(workopenfilenode->ofn_OpenFile.of_FileContents, (APTR) dp->dp_Arg2, dp->dp_Arg3);
									replypkt1(dp, dp->dp_Arg3);
									workopenfilenode->ofn_OpenFile.of_PointerInFile = workopenfilenode->ofn_OpenFile.of_PointerInFile + dp->dp_Arg3;
								}
								else
								{ /* We read all that is left of the file but not as much as is requested.*/
#ifdef DEBUG
									DPutStr("We read all that is left of the file but not as much as is requested.\n");
#endif
									CopyMem(workopenfilenode->ofn_OpenFile.of_FileContents, (APTR) dp->dp_Arg2, (ULONG) strlen(workopenfilenode->ofn_OpenFile.of_PointerInFile));
									replypkt1(dp, (LONG) strlen(workopenfilenode->ofn_OpenFile.of_PointerInFile));
									workopenfilenode->ofn_OpenFile.of_PointerInFile = workopenfilenode->ofn_OpenFile.of_PointerInFile + strlen(workopenfilenode->ofn_OpenFile.of_PointerInFile);
								}
							}
							else
							{	/* strlen(workopenfilenode->ofn_OpenFile.of_PointerInFile) == 0 ==> EOF */
#ifdef DEBUG
								DPutStr("This file is in EOF state.\n");
#endif
								/* workopenfilenode->ofn_OpenFile.of_PointerInFile == '\0' */
								CopyMem("\0", (APTR) dp->dp_Arg2, 1);
								/*dp->dp_Arg2 = '\0';*/
								replypkt1(dp, 0);
							}
							break;


						case ACTION_END: /* Close file*/
#ifdef DEBUG
							DPutStr("Received packet ACTION_END.\n");
#endif

							workopenfilenode = (struct OpenFileNode *) dp->dp_Arg1;
#ifdef DEBUG
							DPutStr("workopenfilenode->ofn_OpenFile.of_PointerInFile: ");
							DPutStr(workopenfilenode->ofn_OpenFile.of_PointerInFile);
							DPutStr("\n");
#endif
							Remove((struct Node *) workopenfilenode);
							FreeMem(workopenfilenode, sizeof(struct OpenFileNode));
							replypkt1(dp, DOSTRUE);
							break;


						case ACTION_DIE:
#ifdef DEBUG
							DPutStr("Received packet ACTION_END.\n");
#endif
							remove_handler = TRUE;
							replypkt1(dp, DOSTRUE);
							break;
						default:
							replypkt2(dp, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
							break;
					}
				}
			}
			while(remove_handler != TRUE);
#ifdef DEBUG
		DPutStr("Now out of main loop.\n");
#endif

			FreeMem((struct List *) openfilelist, sizeof(struct MinList));  /* Free list header */
		}

		dn->dn_Task = NULL;

		/*Delay( 30 * TICKS_PER_SECOND); DONT USE!! THIS IS A TASK!!!*/
		CloseLibrary((struct Library *) DosBase);
	} /* END: If DosLibrary V 37*/
	else
	{ /* DosLibrary below 37, Should report before quitting, maybe intuition requester*/
	}

#ifdef DEBUG
	DPutStr("Now ending the handler.\n\n\n\n");
#endif

}

static void replypkt1(
struct DosPacket *dp,
LONG res1)
{
	struct MsgPort *mp;
	struct Message *mn;
	
	mp = dp->dp_Port;
	mn = dp->dp_Link;
	
	mn->mn_Node.ln_Name = (char *)dp;
	
	dp->dp_Port = &((struct Process *)FindTask(NULL))->pr_MsgPort;
	dp->dp_Res1 =res1;
	
	PutMsg(mp, mn);
}

static void replypkt2(
struct DosPacket *dp,
LONG res1,
LONG res2)
{
	dp->dp_Res2 = res2;
	
	replypkt1(dp, res1);
}

Viitenumero.c

/*
** This file is free software according to the GNU Public license
** Viite-Handler Copyright (C) 2002 Mikko Koivunalho
** Header file for functions to create viitenumeros.
** Functions to create viitenumeros.
*/

#include 
#include 
#include 

/* Function returns 1 if the string ehdotelma_p is suitable 
** to be modified to a viitenumero, 0 otherwise. */
int is_good_for_viitenumero(const char *ehdotelma_p)
{
	int i;

	if(0 < strlen(ehdotelma_p) && strlen(ehdotelma_p) < 20)
	{
		/* There are 1-19 characters in ehdotelma_p. */
		for(i = 0;  i < strlen(ehdotelma_p); i++)
		{
			if(isdigit(ehdotelma_p[i]) == 0)
			{
				return 0;
			} /* If anything unsuitable is found, we break the 
				** loop and return right away. */
		}
		/* i == strlen(ehdotelma_p) + 1 */
		return 1;
	}
	else
	{
		return 0;
	}
}





/* Make a viitenumero from string alkunumero_p.
** alkunumero_p must be suitable for this purpose,
** i.e. is must pass is_good_for_viitenumero().
** If it does not, the return of this function
** is not defined. You have been warned!
**
** The buffer valmisnumero_p must be atleast of size 
** strlen(alkunumero_p) + 1 + 1 ('\0'). Return value undefined.
*/
int viitenumero(const char *alkunumero_p, char *valmisnumero_p)
{
	int tarkisteluku, summa, kertoluvut_index, i;
	char temp_s[2] = {0, '\0'};
	int kertoluvut[3] = {7, 3, 1};

	for(kertoluvut_index = 0, summa = 0, i = strlen(alkunumero_p) - 1; i >= 0; i--)
	{
		temp_s[0] = alkunumero_p[i];
		summa = atoi(temp_s) * kertoluvut[kertoluvut_index] + summa;
		if(kertoluvut_index == 2) { kertoluvut_index = 0;}
		else { kertoluvut_index = kertoluvut_index + 1; }
	}
	if(summa % 10 == 0) {tarkisteluku = 0;}
	else { tarkisteluku = (summa / 10 + 1) * 10 - summa; }
	strcpy(valmisnumero_p, alkunumero_p);
	temp_s[0] = 48 + tarkisteluku;
	strcat(valmisnumero_p, temp_s);
	return 1;
}




/* Split the string jaksotettava_p into series of jakson_pituus
** beginning from the end of the string.
** Eg. 12345678901 => 1 23456 78901
** The buffer jaksotettava_p length must be atleast 
** strlen(jaksotettava_p) / jakson_pituus + 1 ('\0').
** Return value undefined. Do not set jakson_pituus <= 0
*/
int split_into_series(char *jaksotettava_p, int jakson_pituus)
{
	int i, alkup_i;

	if(strlen(jaksotettava_p) > jakson_pituus && jakson_pituus > 0)
	{
		alkup_i = strlen(jaksotettava_p);
		if(alkup_i % jakson_pituus == 0) {i = jakson_pituus + 1; }
		else {i = alkup_i % jakson_pituus + 1; }
		while(i < alkup_i + alkup_i / jakson_pituus)
		{ 
			strins(&jaksotettava_p[i-1], " ");
			i = i + jakson_pituus + 1;
		}
		return 1;
	}
	else
	{	/* No processing needed, strlen(jaksotettava_p) <= jakson_pituus.*/
		return 1;
	}
}


Viitenumero.h

/*
** This file is free software according to the GNU Public license
** Viite-Handler Copyright (C) 2002 Mikko Koivunalho
** Header file for functions to create viitenumeros.
*/

#ifndef VIITENUMERO_H
#define VIITENUMERO_H 1

extern int is_good_for_viitenumero(const char *ehdotelma_p);
extern int viitenumero(const char *alkunumero_p, char *valmisnumero_p);
extern int split_into_series(char *jaksotettava_p, int jakson_pituus);

#endif

Society_Member.cxx

// File Society_Member.cxx
// Entry of a member

#ifndef SOCIETY_MEMBER_CXX
#define SOCIETY_MEMBER_CXX


#include 
#include 

#include 

#include "Society_Member.h"


void Society_Member::Put_IDnumber(const int number)
{
	member_IDnumber = number;
}

void Society_Member::Put_member_class(const int m_class)
{
	member_member_class = m_class;
}

void Society_Member::Put_surname(const char* surname)
{
	delete member_surname;
	member_surname = new strstream();
	*member_surname << surname << ends;
}

void Society_Member::Put_forenames(const char* forenames)
{
	delete member_forenames;
	member_forenames = new strstream();
	*member_forenames << forenames << ends;
}

void Society_Member::Put_address(const char* address)
{
	delete member_address;
	member_address = new strstream();
	*member_address << address << ends;
}

void Society_Member::Add_association_number(const int number)
{
	int i;
	for(i = 0; (i < MAX_ASSOCIATIONS) && (member_associations[i] != 0); i++)
	{ ; }
	if (i < MAX_ASSOCIATIONS)
	{
		member_associations[i] = number;
	}
}

void Society_Member::Add_membership_year(const int year)
{
	int i;
	for(i = 0; ((i < MAX_MEMBERSHIP_PAID_YEARS) && (member_membership_years[i] != 0)); i++)
	{ ; }
	if (i < MAX_MEMBERSHIP_PAID_YEARS)
	{
		member_membership_years[i] = year;
	}
}

int* Society_Member::Get_association_numbers(int* how_many)
{
	int i;
	for(i = 0; (i < MAX_ASSOCIATIONS) && (member_associations[i] != 0); i++)
	{ ; }
	*how_many = i;
	return &member_associations[0];
}

int* Society_Member::Get_membership_years(int* how_many)
{
	int i;
	for(i = 0; ((i < MAX_MEMBERSHIP_PAID_YEARS) && (member_membership_years[i] != 0)); i++)
	{ ; }
	*how_many = i;
	return &member_membership_years[0];
}

void Society_Member::Add_other_info(const char* info_name, const char* info_content)
{
	if(member_other_info_count < MAX_OTHER_INFO)
	{
		member_other_info_name[member_other_info_count] = new strstream();
		member_other_info_content[member_other_info_count] = new strstream();
		*member_other_info_name[member_other_info_count] << info_name << ends;
		*member_other_info_content[member_other_info_count] << info_content << ends;
		member_other_info_count++;
	}
}

int Society_Member::Get_other_infos(char* info_name[], char* info_content[])
{
	int i;
	strstream* string;
	for(i = 0; i < member_other_info_count; i++)
	{
		string = member_other_info_name[i];
		//*info_name[i] = (member_other_info_name[i])->str();
		info_name[i] = string->str();

		string = member_other_info_content[i];
		//*info_content[i] = (member_other_info_content[i])->str();
		info_content[i] = string->str();
	}
	return member_other_info_count;
}

void Society_Member::Empty_all()
{
	int i;
	delete member_surname;
	delete member_forenames;
	delete member_address;

	member_IDnumber = 0;
	member_member_class = 0;
	member_surname = new strstream();
	member_forenames = new strstream();
	member_address = new strstream();
	for(i = 0; i < MAX_ASSOCIATIONS; i++)
	{ member_associations[i] = 0; }
	for(i = 0; i < MAX_MEMBERSHIP_PAID_YEARS; i++)
	{ member_membership_years[i] = 0; }
	member_other_info_count = 0;
	for(i = 0; i < member_other_info_count; i++)
	{
		delete member_other_info_name[i];
		delete member_other_info_content[i];
	}
	member_other_info_count = 0;
}

Society_Member::Society_Member()
{
	int i;
	member_surname = new strstream();
	member_forenames = new strstream();
	member_address = new strstream();
	for(i = 0; i < MAX_ASSOCIATIONS; i++)
	{ member_associations[i] = 0; }
	for(i = 0; i < MAX_MEMBERSHIP_PAID_YEARS; i++)
	{ member_membership_years[i] = 0; }
	member_other_info_count = 0;
}

Society_Member::~Society_Member()
{
	int i;
	delete member_surname;
	delete member_forenames;
	delete member_address;

	for(i = 0; i < member_other_info_count; i++)
	{
		delete member_other_info_name[i];
		delete member_other_info_content[i];
	}
}


#endif

Society_Member.h

// File Society_Member.h
// Entry of a member

#ifndef SOCIETY_MEMBER_H
#define SOCIETY_MEMBER_H

/*#define MAX_ADDRESS_LINES 5*/
#define MAX_ASSOCIATIONS 10
#define MAX_MEMBERSHIP_PAID_YEARS 20
#define MAX_OTHER_INFO 3


#include 

class Society_Member {
	int member_IDnumber; // An ID number of some sort of a member
	int member_member_class; // e.g. full member or restricted member
	strstream* member_surname;
	strstream* member_forenames;
	strstream* member_address;
	int member_associations[MAX_ASSOCIATIONS]; // other member IDnumbers
	int member_membership_years[MAX_MEMBERSHIP_PAID_YEARS]; // For which years membership is paid for
	strstream* member_other_info_name[MAX_OTHER_INFO];
	strstream* member_other_info_content[MAX_OTHER_INFO];
	int member_other_info_count;
public:
	Society_Member();
	~Society_Member();
	void Empty_all();
	void Put_IDnumber(const int number);
	void Put_member_class(const int m_class);
	void Put_surname(const char* surname);
	void Put_forenames(const char* forenames);
	void Put_address(const char* address);
	void Add_association_number(const int number);
	void Add_membership_year(const int year);
	void Add_other_info(const char* info_name, const char* info_content);
	int Get_IDnumber();
	int Get_member_class();
	char* Get_surname();
	char* Get_forenames();
	char* Get_address();
	int* Get_association_numbers(int* how_many);
	int* Get_membership_years(int* how_many);
	char* Get_other_info_content(const char* info_name);
	int Get_other_infos(char* info_name[], char* info_content[]);
	int Get_other_info_count();
};

inline int Society_Member::Get_IDnumber()
{
	return member_IDnumber;
}

inline int Society_Member::Get_member_class()
{
	return member_member_class;
}

inline char* Society_Member::Get_surname()
{
		return member_surname->str();
}

inline char* Society_Member::Get_forenames()
{
	return member_forenames->str();
}

inline char* Society_Member::Get_address()
{
	return member_address->str();
}

inline int Society_Member::Get_other_info_count()
{
	return member_other_info_count;
}


#endif

Mofile.prl

#!/opt/bin/perl
sub haesanatulo {
	$hakusana = shift @_;
	local $kokotulo_os = shift @_;
	open( TULOPUTKI, "lynx -source http://www.mofile.fi/cgi-bin/mofind.exe/xr1?word=$hakusana |" );

	while(  ) {
		push @$kokotulo_os, $_;
	}
	close( TULOPUTKI );
}
sub erottelesanat {
	$hakusana = shift @_;
	local $raakasanat_os = shift @_;
	local $sanalista_os = shift @_;
	@_ = @$raakasanat_os;
	while( ($rivi = shift @_) ne "" ) {
		chomp;
		if( $rivi =~ /(Key|Word): <b><i>$hakusana<\/i><\/b>/i ) {
			push @sanataulukko, $rivi;
			do {
				$rivi = shift @_;
				push @sanataulukko, $rivi;
			}
			until ( ( $rivi =~ "" ) ||
				( $rivi =~ "" ) )
		}
	}
	if ( $sanataulukko[2] =~ "No words found" ) {
		$sanalista[0] = "No words found by that headword!";
		@$sanalista_os = @sanalista;
		return ( 1 );
	}
	foreach $rivi ( @sanataulukko ) {
		if ( $rivi =~ ".+<td><font color=#0000ee>|.+" ) {
			if ( ( substr( $rivi, 0, 1 ) eq "." ) ||
			     ( substr( $rivi, 0, 1 ) eq " " ) ) { next; }
			$rivi =~  s/ä/ä/g;
			$rivi =~  s/ö/ö/g;
			push( @sanalista, substr( $rivi, 0, ( index( $rivi, "<", 0 ))));
		}
		if ( $rivi =~ "<i> (.+) " ) {
			$tmp = substr( $rivi, 4, ( index( $rivi, " <", 4 ) - 4 ) );
			push @sanalista, ($rivi = join( " ", pop @sanalista, $tmp ) );
		}
	}
	@$sanalista_os = @sanalista;
	return ( 0 );
}


$FORMAT_LINES_PER_PAGE = 1000000;

#
# PÄÄOHJELMA
#

if ( $ARGV[0] =~ "^-" ) {
	$optiot .= shift @ARGV;
	if ( $optiot =~ /^-\?/ ) {
		die "usage: mofile [options] word\l
options:  -1col        Dump the result in one column\l
          -?           This message\l
word:     any English or Finnish word.\n";
	}
}
@komentorivi = @ARGV;
if ( $ARGV[0] ) { 
	$hakusana = shift @ARGV;
	while ( $parametri = shift @ARGV ) {
		$hakusana = $hakusana . "+";
		$hakusana = $hakusana . $parametri;
	}
}
else
	{ $! = 5; die "No headword!\n"; }

if (! &haesanatulo( $hakusana, \@tulos ) ) {
	$! = 5; die "$tulos[0]\n";
}

$hakusana = "";
foreach $parametri ( @komentorivi ) {
	$hakusana .= $parametri;
	$hakusana .= " ";
}
chop $hakusana;

if ( 0 != &erottelesanat( $hakusana, \@tulos, \@sanat ) ) {
	$! = 5; die "$sanat[0]\n";
}

if ( $optiot =~ /-1col/ ) {
	foreach $sana ( @sanat ) {
		print "$sana\n";
	}
	exit;
}

$maxpituus = 0;
foreach $sana ( @sanat ) {
	if ( $maxpituus < length $sana ) {
		$maxpituus = length $sana;
	}
}

$i = 0;
until ( !(defined $sanat[$i]) ) {
	if ( $sanat[$i] =~ "(infinitiivi)" ) { push( @taivutukset, splice( @sanat, $i, 3 ) ); next; }
	$i++;
}

if ( !($term_leveys = $ENV{COLUMNS}) ) {
	$term_leveys = 80
}
$monta_mahtuu = int( $term_leveys / ++$maxpituus );
$kohdan_leveys = int( $term_leveys / $monta_mahtuu ); $kohdan_leveys--;

#
# Lopullinen tulostus
#

print "--------        Headword: $hakusana --------\n";
if ( @taivutukset ) {
	foreach $sana ( @taivutukset ) {
		print "$sana\n";
	}
	print "\n";
}

$j = 0;
foreach $sana ( @sanat ) {
	if ( $j >= $monta_mahtuu - 1 ) {
		printf( "%-$kohdan_leveys-1s\n", $sana );
		$j = 0;
	}
	else {
		printf( "%-$kohdan_leveys-1s ", $sana );
		$j++;
	}
}
if ( @sanat % $monta_mahtuu != 0 ) { print "\n"; }

exit;

string_compilation.e

-- A helper class containing only functions.
indexing
	user, interaction;
	description: "Additional routines for use with strings"
	author: "Mikko Koivunalho"
	original: 22, December, 1999
	last: 22, December, 1999

class STRING_COMPILATION

feature -- Routines

	compile_string(proto_s, begin_esc, end_esc: STRING; components: ARRAY[STRING]): STRING is
			-- proto_s consists of normal characters and escaped 
			-- words which will be replaced with components.
			-- components contains pairs of words, the first being 
			-- the escaped word and second the replacing word.
		require
			proto_s /= Void
			not begin_esc.empty
			not end_esc.empty
			components.upper \\ 2 = 0
		local
			i, j: INTEGER -- i for proto_s, j for #?_esc
			comp_s: STRING
		do
			debug ("string_compilation")
				io.put_string("proto_s: ")
				io.put_string(proto_s)
				io.put_string("%N")
			end
			!!Result.make(0)
			from
				i := 1
			until
				i > proto_s.count
			loop
				debug ("string_compilation")
					io.put_string("character of proto_s: ")
					io.put_character(proto_s.item(i))
					io.put_string("%N")
				end
				if proto_s.item(i) = begin_esc.item(1) and then
					i + begin_esc.count - 1 <= proto_s.count and then
					proto_s.substring(i, i + begin_esc.count - 1).is_equal(begin_esc)
				then
					i := i + begin_esc.count
					debug ("string_compilation")
						io.put_string("begin_esc discovered.%N")
					end
					from
						!!comp_s.make(0)
					until
						i + end_esc.count - 1 > proto_s.count or else
						proto_s.substring(i, i + end_esc.count - 1).is_equal(end_esc)
					loop
						comp_s.append_character(proto_s.item(i))
						i := i + 1
						debug ("string_compilation")
							io.put_string("comp_s: ")
							io.put_string(comp_s)
							io.put_string("%N")
						end
					end
					debug ("string_compilation")
						io.put_string("end_esc or EOL discovered.%N")
					end
					if i + end_esc.count - 1 <= proto_s.count then
						-- end_esc was on the string so we search for the component.
						from
							j := components.lower
						until
							j > components.upper or else
							components.item(j).is_equal(comp_s)
						loop
							j := j + 2
						end
						if j <= components.upper then
							-- comp_s is found within components.
							-- We replace its counterpart.
							Result.append(components.item(j + 1))
						else
							-- comp_s was not in components array.
							-- It will not be swithed but will remain as it is.
							Result.append(begin_esc)
							Result.append(comp_s)
							Result.append(end_esc)
						end
						i := i + end_esc.count
					else
						-- End of line before end_esc.
						Result.append(begin_esc)
						Result.append(comp_s)
						Result.append(proto_s.substring(i - 1, proto_s.count))
					end
				else
					Result.append_character(proto_s.item(i))
					i := i + 1
				end
			end -- loop
			debug ("string_compilation")
				io.put_string("Result: ")
				io.put_string(Result)
				io.put_string("%N")
			end
		ensure
		--rescue
		end

end -- STRING_COMPILATION

STRUCTURED_FILE_example.txt

Filename: STRUCTURED_FILE_example


STRUCTURED FILE - internal version 1.000
multi-line_declaration_end[]=MULTI-LINE_END
make_backup[]=yes
backup_extension[]=.bak
make_backup_backup[]=yes
backup_backup_extension[]=.bkk
--- END OF STRUCTURED FILE HEADER ---
# This comment (body top comment) is obligatory.

#User's name: Only first and last names
user_name[]=Mikko Koivunalho

user_address[multi-line]=
Laaksluomantie 8
20960 TURKU
Finland
MULTI-LINE_END
#Old address:
#Yliopistonkatu 11 a D 60
#20100 TURKU

membership_fee[]=150,00 mk

other_information[multi-line]=
perustajajäsen
MULTI-LINE_END

phone[]=
#used to be (555) 1234 567

# This comment (body bottom comment) is obligatory.

string_compilation_example.txt

Filename: string_compilation_example


#
# An example of the use of string_compilation:
# An extract of a STRUCTURED_FILE preferences file
#
#
# date_time
#
# year =
# month =
# day =
# day_of_year =
# day_of_week_number = 0-6 (0 = sunday)
# day_of_week = monday/tuesday/wednesday/thursday/friday/saturday/sunday
# day_of_week_short = mon/tue/wed/thu/fri/sat/sun
# hour =
# minute =
# second =
# hour_12 = hour in a twelve hour cycle
# am_or_pm = "AM" or "PM"
# hour_zeroes = 01
# minute_zeroes =
# second_zeroes =
# month_zeroes =
# day_zeroes =
# year_short = last 2 digits
#
date_time[multi-line]=
*[day_of_week]* (*[day_of_week_short]*) *[day_zeroes]*.*[month]*.*[year_short]* *[hour_zeroes]*:*[minute_zeroes]*:*[second_zeroes]* (*[am_or_pm]*)
MULTI-LINE_END

weekdays[multi-line]=
maanantai tiistai keskiviikko torstai perjantai lauantai sunnuntai
MULTI-LINE_END

weekdays_short[multi-line]=
ma ti ke to pe la su
MULTI-LINE_END

Viimkone.dcl

$! Koko ohjelman ajon aikana säilyviä symboleja
$ entinenaika :=
$ entinenkone :=
$ varhaisinaika :=
$!
$!
$! Otetaan selvää kuka omistaa prosessin,
$! Avataan hänen nimisensä väliaikainen tiedosto
$!
$ kayttaja = f$user()
$ kayttaja = f$extract( f$locate(",",kayttaja) + 1,  f$locate("]",kayttaja) - f$locate(",",kayttaja) - 2, kayttaja )
$ paikka = f$locate( " ", f$time() )
$ kayttaja[f$length( kayttaja ),2] := 'f$extract( paikka + 1, 2, f$time())'
$ kayttaja[f$length( kayttaja ),2] := 'f$extract( paikka + 4, 2, f$time())'
$ fingertiedosto = "DISK$TMP:" + kayttaja + ".lis"
$!
$! Luotan siihen, että DISK$TMP-hakemistossa ei ole ennestään ylläolevan 
$! nimistä tiedostoa.
$!
$! Tutkitaan onko annettu parametreja.
$!
$! Ensin käyttäjätunnus.
$!
$ IF P1 .EQS. ""
$ THEN
$  GOTO KAYTTOOHJEET
$ ENDIF
$ IF P2 .EQS. ""
$ THEN
$  GOTO KAYTTOOHJEET
$ ENDIF
$!
$!
$! Sitten ensimmäisen koneen nimi.
$!
$ nimi := 'P1'
$ kone := 'P2'
$ argnum = 2
$ goto PAASILMUKKA
$!
$!
$!
$ KONEARGUMENTIT:
$ argnum = argnum + 1
$ IF P'argnum' .EQS. ""
$ THEN
$  goto LOPPULOPPU
$ ENDIF
$ kone = P'argnum'
$!
$!
$! Pääohjelman silmukka
$!
$!
$ PAASILMUKKA:
$ define/user sys$output 'fingertiedosto'
$ multinet finger 'nimi'@'kone'
$ open/read finger_tulostus 'fingertiedosto'
$!
$ sisalla = 3
$! Mikäli "sisalla"-symboli on vielä silmukan lopussa "3", fingerin tulos oli 
$! tuntematon, eikä komentotiedosto tiedä miten lukea sitä.
$! Muut arvot: sisalla = 0 "päivays luettu", 1 "käyttäjä on sisalla tällä 
$! hetkella", 2 "Käyttäjä on tuntematon".
$ tuloaika := ""
$ sijainti = 0
$!
$!
$!    Erottaa joukosta, jos pystyy, oikean fingerin tulostuksen
$!
$!
$ LUKUSILMUKKA:
$ read/end_of_file=LUKULOPPU finger_tulostus luettu
$ rivinpituus = F$LENGTH( luettu )
$ sijainti = F$LOCATE( "On since ", luettu )
$!
$ IF sijainti .NE. rivinpituus
$ THEN
$  sisalla = 1
$  goto LUKULOPPU
$ ENDIF
$ sijainti = F$LOCATE( "Login       Name               TTY         Idle    When    Where", luettu )
$!
$ IF sijainti .NE. rivinpituus
$ THEN
$  read/end_of_file=LUKULOPPU finger_tulostus luettu
$  rivinpituus = F$LENGTH( luettu )
$  IF F$LOCATE( "???", luettu) .EQ. 22
$  THEN
$   sisalla = 2
$   goto LUKULOPPU
$  ENDIF
$  IF F$LOCATE( "< .  .  .  . >", luettu) .EQ. 44
$  THEN
$   sisalla = 4
$   write sys$output "Käyttäjälla ""''nimi'"" on koneessa ""''kone'"" tunnus, muttei han ole kayttanyt konetta."
$   goto LUKULOPPU
$  ENDIF
$  sijainti = F$LOCATE( "<", luettu )
$  IF sijainti .NE. rivinpituus
$  THEN
$   sisalla = 0
$   sijainti = sijainti + 1
$   gosub FINGERTYYPPI2
$   goto LUKULOPPU
$  ELSE
$   sisalla = 1
$   goto LUKULOPPU
$  ENDIF
$ ENDIF
$!
$ IF F$LOCATE( "?Sorry, could not find """, luettu) .EQ. 0
$ THEN
$  sisalla = 2
$  goto LUKULOPPU
$ ENDIF
$!
$ IF F$LOCATE( "No finger information on ", luettu) .EQ. 0
$ THEN
$  sisalla = 2
$  goto LUKULOPPU
$ ENDIF
$!
$ IF F$LOCATE( "Last login [Never]", luettu) .EQ. 0
$ THEN
$  sisalla = 4
$  write sys$output "Käyttäjällä ""''nimi'"" on koneessa ""''kone'"" tunnus, muttei hän ole käyttänyt sitä."
$  goto LUKULOPPU
$ ENDIF
$!
$ sijainti = F$LOCATE( "Last login ", luettu )
$ IF sijainti .NE. rivinpituus
$ THEN
$  sisalla = 0
$  vali = F$extract( 18, 1, luettu )
$  IF vali .EQS. " "
$  THEN
$   sijainti = sijainti + 15
$   gosub FINGERTYYPPI2
$   goto LUKULOPPU
$  ELSE
$   gosub FINGERTYYPPI1
$   goto LUKULOPPU
$  ENDIF
$ ENDIF
$ goto LUKUSILMUKKA
$!
$ LUKULOPPU:
$ IF sisalla .EQ. 1
$ THEN
$  write sys$output "Käyttäjä ""''nimi'"" on tällä hetkellä koneessa ""''kone'""."
$ ENDIF
$ IF sisalla .EQ. 2
$ THEN
$  write sys$output "Käyttäjä ""''nimi'"" on tuntematon koneessa ""''kone'""."
$ ENDIF
$ IF sisalla .EQ. 3
$ THEN
$  write sys$output "Fingerin tulos oli tuntematon, eikä komentotiedosto "
$  write sys$output "tiedä miten lukea sitä."
$  type 'fingertiedosto'
$ ENDIF
$!
$!
$!       Ohjelman loppu, mutta ensin tarkistetaan oliko lisää argumentteja
$!
$!
$ LOPPU:
$ close finger_tulostus
$ goto KONEARGUMENTIT
$!
$ LOPPULOPPU:
$ delete 'fingertiedosto';*
$ IF entinenaika .NES. ""
$ THEN
$  paivays := 'f$extract( 0, 10, entinenaika )'
$  kellonlyoma := 'f$extract( 11, 5, entinenaika )'
$  write sys$output "Käyttäjä ""''nimi'"" tuli viimeiseksi ''paivays' klo ''kellonlyoma' koneeseen ""''entinenkone'""."
$ ENDIF
$ EXIT
$!
$!
$!        fingertyyppi1
$!
$!
$ FINGERTYYPPI1:
$  lopulaika = ""
$  tuloaika = F$extract( sijainti+15, 23, luettu )
$  sijainti = F$LOCATE( " ", tuloaika )
$  tuloaika[ 'sijainti', 1 ] := " "
$  sijainti = F$length( tuloaika )
$  tuloaika[ 'sijainti'-7, 7 ] := ":00.00"
$  sijainti = F$LOCATE( "-", tuloaika )
$  IF sijainti .EQ. 1
$  THEN 
$   lopulaika[0,1] := "0"
$   lopulaika[1,50] := 'tuloaika'
$  ELSE
$   lopulaika[0,7] := 'tuloaika'
$  ENDIF
$  sijainti = F$LOCATE( " ", tuloaika )
$  lopulaika[7,2] := "19"
$  IF sijainti .EQ. 8
$  THEN
$   vali = F$extract( sijainti-2, 2, tuloaika )
$   lopulaika[9,2] := 'vali'
$  ENDIF
$  IF sijainti .EQ. 9
$  THEN
$   vali = F$extract( sijainti-2, 2, tuloaika )
$   lopulaika[9,2] := 'vali'
$  ENDIF
$  lopulaika[11,1] := ":"
$  vali = F$extract( sijainti+2, 1, tuloaika )
$  IF vali .EQS. ":"
$  THEN
$   lopulaika[12,1] := "0"
$   lopulaika[13,4] := 'F$extract( sijainti+1, 4, tuloaika )'
$  ELSE
$   lopulaika[12,5] := 'F$extract( sijainti+1, 5, tuloaika )'
$  ENDIF
$  lopulaika[17,6] := ":00.00"
$  cvtuloaika = F$CVTIME( lopulaika )
$  GOSUB VERTAAPAIVAYS
$ RETURN
$!
$!
$!        fingertyyppi2
$!
$!
$ FINGERTYYPPI2:
$  lopulaika = ""
$  tuloaika = F$extract( sijainti, 12, luettu )
$  vali = F$extract( 4, 1, tuloaika )
$  IF vali .EQS. " "
$  THEN
$   lopulaika[0,1] := "0"
$   lopulaika[1,1] := 'f$extrack( 5, 1, tuloaika )'
$  ELSE
$   lopulaika[0,2] := 'f$extrack( 4 , 2, tuloaika )'
$  ENDIF
$  lopulaika[2,1] := "-"
$  lopulaika[3,3] := 'f$extrack( 0, 3, tuloaika )'
$  lopulaika[6,1] := "-"
$  vali := 'f$time()'
$  vali := 'f$cvtime( vali )'
$  lopulaika[7,4] := 'f$extract( 0, 4, vali )'
$  lopulaika[11,1] := ":"
$  lopulaika[12,5] := 'f$extract( 7, 5, tuloaika )'
$  lopulaika[17,6] := ":00.00"
$  cvtuloaika = F$CVTIME( lopulaika )
$  GOSUB VERTAAPAIVAYS
$ RETURN
$!
$!
$!        Tarkista päiväys
$!
$!
$ VERTAAPAIVAYS:
$ IF entinenaika .EQS. ""
$ THEN
$  entinenaika := 'cvtuloaika'
$  entinenkone := 'kone'
$ ELSE
$  vali := 'f$extrack( 0, 4, cvtuloaika )'
$  valientinen := 'f$extrack( 0, 4, entinenaika )'
$! ----- VUOSI
$  IF 'valientinen' .LTS. 'vali'
$  THEN
$   entinenaika := 'cvtuloaika'
$   entinenkone := 'kone'
$   RETURN
$  ENDIF
$  IF 'valientinen' .EQS. 'vali'
$  THEN
$  vali := 'f$extrack( 5, 2, cvtuloaika )'
$  valientinen := 'f$extrack( 5, 2, entinenaika )' 
$! ----- KUUKAUSI
$  IF 'valientinen' .LTS. 'vali'
$  THEN
$   entinenaika := 'cvtuloaika'
$   entinenkone := 'kone'
$   RETURN
$  ENDIF
$  IF 'valientinen' .EQS. 'vali
$  THEN
$  vali := 'f$extrack( 8, 2, cvtuloaika )'
$  valientinen := 'f$extrack( 8, 2, entinenaika )'
$! ----- PAIVA
$  IF 'valientinen' .LTS. 'vali'
$  THEN
$   entinenaika := 'cvtuloaika'
$   entinenkone := 'kone'
$   RETURN
$  ENDIF
$  IF 'valientinen' .EQS. 'vali
$  THEN
$  vali := 'f$extrack( 11, 2, cvtuloaika )'
$  valientinen := 'f$extrack( 11, 2, entinenaika )'
$! ----- TUNTI
$  IF valientinen .LTS. vali
$  THEN
$   entinenaika := 'cvtuloaika'
$   entinenkone := 'kone'
$   RETURN
$  ENDIF
$  IF 'valientinen' .EQS. 'vali
$  THEN
$  vali := 'f$extrack( 14, 2, cvtuloaika )'
$  valientinen := 'f$extrack( 14, 2, entinenaika )'
$! ----- MINUUTTI
$  IF valientinen .LTS. vali
$  THEN
$   entinenaika := 'cvtuloaika'
$   entinenkone := 'kone'
$   RETURN
$  ENDIF
$  IF 'valientinen' .EQS. 'vali
$  THEN
$  vali := 'f$extrack( 17, 2, cvtuloaika )'
$  valientinen := 'f$extrack( 17, 2, entinenaika )'
$! ----- SEKUNTI
$  IF valientinen .LTS. vali
$  THEN
$   entinenaika := 'cvtuloaika'
$   entinenkone := 'kone'
$   RETURN
$  ENDIF
$  ENDIF
$  ENDIF
$  ENDIF
$  ENDIF
$  ENDIF
$ ENDIF
$ RETURN
$!
$!
$!    Käyttöohjeet
$!
$!
$ KAYTTOOHJEET:
$ write sys$output "     Tämä komentoproseduuri vaatii kaksi tai useampia argumentteja"
$ write sys$output "     käyttäjätunnuksen ja yhden tai useamman Internet-tietokoneen nimen."
$ write sys$output "     VIIMKONE [käyttäjätunnus] [kone]...[kone]"
$ write sys$output "Ohjelman suoritus keskeytetty...
$ EXIT
$ RETURN

Viimkone.sh

#!/bin/sh
#Muutama funktio helpottamaan käyttoä.

kayttoohjeet() {
 echo "     Tämä käskytiedosto tarvitsee toimiakseen ainakin 2 parametria:";
 echo "     käyttäjätunnuksen ja yhden tai useamman Internet-tietokoneen nimen.";
 echo "     käyttö: viimkone käyttäjätunnus kone [kone]...";
}

luvustakuukaudeksi() {
 case $1 in
  Jan) echo "1";;
  Feb) echo "2";;
  Mar) echo "3";;
  Apr) echo "4";;
  May) echo "5";;
  Jun) echo "6";;
  Jul) echo "7";;
  Aug) echo "8";;
  Sep) echo "9";;
  Oct) echo "10";;
  Nov) echo "11";;
  Dec) echo "12";;
 esac
}

paivaysvertailu() {
 if test "$KOKOLUKU" -gt "$VIIMEINENKOKOLUKU"; then
  VIIMEINENKOKOLUKU=$KOKOLUKU
  VIIMEINENKOKOPAIVAYS=$KOKOPAIVAYS
  VIIMEINENKONE=$KONE
 fi
}

luvustamerkeiksi() {
 LMVUOSI=`expr $1 / \( 60 \* 24 \* 30 \* 12 \)`
 LOPUT=`expr $1 % \( 60 \* 24 \* 30 \* 12 \)`
 LMKUUKAUSI=`expr $LOPUT / \( 60 \* 24 \* 30 \)`
 LOPUT=`expr $LOPUT % \( 60 \* 24 \* 30 \)`
 LMPAIVA=`expr $LOPUT / \( 60 \* 24 \)`
 LOPUT=`expr $LOPUT % \( 60 \* 24 \)`
 LMTUNTI=`expr $LOPUT / 60`
 LMMINUUTTI=`expr $LOPUT % 60`

 if test "${LMTUNTI}" -lt "10"; then
  echo -n "0"; echo -n "${LMTUNTI}"
 else
  echo -n "${LMTUNTI}"
 fi
 echo -n ":"

 if test "${LMMINUUTTI}" -lt "10"; then
  echo -n "0"; echo -n "${LMMINUUTTI}"
 else
  echo -n "${LMMINUUTTI}"
 fi
 echo -n " "

 if test "${LMPAIVA}" -lt "10"; then
  echo -n "0"; echo -n "${LMPAIVA}"
 else
  echo -n "${LMPAIVA}"
 fi
 echo -n "."

 if test "${LMKUUKAUSI}" -lt "10"; then
  echo -n "0"; echo -n "${LMKUUKAUSI}"
 else
  echo -n "${LMKUUKAUSI}"
 fi
 echo ".${LMVUOSI}"

# echo "${LMTUNTI}:${LMMINUUTTI} ${LMPAIVA}.${LMKUUKAUSI}.${LMVUOSI}"
}

fingertyyppi1() {
 KOHTA=$1
# Erotellaan vuosi - jos sitä on
 VUOSIALKUKOHTA=`expr $KOHTA + 13`
 ONKOVUOTTAKOHTA=`expr $VUOSIALKUKOHTA + 1`
 ONKOVUOTTA=`echo "$RIVI" | cut -c${ONKOVUOTTAKOHTA}`
# Ongelmia tässä - Entä jos vuosiluku onkin jotain muuta vuosisataa kuin 9:ttä?
# Leikataan myös talteen koko päiväys
 if test "$ONKOVUOTTA" = '9'; then
  LOPPUKOHTA=`expr $VUOSIALKUKOHTA + 4`
  LUKU=`echo "$RIVI" | cut -c${VUOSIALKUKOHTA}-${LOPPUKOHTA}`
  KOKOLUKU=`expr 60 \* 24 \* 30 \* 12 \* $LUKU`
 else
  LOPPUKOHTA=`expr $KOHTA + 11`
  LUKU=`date +%y`
  KOKOLUKU=`expr 60 \* 24 \* 30 \* 12 \* 19${LUKU}`
 fi
# Erotellaan kuukausi
 LOPPUKOHTA=`expr $KOHTA + 2`
 KUUKAUSI=`echo "$RIVI" | cut -c${KOHTA}-${LOPPUKOHTA}`
 LUKU=`luvustakuukaudeksi $KUUKAUSI`
 KOKOLUKU=`expr 60 \* 24 \* 30 \* $LUKU + $KOKOLUKU`
# Erotellaan kuukauden päivä
 KOHTA=`expr $KOHTA + 4`
 LOPPUKOHTA=`expr $KOHTA + 1`
 LUKU=`echo "$RIVI" | cut -c${KOHTA}-${LOPPUKOHTA}`
 KOKOLUKU=`expr 60 \* 24 \* $LUKU + $KOKOLUKU`
# Erotellaan tunnit
 KOHTA=`expr $KOHTA + 3`
 LOPPUKOHTA=`expr $KOHTA + 1`
 LUKU=`echo "$RIVI" | cut -c${KOHTA}-${LOPPUKOHTA}`
 KOKOLUKU=`expr 60 \* $LUKU + $KOKOLUKU`
# Erotellaan minuutit
 KOHTA=`expr $KOHTA + 3`
 LOPPUKOHTA=`expr $KOHTA + 1`
 LUKU=`echo "$RIVI" | cut -c${KOHTA}-${LOPPUKOHTA}`
 KOKOLUKU=`expr $LUKU + $KOKOLUKU`
}

fingertyyppi2() {
 KOHTA=$1
# Irrotetaan rivin alku
 RIVI=`echo "$RIVI" | cut -c${KOHTA}-80`
 KOHTA=0
# Leikataan talteen koko päiväys
 KOHTA=`expr $KOHTA + 2`
 ONKOPITKA=`echo "$RIVI" | cut -c${KOHTA}`
 if test "${ONKOPITKA}" = "-"; then
  KOHTA=`expr $KOHTA`
 else
  KOHTA=`expr $KOHTA + 1`
 fi
 KOHTA=`expr $KOHTA + 9`
 ONKOPITKA=`echo "$RIVI" | cut -c${KOHTA}`
 if test "${ONKOPITKA}" = ":"; then
  KOHTA=`expr $KOHTA`
 else
  KOHTA=`expr $KOHTA + 1`
 fi
 KOHTA=`expr $KOHTA + 2`
# Koko päiväys leikattu
 KOHTA=0
# Erotellaan kuukauden päivä
 LUKU=`echo "$RIVI" | cut -d"-" -f1`
 KOKOLUKU=`expr 60 \* 24 \* $LUKU`
# Erotellaan kuukausi 
 KUUKAUSI=`echo "$RIVI" | cut -d"-" -f2`
 LUKU=`luvustakuukaudeksi $KUUKAUSI`
 KOKOLUKU=`expr 60 \* 24 \* 30 \* $LUKU + $KOKOLUKU`
# Erotellaan vuosi
 LUKU=`echo "$RIVI" | cut -d"-" -f3`
 LUKU=`echo "$LUKU" | cut -c0-2`
 KOKOLUKU=`expr 60 \* 24 \* 30 \* 12 \* 19${LUKU} + $KOKOLUKU`
# Erotellaan tunnit
 RIVI=`echo "$RIVI" | cut -d" " -f2`
 LUKU=`echo "$RIVI" | cut -d":" -f1`
 KOKOLUKU=`expr 60 \* $LUKU + $KOKOLUKU`
# Erotellaan minuutit
 LUKU=`echo "$RIVI" | cut -d":" -f2`
 LUKU=`echo "$LUKU" | cut -c1-2`
 KOKOLUKU=`expr $LUKU + $KOKOLUKU`
}

# Mikäli "SISALLA"-symboli on vielä silmukan lopussa "3", fingerin tulos oli 
# tuntematon, eika komentotiedosto tiedÄ miten lukea sitä.
# Muut arvot: SISALLA = 0 "päiväys luettu", 1 "käyttäjä on sisällä tällä 
# hetkellä", 2 "Käyttäjä on tuntematon".
luetulostus() {
 SISALLA=3

 if RIVI=`egrep "^Last login (Mon|Tue|Wed|Thu|Fri|Sat|Sun) [0-9]" $TULOSTETIEDOSTONIMI`; then
  SISALLA=0
  fingertyyppi2 16
 fi

 if RIVI=`grep "^Last login ... ... .. ..:.. " $TULOSTETIEDOSTONIMI`; then
  SISALLA=0
  fingertyyppi1 16
 fi

# 44 kpl:tta pisteitä
# if RIVI=`grep "............................................<... .. ..:..>" $TULOSTETIEDOSTONIMI`
# TYHJAA=`expr 9 - $NI
 if RIVI=`grep "^${NIMI} \{1,7\} .\{35\}<... .. ..:..> " $TULOSTETIEDOSTONIMI`; then
  SISALLA=0
  fingertyyppi1 46
 fi

# Käyttäjä on sisällä koneessa tällä hetkellä.
 if RIVI=`grep "^On since " $TULOSTETIEDOSTONIMI`; then
  SISALLA=1
 fi

 if RIVI=`grep "^${NIMI} \{1,7\} .\{35\} .. ... ..:..  " $TULOSTETIEDOSTONIMI`; then
  SISALLA=1
 fi

# Käyttäjä on tuntematon käyttäjä.
 if RIVI=`grep "^.\{22\}???" $TULOSTETIEDOSTONIMI`; then
  SISALLA=2
 fi

 if RIVI=`grep "^?Sorry, could not find \"" $TULOSTETIEDOSTONIMI`; then
  SISALLA=2
 fi

# Käyttäjä ei ole koskaan vieraillut tässä koneessa.
 if RIVI=`grep "^${NIMI}.\{1,7\}.\{0,80\}< .  .  .  . >" $TULOSTETIEDOSTONIMI`; then
  SISALLA=4
 fi

 if RIVI=`grep "^Last login [Never]" $TULOSTETIEDOSTONIMI`; then
  SISALLA=4
 fi

 if RIVI=`grep "^Never logged in\." $TULOSTETIEDOSTONIMI`; then
  SISALLA=4
 fi

# Tuloksen tulkinta.
 if [ "$SISALLA" = "1" ]; then
  echo "Käyttäjä \"${NIMI}\" on tällä hetkellä \"${KONE}\"-koneessa."
 fi

 if [ "$SISALLA" = "2" ]; then
  echo "Käyttäjä \"${NIMI}\" on tuntematon koneessa \"${KONE}\"."
 fi

 if [ "$SISALLA" = "3" ]; then
  echo "Fingerin tulos oli tuntematon, eika käskytiedosto "
  echo "tiedä miten lukea sitä."
  cat $TULOSTETIEDOSTONIMI
 fi

 if [ "$SISALLA" = "4" ]; then
  echo "Käyttäjällä \"${NIMI}\" on koneessa \"${KONE}\" tunnus muttei hän ole käyttänyt sitä."
 fi

}

#         TÄSTÄ ALKAA PÄÄOHJELMA

#Tiedosto johon tallennetaan fingerin tuloste
AIKA=`date +%H%M%S`
TULOSTETIEDOSTONIMI="/tmp/${USER}${AIKA}"

#Itse ohjelma alkaa
if test "${1}" = "-?"; then
 echo "                     VIIMKONE"
 echo "     Tämä käskytiedosto tutkii missä tietokoneessa"
 echo "     annettu käyttäjätunnus on viimekseksi ollut."; echo ""
 kayttoohjeet
 echo "Tiedoston suoritus keskeytetty..."
 exit 2
fi

if test -z "${1}" || test -z "${2}"; then
 kayttoohjeet
 echo "Tiedoston suoritus keskeytetty..."
 exit 1
fi
NIMI=$1; KONE=$2

argjono=$@; argnum=3;

VIIMEINENKOKOPAIVAYS=0
VIIMEINENKOKOLUKU=0

while test "$KONE"
do
 finger ${NIMI}@${KONE} > $TULOSTETIEDOSTONIMI
 luetulostus
 paivaysvertailu
 KONE=`echo "$argjono" | cut -d" " -f${argnum}`
 argnum=`expr $argnum + 1`
done

if test "$VIIMEINENKOKOLUKU" -ne "0"; then
 VIIMEINENKOKOPAIVAYS=`luvustamerkeiksi $VIIMEINENKOKOLUKU`
 echo "Käyttäjä \"${NIMI}\" tuli viimeksi $VIIMEINENKOKOPAIVAYS \"${VIIMEINENKONE}\"-tietokoneeseen."
fi

rm $TULOSTETIEDOSTONIMI
exit 0

WRITELOG.CBL

       IDENTIFICATION DIVISION.
       PROGRAM-ID. WRITELOG.
       AUTHOR. Mikko Koivunalho.
       DATE-WRITTEN. 15.02.2005.
       DATE-COMPILED.
      *Program writes one line to log file. Line is as follows:
      *YYYYMMDDHHMMSSss.99.X(30).X(30).X(1024)
      *DATE AND TIME.
      *Type of change PIC 99.
      *Name of changing record (key) PIC X(30).
      *Name of changing field PIC X(30).
      *Description of change (e.g. old data and new data) PIC X(1024).

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT OPTIONAL LOGFILE
               ASSIGN TO "MEMBERDB.LOG"
               ORGANIZATION IS LINE SEQUENTIAL
               ACCESS MODE IS SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD LOGFILE.
      *    LABEL RECORDS ARE STANDARD.
       01  LOG-RECORD.
           05 LOG-DATE.
               10 LOG-DATE-YEAR    PIC 9(4).
               10 LOG-DATE-MONTH   PIC 9(2).
               10 LOG-DATE-DAY     PIC 9(2).
           05 LOG-TIME.
               10 LOG-TIME-HOUR    PIC 9(2).
               10 LOG-TIME-MINUTE  PIC 9(2).
               10 LOG-TIME-SECOND  PIC 9(2).
               10 LOG-TIME-SECS    PIC 9(2).
           05 LOG-CHANGE-TYPE      PIC 9(2).
           05 LOG-CHANGE-RECORD    PIC X(30).
           05 LOG-CHANGE-FIELD     PIC X(30).
           05 LOG-CHANGE-DESCRIPTION PIC X(1024).
       WORKING-STORAGE SECTION.
      *VARIABLES FOR PROGRAM CAPTION
       77  PROGRAM-NAME-STRING PICTURE IS X(80) VALUE IS "WRITELOG".
       77  PROGRAM-COPYRIGHT-STRING PICTURE IS X(80) VALUE IS
               "Copyrigh 2005 Mikko Koivunalho".
       77  PROGRAM-VERSION-STRING PICTURE IS X(80) VALUE IS "0.01".
      *Other variables
       LOCAL-STORAGE SECTION.
      *Variables for FILE CONTROL
       77  MEMBERDB-FILE-STATUS          PIC 99.
       77  MEMBERDB-FILE-EOF             PIC X VALUE "N".
       LINKAGE SECTION.
       01  PARAMS-RECORD.
           05 PARAMS-DATE.
               10 PARAMS-DATE-YEAR    PIC 9(4).
               10 PARAMS-DATE-MONTH   PIC 9(2).
               10 PARAMS-DATE-DAY     PIC 9(2).
           05 PARAMS-TIME.
               10 PARAMS-TIME-HOUR    PIC 9(2).
               10 PARAMS-TIME-MINUTE  PIC 9(2).
               10 PARAMS-TIME-SECOND  PIC 9(2).
               10 PARAMS-TIME-SECS    PIC 9(2).
           05 PARAMS-CHANGE-TYPE      PIC 9(2).
           05 PARAMS-CHANGE-RECORD    PIC X(30).
           05 PARAMS-CHANGE-FIELD     PIC X(30).
           05 PARAMS-CHANGE-DESCRIPTION PIC X(1024).

       PROCEDURE DIVISION
           USING PARAMS-RECORD.
       MAIN-LOGIC SECTION.
       PROGRAM-BEGIN.
           DISPLAY PROGRAM-NAME-STRING SPACE "V." SPACE
               PROGRAM-VERSION-STRING SPACE PROGRAM-COPYRIGHT-STRING.
       PROGRAM-SETUP.
           MOVE PARAMS-RECORD TO LOG-RECORD.
           OPEN EXTEND LOGFILE.
      *     DISPLAY "Writing logfile...".
       MAIN-PROCESS.
           WRITE LOG-RECORD.
       PROGRAM-CLEANUP.
           CLOSE LOGFILE.
       PROGRAM-DONE.
           EXIT PROGRAM.
           STOP RUN.

      *----- ----- ----- ----- ----- ----- ----- -----
       FUNCTIONS SECTION.

MEMBERCB.CBL

       IDENTIFICATION DIVISION.                                         MK070827
       PROGRAM-ID. MEMBERDB.
       AUTHOR. Mikko Koivunalho.
       DATE-WRITTEN. 27.08.2005.
       DATE-COMPILED.
      *Database frontend.
      *Uses SCREEN data and video forms.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SOURCE-COMPUTER. DECAlpha.
       OBJECT-COMPUTER. DECAlpha.
       SPECIAL-NAMES.
           DECIMAL-POINT IS COMMA.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           COPY "FILEMEMB.COB".

       DATA DIVISION.
       FILE SECTION.
           COPY "FDMEMB.COB".
       WORKING-STORAGE SECTION.
      *VARIABLES AS FIGURATIVE CONSTANTS
       77  MENU-ADD-MEMBER               PIC X VALUE "1".
       77  MENU-DELETE-MEMBER            PIC X VALUE "2".
       77  MENU-DISPLAY-MEMBER           PIC X VALUE "3".
       77  MENU-EDIT-MEMBER              PIC X VALUE "4".
       77  MENU-EXIT                     PIC X VALUE "0".
      *VARIABLES FOR FILE CONTROL
       77  MEMBERDB-FILE-STATUS          PIC XX.
       77  MEMBERDB-FILE-EOF             PIC X VALUE "N".
      *VARIABLES FOR SCREEN-MENU
       77  DUMMY-ENTER                   PIC X VALUE IS SPACE.
       77  MENU-PICK                      PIC X VALUE SPACE.
       77  MENU-ERROR-MESSAGE            PIC X(79) VALUE IS SPACES.
      *VARIABLES FOR PROGRAM CAPTION
       77  PROGRAM-NAME-STRING PICTURE IS X(80) VALUE IS "ADDMEMBS".
       77  PROGRAM-COPYRIGHT-STRING PICTURE IS X(80) VALUE IS
               "Copyrigh 2007 Mikko Koivunalho".
       77  PROGRAM-VERSION-STRING PICTURE IS X(8) VALUE IS "0.01".
      *OTHER VARIABLES
       77  MEMBER-ID-NEW                 PIC 9(6) VALUE ZERO.
       77  MEMBER-ID-NEW-ASKED           PIC X(6) VALUE SPACES.
       77  MEMBER-ID-NEW-ASKED-C         PIC 9(1) VALUE ZERO.
       77  YESORNO                       PIC X VALUE ZERO.
           88 YESORNOISYES               VALUES ARE "y", "Y".
      *VARIABLES FOR PASSIGN PARAMETERS TO EXTERNAL PROGRAMS.
       01  LOG-ENTRY.
           05 LOG-DATE                PIC 9(8).
           05 LOG-TIME                PIC 9(8).
           05 LOG-CHANGE-TYPE      PIC 9(2).
           05 LOG-CHANGE-RECORD    PIC X(30).
           05 LOG-CHANGE-FIELD     PIC X(30).
           05 LOG-CHANGE-DESCRIPTION PIC X(1024).
       SCREEN SECTION.
       01  WELCOME-SCREEN
           BLANK SCREEN
           FOREGROUND-COLOR IS 7
           BACKGROUND-COLOR IS 0.
           05 LINE 8 COLUMN 23
               VALUE "+----------------------------------+".
           05 LINE 9 COLUMN 23
               VALUE "|                                  |".
           05 LINE 10 COLUMN 23
               VALUE "|  WELCOME TO THE MEMBER-DATABASE  |".
           05 LINE 11 COLUMN 23
               VALUE "|                                  |".
           05 LINE 12 COLUMN 23
               VALUE "+----------------------------------+".
           05 LINE 22 COLUMN 1
               PIC X(80) FROM PROGRAM-NAME-STRING.
           05 LINE 22 COLUMN 10 VALUE "V. ".
           05 LINE 22 COLUMN 13
               PIC X(80) FROM PROGRAM-VERSION-STRING.
           05 LINE 23 COLUMN 1
               PIC X(80) FROM PROGRAM-COPYRIGHT-STRING.
           05 LINE 16 COLUMN 27
               VALUE "[Press *ENTER* to continue]".
       01  MAIN-MENU-SCREEN
           BLANK SCREEN
           FOREGROUND-COLOR IS 7
           BACKGROUND-COLOR IS 0.
           05 LINE 1 COLUMN 10 VALUE "LIST OF MEMBERS".
           05 LINE 3 COLUMN 3 VALUE "1. ADD MEMBER".
           05 LINE 4 COLUMN 3 VALUE "2. DELETE MEMBER".
           05 LINE 5 COLUMN 3 VALUE "3. DISPLAY MEMBER".
           05 LINE 6 COLUMN 3 VALUE "4. EDIT MEMBER".
           05 LINE 13 COLUMN 3 VALUE "0. EXIT PROGRAM".
           05 LINE 23 COLUMN 2 VALUE "SELECT: ".
           05 LINE 23 COLUMN 10 PIC X USING MENU-PICK.
           05 LINE 3 COLUMN 26 VALUE "MEMBER ID:".
           05 LINE 3 COLUMN 37 PIC X(6) FROM MEMBER-ID.
           05 LINE 3 COLUMN 44 VALUE "MEMBER CAT:".
           05 LINE 3 COLUMN 56 PIC 9 FROM MEMBER-CATEGORY.
           05 LINE 5 COLUMN 26 VALUE "SURNAME:".
           05 LINE 5 COLUMN 37 PIC X(40) FROM MEMBER-FAMILYNAME.
           05 LINE 6 COLUMN 26 VALUE "FIRST NAM:".
           05 LINE 6 COLUMN 37 PIC X(40) FROM MEMBER-FIRSTNAMES.
           05 LINE 7 COLUMN 26 VALUE "ADDR. 1:".
           05 LINE 7 COLUMN 37 PIC X(40) FROM MEMBER-ADDRESS-1.
           05 LINE 8 COLUMN 26 VALUE "ADDR. 2:".
           05 LINE 8 COLUMN 37 PIC X(40) FROM MEMBER-ADDRESS-2.
           05 LINE 9 COLUMN 26 VALUE "ADDR. 3:".
           05 LINE 9 COLUMN 37 PIC X(40) FROM MEMBER-ADDRESS-3.
           05 LINE 10 COLUMN 26 VALUE "ADDR. 4:".
           05 LINE 10 COLUMN 37 PIC X(40) FROM MEMBER-ADDRESS-4.
           05 LINE 11 COLUMN 26 VALUE "POSTCODE:".
           05 LINE 11 COLUMN 37 PIC X(10) FROM MEMBER-ADDRESS-POSTCODE.
           05 LINE 12 COLUMN 26 VALUE "CITY:".
           05 LINE 12 COLUMN 37 PIC X(40) FROM MEMBER-ADDRESS-CITY.
           05 LINE 13 COLUMN 26 VALUE "COUNTRY:".
           05 LINE 13 COLUMN 37 PIC X(40) FROM MEMBER-ADDRESS-COUNTRY.
           05 LINE 14 COLUMN 26 VALUE "TEL.".
           05 LINE 14 COLUMN 37 PIC X(40) FROM MEMBER-TELEPHONE.
           05 LINE 15 COLUMN 26 VALUE "EMAIL:".
           05 LINE 16 COLUMN 11 PIC X(80) FROM MEMBER-EMAIL.
           05 LINE 17 COLUMN 26 VALUE "ADDED:".
           05 LINE 17 COLUMN 37 PIC 9(2) FROM MEMBER-ADDED-DATE-DAY.
           05 LINE 17 COLUMN 39 VALUE ".".
           05 LINE 17 COLUMN 40 PIC 9(2) FROM MEMBER-ADDED-DATE-MONTH.
           05 LINE 17 COLUMN 42 VALUE ".".
           05 LINE 17 COLUMN 43 PIC 9(4) FROM MEMBER-ADDED-DATE-YEAR.
           05 LINE 24 COLUMN 2 PIC X(79) FROM MENU-ERROR-MESSAGE.

       PROCEDURE DIVISION.
       MAIN-LOGIC SECTION.
       PROGRAM-BEGIN.
           PERFORM DISPLAY-WELCOME-SCREEN.
       PROGRAM-SETUP.
           OPEN I-O MEMBERDB-FILE.
       MAIN-PROCESS.
           PERFORM WITH TEST AFTER UNTIL MENU-PICK IS EQUAL TO ZERO
               PERFORM GET-MAIN-MENU-PICK
               EVALUATE MENU-PICK
                   WHEN MENU-ADD-MEMBER
                       MOVE SPACE TO MENU-PICK, MENU-ERROR-MESSAGE
                       PERFORM ADD-MEMBER
                   WHEN MENU-DELETE-MEMBER
                       MOVE SPACE TO MENU-PICK, MENU-ERROR-MESSAGE
                       PERFORM DELETE-MEMBER
                   WHEN MENU-DISPLAY-MEMBER
                       MOVE SPACE TO MENU-PICK, MENU-ERROR-MESSAGE
                       PERFORM DISPLAY-MEMBER
                   WHEN MENU-EDIT-MEMBER
                       MOVE SPACE TO MENU-PICK, MENU-ERROR-MESSAGE
                       PERFORM EDIT-MEMBER
                   WHEN MENU-EXIT
                       CONTINUE
                   WHEN OTHER
                       MOVE SPACE TO MENU-PICK
                       MOVE "NOT VALID SELECTION" TO MENU-ERROR-MESSAGE
               END-EVALUATE
           END-PERFORM.

      *     DISPLAY "MENU PICK: " MENU-PICK.
      *     PERFORM DO-THE-PICK UNTIL MENU-PICK = 0.
       PROGRAM-CLEANUP.
           CLOSE MEMBERDB-FILE.
       PROGRAM-DONE.
           EXIT PROGRAM.
           STOP RUN.

      *----- ----- ----- ----- ----- ----- ----- -----
       FUNCTIONS SECTION.

      *Writes the log (a new record).
      *REQUIRES VARIABLES:
      * MEMBER-ID
      * LOG-DATE
      * LOG-TIME
      * LOG-CHANGE-TYPE
      * LOG-CHANGE-RECORD
      * LOG-CHANGE-FIELD
      * LOG-CHANGE-DESCRIPTION
      *REQUIRES EXTERNAL PROGRAMS:
      * WRITELOG
       LOG-NEW-RECORD.
           ACCEPT LOG-DATE FROM DATE YYYYMMDD.
           ACCEPT LOG-TIME FROM TIME.
           MOVE 1 TO LOG-CHANGE-TYPE.
           MOVE MEMBER-ID TO LOG-CHANGE-RECORD.
           MOVE SPACES TO LOG-CHANGE-FIELD.
           MOVE "NEW RECORD" TO LOG-CHANGE-DESCRIPTION.
           CALL "WRITELOG"
               USING LOG-ENTRY.

      *Writes the log (delete a record).
      *REQUIRES VARIABLES:
      * LOG-DATE
      * LOG-TIME
      * LOG-CHANGE-TYPE
      * LOG-CHANGE-RECORD
      * LOG-CHANGE-FIELD
      * LOG-CHANGE-DESCRIPTION
      *REQUIRES EXTERNAL PROGRAMS:
      * WRITELOG
       LOG-DELETE-RECORD.
           ACCEPT LOG-DATE FROM DATE YYYYMMDD.
           ACCEPT LOG-TIME FROM TIME.
           MOVE 1 TO LOG-CHANGE-TYPE.
           MOVE MEMBER-ID TO LOG-CHANGE-RECORD.
           MOVE SPACES TO LOG-CHANGE-FIELD.
           MOVE "DELETE RECORD" TO LOG-CHANGE-DESCRIPTION.
           CALL "WRITELOG"
               USING LOG-ENTRY.

      *Add a new member to DB with ID one higher than previous.
      *REQUIRES VARIABLES:
      * MEMBER-ID-NEW (NUMERIC)
      * MEMBER-RECORD
      *REQUIRES FILES:
      * MEMBER-DB (OPENED)
      *REQUIRES INTERNAL FUNCTIONS:
      * LOG-NEW-RECORD
       ADD-MEMBER.
           INITIALIZE MEMBER-RECORD.
           MOVE "999999" TO MEMBER-ID.
           START MEMBERDB-FILE
               KEY IS LESS THAN MEMBER-ID
               INVALID KEY
                   DISPLAY "INVALID KEY " MEMBER-ID
           END-START.
           READ MEMBERDB-FILE NEXT RECORD
               AT END
                   DISPLAY "AT END: " MEMBER-ID AT LINE 24 COLUMN 2
           END-READ
           MOVE MEMBER-ID TO MEMBER-ID-NEW.
           INITIALIZE MEMBER-RECORD.
           ADD 1 TO MEMBER-ID-NEW.
           MOVE MEMBER-ID-NEW TO MEMBER-ID.
           ACCEPT MEMBER-ADDED-DATE FROM DATE YYYYMMDD.
           PERFORM LOG-NEW-RECORD.
           WRITE MEMBER-RECORD
               INVALID KEY
                   DISPLAY "No new record created! Aborting..."
           END-WRITE.

      *Asks for a confirmation and deletes the displayed member.
      *REQUIRES VARIABLES:
      * YESORNO
      *REQUIRES FILES:
      * MEMBERDB-FILE
       DELETE-MEMBER.
           INITIALIZE YESORNO.
           DISPLAY "Delete this member? (Y/N)" AT LINE 23 AT COLUMN 1.
           ACCEPT YESORNO FROM LINE 23 FROM COLUMN 28
               PROTECTED SIZE 1.
           IF YESORNOISYES
           THEN
               DELETE MEMBERDB-FILE
                   INVALID KEY
                       MOVE "INVALID KEY!" TO MENU-ERROR-MESSAGE
                   NOT INVALID KEY
                       MOVE "MEMBER DELETED." TO MENU-ERROR-MESSAGE
               END-DELETE
           ELSE
               MOVE "MEMBER NOT DELETED." TO MENU-ERROR-MESSAGE
           END-IF.
           INITIALIZE YESORNO.

      *Asks for a member ID.
      *REQUIRES VARIABLES:
      * MEMBER-ID-NEW (NUMERIC)
       ASK-MEMBER-ID.
           DISPLAY "Enter ID!" AT LINE 4 AT COLUMN 26.
           DISPLAY "+-------+" AT LINE 5 AT COLUMN 26.
           DISPLAY "|       |" AT LINE 6 AT COLUMN 26.
           DISPLAY "+-------+" AT LINE 7 AT COLUMN 26.
           ACCEPT MEMBER-ID-NEW LINE 6 COLUMN 27
               PROTECTED SIZE 6 WITH CONVERSION.

      *Searches database for a member by id and displays it.
      *REQUIRES VARIABLES:
      * MEMBER-ID-NEW (NUMERIC)
      * MEMBER-RECORD
      *REQUIRES FILES:
      * MEMBERDB-FILE
       DISPLAY-MEMBER.
           INITIALIZE MEMBER-ID-NEW.
           PERFORM ASK-MEMBER-ID.
           MOVE MEMBER-ID-NEW TO MEMBER-ID.
           READ MEMBERDB-FILE
               INVALID KEY
                   MOVE SPACES TO MEMBER-RECORD
                   MOVE "INVALID MEMBER ID" TO MENU-ERROR-MESSAGE
               NOT INVALID KEY
                   CONTINUE
           END-READ.

      *Edits the current (displayed) member.
      *REQUIRES VARIABLES:
      * YESORNO
      *
       EDIT-MEMBER.
           ACCEPT MEMBER-FAMILYNAME FROM LINE 5 FROM COLUMN 37
               PROTECTED SIZE 40 WITH EDITING DEFAULT IS CURRENT VALUE.
           ACCEPT MEMBER-FIRSTNAMES FROM LINE 6 FROM COLUMN 37
               PROTECTED SIZE 40 WITH EDITING DEFAULT IS CURRENT VALUE.
           ACCEPT MEMBER-ADDRESS-1 FROM LINE 7 FROM COLUMN 37
               PROTECTED SIZE 40 WITH EDITING DEFAULT IS CURRENT VALUE.
           ACCEPT MEMBER-EMAIL FROM LINE 16 FROM COLUMN 11
               PROTECTED SIZE 80 WITH EDITING DEFAULT IS CURRENT VALUE.
           DISPLAY "Accept changes? (Y/N)" AT LINE 24 AT COLUMN 2.
           INITIALIZE YESORNO.
           ACCEPT YESORNO FROM LINE 23 FROM COLUMN 10
               PROTECTED SIZE 1.
           IF YESORNOISYES
           THEN
               REWRITE MEMBER-RECORD
                   INVALID KEY
                       MOVE "INVALID KEY!" TO MENU-ERROR-MESSAGE
                   NOT INVALID KEY
                       MOVE "CHANGES SAVED" TO MENU-ERROR-MESSAGE
               END-REWRITE
           ELSE
               READ MEMBERDB-FILE
                   INVALID KEY
                       MOVE "INVALID MEMBER ID" TO MENU-ERROR-MESSAGE
               END-READ
               MOVE "CHANGES NOT SAVED! VALUES RETURNED TO ORIGINAL."
                   TO MENU-ERROR-MESSAGE
           END-IF.


      *----- ----- ----- ----- ----- ----- ----- -----
      *    MENUS AND SCREENS

      *Displays the Welcome screen and waits for user to press *ENTER*.
      *REQUIRES VARIABLES:
      * DUMMY-ENTER
      *REQUIRES SCREENS:
      * WELCOME-SCREEN
       DISPLAY-WELCOME-SCREEN.
           DISPLAY WELCOME-SCREEN.
           ACCEPT DUMMY-ENTER WITH NO ECHO END-ACCEPT.

      *Displays the Main program screen.
      *REQUIRES SCREENS:
      * MAIN-MENU-SCREEN
       DISPLAY-MAIN-MENU-SCREEN.
           DISPLAY MAIN-MENU-SCREEN.

      *Displays Main menu screen and waits for menu pick.
      *REQUIRES VARIABLES:
      * MENU-PICK-IS-VALID 88
      *REQUIRES INTERNAL FUNCTIONS:
      * DISPLAY-MAIN-MENU-SCREEN
      * ACCEPT-MAIN-MENU-SCREEN
      * RE-ACCEPT-MENU-SCREEN
       GET-MAIN-MENU-PICK.
           DISPLAY MAIN-MENU-SCREEN.
           ACCEPT MAIN-MENU-SCREEN.

FILEMEMB.CBL

       FD  MEMBERDB-FILE.
      *     LABEL RECORDS ARE STANDARD.
       01  MEMBER-RECORD.
           05 MEMBER-ID                     PIC X(6).
      * MEMBER-ID is the record key
      * possible IDs are 1 through 999999.
           05 MEMBER-CATEGORY               PIC 9(1).
      * possible categories are 1 through 9.
           05 MEMBER-FAMILYNAME             PIC X(40).
           05 MEMBER-FIRSTNAMES             PIC X(40).
           05 MEMBER-ADDRESS-1              PIC X(40).
           05 MEMBER-ADDRESS-2              PIC X(40).
           05 MEMBER-ADDRESS-3              PIC X(40).
           05 MEMBER-ADDRESS-4              PIC X(40).
           05 MEMBER-ADDRESS-POSTCODE       PIC X(10).
           05 MEMBER-ADDRESS-CITY           PIC X(40).
           05 MEMBER-ADDRESS-COUNTRY        PIC X(40).
           05 MEMBER-TELEPHONE              PIC X(40).
           05 MEMBER-EMAIL                  PIC X(80).
           05 MEMBER-PAID-FOR-YEARS         PIC X(100).
           05 MEMBER-ADDED-DATE.
               10 MEMBER-ADDED-DATE-YEAR    PIC 9(4).
               10 MEMBER-ADDED-DATE-MONTH   PIC 9(2).
               10 MEMBER-ADDED-DATE-DAY     PIC 9(2).
           05 MEMBER-REMOVED-DATE.
               10 MEMBER-REMOVED-DATE-YEAR    PIC 9(4).
               10 MEMBER-REMOVED-DATE-MONTH   PIC 9(2).
               10 MEMBER-REMOVED-DATE-DAY     PIC 9(2).
      * If content is any date, then this record should 
      * be skipped when making print lists.
      * use this record, however, when determining new IDs.
       66 MEMBER-FULLNAME RENAMES MEMBER-FAMILYNAME
           THROUGH MEMBER-FIRSTNAMES.
      * MEMBER-FULLNAME is the ALTERNATE KEY.

FDMEMB.CBL

           SELECT OPTIONAL MEMBERDB-FILE
               ASSIGN TO "MEMBERDB.DAT"
               ORGANIZATION IS INDEXED
               RECORD KEY IS MEMBER-ID
               ALTERNATE RECORD KEY IS MEMBER-FULLNAME WITH DUPLICATES
               ACCESS MODE IS DYNAMIC.

[Best viewed with Any Browser] [Built With HSC - HTML Sucks Completely] Made With Cascading Style Sheets Built With Amiga

Copyright © Mikko Johannes Koivunalho 1997---2007
email: mikko . koivunalho NOSPAM kolumbus . fi
homepage: http://www.kolumbus.fi/mikko.koivunalho

This page last updated or changed 11.09.2007.