home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / maxiftp3.zip / maxiftp.cmd < prev    next >
OS/2 REXX Batch file  |  1994-02-18  |  84KB  |  2,624 lines

  1. /*------------------------------------------------------------------
  2.  * miniftp.cmd :
  3.  *------------------------------------------------------------------
  4.  * 03-16-93 originally by Patrick J. Mueller & Cliff Nadler
  5.  *------------------------------------------------------------------
  6.  * maxiftp.cmd:
  7.  *------------------------------------------------------------------
  8.  *   by Albert L. Crosby <acrosby@comp.uark.edu> with
  9.  *       suggestions by many people on the internet.
  10.  *   This work is freely redistributeable,  Please forward any
  11.  *    suggestions or enhancements to me and I'll incorporate them
  12.  *    in a future release.
  13.  *------------------------------------------------------------------ */
  14.  
  15. /*------------------------------------------------------------------
  16.  * set up call
  17.  *------------------------------------------------------------------*/
  18. call on halt
  19. call on error
  20.  
  21. parse arg xargs
  22.  
  23. /* Add anything in the environment variable MAXIFTP */
  24. xargs=strip(value('MAXIFTP',,'OS2ENVIRONMENT')||" "||xargs)
  25.  
  26. /* Build a blank delimited list of command line options */
  27.  
  28. args=" "
  29.  
  30. if pos(left(xargs,1),"/-")<>0 then
  31.    do
  32.    rest=xargs
  33.    do until pos(left(rest,1),"/-")=0
  34.       parse var rest argument rest
  35.       if pos('"',argument)<>0 then
  36.          do
  37.          parse var rest remainder'"' rest
  38.          argument=argument||' '||remainder||'"'
  39.          end
  40.       args=args||" "||substr(argument,2)
  41.       rest=strip(rest)
  42.    end
  43.    parse var rest host rest
  44.    if pos('"',host)<>0 then
  45.       do
  46.       parse var rest rest'"' user pass .
  47.       host=host||" "||rest||'"'
  48.       end
  49.    else parse var rest user pass .
  50.    end
  51. else
  52.    do
  53.    parse var xargs host rest
  54.    if pos('"',host)<>0 then
  55.       do
  56.       parse var rest rest'"' user pass .
  57.       host=host||" "||rest||'"'
  58.       end
  59.    else parse var rest user pass .
  60.    end
  61.  
  62. call LoadFunctions
  63.  
  64. parse value SysTextScreenSize() with rows cols
  65.  
  66. debug=0
  67.  
  68. "@echo off"
  69. "ansi on > nul"
  70.  
  71. trace off
  72.  
  73. version="MaxiFTP version 3 BETA revision 189"
  74. credits.0=3
  75. credits.1="by Albert Crosby, 02/18/94"
  76. credits.2="(based on original code by Patrick J. Mueller & Cliff Nadler of IBM"
  77. credits.3="    and additional suggestions from Al Dhir's sFTP)"
  78. /* List of filetypes currently assumed to be binary for get and put */
  79. binaries=value("BINARIES",,"OS2ENVIRONMENT")
  80. if binaries="" then
  81.    binaries="EXE COM ZIP SYS DLL DEV ARJ ARC Z TAR GZ BIN DSK LZH ARK LBR JPG JPEG GIF PCX "
  82. asciis=value("ASCIIS",,"OS2ENVIRONMENT")
  83. if asciis="" then
  84.    asciis="TXT"
  85.  
  86. !history.0=0
  87. hostname=""
  88. phost=""
  89. origdir=directory()
  90. mode="ASCII"
  91. delay=20
  92. clobber=0
  93. hostlist.0=0
  94. prompt=1
  95. display=0
  96. unique=0
  97. quiet=0
  98. ask=0
  99. touch=0
  100. netrc=1
  101. visual=0
  102. macros=1
  103. retries=1
  104. longname=1
  105. comment=1
  106. bell=1
  107. remember=1
  108. dircmd="dir"
  109. viscmd="get"
  110. greenonblack=d2c(27)||"[1;32m"
  111. normvideo=d2c(27)||"[0m"
  112. inversevideo=d2c(27)||"[7m"
  113. boldvideo=d2c(27)||"[1m"
  114. promptstr='progname||" ["||boldvideo||phost||":"||dirname||normvideo"] "'
  115.  
  116. stem.0=1
  117. stem.1="No directory has been transferred - issue a ls or dir command."
  118.  
  119. pager=value('pager',,'os2environment')
  120. if pager="" then pager=more
  121.  
  122. parse source . . name
  123. parse value filespec('name',name) with progname".".
  124.  
  125. call ProcessINI filespec('drive',name)||filespec('path',name)||progname||'.ini' 
  126. memoryfile=value("ETC",,"OS2ENVIRONMENT")||"\MAXIFTP.DIR"
  127.  
  128. if translate(progname)=="VISFTP" then visual=1
  129.  
  130. if hostname="" | hostname="HOST.!NAME" then hostname=value("HOSTNAME",,"OS2ENVIRONMENT")
  131.  
  132. anonpass="os2user"||"@"||hostname
  133.  
  134. netrcfile=stream('netrc','c','query exists')
  135. if netrcfile="" then
  136.    netrcfile=value("ETC",,"OS2ENVIRONMENT")||"\NETRC"
  137.  
  138. if (pos(" N-",args)=0) & (pos(" M-",args)=0) then Call ProcessNETRC
  139.  
  140. call ProcessArgs args
  141.  
  142. if \quiet then call Credits
  143.  
  144. if (host="?") then
  145.    do
  146.    call Usage "INTRO"
  147.    exitcode=0
  148.    signal done
  149.    end
  150.  
  151. oq=quiet; 
  152. quiet=1
  153. call Mode mode
  154. quiet=oq
  155.  
  156. if pos("@",host)<>0 then
  157.    do
  158.    pass=user
  159.    parse var host user"@"host
  160.    end
  161.  
  162. request=""
  163.  
  164. if pos(":",host)<>0 then parse var host host":"request
  165. if pos('"',request)<>0 then parse var request '"'request'"'
  166.  
  167. if visual & (request="") then 
  168.    do
  169.    visual=visinit()
  170.    if visual then signal visftp
  171.    end
  172.  
  173. if host = "" then
  174.    do
  175.    say "Welcome to "version
  176.    say
  177.    say "Type HELP for more information and HELP NEWS for an overview"
  178.    say
  179.    call Usage "CONNECT"
  180.    say
  181.    end
  182.  
  183. if (host <> "") then
  184.    call Connect
  185.  
  186. if request<>"" then
  187.    do
  188.    if host="" then exit -1;
  189.    prompt=0
  190.    clobber=1
  191.    /* Reprocess args to handle different defaults for colon-mode */
  192.    oquiet=quiet
  193.    quiet=1
  194.    call ProcessArgs args
  195.    quiet=oquiet
  196.  
  197.    if display then call QueryStatus
  198.  
  199.    /* If request contains wildcards, do an mget */
  200.    if verify(request,"*?","MATCH")<>0 then
  201.       err=mget(request)
  202.    else err=get(request)
  203.    exitcode=err
  204.    signal done
  205.    end
  206.  
  207. do while (cmd <> "QUIT") & (cmd <> "EXIT") & (cmd <> "BYE")
  208.  
  209.  
  210. /* Modified so default directory is not queried if not connected to
  211.    a host.  This seemed rather pointless to me... */
  212.  
  213.  
  214.    if (host = "") then
  215.       do
  216.          status = "not connected to a host"
  217.          dir = ""
  218.          phost="not connected"
  219.       end
  220.    else status = "connected to" user"@"host
  221.  
  222. if display then Call DisplayStatus
  223.  
  224. /* Moved displaying the prompt until AFTER the status is displayed. */
  225.  
  226.    /*---------------------------------------------------------------
  227.     * print prompt
  228.     *---------------------------------------------------------------*/
  229.    Call SysCurState("ON")
  230.    if pos('"',dir)<>0 then parse var dir '"'dirname'"'.
  231.    else dirname=dir
  232.  
  233.    /*---------------------------------------------------------------
  234.     * get command
  235.     *---------------------------------------------------------------*/
  236.    if queued()=0 then 
  237.       do
  238.       interpret "Call Charout," promptstr
  239.       parse value CmdLine() with cmd cmdargs
  240.       end
  241.    else parse pull cmd cmdargs
  242.  
  243.    err=ProcessCMD(cmd, cmdargs)
  244.  
  245.    if (cmd="CD") | (cmd="CDUP") then
  246.       err = FtpPwd('dir')
  247.  
  248.    /*------------------------------------------------------------------
  249.     * check error
  250.     *------------------------------------------------------------------*/
  251.    /* Appears to be an error in error handling....  err may be -1 with
  252.       FTPERRNO of 0 */
  253.    if (err <> 0) & (ftperrno <>0) then
  254.       do
  255.       say "Error from FTP:" english(FTPERRNO) "["err"]"
  256.       if ftperrno="FTPCONNECT" then
  257.          do
  258.          say "Remote server closed connection."
  259.          host=""
  260.          phost=""
  261.          dir=""
  262.          end
  263.       end
  264.  
  265. end
  266.  
  267. /*------------------------------------------------------------------
  268.  * quit
  269.  *------------------------------------------------------------------*/
  270.  
  271. exitcode=0
  272.  
  273. done:
  274. rc = FtpSetUser("X","X","X")
  275. rc = FtpLogoff()
  276. call directory origdir
  277. /* Empty the queue in case any macro commands are pending. */
  278. do queued()
  279.    parse pull .
  280. end
  281. exit exitcode
  282.  
  283. /*****
  284.  * External error condition
  285. ******/
  286.  
  287. Error:
  288.    return
  289.  
  290. /*------------------------------------------------------------------
  291.  * break condition
  292.  *------------------------------------------------------------------*/
  293. halt:
  294.    say
  295.    call Charout, "Do you really want to quit (yes|no)? "
  296.    do until pos(translate(key),"YN")<>0
  297.       key=SysGetKey("NoECHO")
  298.    end
  299.    say key
  300.    if translate(key)="N" then return;
  301.    say "Terminating ..."
  302.    exitcode=-1
  303.    signal done
  304.  
  305. get: procedure expose debug binaries file2 clobber quiet touch dir longname comment host user bell visual asciis
  306.  
  307. parse arg remotefile, localfile, mode
  308.  
  309. curdir=dir
  310. globbed=0
  311. pipe=0
  312.  
  313. if remotefile=localfile then localfile=""
  314.  
  315. if translate(localfile)="CON" | translate(localfile)="CON:" then
  316.    localfile="-"
  317.  
  318. if verify(remotefile,"*?","MATCH")<>0 then /* It's a wildcard request */
  319.    do
  320.    err=FtpLS(remotefile,'files.')
  321.    if files.0=0 then
  322.       do
  323.       if \quiet then say "No files matching "remotefile" can be found."
  324.       return -2
  325.       end
  326.    remotefile=files.1
  327.    end
  328.  
  329. if wordpos(extension(remotefile),binaries)\=0 then
  330.    if mode="" then mode="BINARY"
  331.  
  332. if wordpos(extension(remotefile),asciis)\=0 then
  333.    if mode="" then mode="ASCII"
  334.  
  335. if remotefile="." then
  336.    do
  337.    remotefile=CmdLine("T=Remote file: ","No History")
  338.    if remotefile="" then return -1
  339.    localfile=CmdLine("T=Local file: ","No History")
  340.    end
  341.  
  342. if left(localfile,1)="|" then 
  343.    do 
  344.    pipe=1
  345.    destination=substr(localfile,2)
  346.    localfile=systempfilename("tmp?????")
  347.    end
  348.  
  349. if left(localfile,1)=">" then
  350.    do
  351.    say "Redirection not supported.  Use GET remotefile localfile instead."
  352.    return -2
  353.    end
  354.  
  355. ndir = filespec('drive',remotefile)||filespec('path',remotefile)
  356. if ndir<>"" then
  357.    do
  358.    err=FtpPWD('olddir')
  359.    err=FtpChDir(ndir)
  360.    err=FtpPWD('curdir')
  361.    if pos('"',olddir)<>"" then parse var olddir '"'olddir'"'.
  362.    if pos('"',curdir)<>"" then parse var curdir '"'curdir'"'.
  363.    end
  364. request=remotefile
  365. remotefile=filespec('name',remotefile)
  366.  
  367. err=FtpDir(remotefile,'files.')
  368. if files.0=0 then
  369.    do
  370.    if dir<>"" then junk=FtpChDir(olddir)
  371.    say "Remote file "request" does not exist."
  372.    return -2
  373.    end
  374.  
  375. if localfile="" then localfile=remotefile
  376.  
  377. if \pipe then
  378. do
  379. "dir "localfile" 1>nul 2>nul"
  380. if (rc=123) | (rc=206) then /* File name isn't valid... */
  381.    do
  382.    if left(localfile,1)="." then localfile="_"||substr(localfile,2)
  383.    if pos('.',localfile)<>0 then
  384.       localfile=translate(translate(strip(left(left(localfile,lastpos('.',localfile)-1),8)),'__','. ')||"."||strip(left(extension(localfile),3)))
  385.    else localfile=translate(translate(strip(left(localfile,8)),'__','. '))
  386.    if stream(localfile,'c','query exists')<>"" then
  387.       do
  388.       say "Unable to generate a unique local name for the transfer."
  389.       say "Specify a local name on the get command and try again."
  390.       return -1
  391.       end
  392.    globbed=1
  393.    end
  394.  
  395. if \quiet & localfile\="-" then say "Transferring "request" as "localfile" ..."
  396. else if \quiet then say "Contents of "request":"
  397. end
  398. else if \quiet then say "Piping "request" to "destination" ..."
  399.  
  400. file2=localfile
  401.  
  402. if (\clobber) & (stream(localfile,"C","QUERY EXISTS")<>"") then
  403.    if \visual then
  404.       do
  405.       call Charout, localfile || " already exists.  Replace (yes|no)? "
  406.       do until pos(translate(key),"YN")<>0
  407.          key=SysGetKey("NoECHO")
  408.       end
  409.       say key
  410.       if translate(key)="N" then return 1;
  411.       end
  412.    else if vmsg("File Exists",localfile || " already exists.  Replace?  ",6)=="NO" then return 1;
  413.  
  414. call time "r"
  415.  
  416. if mode\="" then
  417.    err=FtpGet(localfile,remotefile,mode)
  418. else err=FtpGet(localfile,remotefile)
  419.  
  420. elapsed = strip(format(time("e"),10,2))
  421. bytes = stream(localfile,"C","QUERY SIZE")
  422. if \quiet & (elapsed<>0) & (datatype(bytes)="NUM") then
  423.    say "Recieved" bytes "bytes in" elapsed "seconds:" strip(format(bytes/elapsed,10,2))  "bytes/second."
  424. if \quiet & bell then call beep 278,200
  425.  
  426. if ndir<>"" then junk=FtpChDir(olddir)
  427.  
  428. if \pipe then
  429. do
  430. if stream(localfile,"C","QUERY EXISTS")="" then return -2
  431. if touch then
  432.    call touch files.1, localfile
  433.  
  434. if globbed & longname then
  435.     call PutLong localfile, remotefile
  436.  
  437. if comment then
  438.    call PutComment localfile, user, host, curdir, remotefile
  439. end
  440.  
  441. if pipe then
  442.    do
  443.    call stream localfile,'C','CLOSE'
  444.    destination||"<"||localfile
  445.    call SysFileDelete localfile
  446.    end
  447.  
  448. return err
  449.  
  450. put: procedure expose debug binaries unique quiet bell visual
  451.  
  452. parse arg localfile, remotefile, lunique
  453.  
  454. if remotefile="" then remotefile=localfile
  455. if remotefile="" | remotefile="." then
  456.    do
  457.    say "You must specify a file name!"
  458.    return
  459.    end
  460. remotefile=filespec('name',remotefile)
  461. if \quiet then say "Transferring "localfile" as "remotefile" ..."
  462.  
  463. call time "r"
  464.  
  465. if (unique) | (lunique=1) then
  466.    if wordpos(extension(localfile),binaries)=0 then
  467.       err = FtpPutUnique(localfile,remotefile)
  468.    else err=FtpPutUnique(localfile,remotefile,"BINARY")
  469. else if wordpos(extension(localfile),binaries)=0 then
  470.       err = FtpPut(localfile,remotefile)
  471.    else err=FtpPut(localfile,remotefile,"BINARY")
  472.  
  473. elapsed = strip(format(time("e"),10,2))
  474. bytes = stream(localfile,"C","QUERY SIZE")
  475. if \quiet & (elapsed<>0) & (datatype(bytes)="NUM") then
  476.    say "Transmitted" bytes "bytes in" elapsed "seconds:" strip(format(bytes/elapsed,10,2))  "bytes/second."
  477.  
  478. if \quiet & bell then call beep 278,200
  479.  
  480. return err
  481.  
  482. extension: procedure
  483.  
  484. arg filename
  485. /* If no period or only period is first char, then return "" */
  486. if lastpos(".",filename)<2 then return ""
  487. return substr(right(filename,pos(".",reverse(filename))),2)
  488.  
  489. mget: procedure expose debug binaries file2 clobber quiet touch dir longname comment host user prompt bell visual
  490.  
  491. parse arg spec, dest
  492.  
  493. if dest\="" then 
  494.    if left(dest,1)\="|" & (dest\="-") then
  495.    do
  496.    say "Destination for MGET can only be a pipe."
  497.    return -1
  498.    end
  499.  
  500. if \quiet then say "Transferring file list..."
  501. if spec="" then spec="."
  502. err=FtpLS(spec,'files.')
  503. if \quiet then say files.0 "files requested."
  504. all=\prompt
  505. do i=1 to files.0
  506.    if prompt & \all then
  507.       do
  508.       if \visual then
  509.       do
  510.          Call Charout, "Get "files.i" (yes|no|all|quit)? "
  511.          do until pos(translate(key),"YNAQ")<>0
  512.             key=SysGetKey("NoEcho")
  513.          end
  514.          say key
  515.          key=translate(key)
  516.          select
  517.             when key="Y" | key="A" then
  518.                do
  519.                err=get(files.i)
  520.                if key="A" then all=\all
  521.                end
  522.             when key="Q" then leave
  523.             otherwise nop;
  524.          end
  525.       end
  526.       else if vmsg("Multiple File Request","Get "||files.i||"? ",6)=="YES" then err=get(files.i)
  527.       end
  528.    else err=get(files.i)
  529. end
  530. if \quiet then say "Transfer complete."
  531. return 0
  532.  
  533. mput: procedure expose debug prompt clobber unique quiet bell visual
  534.  
  535. parse arg spec
  536.  
  537. if \quiet then say "Obtaining file list..."
  538. if spec="" then spec="."
  539. if spec="." then spec="*"
  540. rc=SysFileTree(spec, "files.", "FO")
  541. if \quiet then say files.0 "files to be transferred."
  542. all=\prompt
  543. do i=1 to files.0
  544.    if prompt & \all then
  545.       do
  546.       if \visual then
  547.          do
  548.          Call Charout, "Put "files.i" (yes|no|all|quit)? "
  549.          do until pos(translate(key),"YNAQ")<>0
  550.             key=SysGetKey("NoEcho")
  551.          end
  552.          say key
  553.          key=translate(key)
  554.          select
  555.             when key="Y" | key="A" then
  556.                do
  557.                err=put(files.i)
  558.                if key="A" then all=\all
  559.                end
  560.             when key="Q" then leave
  561.             otherwise nop;
  562.          end
  563.          end
  564.       else if vmsg("Multiple File Request","Put "||files.i||"? ",6)=="YES" then err=get(files.i)
  565.       end
  566.    else err=put(files.i)
  567. end
  568. if \quiet then say "Transfer complete."
  569. return 0
  570.  
  571. english: procedure
  572.  
  573. arg code
  574.  
  575. select
  576.    when code="FTPSERVICE"     then phrase="unknown service"
  577.    when code="FTPHOST"        then phrase="unknown host"
  578.    when code="FTPSOCKET"      then phrase="unable to obtain socket"
  579.    when code="FTPCONNECT"     then phrase="unable to connect to server"
  580.    when code="FTPLOGIN"       then phrase="login failed"
  581.    when code="FTPABORT"       then phrase="transfer aborted"
  582.    when code="FTPLOCALFILE"   then phrase="problem openning local file"
  583.    when code="FTPDATACONN"    then phrase="problem initializing data connection"
  584.    when code="FTPCOMMAND"     then phrase="command failed"
  585.    when code="FTPPROXYTHIRD"  then phrase="proxy server does not support third party transfers"
  586.    when code="FTPNOPRIMARY"   then phrase="no primary connection for proxy transfer"
  587.    when code="0"              then phrase="no error (possibly unknown error)"
  588. otherwise
  589.    phrase="unknown error condition "||code||" - contact author"
  590. end
  591.  
  592. return phrase
  593.  
  594. /*------------------------------------------------------------------
  595.  * some help
  596.  *------------------------------------------------------------------*/
  597. Usage: procedure expose debug binaries version credits. pager
  598.  
  599. arg cmd .
  600.  
  601. select
  602.    when cmd="!" then say "!     issue a command to OS/2"
  603.    when cmd="CONNECT" | cmd="OPEN" then
  604.       do
  605.       say "connect host [user [password]]"
  606.       say "connect user@host [password]"
  607.       say "open host [user [password]]"
  608.       say "open user@host [password]"
  609.       say
  610.       say "Connect to a remote host.  If user is not specified, anonymous"
  611.       say "is selected as the default."
  612.       end
  613.    when cmd="USER"  then say "user      Open connection to current host for a [new] user"
  614.    when cmd="CLOSE" then say "close     Close the remote session"
  615.    when cmd="LCD"   then say "lcd       Change the local working directory"
  616.    when cmd="SITE"  then say "site command      Issue a site specific command to the remote host"
  617.    when cmd="VERSION"  then say "version      Display information about the version of MaxiFTP"
  618.    when cmd="QUOTE"  then say "quote command      execute the command on the remote host"
  619.    when cmd="REDIR" then say "redir     redisplay the last dir or ls results"
  620.    when cmd="PREDIR" then say "predir     redisplay the last dir or ls results through the pager"
  621.    when cmd="PAGER" then
  622.       do
  623.       say "By default, MaxiFTP uses MORE as the pager.  If you prefer a different pager, put it's"
  624.       say "name (and path information) in the PAGER environment variable."
  625.       end
  626.    when cmd="VISUAL" then
  627.       do
  628.       say "VISUAL"
  629.       say
  630.       say "Enter MaxiFTP's VISUAL FTP mode."
  631.       say
  632.       say "In visual mode, MaxiFTP presents a scrollable list of files and directories from the"
  633.       say "remote host in a PM dialog box.  You can choose a file to transfer or a directory to"
  634.       say "enter by clicking with your mouse.  Choosing CANCEL will exit MaxiFTP.  You can also"
  635.       say "issue any MaxiFTP command, and output will be displayed in the window where MaxiFTP"
  636.       say "started."
  637.       say
  638.       say "VISUAL mode may be selected by typing the command VISUAL at the MaxiFTP prompt, or"
  639.       say "starting MaxiFTP with the /V option.  If you rename MaxiFTP to VisFTP, VISUAL mode"
  640.       say "will be the default."
  641.       end
  642.    when (cmd="DIR") | (cmd="LS")  | (cmd="PDIR") then
  643.       do
  644.       say "dir [pattern] [|command]"
  645.       say "pdir [pattern]"
  646.       say "ls [pattern] [|command]"
  647.       say
  648.       say "  Display a directory listing for the remote host"
  649.       say "  The output can be piped into another program."
  650.       say "  ls displays a short listing, dir displays a long format listing"
  651.       say "  pdir displays a directory using the more command."
  652.       say "Examples:"
  653.       say "  dir"
  654.       say "  ls *.exe"
  655.       say '  dir "|head -20"'
  656.       end
  657.    when (cmd="TYPE") | (cmd="PAGE") | (cmd="MORE") then
  658.       do
  659.       say "type filename [|command]"
  660.       say "page filename"
  661.       say
  662.       say "Display a remote file on the screen."
  663.       say "The page command uses MORE as a pager.  The output from type can"
  664.       say "optionally be piped to another program."
  665.       end
  666.   when (cmd="GET") | (cmd="PUT") then
  667.       do
  668.       say "get remotefile [localfile]"
  669.       say "aget remotefile [localfile]"
  670.       say "bget remotefile [localfile]"
  671.       say "put localfile [remotefile]"
  672.       say "uput localfile [remotefile]"
  673.       say
  674.       say "GET transfers a file from a remote host to the PC."
  675.       say "AGET transfers a file from a remote host to the PC in ASCII mode."
  676.       say "BGET transfers a file from a remote host to the PC in BINARY mode."
  677.       say "PUT transfers a file from the PC to a remote host."
  678.       say "UPUT transfers a file from the PC to a remote host, ensuring a unique name."
  679.       say
  680.       say "There are two transfer modes - ASCII and BINARY.  You can set the"
  681.       say "default mode with the ASCII and BINARY commands.  This client will"
  682.       say "assume binary mode for the following extensions: "binaries
  683.       say
  684.       say "GET also ensures that the file transferred has a valid local name.  It creates"
  685.       say "a valid local name from the remote name if necessary."
  686.       end
  687.   when (cmd="RENAME") then  say "rename oldname newname       rename a remote file"
  688.   when (cmd="DELETE") then  say "delete filename     delete a remote file"
  689.   when (cmd="SYS") then  say "sys       Display information about remote host"
  690.   when (cmd="QUIT") | (cmd="BYE") | (cmd="EXIT") then say "quit      Leave MaxiFTP"
  691.   when (cmd="ASCII") then say "ascii     set default transfer mode to 7-bit ASCII"
  692.   when (cmd="BINARY") then say "binary    set default transfer mode to 8-bit BINARY"
  693.   when (cmd="PWD") then say "pwd       display remote working directory"
  694.   when (cmd="MD") then say "md       create a new directory on the remote host"
  695.   when (cmd="CDUP") then say "cdup       change to a the parent of the current remote working directory"
  696.   when (cmd="RD") then say "rd       remove an existing directory on the remote host"
  697.   when (cmd="APPEND") then say "append      transfer an ASCII file and append to an existing local file"
  698.   when (cmd="SHOW") | (cmd="QUERY") then say "show       display information about the current connection"
  699.   when (cmd="CREATE") then say "create      create an empty file on the remote host"
  700.   when (cmd="CD") then
  701.      do
  702.      say "cd directory"
  703.      say "directory"
  704.      say
  705.      say "Change to the specified directory on the remote host.  If you type a"
  706.      say "directory name by itself that does not match a MaxiFtp command,"
  707.      say "MaxiFtp assumes the CD command."
  708.      end
  709.   when (cmd="MGET")|(cmd="MPUT") then
  710.      do
  711.      say "mget filespec"
  712.      say "mput filespec"
  713.      say
  714.      say "Transfer a group of files at once."
  715.      say "If PROMPT is set (default), you will be asked to confirm each transfer."
  716.      say "The values of CLOBBER and UNIQUE will affect local and remote filenames."
  717.      end
  718.   when (cmd="NETRC") then
  719.      do
  720.      say "MaxiFTP will use the TCP/IP NETRC file to obtain information about a host"
  721.      say "and connections.  MaxiFTP uses the hostname, username, and password fields."
  722.      say "At present, it ignores any other values."
  723.      say
  724.      say "By default, when you issue a connection command, MaxiFTP will look for an"
  725.      say "entry in your NETRC file and use those values, unless you have specified"
  726.      say "different values at the command line or MaxiFTP prompt."
  727.      say
  728.      say "MaxiFTP looks for a NETRC file first in your current directory and then"
  729.      say "in the directory pointed to by the %etc% variable."
  730.      say
  731.      say "If you wish MaxiFTP to ignore the NETRC file, you can issue the"
  732.      say "TOGGLE NETRC command or place the -n- option on the command line or in the"
  733.      say "MAXIFTP environment variable."
  734.      end
  735.   when (cmd="TOGGLE") then
  736.      do
  737.      say "toggle [setting]"
  738.      say
  739.      say "where setting is one of the following:"
  740.      say
  741.      say "CLOBBER  controls whether or not MaxiFTP will automatically overwrite existing"
  742.      say "         files."
  743.      say "PROMPT   determines whether or not MaxiFTP will prompt during mget and mput."
  744.      say "DISPLAY  toggles displaying a status bar at the top of the screen."
  745.      say "UNIQUE   determines whether or not remote file targets during put will be"
  746.      say "         forced to be unique."
  747.      say "QUIET    determines whether messages will be displayed about command success"
  748.      say "         and failure."
  749.      say "TOUCH    make the timestamp on files transferred with get/mget match the remote"
  750.      say "         filesystem."
  751.      say "NETRC    use the NETRC file for information about a connection."
  752.      say "LONGNAME store the real name in the .LONGNAME extended attribute on a FAT"
  753.      say "         partition if the name is globbed."
  754.      say "COMMENT  store information in the .COMMENT extended attribute."
  755.      say "BELL     ring a bell after connecting and after a transfer."
  756.      end
  757.   when (cmd="COLON-MODE") then
  758.      do
  759.      say "MaxiFTP implements a simple, automated FTP transfer.  You can invoke MaxiFTP"
  760.      say "with a colon in the host name, followed by the name of the file to transfer."
  761.      say "MaxiFTP will attempt (one time) to connect to the remote host, transfer the "
  762.      say "file, and close the connection."
  763.      say "The filename can include a wildcard.  CLOBBER and NOPROMPT are assumed"
  764.      say "for automated transfers."
  765.      say "You can also specify a user name with or without a password to identify the"
  766.      say "user account on the remote host."
  767.      end
  768.   when (cmd="TOUCH") then
  769.      do
  770.      say "MaxiFTP has a special option called TOUCH that is not included in most FTP"
  771.      say "clients.  In TOUCH mode, MaxiFTP will attempt to make the timestamp on a"
  772.      say "transferred file match the timestamp on the remote system."
  773.      say
  774.      say "This requires having a TOUCH program compatible with the Unix TOUCH command"
  775.      say "installed on your system.  One source for such a program is the GNU File"
  776.      say "Utilities for OS/2 available from ftp-os2.cdrom.com and other anonymous"
  777.      say "FTP sites."
  778.      say
  779.      end
  780.   when (cmd="NEWS") then
  781.      do
  782.      parse source . . name
  783.      news=filespec('drive',name)||filespec('path',name)||"maxiftp.new"
  784.      if stream(news,"C","QUERY EXISTS")<>"" then
  785.         pager " <"news
  786.      else say "No news available."
  787.      end
  788.   when (cmd="CREDITS") then
  789.      do
  790.      say version credits.1
  791.      say
  792.      say "MaxiFTP is based on the MiniFTP client included with the RxFTP package"
  793.      say "from the IBM Employee Written Software Program (EWS)."
  794.      say
  795.      say "Special thanks to Al Dhir for suggesting several features."
  796.      say
  797.      say "My warmest thanks go to Micheal Gleason, author of the NcFTP program"
  798.      say "for Unix.  His program is the inspiration for many of the features found"
  799.      say "in MaxiFTP, and, with his permission, the manual for MaxiFTP is based upon"
  800.      say "the NcFTP manual."
  801.      say
  802.      say "Other ideas for MaxiFTP features have came from the OS/2 internet community."
  803.      say "If you have any suggestions, please mail them to me!"
  804.      say
  805.      say "Albert Crosby - acrosby@comp.uark.edu"
  806.      end
  807.   when (cmd="PROMPT") then
  808.      do
  809.      say "The MaxiFTP prompt can be customized.  The command to use is:"
  810.      say "    set prompt=promptstring"
  811.      say
  812.      say "Special information can be inserted in the prompt using the following"
  813.      say "codes:"
  814.      say "@D         Inserts the current remote directory."
  815.      say "@L         Inserts the current local directory."
  816.      say "@H         Inserts the name of the remote host."
  817.      say "@0         Inserts the name of the calling program."
  818.      say "@B         Turns on boldface mode."
  819.      say "@Cfb       Set color to foreground f on background b"
  820.      say "@I or @R   Turns on inverse (reverse) video mode."
  821.      say "@N         Inserts a newline character."
  822.      say "@P         Turns off any video modes you might have set with @B, @I, or @R"
  823.      say "@T         Inserts the current time."
  824.      say '@S         Inserts the current connection status.'
  825.      say "@_         Inserts a blank."
  826.      say "@@         Inserts an at sign."
  827.      say "@E         Inserts the most recent FTP Error code."
  828.      say
  829.      say "The default prompt is  set prompt=@0@S[@B@H:@D@P]@S"
  830.      say
  831.      say "See MaxiFTP.MAN for more information."
  832.      end
  833.   when (cmd="COLORS") then
  834.      do
  835.      say "The @C code in the SET PROMPT command uses colors in the following"
  836.      say "fashion:"
  837.      say
  838.      say " @Cfb Set color to foreground f on background b; where f and b"
  839.      say "      are numbers between 0 and 7.  0 is black; 1 is red; 2 is green; 3"
  840.      say "      is yellow; 4 is blue; 5 is magenta; 6 is cyan; 7 is white."
  841.      end
  842.   when (cmd="OPTIONS") then
  843.      do
  844.      say "MaxiFTP has several command line options which can be specified when starting"
  845.      say "the program, preceeded by a dash ('-') or in the environment variable MAXIFTP."
  846.      say "These are:"
  847.      say
  848.      say " option  type     desc"
  849.      say " ------  -------  -----------------------------------------"
  850.      say " b                default to binary file transfer mode"
  851.      say " i                ignore prompts (assumes you know what you're doing)"
  852.      say " a       string   anonymous ftp password"
  853.      say " h       string   display help on a topic"
  854.      say " q       t/f      quiet mode"
  855.      say " d       t/f      display the status banner"
  856.      say " c       t/f      set CLOBBER mode"
  857.      say " p       t/f      set PROMPT mode"
  858.      say " u       t/f      set UNIQUE mode"
  859.      say " t       t/f      set TOUCH mode"
  860.      say " n       t/f      use/do not use NETRC for connection info"
  861.      say " l       string   set the default local directory"
  862.      say ' m       t/f      use "#macros" from netrc file'
  863.      say ' x       t/f      use rxSock to determine hostname'
  864.      say " r       n:s      Retry N times with S second pause between tries"
  865.      say
  866.      say "The t/f (true/false) options default to true.  If followed by a '-',"
  867.      say "they set the value to false.  The string options must be followed"
  868.      say "immeadiately by a string (with no blanks) that sets the value."
  869.      end
  870. otherwise
  871.    say version credits.1
  872.    say
  873.    say "   Commands available are:"
  874.    say "       !       cdup     dir   mget    put    redir   toggle   set"
  875.    say "       ?       close    get   open    pwd    rename  type     echo"
  876.    say "       append  connect  help  page    quit   show    uput     aget"
  877.    say "       ascii   create   ls    pdir    quote  site    version  bget"
  878.    say "       cd      delete   md    predir  rd     sys     lcd      addhost"
  879.    say
  880.    say "Additional topics:  colon-mode  intro  options  touch  netrc credits"
  881.    say "                    prompt"
  882.    say
  883.    say "Enter HELP COMMAND for more information or HELP NEWS for news."
  884.    say "Refer to MaxiFTP.MAN for additionl information on MaxiFTP."
  885.    if cmd="INTRO" then
  886.       do
  887.       say
  888.       say "MaxiFTP may be called with [user@]host:filename to transfer a file and exit."
  889.       say
  890.       say "See MaxiFTP.NEW for additional information or the online help."
  891.       say
  892.       say "Syntax: "
  893.       say "   MaxiFTP [-options] [host[:filename] [user [password]]]"
  894.       say "   MaxiFTP [-options] [user@host[:filename] [password]]"
  895.       end
  896. end
  897.  
  898.    return 0
  899.  
  900. displaystatus: procedure expose debug host dir rows cols mode clobber prompt status unique quiet touch bell
  901.  
  902.    /*---------------------------------------------------------------
  903.     * print status
  904.     *---------------------------------------------------------------*/
  905.    say
  906.    say
  907.    say
  908.    parse value SysCurPos(0,0) with row col
  909.  
  910.    do i = 1 to 5
  911.       call SysCurPos i-1, 0
  912.       say copies(" ",cols)
  913.    end
  914.    call SysCurPos 0,0
  915.  
  916.  
  917.    say d2c(218) || copies(d2c(196),cols-3) || d2c(191)
  918.    say d2c(179) || left(" MaxiFtp: "||time()||" "||status,cols-3) || d2c(179)
  919.    say d2c(179) || left(" directory: "||dir,cols-3) || d2c(179)
  920.    line=" " || left(mode,7)
  921.    if clobber then line=line||"CLOBBER   "
  922.    else line=line||"NOCLOBBER "
  923.    if prompt then line=line||"PROMPT   "
  924.    else line=line||"NOPROMPT "
  925.    if quiet then line=line||"QUIET   "
  926.    if touch then line=line||"TOUCH   "
  927.    if unique then line=line||"UNIQUE"
  928.    say d2c(179) || left(line,cols-3) || d2c(179)
  929.    say d2c(192) || copies(d2c(196),cols-3) || d2c(217)
  930.  
  931. if row>6 then call SysCurPos row-2, col
  932. else Call SysCurPos 5, col
  933.  
  934. return
  935.  
  936. querystatus:
  937. arg varname
  938.  
  939. if varname\="" then
  940.   do
  941.   do i=1 to words(varname)
  942.   say "Value of "word(varname,i)":"
  943.   interpret 'say "   "'word(varname,i)
  944.   end
  945.   return
  946.   end
  947.  
  948. say version credits.1
  949. vsn="Junk"
  950. junk=FtpVersion('vsn')
  951. say "rxFTP version "vsn
  952. if host<>"" then
  953.    do
  954.    say "Remote host: "host
  955.    say "Remote directory: "dir
  956.    say "Remote system type: "sys
  957.    end
  958. else say "Not connected to a remote host."
  959. say "Default transfer mode: "mode
  960. say "Local directory: "directory()
  961. say "Anonymous password: "anonpass
  962. say "Pager: "pager
  963. say "Prompt: "promptstr
  964. say "NETRC file: "netrcfile
  965. if clobber then
  966.    say "CLOBBER is ON (local files are overwritten)"
  967. else say "CLOBBER is OFF (local files are not overwritten)"
  968. if prompt then
  969.    say "MPROMPT is ON (you will be prompted during mget and mput"
  970. else say "MPROMPT is OFF (you will not be prompted during mget and mput"
  971. if display then
  972.    say "DISPLAY is ON (status bar is displayed)"
  973. else say "DISPLAY is OFF (status bar is not displayed)"
  974. if unique then
  975.    say "UNIQUE is ON (remote files during put will be forced to unique names)"
  976. else say "UNIQUE is OFF (remote files during put will not be forced to unique names)"
  977. if quiet then
  978.    say "QUIET is ON (messages will not be displayed)"
  979. else say "QUIET is OFF (messages will be displayed)"
  980. if touch then
  981.    say "TOUCH is ON (files received will be stamped with the remote timestamp)"
  982. else say "TOUCH is OFF (files received will not be stamped with the remote timestamp)"
  983. if netrc then
  984.    say "NETRC is ON (the NETRC file will be searched for connection info)"
  985. else say "NETRC is OFF (the NETRC file will not be searched for connection info)"
  986. if macros then
  987.    say "MACROS is ON (NETRC macros will be processed for hosts in the NETRC)"
  988. else say "MACROS is OFF (NETRC macros will not be processed for hosts in the NETRC)"
  989. if visual then
  990.    say "VISUAL mode is ON (VREXX will be used for directory lists & dialogs)"
  991. else say "VISUAL mode is OFF (VREXX will not be used.)"
  992. if remember then
  993.    say "REMEMBER is ON (Last directory for this host will be kept for next connect.)"
  994. else say "REMEMBER is OFF (Last directory for this host will not be kept for next connect.)"
  995. say "File types automatically transferred in binary mode:"
  996. say "   "binaries
  997. say "File types automatically transferred in ASCII mode:"
  998. say "   "asciis
  999.  
  1000. return
  1001.  
  1002. connect: procedure expose debug host user pass phost anonpass quiet netrc sys dir retries delay bell visual needdir netrcfile hostlist. hostnetrcline memoryfile remember ask
  1003.  
  1004. if visual then needdir=1
  1005. if (host="") then
  1006.    if visual then 
  1007.       do
  1008.       host=VListNETRC()
  1009.       if host="" | (ask) then if GetHost()="CANCEL" then return -2
  1010.       end
  1011.    else
  1012.       do
  1013.       call Usage "CONNECT"
  1014.       return -1
  1015.       end
  1016. if netrc then Call LookUpNETRC hostnetrcline
  1017. do attempt=1 to retries
  1018.          if pos("@",host)<>0 then
  1019.             do
  1020.             pass=user
  1021.             parse var host user"@"host
  1022.             end
  1023.          if pos(":",host)<>0 then
  1024.             do
  1025.             end
  1026.          if netrc & (newhost<>"") then host=newhost
  1027.          if netrc & (user="") & (newuser<>"") then
  1028.             do
  1029.             user=newuser
  1030.             pass=newpass
  1031.             end
  1032.          if (user="") then user="anonymous"
  1033.          if (translate(user)="#ASK") | ((ask) & (\visual)) then
  1034.             do
  1035.             newuser=cmdline("T=Enter the username: ","D="||user,"No History")
  1036.             if newuser\="" then
  1037.                do
  1038.                pass=""
  1039.                user=newuser
  1040.                end
  1041.             end
  1042.          if (translate(user)="ANONYMOUS") & (pass="") then pass=anonpass
  1043.          if (translate(user)<>"ANONYMOUS") & (pass="") then
  1044.             if visual then call GetHost
  1045.             else
  1046.                do
  1047.                call charout ,"Enter the password: "
  1048.                pass=CmdLine("hidden")
  1049.                end
  1050.          if pos(".",host)="" then phost=host
  1051.          else parse var host phost".".
  1052.          if \quiet then say "Connecting to "user"@"host"..."
  1053.          call FtpSetUser host, user, pass
  1054.          sys=""
  1055.          err = FtpSYS('sys')
  1056.          if err<>0 & FTPERRNO<>0 & FTPERRNO<>"FTPCOMMAND" then
  1057.             do
  1058.             if attempt=retries then
  1059.                do;
  1060.                if \quiet then say "Connection failed: "english(FTPERRNO)
  1061.                host=""
  1062.                phost=""
  1063.                user=""
  1064.                do queued()
  1065.                   parse pull .
  1066.                end
  1067.                leave
  1068.                end
  1069.             else
  1070.                do
  1071.                if \quiet then say "Connection failed: "english(FTPERRNO) "retrying... (try "attempt")"
  1072.                call SysSleep Delay
  1073.                iterate
  1074.                end
  1075.             end
  1076.          if word(sys,1)="VM" then
  1077.             do
  1078.             if visual then
  1079.                do
  1080.                say "Prompting for ACCOUNT"
  1081.                p.0=1
  1082.                p.1="Enter the account password:"
  1083.                w.0=1
  1084.                w.1=30
  1085.                h.0=1
  1086.                h.1=1
  1087.                r.0=0
  1088.                r.1=''
  1089.                button=VMultBox("Remote system is VM",'p','w','h','r',2)
  1090.                if button='OK' then account=r.1
  1091.                end
  1092.             else
  1093.             do;
  1094.             say "Remote system is VM/CMS.  Please enter the minidisk password:"
  1095.             account=CmdLine("hidden")
  1096.             end
  1097.             call FtpSetUser host, user, pass, account
  1098.             end
  1099.          err = FtpPWD('dir')
  1100.          if err<>0 & FTPERRNO<>0 then
  1101.             do
  1102.             if attempt=retries then
  1103.                do;
  1104.                say "Connection failed: "english(FTPERRNO)
  1105.                host=""
  1106.                phost=""
  1107.                user=""
  1108.                do queued()
  1109.                   parse pull .
  1110.                end
  1111.                end
  1112.             else
  1113.                do
  1114.                if \quiet then say "Connection failed: "english(FTPERRNO) "retrying... (try "attempt")"
  1115.                call SysSleep Delay
  1116.                end
  1117.             end
  1118.          else 
  1119.             do
  1120.             if \quiet & bell then call beep 278,200
  1121.             Call Remember_Dir
  1122.             leave;
  1123.             end
  1124. end
  1125. return  err
  1126.  
  1127. toggle: procedure expose debug prompt clobber display unique quiet touch netrc longname comment bell remember ask macros
  1128. arg toggles
  1129.  
  1130. do i=1 to words(toggles)
  1131. toggle=word(toggles,i)
  1132. select
  1133.    when (toggle="PROMPT") | (toggle="MPROMPT") then prompt=\prompt
  1134.    when toggle="CLOBBER" then clobber=\clobber
  1135.    when toggle="STATUS" | toggle="DISPLAY" then display=\display
  1136.    when toggle="UNIQUE" then unique=\unique
  1137.    when toggle="QUIET" then quiet=\quiet
  1138.    when toggle="TOUCH" then touch=\touch
  1139.    when toggle="NETRC" then netrc=\netrc
  1140.    when toggle="COMMENT" then comment=\comment
  1141.    when toggle="LONGNAME" then longname=\longname
  1142.    when toggle="BELL" then bell=\bell
  1143.    when toggle="REMEMBER" then remember=\remember
  1144.    when toggle="ASK" then ask=\ask
  1145. otherwise
  1146.    say "Unknown option: "toggle
  1147.    return
  1148. end
  1149. end
  1150. return
  1151.  
  1152. processargs:
  1153.  
  1154. parse arg args
  1155.  
  1156. do until length(args)=0
  1157.    tf=1
  1158.    key=translate(left(args,1))
  1159.    args=substr(args,2)
  1160.    if key=" " then iterate
  1161.    if key='"' then
  1162.       do
  1163.       parse var args '"'.'"'args
  1164.       iterate
  1165.       end
  1166.    if left(args,1)="-" then
  1167.       do
  1168.       tf=0
  1169.       args=substr(args,2)
  1170.       end
  1171.    select
  1172.       when key="B" then if tf then mode="BINARY" else mode="ASCII"
  1173.       when key="I" then /* Know what you're doing... */
  1174.          do
  1175.          prompt=0
  1176.          clobber=1
  1177.          end
  1178.       when key="A" then /* Anonymous password */
  1179.          if left(args,1)<>" " then
  1180.             do
  1181.             if left(args,1)='"' then
  1182.                parse var args '"'anonpass'"'args
  1183.             else
  1184.                do
  1185.                anonpass=subword(args,1,1)
  1186.                args=subword(args,2)
  1187.                end
  1188.             end
  1189.       when key="R" then /* speficy retries[:delay] */
  1190.          do
  1191.          time=subword(args,1,1)
  1192.          args=subword(args,2)
  1193.          parse var time retries":"delay
  1194.          if \datatype(delay,"W") then delay=60
  1195.          if \datatype(retries,"W") then retries=1
  1196.          end
  1197.       when key="L" then /* change local directory */
  1198.          if left(args,1)<>" " then
  1199.             do
  1200.             if left(args,1)='"' then
  1201.                parse var args '"'ldir'"'args
  1202.             else
  1203.                do
  1204.                ldir=subword(args,1,1)
  1205.                args=subword(args,2)
  1206.                end
  1207.             call directory ldir
  1208.             end
  1209.       when key="H" |key="?" then /* Help */
  1210.          do
  1211.          if \quiet then call Credits
  1212.          if left(args,1)<>" " then
  1213.             do
  1214.             if left(args,1)='"' then
  1215.                parse var args '"'topic'"'args
  1216.             else
  1217.                do
  1218.                topic=subword(args,1,1)
  1219.                args=subword(args,2)
  1220.                end
  1221.             Call Usage topic
  1222.             end
  1223.          else if host<>"" then call Usage host
  1224.          else Call Usage "INTRO"
  1225.          exitcode=0
  1226.          signal done
  1227.          end
  1228.       when key="U" then unique=tf
  1229.       when key="C" then clobber=tf
  1230.       when key="P" then prompt=tf
  1231.       when key="D" then display=tf
  1232.       when key="Q" then quiet=tf
  1233.       when key="T" then touch=tf
  1234.       when key="N" then netrc=tf
  1235.       when key="X" then nop;
  1236.       when key="K" then ask=tf
  1237.       when key="M" then macros=tf;
  1238.       when key="V" then 
  1239.          do
  1240.          visual=tf
  1241.          if visual then visual=1
  1242.          end
  1243.    otherwise
  1244.       if \quiet then say "Invalid option: "key
  1245.    end
  1246. end
  1247.  
  1248. return
  1249.  
  1250. Credits:
  1251.  
  1252. do
  1253.    call charout, greenonblack
  1254.  
  1255.    if translate(progname)<>"MAXIFTP" then say name "is ..."
  1256.    say version
  1257.    do i=1 to credits.0
  1258.    say credits.i
  1259.    end
  1260.    junk=FtpVersion('vsn')
  1261.    say "Using RxFTP version "vsn normvideo
  1262. end
  1263. return
  1264.  
  1265. LoadFunctions:
  1266.  
  1267. /*------------------------------------------------------------------
  1268.  * load functions, if needed
  1269.  *------------------------------------------------------------------*/
  1270. if RxFuncQuery("FtpLoadFuncs") then
  1271.    do
  1272.    rc = RxFuncAdd("FtpLoadFuncs","RxFtp","FtpLoadFuncs")
  1273.    if rc\=0 then
  1274.       do
  1275.       say "Error loading the rxFtp package.  rxFtp is an extension to REXX"
  1276.       say "that allows it to utilize the FTP protocol and API. You can"
  1277.       say "obtain rxFTP via anonymous FTP from software.watson.ibm.com or"
  1278.       say "ftp-os2.cdrom.com."
  1279.       end
  1280.    rc = FtpLoadFuncs()
  1281.    end
  1282.  
  1283. if RxFuncQuery("SysLoadFuncs") then
  1284.    do
  1285.    rc = RxFuncAdd("SysLoadFuncs","RexxUtil","SysLoadFuncs")
  1286.    rc = SysLoadFuncs()
  1287.    end
  1288.  
  1289. if pos(" X-",translate(args))=0 & RxFuncQuery("SockLoadFuncs") then
  1290.    do
  1291.    rc = RxFuncAdd("SockLoadFuncs","RxSock","SockLoadFuncs")
  1292.    if rc\=0 then
  1293.       hostname=value("HOSTNAME",,"OS2ENVIRONMENT")
  1294.    else 
  1295.       do
  1296.        rc = SockLoadFuncs()
  1297.        if rc=0 then
  1298.           do
  1299.           addr = SockGetHostId()
  1300.           rc= SockGetHostByAddr(addr,"host.!")
  1301.           hostname=host.!name
  1302.           end
  1303.        else hostname=value("HOSTNAME",,"OS2ENVIRONMENT")
  1304.        end
  1305.    end
  1306. else hostname=value("HOSTNAME",,"OS2ENVIRONMENT")
  1307.  
  1308. return
  1309.  
  1310. touch: procedure
  1311.  
  1312. parse arg direntry, file
  1313.  
  1314. sys=""
  1315. err=FtpSYS('sys')
  1316. sys=word(sys,1)
  1317.  
  1318. if right(file,1)=":" then return 0
  1319. if wordpos(translate(file),"NUL CON LPT1 LPT2 LPT3 LPT4 PRN -") then return 0
  1320.  
  1321. select
  1322.    when sys="Netware" then
  1323.       do
  1324.       parse var direntry . 42 time 57 .
  1325.       time=subword(time,1,2)||", "||subword(time,3)
  1326.       end
  1327.    when sys="Windows_NT" | sys="UNIX" | left(direntry,1)="-" then
  1328.       do
  1329.       time=subword(direntry,words(direntry)-3,3)
  1330.       if pos(":",time)=0 then
  1331.          time=subword(time,1,2)||", "||subword(time,3)
  1332.       end
  1333.    when sys="OS/2" then
  1334.       do;
  1335.       parse var direntry . 36 time 52 .
  1336.       months="Jan Feb Mar Apr May June July Aug Sep Oct Nov Dec"
  1337.       parse var time month"-"day"-"year time
  1338.       time=word(months,month)||" "||day||", "||year||" "||time
  1339.       end
  1340.    when sys="Windows_NT" then parse var direntry . 45 time 57 .
  1341.    when sys="VM" then parse var direntry . 54 time 71 .
  1342. otherwise
  1343.    say "Cannot determine timestamp for operating system "SYS
  1344.    say "If you would like to have TOUCH updated for this OS, contact the author"
  1345.    say "with the OS information and the contents of the next line."
  1346.    say direntry
  1347.    say file
  1348.    return -1
  1349. end
  1350.  
  1351. 'touch -d "'strip(time)'"' file
  1352. if rc=1041 then say "The TOUCH mode requires a TOUCH program compatible with the UNIX touch command."
  1353. if rc<>0 then say "TOUCH appears to have failed.  You may want to contact the author for assistance."
  1354.  
  1355. return 0
  1356.  
  1357. create: procedure expose debug quiet bell
  1358.  
  1359. parse arg fn
  1360.  
  1361. if \quiet then say "Creating message "fn
  1362. tempfile=systempfilename("tmp?????")
  1363. call lineout tempfile
  1364. call stream tempfile,'C','CLOSE'
  1365. oq=quiet
  1366. quiet=1
  1367. unique=0
  1368. binaries=""
  1369. err=put(tempfile,fn)
  1370. quiet=oq
  1371. call SysFileDelete tempfile
  1372. return 1
  1373.  
  1374. lookupnetrc: procedure expose debug host newhost newuser newpass netrcfile hostlist.
  1375.  
  1376. newhost=""
  1377. newuser=""
  1378. newpass=""
  1379.  
  1380. if host="" then return 0
  1381.  
  1382. do i=1 to hostlist.0
  1383.    if \abbrev(translate(word(hostlist.i,1)),translate(host)) then iterate
  1384.    host=word(hostlist.i,1)
  1385.    leave
  1386. end
  1387.  
  1388. if SysFileSearch(host, netrcfile,'hits.','N')<>0 then
  1389.    return -1
  1390. if hits.0=0 then return 0
  1391. if datatype(arg(1),'W') then hits.1=arg(1)
  1392. do i=1 to word(hits.1,1)-1
  1393.    call linein netrcfile
  1394. end
  1395.  
  1396. found=0
  1397. do while (stream(netrcfile,'s')="READY")
  1398.    line=linein(netrcfile)
  1399.    if \found & (left(line,1)="#") | (left(line,1)=";") | (length(line)=0) then iterate
  1400.    if (substr(line,2,1)="#") | (substr(line,2,1)=";") then iterate
  1401.    tline=translate(line)
  1402.    if \found & pos('MACHINE',tline)=0 then iterate;
  1403.    if pos("MACHINE",tline)<>0 then
  1404.       if found then leave
  1405.       else machinename=word(line,wordpos("MACHINE",tline)+1)
  1406.    if \abbrev(translate(machinename),translate(host)) then iterate
  1407.    found=1
  1408.    if (left(line,1)<>"#") & (left(line,1)<>";") then
  1409.       do
  1410.       if pos("LOGIN",tline)<>0 then newuser=word(line,wordpos("LOGIN",tline)+1)
  1411.       if pos("PASSWORD",tline)<>0 then newpass=word(line,wordpos("PASSWORD",tline)+1)
  1412.       end
  1413.    else
  1414.       do
  1415.       parse var line 2 cmd cmdargs
  1416.       Queue cmd cmdargs
  1417.       end
  1418. end
  1419. call Stream netrcfile, 'c', 'close'
  1420. if found then newhost=machinename
  1421. return found
  1422.  
  1423. dir: procedure expose stem. pager sys host dir phost status quiet bell visual dircmd
  1424.  
  1425. parse arg cmd, mask, dest
  1426.  
  1427.    if cmd="" then cmd="DIR"
  1428.    if mask="" then mask="."
  1429.  
  1430.    if ((cmd="DIR")|(cmd="PDIR")) & left(mask,1)<>"-" then
  1431.       do
  1432.       if cmd="PDIR" then cmd="P"||translate(word(dircmd,1))
  1433.       else cmd=translate(word(dircmd,1))
  1434.       mask=subword(dircmd,2) mask
  1435.  
  1436.       end
  1437.  
  1438.    err=0
  1439.    select
  1440.    when (cmd="LS") | (cmd="PLS") then
  1441.       if (mask=".") & (pos("UNIX",translate(sys))<>0)
  1442.          then err=FtpLS("-CF",'stem.')
  1443.          else err=FtpLS(mask,'stem.')
  1444.    when (cmd="DIR")| (cmd="PDIR") then err=FtpDIR(mask,'stem.')
  1445.    when (cmd='ls') then err=FtpLS(mask,'stem.')
  1446.    when (cmd="REDIR") | (cmd="PREDIR") then nop;
  1447.    otherwise return -1
  1448.    end
  1449.  
  1450.    if (left(cmd,1)<>"P") & (left(dest,1)<>'|') & (left(dest,1)<>'>') then
  1451.       do i = 1 to stem.0
  1452.          say stem.i
  1453.       end
  1454.    else
  1455.       do
  1456.       if left(cmd,1)="P" then dest="|"||pager
  1457.       tempfile=systempfilename("ftp?????")
  1458.       if left(dest,2)='>>' then tempfile=substr(dest,3)
  1459.       else if left(dest,1)='>' then
  1460.          do;
  1461.          tempfile=substr(dest,2)
  1462.          call SysFileDelete tempfile
  1463.          end;
  1464.       do i = 1 to stem.0
  1465.          call lineout tempfile, stem.i
  1466.       end
  1467.       if left(dest,1)="|" then substr(dest,2) "<" tempfile
  1468.       call stream tempfile,'C','CLOSE'
  1469.       if left(dest,1)<>'>' then call SysFileDelete tempfile
  1470.       end
  1471.    if (err <> 0) & (ftperrno <>0) then
  1472.       do
  1473.       say "Error from FTP:" english(FTPERRNO)
  1474.       if ftperrno="FTPCONNECT" then
  1475.          do
  1476.          say "Remote server closed connection."
  1477.          host=""
  1478.          phost=""
  1479.          dir=""
  1480.          end
  1481.       end
  1482. return err
  1483.  
  1484. ProcessCMD:
  1485.  
  1486. parse arg cmd, cmdargs
  1487.  
  1488.    if cmd="" then return 0
  1489.  
  1490.    if left(cmdargs,1)='"' then
  1491.       parse var cmdargs '"'file1'"' rest
  1492.    else parse var cmdargs file1 rest
  1493.    if left(cmdargs,1)='"' then
  1494.       parse var rest '"'file2'"' rest
  1495.    else parse var rest file2 rest
  1496.  
  1497.    if (left(file1,1)="|") then
  1498.       do
  1499.       rest=file2||" "||rest
  1500.       file2=file1
  1501.       file1=""
  1502.       end
  1503.    if (left(file1,1)=">") then
  1504.       do
  1505.       rest=file2||" "||rest
  1506.       file2=file1
  1507.       file1=""
  1508.       end
  1509.    if (file1 = "") then file1 = "."
  1510.    if (file2 = "") then file2 = file1
  1511.  
  1512.    /*------------------------------------------------------------------
  1513.     * sanity check
  1514.     *------------------------------------------------------------------*/
  1515.    origcmd=cmd
  1516.    cmd = translate(cmd)
  1517.  
  1518.    if (host="") & (left(cmd,1)<>"!") & (wordpos(cmd,"CONNECT FTP OPEN VERSION ? HELP QUIT TOGGLE SHOW QUERY ASCII LCD SET BINARY VISUAL ECHO CREDITS ADDHOST")=0) then
  1519.       do
  1520.       say "You have not provided host, userid, and password information."
  1521.       say "Use the CONNECT command to provide this information."
  1522.       return 0
  1523.       end
  1524.  
  1525.    /*---------------------------------------------------------------
  1526.     * run command
  1527.     *---------------------------------------------------------------*/
  1528.    err = 0
  1529.    select
  1530.       when left(cmd,1)="!"   then
  1531.          do
  1532.          if cmd="!" then value("COMSPEC",,"OS2ENVIRONMENT")
  1533.          else substr(origcmd,2) cmdargs
  1534.          end
  1535.       when (cmd = "MGET")    then  err=mget(file1)
  1536.       when (cmd = "ECHO")    then say cmdargs
  1537.       when (cmd = "MPUT")    then err=mput(file1)
  1538.       when (cmd = "QUIT") | (cmd="BYE") | (cmd="EXIT") then
  1539.          do
  1540.          call Update_Memory
  1541.          return 0
  1542.          end
  1543.       when (cmd = "SET")     then Call Set cmdargs
  1544.       when (cmd = "BINARY")  then err = Mode("BINARY")
  1545.       when (cmd = "ASCII")   then err = Mode("ASCII")
  1546.       when (cmd = "GET")     then err = Get(file1,file2)
  1547.       when (cmd = "AGET")     then err = Get(file1,file2,"ASCII")
  1548.       when (cmd = "BGET")     then err = Get(file1,file2,"BINARY")
  1549.       when (cmd = "PUT")     then err = Put(file1,file2)
  1550.       when (cmd = "UPUT")    then err = Put(file1,file2,1)
  1551.       when (cmd = "CREATE")  then err = Create(file1)
  1552.       when (cmd = "DELETE")  then err = FtpDelete(file1)
  1553.       when (cmd = "RENAME")  then err = FtpRename(file1,file2)
  1554.       when (cmd = "APPEND")  then err = FtpAppend(file1,file2,"ASCII")
  1555.       when (cmd = "MODE")    then err = Mode(file1)
  1556.       when (cmd = "QUOTE")   then err = FtpQuote(cmdargs)
  1557.       when (cmd = "SITE")    then err = FtpSite(cmdargs)
  1558.       when (cmd = "CD")      then err = FtpChDir(file1)
  1559.       when (cmd = "CDUP")    then err = FtpChDir('..')
  1560.       when (cmd = "MD")      then err = FtpMkDir(file1)
  1561.       when (cmd = "RD")      then err = FtpRmDir(file1)
  1562.       when (cmd = "CREDITS") then call Credits
  1563.       when (cmd = "ADDHOST") then
  1564.          do
  1565.          hostlist.0=hostlist.0+1
  1566.          i=hostlist.0
  1567.          hostlist.i=cmdargs
  1568.          end
  1569.       when (cmd = "VISUAL")  then 
  1570.          if \visual then 
  1571.             do; 
  1572.             visual=visinit(); 
  1573.             if visual then call visftp; 
  1574.             end
  1575.       when (cmd = "TOGGLE")  then Call toggle cmdargs
  1576.       when (cmd = "VERSION") then say version credits.1
  1577.       when (cmd = "SHOW") | (cmd="QUERY")    then Call QueryStatus cmdargs
  1578.       when (cmd="DIR") | (cmd="PDIR") | (cmd="REDIR") | (cmd="PREDIR") | (cmd="LS") | (cmd="PLS")
  1579.                              then err = Dir(cmd,file1,file2)
  1580.       when (cmd = "?") | (cmd="HELP")
  1581.                              then call Usage file1
  1582.  
  1583.       when (cmd = "TYPE")|(cmd = "PAGE")|(cmd="MORE")    then
  1584.          if file2==file1 then
  1585.             do
  1586.             if cmd="TYPE" then err=get(file1,"-")
  1587.             else err=get(file1,"|"||pager)
  1588.             end
  1589.          else err=get(file1,file2)
  1590.  
  1591.       when (cmd = "MTYPE")|(cmd="MPAGE") then
  1592.          if cmd="TYPE" then err=mget(file1,"-")
  1593.          else err=mget(file1,"|"||pager)
  1594.  
  1595.       when (cmd = "CONNECT") | (cmd="OPEN") | (cmd="FTP") then
  1596.          do
  1597.          if remember then call Update_Memory
  1598.          if left(cmdargs,1)='"' then
  1599.             parse var cmdargs '"'host'"' rest
  1600.          else parse var cmdargs host rest
  1601.          if left(cmdargs,1)='"' then
  1602.             parse var rest '"'user'"' rest
  1603.          else parse var rest user rest
  1604.          parse var rest pass
  1605.          err=Connect()
  1606.          end
  1607.  
  1608.       when (cmd = "USER") then
  1609.          do
  1610.          oask=ask
  1611.          ask=0
  1612.          if cmdargs="" then ask=1
  1613.          if left(cmdargs,1)='"' then
  1614.             parse var cmdargs '"'user'"' rest
  1615.          else user=cmdargs
  1616.          pass=""
  1617.          err=Connect()
  1618.          ask=oask
  1619.          end
  1620.  
  1621.       when (cmd = "LCD") then
  1622.          do
  1623.          ldir=directory(file1)
  1624.          if \quiet then say "Local working directory is now "ldir
  1625.          end
  1626.  
  1627.       when (cmd = "CLOSE")   then
  1628.          do
  1629.          if host="" then return
  1630.          if remember then call Update_Memory
  1631.          rc = FtpLogoff()
  1632.          host = ""
  1633.          user = ""
  1634.          pass = ""
  1635.          end
  1636.  
  1637.       when (cmd = "PWD")     then
  1638.          do
  1639.          junk = FtpPwd('dir')
  1640.          say "Current Remote Directory :" dir
  1641.          end
  1642.  
  1643.       when (cmd = "SYS")     then
  1644.          do
  1645.          sys=""
  1646.          junk = FtpSys('sys')
  1647.          say "Remote operating system is:" sys
  1648.          end
  1649.  
  1650.       otherwise /* Try changing to a directory with the name of command. */
  1651.          err = FtpChDir(origcmd)
  1652.          if (err<>0) then
  1653.             do
  1654.             say "Invalid command. Use ? for help."
  1655.             return 0
  1656.             end
  1657.          else 
  1658.             do
  1659.             err=FtpPWD('dir')
  1660.             if visual then needdir=1
  1661.             end
  1662.          return err
  1663.    end
  1664.  
  1665. if debug then say sourceline() "Error code is "err "/ftp error no. "ftperrno "["english(FTPERRNO)"]"
  1666.  
  1667. return err
  1668.  
  1669. ProcessNETRC:
  1670.  
  1671. call stream netrcfile, 'c', 'open read'
  1672. do while (stream(netrcfile,'s')="READY")
  1673.    line=linein(netrcfile)
  1674.    if (left(line,1)<>"#") & (left(line,1)<>";") then leave
  1675.    if (substr(line,2,1)="#") | (substr(line,2,1)=";") then iterate
  1676.    parse var line 2 cmd cmdargs
  1677.    if (translate(cmd)="VISUAL") then visual=1
  1678.    else Call ProcessCMD cmd, cmdargs
  1679. end
  1680. call Stream netrcfile, 'c', 'close'
  1681. return
  1682.  
  1683. ProcessINI:
  1684.  
  1685. arg inifile
  1686.  
  1687. call stream inifile, 'c', 'open read'
  1688. do while (stream(inifile,'s')="READY")
  1689.    line=linein(inifile)
  1690.    if (left(line,1)="#") | (left(line,1)=";") then iterate
  1691.    parse var line cmd cmdargs
  1692.    if (translate(cmd)="VISUAL") then visual=1
  1693.    else Call ProcessCMD cmd, cmdargs
  1694. end
  1695. call Stream inifile, 'c', 'close'
  1696. return
  1697.  
  1698. set:
  1699. parse arg command
  1700.  
  1701. if (pos('=',command)<>0) then parse var command var"="parm
  1702. else parse var command var parm
  1703. var=translate(var)
  1704. if left(parm,1)='"' then parse var parm '"'parm'"'
  1705.  
  1706. select
  1707.    when (var='BINARIES') then binaries=parm
  1708.    when (var='ASCIIS') then asciis=parm
  1709.    when (var='ANONPASS') then anonpass=parm
  1710.    when (var='VISCMD') then viscmd=parm
  1711.    when (var='PAGER') then pager=parm
  1712.    when (var='DIRCMD') then dircmd=parm
  1713.    when (var='LDIR') then ldir=directory(parm)
  1714.    when (var='MODE') then Call Mode(parm)
  1715.    when (var='NETRCFILE') then
  1716.       do
  1717.       oldnetrc=netrcfile
  1718.       netrcfile=stream(parm,'c','query exists')
  1719.       if netrcfile="" then netrcfile=oldnetrcfile
  1720.       end
  1721.    when (var='MPROMPT') then
  1722.       do
  1723.       if parm=1 then prompt=1
  1724.       if parm=0 then prompt=0
  1725.       end
  1726.    when wordpos(var,"TOUCH UNIQUE CLOBBER DISPLAY QUIET NETRC MACROS REMEMBER COMMENT LONGNAME ASK BELL")<>0 then
  1727.       do
  1728.       if (parm=1) | translate(parm)="ON" then interpret var||'=1'
  1729.       if (parm=0) | translate(parm)="OFF" then interpret var||'=0'
  1730.       end
  1731.    when (var='PROMPT') then
  1732.       do
  1733.       promptstr='""'
  1734.       do while (length(parm)>0)
  1735.          char=left(parm,1)
  1736.          parm=substr(parm,2)
  1737.          if char='@' then
  1738.             do
  1739.             var=translate(left(parm,1))
  1740.             parm=substr(parm,2)
  1741.             select
  1742.                when var="B" then promptstr=promptstr||'||boldvideo'
  1743.                when var="0" then promptstr=promptstr||'||progname'
  1744.                when var="D" then promptstr=promptstr||'||dirname'
  1745.                when var="U" then promptstr=promptstr||'||user'
  1746.                when var="L" then promptstr=promptstr||'||directory()'
  1747.                when var="H" then promptstr=promptstr||'||phost'
  1748.                when (var="I") | (var="R")
  1749.                             then promptstr=promptstr||'||inversevideo'
  1750.                when var="N" then promptstr=promptstr||'||d2c(13)||d2c(10)'
  1751.                when var="P" then promptstr=promptstr||'||normvideo'
  1752.                when var="%" then promptstr=promptstr||'||!history.0+1'
  1753.                when var="T" then promptstr=promptstr||'||time()'
  1754.                when var="S" then promptstr=promptstr||'||status'
  1755.                when var="E" then promptstr=promptstr||'||FTPERRNO'
  1756.                when var="_" then promptstr=promptstr||'||" "'
  1757.                when var="@" then promptstr=promptstr||'||"@"'
  1758.                when var="C" then
  1759.                   do
  1760.                   colors=left(parm,2)
  1761.                   if (length(colors)=2) & (verify(colors,"01234567")=0) then
  1762.                      do
  1763.                      parm=substr(parm,3)
  1764.                      promptstr=promptstr||'||d2c(27)||"['||left(colors,1)+30||';'||right(colors,1)+40||'m"'
  1765.                      end
  1766.                   end
  1767.             otherwise nop
  1768.             end
  1769.             end
  1770.          else if char='"' then promptstr=promptstr||'||""""'
  1771.          else promptstr=promptstr||'||"'char'"'
  1772.       end
  1773.       end
  1774.    when (var="") then nop
  1775.    otherwise
  1776.       do
  1777.       say "You cannot set the value of "var
  1778.       return 0
  1779.       end
  1780. end
  1781. return 1
  1782.  
  1783. mode: procedure expose mode quiet
  1784.  
  1785. parse upper arg modename
  1786.  
  1787. err=0
  1788. if modename="ASCII" then
  1789.     err = FtpSetBinary("ASCII")
  1790. else if modename="BINARY" then err= FtpSetBinary("BINARY")
  1791. else
  1792.    do
  1793.    if \quiet then say "Invalid value for mode: "modename
  1794.    return 0
  1795.    end
  1796. mode=modename
  1797. if \quiet then say "File transfer mode set to "mode "("err")"
  1798.  
  1799. return 0
  1800.  
  1801. PutComment: procedure
  1802.  
  1803. parse arg file, user, remotehost, remotedir, remotename
  1804.  
  1805. if pos('"',remotedir)<>0 then parse var remotedir '"'remotedir'"'.
  1806.  
  1807. message="Transferred from "user"@"remotehost":"remotedir
  1808.  
  1809. if pos(right(message,1),"/\")=0 then message=message||'/'
  1810.  
  1811. message=translate(message,'/','\')||remotename||" at "||time()||" on "||date()"."
  1812.  
  1813. RetCode = SysPutEA(File, '.COMMENT','FDFF'x||D2C(LENGTH(Message))||'00'x||Message)
  1814. return RetCode
  1815.  
  1816. putlong: procedure
  1817. parse arg FileName, LongName
  1818.  
  1819. if FileName = '' | LongName= '' then DO
  1820.    return 0
  1821.    end  /* Do */
  1822.  
  1823. Return SysPutEA(FileName, '.LONGNAME','FDFF'x||D2C(LENGTH(LongName))||'00'x||LongName)
  1824.  
  1825. visinit: procedure expose origdir
  1826.  
  1827. if RxFuncQuery("VInit") then
  1828.    do
  1829.    rc = RxFuncAdd("VInit","VREXX","VINIT")
  1830.    if rc\=0 then
  1831.       do
  1832.       say "Error loading the VRexx/2 package.  VRexx/2 is an extension to REXX"
  1833.       say "that allows it to create PM list boxes and dialogs.  It is necessary"
  1834.       say "to use MaxiFTP's VISUAL mode.  You can obtain VRexx/2 via anonymous"
  1835.       say "FTP from software.watson.ibm.com or ftp-os2.cdrom.com."
  1836.       say
  1837.       say "Unable to switch to Visual mode."
  1838.       visual=0
  1839.       return 0
  1840.       end
  1841.    end
  1842.  
  1843. initcode = Vinit()
  1844. if initcode = 'ERROR' then 
  1845.    do
  1846.    say "Unable to switch to Visual mode."
  1847.    visual=0
  1848.    return 0
  1849.    end
  1850.  
  1851. signal on halt name CLEANUP2
  1852. signal on error name CLEANUP2
  1853. signal on syntax name CLEANUP2
  1854.  
  1855. call VdialogPos 50,50
  1856.  
  1857. return 1
  1858.  
  1859. visftp:
  1860.  
  1861. say "MaxiFTP is entering Visual mode."
  1862.  
  1863. needdir=1
  1864. do forever
  1865.    if connect()=0 then leave
  1866.    host=""
  1867.    user=""
  1868.    pass=""
  1869. end
  1870.  
  1871. do queued()
  1872.    parse pull cmd cmdargs
  1873.    call processcmd cmd, cmdargs
  1874. end
  1875.  
  1876. request=""
  1877. do forever                                    
  1878.    request=visdir()
  1879.    if request="**CANCEL**" then 
  1880.       do
  1881.       if vmsg("Leave MaxiFTP?","Do you wish to leave MaxiFTP? ",6)=="YES" then 
  1882.          do
  1883.          call Update_Memory
  1884.          signal cleanup
  1885.          end
  1886.       iterate
  1887.       end
  1888.    if left(request,1)='<' then
  1889.       do
  1890.       cmd=word(request,1)
  1891.       select
  1892.          when cmd="<MODE>" then call toggle 'MODE'
  1893.          when cmd="<DIR>" then call dir
  1894.          when cmd="<HOST>" then
  1895.             do
  1896.                oldhost=host
  1897.                olduser=user
  1898.                oldpass=pass
  1899.                host=""
  1900.                user=""
  1901.                pass=""
  1902.                if connect()<>0 then
  1903.                   do
  1904.                      host=oldhost
  1905.                      user=olduser
  1906.                      pass=oldpass
  1907.                      if connect<>-2 then
  1908.                      do forever
  1909.                         if connect()=0 then leave
  1910.                         host=""
  1911.                         user=""
  1912.                         pass=""
  1913.                      end
  1914.                   end
  1915.                do queued()
  1916.                   parse pull cmd cmdargs
  1917.                   call processcmd cmd, cmdargs
  1918.                end
  1919.             end
  1920.          when cmd="<LCD>" then
  1921.             do
  1922.             prompt.0=1
  1923.             prompt.1="Enter the new local directory "
  1924.             prompt.vstring=directory()
  1925.             button=VInputBox("Change Local Directory",'prompt',60,3)
  1926.             if button<>"CANCEL" then call directory prompt.vstring
  1927.             list.2="<LCD> Current local dir: "directory()
  1928.             end
  1929.          when cmd="<VISCMD>" then
  1930.             do
  1931.             if translate(viscmd)="GET" then viscmd="TYPE"
  1932.             else viscmd="GET"
  1933.             list.5="<VISCMD> Visual selection action is "translate(viscmd)
  1934.             end
  1935.          when cmd="<PUT>" then
  1936.             do
  1937.             filespec=directory()
  1938.             if right(filespec,1)="\" then filespec=filespec||"*.*"
  1939.             else filespec=filespec||"\*.*"
  1940.             button=VFileBox("Choose the file to send.",filespec,'file')
  1941.             if button="OK" then put(file.vstring)
  1942.             end
  1943.          when cmd="<CMD>" then
  1944.             do
  1945.             prompt.0=2
  1946.             prompt.1="Enter a MaxiFTP command          "
  1947.             prompt.2="(Output will go the window where MaxiFTP started.)          "
  1948.             prompt.vstring="dir"
  1949.             button=VInputBox("MaxiFTP Command Line",'prompt',75,3)
  1950.             if button<>"CANCEL" then 
  1951.                do
  1952.                parse var prompt.vstring cmd cmdargs
  1953.                call processcmd cmd, cmdargs
  1954.                end
  1955.             say "MaxiFTP is in Visual mode."
  1956.             end
  1957.          otherwise say "Unimplemented: "cmd
  1958.       end
  1959.       end
  1960.    else do
  1961.       if pos(right(request,1),'/@*')<>0 then request=left(request,length(request)-1)
  1962.       err = FtpChDir(request)
  1963.       if err<>0 then call ProcessCMD VisCMD, request
  1964.       else needdir=1
  1965.       if err<>0 then
  1966.          if word(request,1)<>request then 
  1967.             do
  1968.             err=FtpChDir(word(request,1))
  1969.             if err<>0 then err=Get(word(request,1))
  1970.             end
  1971.       end
  1972. end
  1973.  
  1974. signal cleanup
  1975.  
  1976. visdir:
  1977. if needdir then
  1978.    do
  1979.    err=dir('LS','-F','>nul')
  1980.    if err<>0 then err=dir('ls','.','>nul')
  1981.    if err<>0 then 
  1982.       do
  1983.       stem.0=1
  1984.       stem.1="<NO FILES AVAILABLE>"
  1985.       end
  1986.    list.0=7
  1987.    list.1="<HOST> "||user||"@"||host
  1988.    list.2="<LCD> Current local dir: "directory()
  1989.    list.3="<CMD> Issue a MaxiFTP command"
  1990.    list.4="<DIR> Show long information about this listing in the text window"
  1991.    list.5="<VISCMD> Visual selection action is "translate(viscmd)
  1992.    list.6="<PUT> Transfer a file TO "host
  1993.    list.7=".."
  1994.    do i=1 to stem.0
  1995.       j=list.0+1
  1996.       list.j=stem.i
  1997.       list.0=j
  1998.    end
  1999.    needdir=0
  2000.    end
  2001. err = FtpPwd('dir')
  2002. if pos('"',dir)<>0 then parse var dir '"'dirname'"'.
  2003. else dirname=dir
  2004. button=VlistBox('Directory Listing: 'dirname,list,80,8,3)
  2005. if button="CANCEL" then return "**CANCEL**"
  2006. return list.vstring
  2007.  
  2008. GetHost: procedure expose host user pass netrcfile hostnetrcline
  2009. cancel=0
  2010. p.0=3
  2011. p.1="Remote host: "
  2012. p.2="Username: "
  2013. p.3="Password: "
  2014. w.0=3
  2015. w.1=40
  2016. w.2=40
  2017. w.3=40
  2018. h.0=3
  2019. h.1=0
  2020. h.2=0
  2021. h.3=1
  2022. r.=""
  2023. r.0=3
  2024. if host<>"" then r.1=host
  2025. if user<>"" then r.2=user
  2026. else r.2="anonymous"
  2027. if pass<>"" then r.3=pass
  2028. do until host<>""
  2029. button=VMultBox('Specify a host',p,w,h,r,3)
  2030. if button="CANCEL" then 
  2031.    do
  2032.    if vmsg("Leave MaxiFTP?","Do you wish to leave MaxiFTP? ",6)=="YES" then signal cleanup
  2033.    else cancel=1
  2034.    host=""
  2035.    user=""
  2036.    pass=""
  2037.    end
  2038. else
  2039.    do
  2040.    host=r.1
  2041.    user=r.2
  2042.    pass=r.3
  2043.    end
  2044. if host<>"" then
  2045.    if (vmsg("Update NETRC file","Add this entry to the end of the NETRC file? ",6)=="YES") then
  2046.    do
  2047.    p.0=1
  2048.    p.1="Alias: "
  2049.    w.0=1
  2050.    w.1=50
  2051.    h.0=1
  2052.    h.1=0
  2053.    r.=""
  2054.    r.0=1
  2055.    alias=""
  2056.    button=VMultBox('Alias for host 'host,p,w,h,r,3)
  2057.    if button<>"CANCEL" then alias="#alias "r.1
  2058.    say "Updating "netrcfile
  2059.    call lineout netrcfile, "##Following entry added by MaxiFTP on "date()
  2060.    call lineout netrcfile, "machine "host alias
  2061.    if user<>"" then call lineout netrcfile, "     login "user
  2062.    if pass<>"" then call lineout netrcfile, "     password "pass
  2063.    call lineout netrcfile
  2064.    end
  2065. if cancel then leave
  2066. if host="" then host=VListNETRC()
  2067. end
  2068. return button
  2069.  
  2070. vmsg: procedure expose origdir
  2071.  
  2072. n=0
  2073. do i=2 to arg()-1
  2074.    n=n+1
  2075.    prompt.n=arg(i)
  2076. end
  2077. prompt.0=n
  2078.  
  2079. return VMsgBox(arg(1),'prompt',arg(arg()))
  2080.  
  2081. VListNETRC: procedure expose visual origdir netrcfile hostnetrcline hostlist.
  2082.  
  2083. say "Presenting a visual list of hosts..."
  2084.  
  2085. if SysFileSearch('machine', netrcfile,'hits.','N')\=0 then hits.0=0
  2086.  
  2087. hostnetrcline=""
  2088.  
  2089. n=1
  2090. do i=1 to hostlist.0
  2091.    n=n+1
  2092.    hosts.0=n
  2093.    if words(hostlist.i)=1 then hosts.n=hostlist.i
  2094.    else hosts.n=left(subword(hostlist.i,2),100," ")||"@"||word(hostlist.i,1)
  2095. end
  2096.  
  2097. do i=1 to hits.0
  2098.    if left(word(hits.i,2),1)="#" then iterate
  2099.    n=n+1
  2100.    hosts.0=n
  2101.    parse var hits.i "#alias "alias
  2102.    if alias<>"" then
  2103.       hosts.n=left(alias,100," ")||"@"||word(hits.i,1+wordpos('MACHINE',translate(hits.i)))||" <"||word(hits.i,1)||">"
  2104.    else hosts.n=left(word(hits.i,1+wordpos('MACHINE',translate(hits.i))),100," ")||"<"||word(hits.i,1)||">"
  2105. end
  2106. if n=1 then return ""
  2107.  
  2108. hosts.1="<Enter a different host>"
  2109.  
  2110. button=VlistBox('Host Selection: ','hosts',45,8,3)
  2111. if button="CANCEL" | hosts.vstring="<Enter a different host>" then return ""
  2112. parse var hosts.vstring host "<"hostnetrcline">"
  2113. if pos("@",host)<>0 then parse var host with " @"host
  2114. return strip(host)
  2115.  
  2116. addslash: procedure
  2117.  
  2118. parse arg path
  2119. if right(path,1)="\" then return path
  2120. else return path||"\"
  2121.  
  2122. Remember_Dir: procedure expose host memoryfile dir remember
  2123. if \remember | host="" then return 0
  2124. if SysFileSearch(host, memoryfile, "hits.")\=0 then hits.0=0
  2125. call Stream memoryfile, 'c', 'close'
  2126. if hits.0=0 then return 0
  2127. do i=1 to hits.0
  2128.    parse var hits.1 nhost ndir
  2129.    if translate(host)==translate(nhost) then err=FtpChDir(ndir)
  2130. end
  2131. err=FtpPwd('dir')
  2132. return 1
  2133.  
  2134. Update_Memory: procedure expose host memoryfile dir remember
  2135. if \remember | host="" | dir="" then return 0
  2136. tempfile=systempfilename("tmp?????")
  2137.  
  2138. if pos('"',dir)<>0 then parse var dir '"'dirname'"'.
  2139. else dirname=dir
  2140.  
  2141. written=0
  2142. call stream tempfile, 'c', 'open write'
  2143. if stream(memoryfile, 'c', 'open read') ="READY:" then
  2144. do while (stream(memoryfile,'s')="READY")
  2145.    line=linein(memoryfile)
  2146.    parse var line ohost odir
  2147.    if translate(host)==translate(ohost) then
  2148.       do
  2149.       call lineout tempfile, host dirname
  2150.       written=1
  2151.       end
  2152.    else if line\="" then call lineout tempfile, line
  2153. end
  2154. if \written then call lineout tempfile, host dirname
  2155. call Stream memoryfile, 'c', 'close'
  2156. call Stream tempfile, 'c', 'close'
  2157. "@copy "tempfile memoryfile "1>nul 2>nul"
  2158. Call SysFileDelete tempfile
  2159. return 1
  2160.  
  2161. CLEANUP2:
  2162.  
  2163. CLEANUP:          
  2164.    call VExit
  2165. signal done
  2166.  
  2167. /* BEGINNING OF CmdLine CODE BY ALBERT CROSBY */
  2168. /*
  2169.        CmdLine.CMD Version 1.1
  2170.        (c) 1994 by Albert Crosby <acrosby@comp.uark.edu>
  2171.  
  2172.        This code may be distributed freely and used in other programs.
  2173.        Please give credit where credit is due.
  2174.  
  2175.        CmdLine.CMD is REXX code that creates a full featured version
  2176.        of the OS/2 command line parser that may be called from your
  2177.        programs.
  2178. */
  2179.  
  2180. /* This is a CmdLine function for REXX.  It supports:
  2181.        *       OS/2 style command history. (1)
  2182.        *       Keeps insert state. (1)
  2183.        *       Command line _can_ include control chars.
  2184.        *       Allows for "hidden" input, for passwords.
  2185.        *       A call can be restricted from accessing the history.
  2186.        *       A call can be restricted from updating the history.
  2187.        *       A predefined value can be given to extended keys. (1) (2)
  2188.  
  2189.    NOTE:
  2190.        (1) These functions work ONLY if CmdLine is included in the source
  2191.            file for your program. 
  2192.        (2) Format: !history.nn="string" where nn is the DECIMAL value for
  2193.            the second character returned when the extended key is pressed.
  2194. */
  2195.  
  2196. /* The following two lines are used in case CmdLine is called as an 
  2197.    external function */
  2198.  
  2199. parse source . . name
  2200. if translate(filespec("name",name))="CMDLINE.CMD" then signal extproc
  2201.  
  2202. CmdLine: procedure expose !history.
  2203. extproc: /* CmdLine called as an external proc or command line */
  2204.  
  2205. /* Parameters can be any combination of:
  2206.    Hidden : Characters are displayed as "*", no history, not kept.
  2207.    Forget : Do not add the result of this call to the history list.
  2208.    No History : Do not allow access to the history list.
  2209.    Clear : Clear the history list with this call (no input action made.)
  2210.            Also clears any predefined keys!
  2211.    Insert : Set insert mode ON.
  2212.    Overwrite : Set overwrite mode OFF.
  2213.    SameLine : Keep cursor on sameline after input. (Default: off)
  2214.    Required : null values are not accepted. (Default: off)
  2215.    Default : an initial value.
  2216.    Position: position for cursor (Default: end of input)
  2217.    Valid : Next parameter specifies the valid charachters (no translation)
  2218.            unless specified elsewhere. (1)
  2219.    Upper : Translate input to upper case. (1)
  2220.    Lower : Translate input to lower case. (1)
  2221.    Width : Next parameter specifies the maximum width. (1)
  2222.    Autoskip : Do not wait for enter after last char on a field with a width.
  2223.    X : Next parameter specifies the initial X (column) position.
  2224.    Y : Next parameter specifies the initial Y (row) position.
  2225.    Tag : Displays the next parameter as a prompt in front of the
  2226.             entry field.
  2227.    
  2228.  
  2229.    Only the first letter matters.  Enter each desired parameter seperated
  2230.    by commas.
  2231.  
  2232.    NOTES:
  2233.       (1)  Upper, Lower, Width, and VALID preclude access to the history 
  2234.            list.
  2235. */
  2236.  
  2237. word=""
  2238. pos=0
  2239. hidden=0
  2240. history=1
  2241. keep=1
  2242. sameline=0
  2243. required=0
  2244. reset=0
  2245. valid=xrange()
  2246. upper=0
  2247. lower=0
  2248. width=0
  2249. autoskip=0
  2250. parse value SysCurPos() with x y
  2251. do i=1 to arg()
  2252.    cmd=translate(left(arg(i),1))
  2253.    parm=""
  2254.    if pos("=",arg(i))\=0 then
  2255.       parse value arg(i) with ."="parm
  2256.    select
  2257.       when cmd="D" then /* Default result */
  2258.          do
  2259.          if parm="" then
  2260.             do;i=i+1;parm=arg(i);end
  2261.          word=parm
  2262.          if pos=0 then pos=length(word)
  2263.          end
  2264.       when cmd="P" then /* Initial Position */
  2265.          do
  2266.          if parm="" then
  2267.             do;i=i+1;parm=arg(i);end
  2268.          if datatype(parm,"W") then
  2269.             pos=parm
  2270.          else if parm="*" then
  2271.             pos=length(word)
  2272.          end
  2273.       when cmd="X" then
  2274.          do
  2275.          parse value SysCurPos() with x y
  2276.          if parm="" then
  2277.             do;i=i+1;parm=arg(i);end
  2278.          if datatype(parm,"W") then
  2279.             Call SysCurPos parm,y
  2280.          end
  2281.       when cmd="Y" then
  2282.          do
  2283.          parse value SysCurPos() with x y
  2284.          if parm="" then
  2285.             do;i=i+1;parm=arg(i);end
  2286.          if datatype(parm,"W") then
  2287.             Call SysCurPos x,parm
  2288.          end
  2289.       when cmd="T" then
  2290.          do
  2291.          if parm="" then
  2292.             do;i=i+1;parm=arg(i);end
  2293.          call charout, parm
  2294.          end
  2295.       when cmd="H" then
  2296.          do
  2297.          hidden=1
  2298.          keep=0
  2299.          history=0
  2300.          end
  2301.       when cmd="C" then
  2302.          reset=1
  2303.       when cmd="O" then
  2304.          !history.insert=0
  2305.       when cmd="I" then
  2306.          !history.insert=1
  2307.       when cmd="F" then
  2308.          keep=0
  2309.       when cmd="S" then
  2310.          sameline=1
  2311.       when cmd="R" then
  2312.          required=1
  2313.       when cmd="V" then
  2314.          do
  2315.          if parm="" then
  2316.             do;i=i+1;parm=arg(i);end
  2317.          valid=parm
  2318.          history=0
  2319.          keep=0
  2320.          end
  2321.       when cmd="U" then
  2322.          do; upper=1; lower=0; history=0; keep=0; end
  2323.       when cmd="L" then
  2324.          do; upper=0; lower=1; history=0; keep=0; end
  2325.       when cmd="A" then
  2326.          autoskip=1
  2327.       when cmd="W" then
  2328.          do
  2329.          if parm="" then
  2330.             do;i=i+1;parm=arg(i);end
  2331.          width=parm
  2332.          if \datatype(width,"Whole") then width=0
  2333.          if width<0 then width=0
  2334.          history=0
  2335.          keep=0
  2336.          end
  2337.     otherwise nop
  2338.     end
  2339. end
  2340.  
  2341. if width=0 then autoskip=0
  2342.  
  2343. if reset then
  2344.    do
  2345.    drop !history.
  2346.    return ""
  2347.    end
  2348.  
  2349. if symbol("!history.0")="LIT" then
  2350.    !history.0=0
  2351. if symbol("!history.insert")="LIT" then
  2352.    !history.insert=1
  2353.  
  2354. if width<>0 then
  2355.    if length(word)>width then
  2356.       word=left(word,width)
  2357.  
  2358. if pos>length(word)
  2359.    then pos=length(word)
  2360.  
  2361. if word\=="" then
  2362.    do
  2363.    if \hidden then call Charout, word
  2364.    else call Charout, copies("*",length(word))
  2365.    call Charout, copies(d2c(8),length(word)-pos)
  2366.    end
  2367.  
  2368. historical=-1
  2369. key=SysGetKey("NoEcho")
  2370. do forever /* while key\=d2c(13)*/
  2371.    if key=d2c(13) then /* Enter key */
  2372.       if required & word="" then nop;
  2373.       else leave
  2374.    else if (key=d2c(8)) then /* Backspace */
  2375.       do
  2376.       if length(word)>0 then
  2377.       do
  2378.       word=delstr(word,pos,1)
  2379.       call rubout 1
  2380.       pos=pos-1
  2381.       if pos<length(word) then
  2382.          do
  2383.          if \hidden then call charout, substr(word,pos+1)||" "
  2384.          else call charout, copies("*",length(substr(word,pos+1)))||" "
  2385.          call charout, copies(d2c(8),length(word)-pos+1)
  2386.          end
  2387.       end
  2388.       end
  2389.    else if key=d2c(27) then /* Escape */
  2390.       do
  2391.       if pos<length(word) then
  2392.          if \hidden then call charout, substr(word,pos+1)
  2393.          else call charout, copies("*",length(substr(word,pos+1)))
  2394.       call rubout length(word)
  2395.       word=""
  2396.       pos=0
  2397.       end
  2398.    else if key=d2c(10) | key=d2c(9) then /* Ctrl-Enter and TAB */
  2399.       nop; /* Ignored */
  2400.    else if key=d2c(224) | key=d2c(0) then /* Extended key handler */
  2401.       do
  2402.       key2=SysGetKey("NoEcho")
  2403.       select
  2404.          when key2=d2c(59) then /* F1 */
  2405.             if (history) & (!history.0<>0) then
  2406.                do
  2407.                if symbol('search')='LIT' then
  2408.                   search=word
  2409.                if symbol('LastFind')='LIT' then
  2410.                   search=word
  2411.                else if LastFind\=word
  2412.                   then search=word
  2413.                if historical=-1 then
  2414.                   start=!history.0
  2415.                else start=historical-1
  2416.                if start=0 then start=!history.0
  2417.                found=0
  2418.                do i=start to 1 by -1
  2419.                   if abbrev(!history.i,search) then
  2420.                      do
  2421.                      found=1
  2422.                      historical=i
  2423.                      LastFind=!history.i
  2424.                      leave
  2425.                      end
  2426.                end
  2427.                if found then
  2428.                   do
  2429.                   if pos<length(word) then
  2430.                      if \hidden then call charout, substr(word,pos+1)
  2431.                      else call charout, copies("*",length(substr(word,pos+1)))
  2432.                   call rubout length(word)
  2433.                   word=!history.historical
  2434.                   pos=length(word)
  2435.                   if \hidden then call charout, word
  2436.                   else call charout, copies("*",length(word))
  2437.                   end
  2438.                end
  2439.          when key2=d2c(72) then /* Up arrow */
  2440.             if (history) & (!history.0<>0) then
  2441.                do
  2442.                if historical=-1 then
  2443.                   historical=!history.0
  2444.                else historical=historical-1
  2445.                if historical=0 then
  2446.                   historical=!history.0
  2447.                if pos<length(word) then
  2448.                   if \hidden then call charout, substr(word,pos+1)
  2449.                   else call charout, copies("*",length(substr(word,pos+1)))
  2450.                call rubout length(word)
  2451.                word=!history.historical
  2452.                pos=length(word)
  2453.                if \hidden then call charout, word
  2454.                else call charout, copies("*",length(word))
  2455.                end
  2456.          when key2=d2c(80) then /* Down arrow */
  2457.             if (history) & (!history.0<>0) then
  2458.                do
  2459.                if historical=-1 then
  2460.                   historical=1
  2461.                else historical=historical+1
  2462.                if historical>!history.0 then
  2463.                   historical=1
  2464.                if pos<length(word) then
  2465.                   if \hidden then call charout, substr(word,pos+1)
  2466.                   else call charout, copies("*",length(substr(word,pos+1)))
  2467.                call rubout length(word)
  2468.                word=!history.historical
  2469.                pos=length(word)
  2470.                if \hidden then call charout, word
  2471.                else call charout, copies("*",length(word))
  2472.                end
  2473.          when key2=d2c(75) then /* Left arrow */
  2474.             if pos>0 then
  2475.                do
  2476.                call Charout, d2c(8)
  2477.                pos=pos-1
  2478.                end
  2479.          when key2=d2c(77) then /* Right arrow */
  2480.             if pos<length(word) then
  2481.                do
  2482.                if \hidden then call Charout, substr(word,pos+1,1)
  2483.                else call charout, "*"
  2484.                pos=pos+1
  2485.                end
  2486.          when key2=d2c(115) then /* Ctrl-Left arrow */
  2487.             if pos>0 then
  2488.                do
  2489.                call charout, d2c(8)
  2490.                pos=pos-1
  2491.                do forever
  2492.                   if pos=0 then leave
  2493.                   if substr(word,pos+1,1)\==" " & substr(word,pos,1)==" " then
  2494.                         leave
  2495.                   else
  2496.                      do
  2497.                      call charout, d2c(8)
  2498.                      pos=pos-1
  2499.                      end
  2500.                end
  2501.                end
  2502.          when key2=d2c(116) then /* Ctrl-Right arrow */
  2503.             if pos<length(word) then
  2504.                do
  2505.                if \hidden then call Charout, substr(word,pos+1,1)
  2506.                else call charout, "*"
  2507.                pos=pos+1
  2508.                do forever
  2509.                   if pos=length(word) then
  2510.                      leave
  2511.                   if substr(word,pos,1)==" " & substr(word,pos+1,1)\==" " then
  2512.                      leave
  2513.                   else
  2514.                      do
  2515.                      if \hidden then call Charout, substr(word,pos+1,1)
  2516.                      else call charout, "*"
  2517.                      pos=pos+1
  2518.                      end
  2519.                end
  2520.                end
  2521.          when key2=d2c(83) then /* Delete key */
  2522.             if pos<length(word) then
  2523.                do
  2524.                word=delstr(word,pos+1,1)
  2525.                if \hidden then call Charout, substr(word,pos+1)||" "
  2526.                else call Charout, copies("*",length(substr(word,pos+1)))||" "
  2527.                call charout, copies(d2c(8),length(word)-pos+1)
  2528.                end
  2529.          when key2=d2c(82) then /* Insert key */
  2530.             !history.insert=\!history.insert
  2531.          when key2=d2c(79) then /* End key */
  2532.             if pos<length(word) then
  2533.                do
  2534.                if \hidden then call Charout, substr(word,pos+1)
  2535.                else call Charout, copies("*",length(substr(word,pos+1)))
  2536.                pos=length(word)
  2537.                end
  2538.          when key2=d2c(71) then /* Home key */
  2539.             if pos\=0 then
  2540.                do
  2541.                call Charout, copies(d2c(8),pos)
  2542.                pos=0
  2543.                end
  2544.          when key2=d2c(117) then /* Control-End key */
  2545.             if pos<length(word) then
  2546.                do
  2547.                call Charout, copies(" ",length(word)-pos)
  2548.                call Charout, copies(d2c(8),length(word)-pos)
  2549.                word=left(word,pos)
  2550.                end
  2551.          when key2=d2c(119) then /* Control-Home key */
  2552.             if pos>0 then
  2553.                do
  2554.                if pos<length(word) then
  2555.                   if \hidden then call charout, substr(word,pos+1)
  2556.                   else call charout, copies("*",length(substr(word,pos+1)))
  2557.                call rubout length(word)
  2558.                word=substr(word,pos+1)
  2559.                if \hidden then call Charout, word
  2560.                else call Charout, copies("*",length(word))
  2561.                call Charout, copies(d2c(8),length(word))
  2562.                pos=0
  2563.                end
  2564.       otherwise 
  2565.          if history & symbol('!history.key.'||c2d(key2))\='LIT' then /* Is there a defined string? */
  2566.             do
  2567.                if pos<length(word) then
  2568.                   if \hidden then call charout, substr(word,pos+1)
  2569.                   else call charout, copies("*",length(substr(word,pos+1)))
  2570.                call rubout length(word)
  2571.                i=c2d(key2)
  2572.                word=!history.key.i
  2573.                pos=length(word)
  2574.                if \hidden then call charout, word
  2575.                else call charout, copies("*",length(word))
  2576.             end
  2577.       end
  2578.       end
  2579.    else if width=0 | length(word)<width then /* The key is a normal key & within width */
  2580.       do
  2581.       if upper then key=translate(key);
  2582.       if lower then key=translate(key,"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  2583.       if pos(key,valid)\=0 then
  2584.          do;
  2585.          if \hidden then call Charout, key;
  2586.          else call charout, "*"
  2587.          if !history.insert then
  2588.             word=insert(key,word,pos);
  2589.          else word=overlay(key,word,pos+1)
  2590.          pos=pos+1; 
  2591.          if pos<length(word) then
  2592.             do
  2593.             if \hidden then 
  2594.                call Charout, substr(word,pos+1)
  2595.             else call Charout, copies("*", length(substr(word,pos+1)))
  2596.             call Charout, copies(d2c(8),length(word)-pos)
  2597.             end
  2598.          end
  2599.       else beep(400,4)
  2600.       end
  2601.    if autoskip & length(word)=width then leave
  2602.    key=SysGetKey("NoEcho")
  2603. end
  2604. if \sameline then say
  2605. if (keep) & (word\=="") then
  2606.    do
  2607.    historical=!history.0
  2608.    if word\=!history.historical then
  2609.       do
  2610.       !history.0=!history.0+1
  2611.       historical=!history.0
  2612.       !history.historical=word
  2613.       end
  2614.    end
  2615. return word
  2616.  
  2617. rubout: procedure
  2618. arg n
  2619. do i=1 to n
  2620.    call Charout, d2c(8)||" "||d2c(8)
  2621. end
  2622. return
  2623. /* END OF CmdLine CODE BY ALBERT CROSBY */
  2624.