TweetFollow Us on Twitter

XModem Transfer
Volume Number:2
Issue Number:12
Column Tag:Forth Forum

Batch Text File Transfer by XMODEM

By Jörg Langowski, MacTutor Editorial Board, Grenoble, France

If you don't happen to have a tape backup unit for your Macintosh, but access to some larger computer installation over a terminal line (with, usually, lots of disk space), there is a slow but secure way to keep backups of your files by uploading them to that machine. There are all sorts of communication utilities that help in doing so, most of them using the XMODEM protocol for up- and downloading files.

The telecommunications programs I am aware of at this moment are excellent tools for transferring single files to and from the Macintosh. If one wants to back up one full disk by such a file transfer, however, the task becomes a little tedious since one has to select one file at a time; particularly time-consuming if your disk has 327 short text files on it. The XMODEM standard does offer a batch transfer mode in which the filename is transferred followed by the file, and if anyone out there knows of a terminal program that has this mode implemented, please let me know.

For the time being, the transfer of one whole volume on a HFS disk from the Mac to another system by XMODEM batch transfer is a neat little project to implement in Forth. Besides being useful, it'll help us gain some insight into HFS file handling.

We will limit ourselves to transferring text files here. Since you all are aware that files on the Macintosh consist of the data and the resource fork, applications and other resource-containing files have to be changed into text files using a utility like BINHEX first. However based on our example, it is rather easy to implement the MacBinary format [a special standard combining both forks together with the Finder information into one structure: see MacTutor V2#1], and transfer files of any type automatically. At the present time, a text file backup system is fine for me, since the documents I most often have to back up, like manuscripts or current versions of Mach 2 and NEON source files, are text files anyway.

The XMODEM Batch Transfer Protocol

How does an XMODEM file transfer proceed? Each file is split up into sectors of 128 bytes each (a relic of old CP/M times), each sector starting at one. A simple file transfer (not batch mode) has the following protocol:

Simple File Transfer Protocol

Sender Receiver

waits for NAK,

80 sec timeout sends NAK at

10 sec intervals

Loop n times for n sectors

send header:

SOH ($01)

sector no. (0-255)

sector no. complement (255-0)

send sector data, 128 bytes

send checksum:

(header bytes + sector data ) mod 255

compute checksum and

compare with checksum

byte

send ACK ($06) if OK and

NAK if not

If ACK received, increment

sector no.

otherwise resend sector

End Loop

send EOT ($04)

send ACK

The transfer may be cancelled at any time by the sender or receiver transmitting a Ctrl-X ($24) to the other end.

In batch mode, there is an additional protocol defined to transmit the filename before the file transfer starts:

Batch Mode Protocol

Sender Receiver

waits for NAK,

80 sec timeout sends NAK at

10 sec intervals

sends ACK

awaits filename characters,

1 sec timeout

on error -> above NAK

Loop 11 times

send filename char

(bit 7=0, upper case)

add to checksum receive filename char

await ACK 1 sec timeout add to checksum

ACK

End Loop

send Ctrl-Z

add to checksum receive Ctrl-Z

add to checksum

send checksum

receive checksum +

verify

ACK if OK and "u" if not await ACK

- Normal file transfer starts -

NAK

SOH

.

.

.

EOT

ACK

expect NAK to start new

filename NAK

ACK for new file name

or EOT if finished

The routines necessary to implement this protocol are printed in Listing 1. [For the single file transfer protocol, I adapted some routines from an article by Robert Taylor in Dr. Dobbs Journal 83 (9/1983) p.66.]

The program in Listing 1 is best used by having some terminal emulation like Mockterminal running on the Macintosh at the same time. Mach2 users may append this code directly to the "Terminal Emulator" example on the Mach2 demo disk and then start the terminal emulator before doing the transfer. For other Forths, the program should be easily adaptable, you'll have to change the routines for setting the baud rate and for doing the modem input and output.

Look at the definition of the words send and send-filename and the inner loop of send for the implementation of the XMODEM protocol in Forth.

In order to be able to transfer one whole volume automatically, we must have some means for accessing the files on a volume one by one. The example contains some file and directory handling words for this purpose. I have also left in there some words I needed in testing (like $openWD) which are not used in the actual implementation, but could be useful at times.

The Mach2 word $open opens a file on the default volume, given a name string, and returns a file ID number. In order to access files in any arbitrary folder (= HFS volume), we have to get a volume ID number first and set the default volume to this ID. The word promptvolID calls the standard file package to prompt the user to select a file in the folder that is to be transferred. Then the volume ID of this folder is returned. Calling setvol with a volume ID sets the default volume. getidxfile takes as its input a volume ID and an index n and will look for the n-th file in the volume. The filename is returned in a global parameter block, parblock, and can be used to open the file with $open after setting the correct default volume. send-batch simply scans the default volume for all files and transfers them one by one. So far, for text files only. Have fun implementing the MacBinary standard.

Reader Feedback

I got several comments on the floating point routines that we published lately. Ed Moskowitz from Winfield, IL points out correctly that the routines will not work with the new Mach2 since they use D5 and D6. This is correct, and D5 and D6 have to be added to the MOVEM.L save and restore list at the routine entry and exits to fix this bug.

Mike Morton from Cambridge, MA had some useful comments to make about improving the speed of those routines. Excerpts from his letter:

"• If you're doing lots of _Pack4 calls, you can do a GetTrapAddress and call the package directly, saving 30 to 50 microseconds ( )

• Instead of BTST #7, D0, you can do TST.B D0 and use BMI to see if the bit is on.

...

• when you AND #$7FFFFF to both D3 and D1 in the division routine, it's faster to move the mask to a register just once and use it twice. ( )

• When you're doing multiplications [and divisions, JL], you might optimize for cases where some of the number is zero. So your four MULUs might test whether the operands are zero and handle that case explicitly. I spent some time writing 32-bit fixed-point multiply routines a while ago, and this helped a lot because many numbers don't have any bits on the bottom of the longword. If you expect many of the f.p. numbers to not have many significant bits, this helps some.

( ) "

Thanks, Ed and Mike, for your comments.

NEON News

One letter came clear from Tasmania, from Phil Barnard:

" I am suffering from the new ROM syndrome at the moment, although the faster and more spacious drives are very pleasant after the original units. Having to replace the interrupt vectors for the interrupt button was a nuisance. As you are no doubt aware the new ROM/System does not present the familiar bomb-box, but an 'empty' dialog.

It may be that the new dialog can be used to resume in the same fashion as the old one, but I don't yet know how. G <return> sends it back into any loop that you may wish to exit. Looking at the register contents by typing A0 through D7 is of limited debugging use. Can it do anything else?"

[Why, I even installed a control panel with flashing LEDs and toggle switches on the top of my Mac. Why do you want anything else? ... Well, G 40F6D8, of course, you probably know by now that that is the address of _Exittoshell and will (sometimes) send you straight back to the Finder. I don't use it for many other things. JL]

"The following routine brings back the old familiar bomb-box on interrupt."

\ installation of custom interrupt vectors; Phil Barnard
\  ** INTERRUPT VECTORS **
\ prints out the seven vectors from the interrupt table
: get.ints ( -- ) cr
 $ 64 -base @ . cr \ VIA
 $ 68 -base @ . cr \ SCC
 $ 6C -base @ . cr \ VIA & SCC
 $ 70 -base @ . cr \ Debugging Button
 $ 74 -base @ . cr \     "
 $ 78 -base @ . cr \     "
 $ 7C -base @ . cr \     "

CREATE RTS $ 4E75 NEXT,

\ installs RTS in the last four (debugging) vectors in the interrupt 
table

: put.ints ( -- )
 $ 80 $ 70 do 'c RTS +base i -base ! 4 +loop get.ints ;

Thanks, Phil. I'll deal with more NEON stuff in the next column where I'll also have some time to write about the new release of NEON, version 2.0. Here only a few headlines:

• NEON 2.0 is fully HFS and Switcher compatible; a new class pathlist has been added to specify HFS search paths. The assembler supports the new ROM calls.

• Applications install as a one piece file, no need to keep a copy of the installed NEON kernel on the disk.

• A decompiler has been added that will handle simple definitions, classes, objects and methods.

• A number of bugs, including the famous IC! have been fixed.

More details about NEON 2.0 and other NEON things later. Till then, good holidays.

Listing 1: XMODEM batch file downloader


( Implementation of Modem7 file transfer protocol, 
 © 9/86 J. Langowski  for MacTutor )

only forth also mac also i/o also assembler
decimal

60 user fileID

10 constant maxerr   0 constant nul24 constant can
4 constant eot   6 constant ack  21 constant nak
1 constant soh   26 constant ctrl-z
117 constant bdnmch

" Select file on volume to upload:" constant seldirtext

variable time-ct variable nak-ct variable cksum    
variable filename 128 vallot
variable xbuffer 128 vallot
variable replyrecord 74 vallot

header parblock 12 allot  ( 12 bytes junk )
header iocompl   4 allot  ( at PB + 12 )
header iores     8 allot  ( at PB + 16 )
header ioref#    4 allot  ( at PB + 24 )
header iomisc    4 allot  ( at PB + 28 )
100 allot ( some elbow room )

: long-timeout  1000 time-ct ! ; (  17 sec timeout )
: short-timeout 100  time-ct ! ; ( 1.7 sec timeout )

: baud-rate ( - )
 1CC0A COMM1 mode  ( 9600 BAUD Xon/Xoff )
 ABORT" Serial Driver Error" ;  (8 bit, No Par, 2Stop)
 
: modout comm1 output emit console output ;
: fin can modout ;  : fini fin close-file ;

: timed-read { | tstart -- char }
 call tickcount -> tstart
 comm1 input
 begin
 ?terminal if key console input exit then
 call tickcount tstart -  time-ct @ > 
 until 
 console input  -1
;

: ?contrlx ?terminal 
 if key 24 = 
 if fini 1 abort" Control-X abort" then
 then 
;

: wait-ack { | errs -- }
 0 -> errs 
 begin  errs maxerr >  nak-ct @ maxerr > or
 if fini 1 abort" Max error count exceeded." then
 ?contrlx 
 timed-read case
 -1  of errs 1+ -> errs ." Timeout" cr endof
 can of 1 abort" Remote Cancel" endof
 ack of 0 nak-ct !  exit endof
 nak of 1 nak-ct +! exit endof
 ( disp-chr 0 )
 endcase
 again
;

: send-setup
 long-timeout
 standard-getfile  0= 
 if fin 1 abort" Cancelled." then
 0 nak-ct !
;

: read-sect { sector# | sect-start -- }
 sector# 128 * -> sect-start
 128 0
 do i sect-start + virtual c@ i xbuffer + c! loop
;

: send-filename
 ack modout 0 cksum c!
 12 1 do filename i + 
 c@ 127 and dup 95 > if 32 - then
 dup modout dup emit
 cksum c@ + cksum c!
 wait-ack
 loop cr
 ctrl-z modout 
 cksum c@ ctrl-z + cksum c!
;
 
: send-hdr { sector# | -- }
 soh modout
 sector# 1+ 255 and dup modout 255 xor modout
 ." Sending " sector# . cr
;

: send-sect ( send sector in xbuffer via XMODEM )
 0 ( sum )
 128 0 do xbuffer i + c@ dup modout + 255 and loop
 cksum c!
;
 
: send-cksum cksum c@ modout ;

: end-send
 close-file
 begin cr ." Sending EOT -" eot modout wait-ack
 nak-ct @ 0= until
 ." ack'd"
;

: get-file-length ( -- sectors )
 fileID w@ ['] ioref# w!  0 ['] iocompl !
 ['] parblock call geteof . cr
 ['] iomisc @ 128 /mod swap 
 if 1+ then
 dup ." File has " . ."  sectors" cr 
;
 
: send ( current file via XMODEM, closes file on exit )
 get-file-length 0
 do
 begin
 i read-sect ( into xbuffer )
 i send-hdr send-sect send-cksum
 wait-ack nak-ct @ 0=
 until
 loop
 end-send
;

: prelude
 short-timeout  begin timed-read -1 = until
 long-timeout  ." Awaiting initial NAK: "
 wait-ack cr   short-timeout
;

: send-one send-setup prelude send ;

: end-batch ack modout eot modout ;

( *** file and directory  handling routines *** )

: getVolID { index | pb -- VRefNum / errcode 0 }
 ( ID for volume no. index )
   ['] parblock -> pb
 0 ['] iocompl !
 filename pb 18 + !
 index pb 28 + w!
 pb call getvolinfo
 ?dup if 0 else pb 22 + w@ then
;

CODE SFGetFile  ( x y prompt numTypes typeList ReplyRecord )
 MOVE.L (A6)+,A1 ( reply record )
 MOVE.L (A6)+,A0 ( type list )
 MOVE.L (A6)+,D3 ( numTypes )
 MOVE.L (A6)+,D2 ( prompt )
 MOVE.L (A6)+,D1 ( y-coord )
 MOVE.L (A6)+,D0 ( x-coord )

 MOVE.W D0,-(A7)
 MOVE.W D1,-(A7) ( where )
 MOVE.L D2,-(A7) ( prompt )
 MOVE.L #0,-(A7) ( no filter proc )
 MOVE.W D3,-(A7) ( num Types )
 MOVE.L A0,-(A7) ( type list )
 MOVE.L #0,-(A7) ( no dialog hook )
 MOVE.L A1,-(A7) ( reply record )

 MOVE.W #2,-(A7)
 _Pack3
 RTS
END-CODE

: promptvolID  ( -- VRefNum flag )
 100 50 seldirtext -1 0 replyrecord 
 SFGetfile
 replyrecord 6 + w@ l_ext
 replyrecord w@ l_ext
;

CODE SFPutFile
 MOVE.L (A6)+,A1
 MOVE.L (A6)+,A0
 MOVE.L (A6)+,D3
 MOVE.L (A6)+,D2
 MOVE.L (A6)+,D1
 MOVE.L (A6)+,D0

 MOVE.W D0,-(A7)
 MOVE.W D1,-(A7)
 MOVE.L D2,-(A7)
 MOVE.L D3,-(A7)
 MOVE.L A0,-(A7)
 MOVE.L A1,-(A7)

 MOVE.W #1,-(A7)
 _Pack3
 RTS
END-CODE

: $openWD { name | pb -- VRefNum / errcode 0 }
   ['] parblock -> pb
 0 ['] iocompl !
 name pb 18 + !
 0 pb 28 + w!
 pb 1 call HFSDispatch
 ?dup if 0 else pb 22 + w@ then
;

: closeWD { volume | pb -- rescode }
   ['] parblock -> pb
 0 ['] iocompl !
 volume pb 22 + w!
 pb 2 call HFSDispatch
;

: setVol { volume | pb -- res code }
   ['] parblock -> pb
 0 ['] iocompl !
 0 pb 18 + !
 volume pb 22 + w!
 pb call setVol
;
 
: getidxfile { volume index | pb -- res code }
   ['] parblock -> pb
 0 ['] iocompl !
 filename pb 18 + !( ioNamePtr )
 volume pb 22 + w! ( ioVRefNum ) 
 0 pb 26 + w!  ( ioFVersNum )
 index pb 28 + w!( ioFDirIndex )
 pb call getfileinfo 
 dup 0= if ." Found: " filename count type cr then
;

: openidxfile { volume index | -- res code }
 volume index getidxfile
 ?dup 0= if filename $open then
;

: find-files { | volume index -- }
 promptvolID 
 if -> volume 1 -> index
 begin 
 volume index getidxfile
 index 1+ -> index
 until
 else ." cancelled" . cr 
 then
;
( *** main definitions for batch file transfer *** )
: display-filename
 12 1 do filename i + c@ 127 and dup 95 > if 32 - then emit
 loop cr
;  
: send-batch{ volume | index }
 1 -> index  0 nak-ct ! 
 begin cr
 filename 1+ 11 32 fill
 volume index getidxfile 0= 
 while
 volume setvol drop 
 filename $open dup 0> 
 if   fileID w!
 begin 
 prelude send-filename
 timed-read cksum c@ <> 
 while 
 ." Checksum Error in filename, restarting" cr
 bdnmch modout
 repeat
 ack modout
 send ( closes file )
 else drop 
 then
 index 1+ -> index
 repeat
 wait-ack end-batch
;
: upload 
 promptvolID
 if send-batch 
 else cr ." Cancelled" cr
 then
;
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Lyn 1.8.8 - Lightweight image browser an...
Lyn is a fast, lightweight image browser and viewer designed for photographers, graphic artists, and Web designers. Featuring an extremely versatile and aesthetically pleasing interface, it delivers... Read more
Adobe Animate CC 2017 16.2.0 - Advanced...
Animate CC 2017 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Flash Professional customer). Animate CC 2017 (was Flash CC) lets you... Read more
Tunnelblick 3.7.0 - GUI for OpenVPN.
Tunnelblick is a free, open source graphic user interface for OpenVPN on OS X. It provides easy control of OpenVPN client and/or server connections. It comes as a ready-to-use application with all... Read more
DEVONthink Pro 2.9.11 - Knowledge base,...
DEVONthink Pro is your essential assistant for today's world, where almost everything is digital. From shopping receipts to important research papers, your life often fills your hard drive in the... Read more
DiskCatalogMaker 6.8.1 - Catalog your di...
DiskCatalogMaker is a simple disk management tool which catalogs disks. Simple, light-weight, and fast Finder-like intuitive look and feel Super-fast search algorithm Can compress catalog data for... Read more
OmniGraffle 7.3 - Create diagrams, flow...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
OmniGraffle Pro 7.3 - Create diagrams, f...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
A Better Finder Rename 10.15 - File, pho...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
Adobe Dreamweaver CC 2017 17.0.2.9391 -...
Dreamweaver CC 2017 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Dreamweaver customer). Adobe Dreamweaver CC 2017 allows you to... Read more
BBEdit 11.6.5 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more

Dynasty Blades new update introduces a n...
Sharpen your weapons -- Dynasty Blades is back with new and improved hack n’ slash stylings. The Romance of the Three Kingdoms-inspired action MMORPG introduces a bunch of fun new features in its latest update. For the uninitiated, Dynasty Blades... | Read more »
Meganoid(2017) (Games)
Meganoid(2017) 1.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: LAUNCH DISCOUNT 20% UNTIL APRIL 2nd! Support, tip and tricks: http://www.orangepixel.net/forum/ Subscribe to our newsletter... | Read more »
Telltale's Guardians of the Galaxy...
Telltale will be releasing their rendition of Guardians of the Galaxy later this month. The first episode, Tangled Up in Blue, features familiar faces including Star-Lord, Groot, Rocket, Gamora, and Drax. If the first episode's title is any... | Read more »
Royal Dungeon (Games)
Royal Dungeon 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: The king and his queen are trapped in their castle which suddenly turned out as a very dangerous place. The goal is to escape... | Read more »
Tom Clancy's ShadowBreak is a real-...
Ubisoft is treating Tom Clancy fans to the series' very first mobile-exclusive game in ShadowBreak, a real-time, multiplayer shooter in which players snipe at enemies in fast-paced tactics-driven combat. [Read more] | Read more »
Power Rangers: Legacy Wars beginner...
Rita Repulsa is back, but this time she's invading your mobile phone in Power Rangers: Legacy Wars. What looks to be a straightforward beat 'em up is actually a tough-as-nails multiplayer strategy game that requires some deft tactical maneuvering.... | Read more »
Hearthstone celebrates the upcoming Jour...
Hearthstone gets a new expansion, Journey to Un'Goro, in a little over a week, and they'll be welcoming the Year of the Mammoth, the next season, at the same time. There's a lot to be excited about, so Blizzard is celebrating in kind. Players will... | Read more »
4 smart and stylish puzzle games like Ty...
TypeShift launched a little over a week ago, offering some puzzling new challenges for word nerds equipped with an iOS device. Created by Zach Gage, the mind behind Spelltower, TypeShift boasts, like its predecessor, a sleak design and some very... | Read more »
The best deals on the App Store this wee...
Deals, deals, deals. We're all about a good bargain here on 148Apps, and luckily this was another fine week in App Store discounts. There's a big board game sale happening right now, and a few fine indies are still discounted through the weekend.... | Read more »
The best new games we played this week
It's been quite the week, but now that all of that business is out of the way, it's time to hunker down with some of the excellent games that were released over the past few days. There's a fair few to help you relax in your down time or if you're... | Read more »

Price Scanner via MacPrices.net

Is Apple Planning An iPhone Based Modular Doc...
Today’s more powerful and larger-screened smartphones and phablets are becoming the default anchor computing device for more and more users computing devices, but even a five or six inch panel is not... Read more
Razer Launches New Razer Blade Pro World’s Fi...
Razer, the gaming and high performance hardware specialists, have announced the new Razer Blade Pro laptop — the first laptop to be qualified for THX Mobile Certification, an accreditation reserved... Read more
Gro CRM’s Apple Small Business Mac And iOS CR...
Gro Software, developers of the Mac CRM software for small business and enterprise, are included in FinancesOnline 2017 CRM Rising Stars and Great User Experience lists by business software review... Read more
Deal alert! 15-inch and 13-inch MacBook Pros...
B&H Photo has the new 2016 15″ and 13″ Apple MacBook Pros in stock today and on sale for up to $200 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.7GHz Touch Bar... Read more
Save up to $420 on a new MacBook Pro with App...
Apple is offering Certified Refurbished 2016 15″ and 13″ MacBook Pros, including some Touch Bar models, for up to $420 off original MSRP. An Apple one-year warranty is included with each model, and... Read more
12-inch 1.2GHz Retina MacBooks on sale for $1...
B&H has 12″ 1.2GHz Retina MacBooks on sale for up to $200 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 12″ 1.2GHz Space Gray Retina MacBook: $1449 $150 off MSRP - 12″ 1.... Read more
Is A New 10.5-inch iPad Still Coming In April...
There was no sign or mention of a long-rumored and much anticipated 10.5-inch iPad Pro in Apple’s product announcements last week. The exciting iPad news was release of an upgraded iPad Air with a... Read more
T-Mobile’s Premium Device Protection Now Incl...
Good news for T-Mobile customers who love their iPhones and iPads. The “Un-carrier” has become the first national wireless company to give customers AppleCare Services at zero additional cost as part... Read more
FileWave Ensures Support for Latest Apple OS...
FileWave multi-platform device management providers announced support for Apple’s release yesterday of iOS 10.3, macOS Sierra 10.12.4, and tvOS 11.2. FileWave has a history of providing zero-day... Read more
Use Apple’s Education discount to save up to...
Purchase a new Mac or iPad using Apple’s Education Store and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free: -... Read more

Jobs Board

Fulltime aan de slag als shopmanager in een h...
Ben jij helemaal gek van Apple -producten en vind je het helemaal super om fulltime shopmanager te zijn in een jonge en hippe elektronicazaak? Wil jij werken in Read more
Fulltime aan de slag als shopmanager in een h...
Ben jij helemaal gek van Apple -producten en vind je het helemaal super om fulltime shopmanager te zijn in een jonge en hippe elektronicazaak? Wil jij werken in Read more
Desktop Analyst - *Apple* Products - Montef...
…technology to improve patient care. JOB RESPONSIBILITIES: Provide day-to-day support for Apple Hardware and Software in the environment based on the team's support Read more
*Apple* Mobile Master - Best Buy (United Sta...
**493168BR** **Job Title:** Apple Mobile Master **Location Number:** 000827-Denton-Store **Job Description:** **What does a Best Buy Apple Mobile Master do?** At Read more
Fulltime aan de slag als shopmanager in een h...
Ben jij helemaal gek van Apple -producten en vind je het helemaal super om fulltime shopmanager te zijn in een jonge en hippe elektronicazaak? Wil jij werken in Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.