$include (..\lib\compStch.ext) /* *============================================================================ * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE * * Permission to use for any purpose, modify, copy, and make enhancements * and derivative works of the software is granted if attribution is given to * R.M. Gillmore, dba the ACME Software Deli, as the author * * While the ACME Software Deli does not work for money, there is nonetheless * a significant amount of work involved. The ACME Software Deli maintains the * rights to all code written, though it may be used and distributed as long as * the following conditions are maintained. * * 1. The copyright statement at the top of each code block is maintained in * your distribution. * 2. You do not identify yourself as the ACME Software Deli * 3. Any changes made to the software are sent to the ACME Software Deli *============================================================================ */ morseCodeSource: do; $if not noID declare IDString (*) byte data ( '@(#)morseCod.p86 $Author: rmgillmore $ $Date:: 2025-05-04 19:35:39#$:', 0 ); $endif $set ( morseCodeModule ) $include (..\lib\comnDefs.ext) $include (..\lib\morseCod.ext) $include (..\lib\ctype.ext) $include (..\lib\string.ext) $include (..\lib\fileIO.ext) /* * from the wikipedia commons, this is the international morse code */ declare DOT literally '''.''', DASH literally '''-'''; declare numberInterCharacterUnits literally '3', numberInterWordUnits literally '7'; declare NUMBER_LETTERS literally '26', NUMBER_DIGITS literally '10'; /* * while letters() and numbers() are represented as arrays of pointers, * each element in the array is actually represented as a length (number of * dots and dashes in the character represented), followed by an array of * the actual dots and dashes. */ declare letters ( NUMBER_LETTERS ) pointer data ( /* A */ @( 2, DOT, DASH ), /* B */ @( 4, DASH, DOT, DOT, DOT ), /* C */ @( 4, DASH, DOT, DASH, DOT ), /* D */ @( 3, DASH, DOT, DOT ), /* E */ @( 1, DOT ), /* F */ @( 4, DOT, DOT, DASH, DOT ), /* G */ @( 3, DASH, DASH, DOT ), /* H */ @( 4, DOT, DOT, DOT, DOT ), /* I */ @( 2, DOT, DOT ), /* J */ @( 4, DOT, DASH, DASH, DASH ), /* K */ @( 4, DASH, DOT, DASH, DOT ), /* L */ @( 4, DOT, DASH, DOT, DOT ), /* M */ @( 2, DASH, DASH ), /* N */ @( 2, DASH, DOT ), /* O */ @( 3, DASH, DASH, DASH ), /* P */ @( 4, DOT, DASH, DASH, DOT ), /* Q */ @( 4, DASH, DASH, DOT, DASH ), /* R */ @( 3, DOT, DASH, DOT ), /* S */ @( 3, DOT, DOT, DOT ), /* T */ @( 1, DASH ), /* U */ @( 3, DOT, DOT, DASH ), /* V */ @( 4, DOT, DOT, DOT, DASH ), /* W */ @( 3, DOT, DASH, DASH ), /* X */ @( 4, DASH, DOT, DOT, DASH ), /* Y */ @( 4, DASH, DOT, DASH, DASH ), /* Z */ @( 4, DASH, DASH, DOT, DOT ) ); declare numbers ( NUMBER_DIGITS ) pointer data ( /* 0 */ @( 5, DASH, DASH, DASH, DASH, DASH ), /* 1 */ @( 5, DOT, DASH, DASH, DASH, DASH ), /* 2 */ @( 5, DOT, DOT, DASH, DASH, DASH ), /* 3 */ @( 5, DOT, DOT, DOT, DASH, DASH ), /* 4 */ @( 5, DOT, DOT, DOT, DOT, DASH ), /* 5 */ @( 5, DOT, DOT, DOT, DOT, DOT ), /* 6 */ @( 5, DASH, DOT, DOT, DOT, DOT ), /* 7 */ @( 5, DASH, DASH, DOT, DOT, DOT ), /* 8 */ @( 5, DASH, DASH, DASH, DOT, DOT ), /* 9 */ @( 5, DASH, DASH, DASH, DASH, DASH ) ); /* * =========================================================================== * name: morseRenderChar * * description: * Given an ASCII character (letter or number/digit), provide the proper * combination of dit and dah (in audio) * =========================================================================== */ morseRenderChar: procedure ( characterToRender ) public; declare characterToRender char; declare dataPointer pointer, codeArray based dataPointer (*) byte, index byte; if isalpha( characterToRender ) then do; declare localCharacter char; localCharacter = toupper( characterToRender ); dataPointer = letters( localCharacter - 'A' ); end; else if isnum( characterToRender ) then do; dataPointer = numbers( characterToRender - '0' ); end; else if ( characterToRender = SPACE ) or ( characterToRender = PERIOD ) then do; dataPointer = NULL; do index = 0 to numberInterWordUnits - 1; call printStringAsciiZ( @( 0 ) ); end; end; if ( dataPointer <> NULL ) then do; declare numberUnits byte; numberUnits = codeArray( 0 ); do index = 1 to numberUnits; if ( codeArray( index ) = DOT ) then call printf( @( '\a', 0 ) ); else do; call printf( @( '\a', 0 ) ); call printf( @( '\a', 0 ) ); call printf( @( '\a', 0 ) ); end; end; end; end morseRenderChar; /* * =========================================================================== * name: morseRenderCharTextual * * description: * Given an ASCII character (letter or number/digit), provide the proper * combination of ASCII dots and dashes to represent the dit and dah * =========================================================================== */ morseRenderCharTextual: procedure ( characterToRender ) public; declare characterToRender char; declare dataPointer pointer, codeArray based dataPointer (*) byte, index byte, numberWritten word; if isalpha( characterToRender ) then do; declare localCharacter char; localCharacter = toupper( characterToRender ); dataPointer = letters( localCharacter - 'A' ); end; else if isnum( characterToRender ) then do; dataPointer = numbers( characterToRender - '0' ); end; else if ( characterToRender = SPACE ) then do; dataPointer = NULL; call printf( @( ' ', 0 ) ); end; else if ( characterToRender = PERIOD ) then do; dataPointer = NULL; call printf( @( '\n', 0 ) ); end; if ( dataPointer <> NULL ) then do; declare numberUnits byte; numberUnits = codeArray( 0 ); do index = 1 to numberUnits; numberWritten = fwrite( stdout, @codeArray( index ), 1 ); end; numberWritten = fwrite( stdout, @( ' ' ), 1 ); end; end morseRenderCharTextual; /* * =========================================================================== * name: morseRenderString * * description: * Given an ASCII character string, generate the proper sequence of dit * and dah until the string has been completely rendered * =========================================================================== */ morseRenderString: procedure ( renderStringPtr ) public; declare renderStringPtr pointer, renderString based renderStringPtr (*) char, stringLength word, index word; stringLength = strlen( renderStringPtr ); do index = 0 to stringLength - 1; call morseRenderChar( renderString( index ) ); end; end morseRenderString; /* * =========================================================================== * name: morseRenderStringTextual * * description: * Given an ASCII character string, generate the proper sequence of ASCII * dot, dash and spaces until the string has been completely rendered * =========================================================================== */ morseRenderStringTextual: procedure ( renderStringPtr ) public; declare renderStringPtr pointer, renderString based renderStringPtr (*) char, stringLength word, index word; call printf( @( ' ', 0 ) ); stringLength = strlen( renderStringPtr ); do index = 0 to stringLength - 1; call morseRenderCharTextual( renderString( index ) ); end; end morseRenderStringTextual; /* * =========================================================================== * name: decodeMorseChar * * description: * Given an ASCII string of dot and dash (along with its length), render * the proper ASCII character, either alphabetic or numeric. If the * sequence is not understood, an asterisk is returned * =========================================================================== */ decodeMorseChar: procedure ( numberChars, stringInPtr ) char public; declare numberChars word, stringInPtr pointer, dotsAndDashes based stringInPtr (*) char; declare representedChar char, done boolean; declare dataPointer pointer, codeArray based dataPointer (*) byte, arrayIndex word, codeIndex word; done = False; representedChar = '*'; /* * we will look in the letters first because there are so many * more letter than numbers */ arrayIndex = 0; do while ( arrayIndex < NUMBER_LETTERS ) and ( not done ); dataPointer = letters( arrayIndex ); if ( codeArray( 0 ) = numberChars ) then do; done = memcmp( @codeArray( 1 ), @dotsandDashes( 0 ), numberChars ); end; if ( done ) then do; representedChar = arrayIndex + 'A'; end; else do; arrayIndex = arrayIndex + 1; end; end; if ( not done ) then do; /* * when we get here, the code did not represent a letter, so all that is * left are numbers */ arrayIndex = 0; do while ( arrayIndex < NUMBER_DIGITS ) and ( not done ); dataPointer = numbers( arrayIndex ); if ( codeArray( 0 ) = numberChars ) then done = memcmp( @codeArray( 1 ), @dotsandDashes( 0 ), numberChars ); if ( done ) then do; representedChar = arrayIndex + '0'; end; else do; arrayIndex = arrayIndex + 1; end; end; /* while not done */ end; return ( representedChar ); end decodeMorseChar; /* * =========================================================================== * name: decodeMorseString * * description: * Given an ASCII string of dot, dash and spaces, translate to ASCII * character string * =========================================================================== */ decodeMorseString: procedure ( stringInPtr ) public; declare stringInPtr pointer, stringIn based stringInPtr (*) char, stringInLength word, spaceChar char, decodedCharacter char, stringHeadIndex word, morseCodeIndex word, characterLength word, numberWritten word; spaceChar = SPACE; /* * the input string is composed of short strings, separated by spaces. * a single space is the delimiter between characters/digits. A double * space is the separator between sentences. */ stringInLength = strlen( stringInPtr ); stringHeadIndex = 0; do morseCodeIndex = 0 to ( stringInLength - 1 ); if ( ( stringIn( morseCodeIndex ) = SPACE ) or ( stringIn( morseCodeIndex ) = NUL ) ) then do; /* * found a character delimiter */ characterLength = morseCodeIndex - stringHeadIndex; if ( characterLength > 0 ) then do; /* * we have a possible character, let's decode and report it */ decodedCharacter = decodeMorseChar( characterLength, @stringIn( stringHeadIndex ) ); numberWritten = fwrite( stdout, @decodedCharacter, 1 ); /* * adjust the string head to the next possible character * in the string */ stringHeadIndex = morseCodeIndex + 1; end; else do; /* * we came to a word delimiter, so write a space */ numberWritten = fwrite( stdout, @spaceChar, 1 ); /* * adjust the string head to the next possible character * in the string */ stringHeadIndex = morseCodeIndex + 1; end; end; end; /* foreach char in string */ end decodeMorseString; end morseCodeSource;