Quiz

 

Synergy/DE pros, see if you can answer this question!

Assuming the following:

  1. myIPAddress resolves to a real host that can accept connections.
  2. myPort is a valid port on myIPAddress and will accept the connection.
  3. MYFILE.TXT exists (and in this case is 6088 bytes long).
  4. The program is linked against WND:tklib.elb.

What will the program below output?

  1. message box with Success!
  2. message box with Socket error = n (where n is the error number)
  3. message box with File error = n (where n is the error number)
  4. any of the above (a) - (c) could happen
  5. it won't compile
  6. it won't run

main
external function
        transmit_file   ,^val
record
        errFlag         ,int
        myIPAddress     ,a*,"127.0.0.1"
        myPort          ,int,1000
        
proc
        xcall u_start
        if (%transmit_file("MYFILE.TXT", myIPAddress, myPort, errFlag)) then
                xcall u_message("Success!")
        else if (errFlag > 0) then
                xcall u_message("Socket error = " + %string(errFlag))
        else
                xcall u_message("File error = " + %string(-errFlag))
        xcall u_finish
end

;----------------------

function transmit_file  ,^val
        req in argFileToSend    ,a
        req in argIPAddress     ,a
        req in argPort          ,n
        req out argErrorCode    ,n

.include "DBLDIR:synsock.def"

external function
        get_actual_file_size    ,^val
        get_buffer_from_file    ,^val
        
.define M_BADRET(f)     if (argErrorCode = f) freturn false 

record fileBuffer
        bufferField             ,a2048

record
        actualLength            ,int 
        fileChannel             ,int,0
        localAddress            ,D_ADDR 
        packet_length           ,int
        socket                  ,D_ADDR 
        totalBytes              ,int,0

record
        group trxStartPacket    ,a
                soh             ,a1
                length          ,d10
                stx             ,a1
        endgroup
        trxEndPacket            ,a1,%char(3)
        
proc
        trxStartPacket.soh = %char(1)
        trxStartPacket.stx = %char(2)
        
        M_BADRET(%get_actual_file_size(argFileToSend, fileChannel, trxStartPacket.length))
        M_BADRET(%ss_inet_addr(argIPAddress,localAddress))
        M_BADRET(%ss_socket(socket, SS_SOCK_STREAM))
        M_BADRET(%ss_connect(socket, argPort, localAddress))
        M_BADRET(%ss_sendbuf(socket, trxStartPacket))
        
        packet_length = ^size(fileBuffer.bufferField)
        repeat
                begin
                M_BADRET(%get_buffer_from_file(fileChannel, fileBuffer.bufferField, actualLength))
                M_BADRET(%ss_sendbuf(socket, fileBuffer.bufferField(1:actualLength)))
                ;; in case we want to keep track of how much has been sent...
                totalBytes += actualLength
                if (actualLength != packet_length) exitloop
				   end
        
        M_BADRET(%ss_sendbuf(socket, trxEndPacket))
        freturn true
end

;-----------------------

function get_actual_file_size   ,^val
        req in  argFileName     ,a
        req inout argChannel    ,n
        req out argTotalLength  ,n

record
        actualFileName  ,a500
        actualSize      ,int,0
        errFlag         ,int
        
proc
        clear argChannel
        xcall u_open(argChannel, "I", argFileName,,, errFlag)
        if (errFlag)
                freturn -errFlag                ;; return negative value with file issues
        xcall filnm(argChannel, actualFileName)
        ;; there are OS specific ways to do this as well as the ability to read to end of file and
        ;; determine the length that way.
        ;;
        ;; For the purpose of this exercise, assume the "best" approach for the situation has been
        ;; followed and the result is in actualSize
        ;;
        ;; We will leave determining the approaches to this to another exercise...
        ;; for now, we will hard code a value...
        actualSize = 6088
        
        argTotalLength = actualSize
        freturn 0
end

;-----------------------

function get_buffer_from_file   ,^val
        req in  argChannel      ,n
        req out argBuffer       ,a
        req out argActualLength ,n

record
        retval  ,int
        
proc
        retval = 0
        
        try 
                begin
                gets(argChannel, argBuffer)
                argActualLength = ^size(argBuffer)
                end
        catch (ex, @Synergex.synergyde.EndOfFileException)
                begin
                ;; we hit end of file, so figure out our actual length
                argActualLength = %rsize
                ;; close the channel - we don't need it anymore
                xcall u_close(argChannel)
                end
        catch (ex, @Synergex.synergyde.SynException)
                begin
                ;; any other type of error should return a bad result
                retval = -ex.Errno
                ;; before we return, we should try to close the file
                xcall u_close(argChannel)
                end
        catch (ex, @Exception)
                begin
                ;; any other type of error should just return a bad result, but we don't know the number
                retval = -9999
                ;; before we return, we should try to close the file
                xcall u_close(argChannel)
                end
        endtry
        
        freturn retval
end

--------------------------
Answer:

The program actually will compile, so (e) is not the correct answer.

Since the program is linked properly to the UI Toolkit library, if the Toolkit library is accessible, it will run, so (f) is not the correct answer.

Based on our assumptions, the socket error shouldn't happen provided there are no network interruptions, so (b) shouldn't be the answer.  We say this with caution, however, as there always could be transmission errors that are unforeseen, so (b) could happen.

Also, based on our assumptions, the file error shouldn't happen.  But again, there are many conditions that could cause it to happen, so it's a possibly correct answer too.

The program should output a message box with Success!, but as pointed out above, we could in fact have socket or file errors, so this is possible, but not definite.

Therefore, the correct answer is (d), any of the answers (a)-(c) could happen.

But, one of the reasons for this quiz is to point out the use of macro substitutions. Take, for example, the transmit_file function.  Within that routine, there are quite a few calls that, if they don't return a successful status, simply need to return to the calling program with the error flag.

Taking just this block of code:

  M_BADRET(%get_actual_file_size(argFileToSend,  fileChannel, trxStartPacket.length))
  M_BADRET(%ss_inet_addr(argIPAddress,localAddress))
  M_BADRET(%ss_socket(socket,  SS_SOCK_STREAM))
  M_BADRET(%ss_connect(socket, argPort,  localAddress))
  M_BADRET(%ss_sendbuf(socket,  trxStartPacket))
We could have written that in a more “traditional” way using something like this:

  argErrorCode = %get_actual_file_size(argFileToSend, fileChannel, trxStartPacket.length)
  if (argErrorCode) 
      freturn false

  argErrorCode = %ss_inet_add(argIPAddress, localAddress)
  if (argErrorCode)
      freturn false
  etc.
There is nothing wrong with this and it can be argued that’s it’s very readable, but it takes several lines of code for every one of these calls and “jumbles up” the flow for someone reading it. By using macro substitution:

.define M_RETBAD(f)     if (argErrorCode = f) freturn false

we were able to keep the code “clean” for reading the flow of the function.

Hopefully this tidbit of information will show you yet another way of using capabilities in Synergy to help you achieve your results easier.