home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / telecomm / nhclb120 / ftpcli.c < prev    next >
C/C++ Source or Header  |  1993-09-26  |  15KB  |  697 lines

  1. /* FTP client (interactive user) code */
  2. #define    LINELEN        128    /* Length of command buffer */
  3. #include <stdio.h>
  4. #ifdef __TURBOC__
  5. #include "fcntl.h"
  6. #endif
  7. #include "global.h"
  8. #include "config.h"
  9. #include "mbuf.h"
  10. #include "netuser.h"
  11. #include "icmp.h"
  12. #include "timer.h"
  13. #include "tcp.h"
  14. #include "ftp.h"
  15. #include "session.h"
  16. #include "cmdparse.h"
  17. #include "telnet.h"
  18. #include "iface.h"
  19. #include "ax25.h"
  20. #include "lapb.h"
  21. #include "finger.h"
  22. #include "nr4.h"
  23. #ifdef    BSD
  24. char *sprintf();
  25. #endif
  26. static ftpsetup();
  27. /* #ifdef __TURBOC__    */
  28. /* #include <fcntl.h>    */
  29. /* #endif        */
  30.  
  31. extern struct session *current;
  32. extern char nospace[];
  33. extern char badhost[];
  34. static char notsess[] = "Not an FTP session!\n";
  35. static char cantwrite[] = "Can't write %s\n";
  36. static char cantread[] = "Can't read %s\n";
  37.  
  38.  
  39. static int sndftpmsg();
  40. static void doreply();
  41. static void ftpccs();
  42. static void ftpcds();
  43. int donothing(),doftpcd(),dolist(),doget(),dols(),doput(),dotype(),doabort(),
  44.     domkdir(),dormdir();
  45.  
  46. struct cmds ftpabort[] = {
  47.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  48.     "abort",    doabort,    0,    NULLCHAR,        NULLCHAR,
  49.     NULLCHAR,    NULLFP,        0,    "Only valid command is \"abort\"", NULLCHAR,
  50. };
  51.  
  52. struct cmds ftpcmds[] = {
  53.     "",        donothing,    0,    NULLCHAR,        NULLCHAR,
  54.     "cd",        doftpcd,    2,    "cd <directory>",    NULLCHAR,
  55.     "dir",        dolist,        0,    NULLCHAR,        NULLCHAR,
  56.     "list",        dolist,        0,    NULLCHAR,        NULLCHAR,
  57.     "get",        doget,        2,    "get remotefile <localfile>",    NULLCHAR,
  58.     "ls",        dols,        0,    NULLCHAR,        NULLCHAR,
  59.     "mkdir",    domkdir,    2,    "mkdir <directory>",    NULLCHAR,
  60.     "nlst",        dols,        0,    NULLCHAR,        NULLCHAR,
  61.     "rmdir",    dormdir,    2,    "rmdir <directory>",    NULLCHAR,
  62.     "put",        doput,        2,    "put localfile <remotefile>",    NULLCHAR,
  63.     "type",        dotype,        0,    NULLCHAR,        NULLCHAR,
  64.     NULLCHAR,    NULLFP,        0,     NULLCHAR,        NULLCHAR,
  65. };
  66.  
  67. /* Handle top-level FTP command */
  68. doftp(argc,argv)
  69. int argc;
  70. char *argv[];
  71. {
  72.     int32 resolve();
  73.     int ftpparse();
  74.     char *inet_ntoa();
  75.     void ftpccr(),ftpccs();
  76.     struct session *s;
  77.     struct ftp *ftp,*ftp_create();
  78.     struct tcb *tcb;
  79.     struct socket lsocket,fsocket;
  80.  
  81.     lsocket.address = ip_addr;
  82.     lsocket.port = lport++;
  83.     printf("Lookup ... ");
  84.     if((fsocket.address = resolve(argv[1])) == 0){
  85.         printf(badhost,argv[1]);
  86.         return 1;
  87.     }
  88.     if(argc < 3)
  89.         fsocket.port = FTP_PORT;
  90.     else
  91.         fsocket.port = atoi(argv[2]);
  92.     printf(" done. Trying to connect to %s ...\n",
  93.            inet_ntoa( fsocket.address));
  94.     /* Allocate a session control block */
  95.     if((s = newsession()) == NULLSESSION){
  96.         printf("Too many sessions\n");
  97.         return 1;
  98.     }
  99.     current = s;
  100.     if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  101.         strcpy(s->name,argv[1]);
  102.     s->type = FTP;
  103.     s->parse = ftpparse;
  104.  
  105.     /* Allocate an FTP control block */
  106.     if((ftp = ftp_create(LINELEN)) == NULLFTP){
  107.         s->type = FREE;
  108.         printf(nospace);
  109.         return 1;
  110.     }
  111.     ftp->state = STARTUP_STATE;
  112.     s->cb.ftp = ftp;    /* Downward link */
  113.     ftp->session = s;    /* Upward link */
  114.  
  115.     /* Now open the control connection */
  116.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  117.         0,ftpccr,NULLVFP,ftpccs,0,(char *)ftp);
  118.     ftp->control = tcb;
  119.     go();
  120.     return 0;
  121. }
  122. /* Parse user FTP commands */
  123. int
  124. ftpparse(line,len)
  125. char *line;
  126. int16 len;
  127. {
  128.     struct mbuf *bp;
  129.  
  130.     switch(current->cb.ftp->state){
  131.     case RECEIVING_STATE:
  132.     case SENDING_STATE:
  133.         /* The only command allowed in data transfer state is ABORT */
  134.         if(cmdparse(ftpabort,line) == -1){
  135.             printf("Transfer in progress; only ABORT is acceptable\n");
  136.         }
  137.         fflush(stdout);
  138.         break;
  139.     case COMMAND_STATE:
  140.         /* Save it now because cmdparse modifies the original */
  141.         bp = qdata(line,len);
  142.  
  143.         if(cmdparse(ftpcmds,line) == -1){
  144.             /* Send it direct */
  145.             if(bp != NULLBUF)
  146.                 send_tcp(current->cb.ftp->control,bp);
  147.             else
  148.                 printf(nospace);
  149.         } else {
  150.             free_p(bp);
  151.         }
  152.         fflush(stdout);
  153.         break;
  154.     case STARTUP_STATE:        /* Starting up autologin */
  155.         printf("Not connected yet, ignoring %s\r\n",line);
  156.         break;
  157.     case USER_STATE:        /* Got the user name */
  158.         line[len] = '\0';
  159.         return sndftpmsg(current->cb.ftp,"USER %s",line);
  160.     case PASS_STATE:        /* Got the password */
  161.         cooked();
  162.         line[len] = '\0';
  163.         return sndftpmsg(current->cb.ftp,"PASS %s",line);
  164.     }
  165.     return 0;
  166. }
  167. /* Handle null line to avoid trapping on first command in table */
  168. /*ARGSUSED*/
  169. static
  170. int
  171. donothing(argc,argv)
  172. int argc;
  173. char *argv[];
  174. {
  175. }
  176. /* Translate 'cd' to 'cwd' for convenience */
  177. /*ARGSUSED*/
  178. static
  179. int
  180. doftpcd(argc,argv)
  181. int argc;
  182. char *argv[];
  183. {
  184.     register struct ftp *ftp;
  185.  
  186.     ftp = current->cb.ftp;
  187.     return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  188. }
  189. /* Translate 'mkdir' to 'xmkd' for convenience */
  190. /*ARGSUSED*/
  191. static
  192. int
  193. domkdir(argc,argv)
  194. int argc;
  195. char *argv[];
  196. {
  197.     register struct ftp *ftp;
  198.  
  199.     ftp = current->cb.ftp;
  200.     return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  201. }
  202. /* Translate 'rmdir' to 'xrmd' for convenience */
  203. /*ARGSUSED*/
  204. static
  205. int
  206. dormdir(argc,argv)
  207. int argc;
  208. char *argv[];
  209. {
  210.     register struct ftp *ftp;
  211.  
  212.     ftp = current->cb.ftp;
  213.     return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  214. }
  215. /* Handle "type" command from user */
  216. static
  217. int
  218. dotype(argc,argv)
  219. int argc;
  220. char *argv[];
  221. {
  222.     register struct ftp *ftp;
  223.  
  224.     ftp = current->cb.ftp;
  225.     if(argc < 2){
  226.         switch(ftp->type){
  227.         case IMAGE_TYPE:
  228.             printf("Image\n");
  229.             break;
  230.         case ASCII_TYPE:
  231.             printf("Ascii\n");
  232.             break;
  233.         }
  234.         return 0;
  235.     }
  236.     switch(*argv[1]){
  237.     case 'i':
  238.     case 'b':
  239.         ftp->type = IMAGE_TYPE;
  240.         sndftpmsg(ftp,"TYPE I\r\n");
  241.         break;
  242.     case 'a':
  243.         ftp->type = ASCII_TYPE;
  244.         sndftpmsg(ftp,"TYPE A\r\n");
  245.         break;
  246.     case 'l':
  247.         ftp->type = IMAGE_TYPE;
  248.         sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  249.         break;
  250.     default:
  251.         printf("Invalid type %s\n",argv[1]);
  252.         return 1;
  253.     }
  254.     return 0;
  255. }
  256. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  257. static
  258. doget(argc,argv)
  259. int argc;
  260. char *argv[];
  261. {
  262.     void ftpdr(),ftpcds();
  263.     char *remotename,*localname;
  264.     register struct ftp *ftp;
  265.     char *mode;
  266.  
  267.     ftp = current->cb.ftp;
  268.     if(ftp == NULLFTP){
  269.         printf(notsess);
  270.         return 1;
  271.     }
  272.     remotename = argv[1];
  273.     if(argc < 3)
  274.         localname = remotename;
  275.     else
  276.         localname = argv[2];
  277.  
  278.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  279.         fclose(ftp->fp);
  280.     ftp->fp = NULLFILE;
  281.  
  282.     if(ftp->type == IMAGE_TYPE) 
  283.         mode = binmode[WRITE_BINARY];
  284.     else
  285.         mode = "w";
  286.  
  287.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  288.         printf(cantwrite,localname);
  289.         return 1;
  290.     }
  291.     ftp->state = RECEIVING_STATE;
  292.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  293.  
  294.     /* Generate the command to start the transfer */
  295.     return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  296. }
  297. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  298. static
  299. dolist(argc,argv)
  300. int argc;
  301. char *argv[];
  302. {
  303.     void ftpdr(),ftpcds();
  304.     register struct ftp *ftp;
  305.  
  306.     ftp = current->cb.ftp;
  307.     if(ftp == NULLFTP){
  308.         printf(notsess);
  309.         return 1;
  310.     }
  311.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  312.         fclose(ftp->fp);
  313.     ftp->fp = NULLFILE;
  314.  
  315.     if(argc < 3){
  316.         ftp->fp = stdout;
  317.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  318.         printf(cantwrite,argv[2]);
  319.         return 1;
  320.     }
  321.     ftp->state = RECEIVING_STATE;
  322.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  323.     /* Generate the command to start the transfer
  324.      * It's done this way to avoid confusing the 4.2 FTP server
  325.      * if there's no argument
  326.      */
  327.     if(argc > 1)
  328.         return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  329.     else
  330.         return sndftpmsg(ftp,"LIST\r\n","");
  331. }
  332. /* Abbreviated (name only) list of remote directory.
  333.  * Syntax: ls <remote directory/file> [<local name>]
  334.  */
  335. static
  336. dols(argc,argv)
  337. int argc;
  338. char *argv[];
  339. {
  340.     void ftpdr(),ftpcds();
  341.     register struct ftp *ftp;
  342.  
  343.     ftp = current->cb.ftp;
  344.     if(ftp == NULLFTP){
  345.         printf(notsess);
  346.         return 1;
  347.     }
  348.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  349.         fclose(ftp->fp);
  350.     ftp->fp = NULLFILE;
  351.  
  352.     if(argc < 3){
  353.         ftp->fp = stdout;
  354.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  355.         printf(cantwrite,argv[2]);
  356.         return 1;
  357.     }
  358.     ftp->state = RECEIVING_STATE;
  359.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  360.     /* Generate the command to start the transfer */
  361.     if(argc > 1)
  362.         return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  363.     else
  364.         return sndftpmsg(ftp,"NLST\r\n","");
  365. }
  366. /* Start transmit. Syntax: put <local name> [<remote name>] */
  367. static
  368. doput(argc,argv)
  369. int argc;
  370. char *argv[];
  371. {
  372.     void ftpdt(),ftpcds();
  373.     char *remotename,*localname;
  374.     char *mode;
  375.     struct ftp *ftp;
  376.  
  377.     if((ftp = current->cb.ftp) == NULLFTP){
  378.         printf(notsess);
  379.         return 1;
  380.     }
  381.     localname = argv[1];
  382.     if(argc < 3)
  383.         remotename = localname;
  384.     else
  385.         remotename = argv[2];
  386.  
  387.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  388.         fclose(ftp->fp);
  389.  
  390.     if(ftp->type == IMAGE_TYPE) 
  391.         mode = binmode[READ_BINARY];
  392.     else
  393.         mode = "r";
  394.  
  395.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  396.         printf(cantread,localname);
  397.         return 1;
  398.     }
  399.     ftp->state = SENDING_STATE;
  400.     ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  401.  
  402.     /* Generate the command to start the transfer */
  403.     return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  404. }
  405. /* Abort a GET or PUT operation in progress. Note: this will leave
  406.  * the partial file on the local or remote system
  407.  */
  408. /*ARGSUSED*/
  409. doabort(argc,argv)
  410. int argc;
  411. char *argv[];
  412. {
  413.     register struct ftp *ftp;
  414.  
  415.     ftp = current->cb.ftp;
  416.  
  417.     /* Close the local file */
  418.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  419.         fclose(ftp->fp);
  420.     ftp->fp = NULLFILE;
  421.  
  422.     switch(ftp->state){
  423.     case SENDING_STATE:
  424.         /* Send a premature EOF.
  425.          * Unfortunately we can't just reset the connection
  426.          * since the remote side might end up waiting forever
  427.          * for us to send something.
  428.          */
  429.         close_tcp(ftp->data);
  430.         printf("Put aborted\n");
  431.         break;
  432.     case RECEIVING_STATE:
  433.         /* Just exterminate the data channel TCB; this will
  434.          * generate a RST on the next data packet which will
  435.          * abort the sender
  436.          */
  437.         del_tcp(ftp->data);
  438.         ftp->data = NULLTCB;
  439.         printf("Get aborted\n");
  440.         break;
  441.     }
  442.     ftp->state = COMMAND_STATE;
  443.     fflush(stdout);
  444. }
  445. /* create data port, and send PORT message */
  446. static
  447. ftpsetup(ftp,recv,send,state)
  448. struct ftp *ftp;
  449. void (*send)();
  450. void (*recv)();
  451. void (*state)();
  452. {
  453.     struct socket lsocket;
  454.     struct mbuf *bp;
  455.  
  456.     lsocket.address = ip_addr;
  457.     lsocket.port = lport++;
  458.  
  459.     /* Compose and send PORT a,a,a,a,p,p message */
  460.  
  461.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  462.         printf(nospace);
  463.         return;
  464.     }
  465.     /* I know, this looks gross, but it works! */
  466.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  467.         hibyte(hiword(lsocket.address)),
  468.         lobyte(hiword(lsocket.address)),
  469.         hibyte(loword(lsocket.address)),
  470.         lobyte(loword(lsocket.address)),
  471.         hibyte(lsocket.port),
  472.         lobyte(lsocket.port));
  473.     bp->cnt = strlen(bp->data);
  474.     send_tcp(ftp->control,bp);
  475.  
  476.     /* Post a listen on the data connection */
  477.     ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  478.         recv,send,state,0,(char *)ftp);
  479. }
  480. /* FTP Client Control channel Receiver upcall routine */
  481. void
  482. ftpccr(tcb,cnt)
  483. register struct tcb *tcb;
  484. int16 cnt;
  485. {
  486.     struct mbuf *bp;
  487.     struct ftp *ftp;
  488.     void doreply();
  489.     char c;
  490.  
  491.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  492.         /* Unknown connection; kill it */
  493.         close_tcp(tcb);
  494.         return;
  495.     }
  496.     /* Hold output if we're not the current session */
  497.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
  498.         return;
  499.  
  500.     if(recv_tcp(tcb,&bp,cnt) > 0){
  501.         while(pullup(&bp,&c,1) == 1){
  502.             switch(c){
  503.             case '\r':    /* Strip cr's */
  504.                 continue;
  505.             case '\n':    /* Complete line; process it */
  506.                 ftp->buf[ftp->cnt] = '\0';
  507.                 doreply(ftp);
  508.                 ftp->cnt = 0;
  509.                 break;
  510.             default:    /* Assemble line */
  511.                 if(ftp->cnt != LINELEN-1)
  512.                     ftp->buf[ftp->cnt++] = c;
  513.                 break;
  514.             }
  515.         }
  516.         fflush(stdout);
  517.     }
  518. }
  519.  
  520.  
  521. /* Process replies from the server */
  522. static
  523. void
  524. doreply(ftp)
  525. register struct ftp *ftp;
  526. {
  527.     void echo(), noecho();
  528.     static char crlf[]="\n";
  529.  
  530.     fwrite(ftp->buf,1,(unsigned)ftp->cnt,stdout);
  531.     fputc('\n', stdout);
  532.     if (ftp->cnt < 3) return;
  533.     ftp->buf[3] = '\0';
  534.     switch(ftp->state){
  535.     case SENDING_STATE:
  536.     case RECEIVING_STATE:
  537.         if (ftp->buf[0] == '5') doabort(0,(char *)0);
  538.         break;
  539.     case STARTUP_STATE:
  540.         if (!strcmp(ftp->buf, "220")){
  541.             ftp->state = USER_STATE;
  542.             printf("Enter user name: ");
  543.             fflush(stdout);
  544.         } else ftp->state = COMMAND_STATE;
  545.         break;
  546.     case COMMAND_STATE:
  547.     case USER_STATE:
  548.         if (!strcmp(ftp->buf, "331")) {
  549.             ftp->state = PASS_STATE;
  550.             noecho();
  551.             printf("Password: ");
  552.             fflush(stdout);
  553.         } else ftp->state = COMMAND_STATE;
  554.         break;
  555.     case PASS_STATE:
  556.         echo();
  557.         ftp->state = COMMAND_STATE;
  558.         break;
  559.     }
  560. }
  561.  
  562. /* FTP Client Control channel State change upcall routine */
  563. /*ARGSUSED*/
  564. static
  565. void
  566. ftpccs(tcb,old,new)
  567. register struct tcb *tcb;
  568. char old,new;
  569. {
  570.     void ftp_delete();
  571.     struct ftp *ftp;
  572.     char notify = 0;
  573.     extern char *tcpstates[];
  574.     extern char *reasons[];
  575.     extern char *unreach[];
  576.     extern char *exceed[];
  577.  
  578.     /* Can't add a check for unknown connection here, it would loop
  579.      * on a close upcall! We're just careful later on.
  580.      */
  581.     ftp = (struct ftp *)tcb->user;
  582.  
  583.     if(current != NULLSESSION && current->cb.ftp == ftp)
  584.         notify = 1;
  585.  
  586.     switch(new){
  587.     case CLOSE_WAIT:
  588.         if(notify)
  589.             printf("%s\n",tcpstates[new]);
  590.         close_tcp(tcb);
  591.         break;
  592.     case CLOSED:    /* heh heh */
  593.         if(notify){
  594.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  595.             if(tcb->reason == NETWORK){
  596.                 switch(tcb->type){
  597.                 case DEST_UNREACH:
  598.                     printf(": %s unreachable",unreach[tcb->code]);
  599.                     break;
  600.                 case TIME_EXCEED:
  601.                     printf(": %s time exceeded",exceed[tcb->code]);
  602.                     break;
  603.                 }
  604.             }
  605.             printf(")\n");
  606.             cmdmode();
  607.         }
  608.         del_tcp(tcb);
  609.         if(ftp != NULLFTP)
  610.             ftp_delete(ftp);
  611.         break;
  612.     default:
  613.         if(notify)
  614.             printf("%s\n",tcpstates[new]);
  615.         break;
  616.     }
  617.     if(notify)
  618.         fflush(stdout);
  619. }
  620. /* FTP Client Data channel State change upcall handler */
  621. /*ARGSUSED*/
  622. static
  623. void
  624. ftpcds(tcb,old,new)
  625. struct tcb *tcb;
  626. char old,new;
  627. {
  628.     struct ftp *ftp;
  629.  
  630.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  631.         /* Unknown connection, kill it */
  632.         close_tcp(tcb);
  633.         return;
  634.     }
  635.     switch(new){
  636.     case FINWAIT2:
  637.     case TIME_WAIT:
  638.         if(ftp->state == SENDING_STATE){
  639.             /* We've received an ack of our FIN, so
  640.              * return to command mode
  641.              */
  642.             ftp->state = COMMAND_STATE;
  643.             if(current != NULLSESSION && current->cb.ftp == ftp){
  644.                 printf("Put complete, %lu bytes sent\n",
  645.                     tcb->snd.una - tcb->iss - 2);
  646.                 fflush(stdout);
  647.             }
  648.         }
  649.         break;        
  650.     case CLOSE_WAIT:
  651.         close_tcp(tcb);
  652.         if(ftp->state == RECEIVING_STATE){
  653.             /* End of file received on incoming file */
  654. #ifdef    CPM
  655.             if(ftp->type == ASCII_TYPE)
  656.                 putc(CTLZ,ftp->fp);
  657. #endif
  658.             if(ftp->fp != stdout)
  659.                 fclose(ftp->fp);
  660.             ftp->fp = NULLFILE;
  661.             ftp->state = COMMAND_STATE;
  662.             if(current != NULLSESSION && current->cb.ftp == ftp){
  663.                 printf("Get complete, %lu bytes received\n",
  664.                     tcb->rcv.nxt - tcb->irs - 2);
  665.                 fflush(stdout);
  666.             }
  667.         }
  668.         break;
  669.     case CLOSED:
  670.         ftp->data = NULLTCB;
  671.         del_tcp(tcb);
  672.         break;
  673.     }
  674. }
  675. /* Send a message on the control channel */
  676. /*VARARGS*/
  677. static
  678. int
  679. sndftpmsg(ftp,fmt,arg)
  680. struct ftp *ftp;
  681. char *fmt;
  682. char *arg;
  683. {
  684.     struct mbuf *bp;
  685.     int16 len;
  686.  
  687.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  688.     if((bp = alloc_mbuf(len)) == NULLBUF){
  689.         printf(nospace);
  690.         return 1;
  691.     }
  692.     sprintf(bp->data,fmt,arg);
  693.     bp->cnt = strlen(bp->data);
  694.     send_tcp(ftp->control,bp);
  695.     return 0;
  696. }
  697.