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