| /**************************************************************************** |
| * examples/ftpc/ftpc_main.c |
| * |
| * Copyright (C) 2011-2013 Gregory Nutt. All rights reserved. |
| * Author: Gregory Nutt <gnutt@nuttx.org> |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * 3. Neither the name NuttX nor the names of its contributors may be |
| * used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include <arpa/inet.h> |
| #include <apps/ftpc.h> |
| |
| #include <apps/readline.h> |
| |
| #include "ftpc.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define FTPC_MAX_ARGUMENTS 4 |
| |
| /**************************************************************************** |
| * Public Types |
| ****************************************************************************/ |
| |
| struct cmdmap_s |
| { |
| const char *cmd; /* Name of the command */ |
| cmd_t handler; /* Function that handles the command */ |
| uint8_t minargs; /* Minimum number of arguments (including command) */ |
| uint8_t maxargs; /* Maximum number of arguments (including command) */ |
| const char *usage; /* Usage instructions for 'help' command */ |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const char g_delim[] = " \t\n"; |
| |
| static int cmd_lhelp(SESSION handle, int argc, char **argv); |
| static int cmd_lunrecognized(SESSION handle, int argc, char **argv); |
| |
| static const struct cmdmap_s g_cmdmap[] = |
| { |
| { "cd", cmd_rchdir, 2, 2, "<directory>" }, |
| { "chmod", cmd_rchmod, 3, 3, "<permissions> <path>" }, |
| { "get", cmd_rget, 2, 4, "[-a|b] <rname> [<lname>]" }, |
| { "help", cmd_lhelp, 1, 2, "" }, |
| { "idle", cmd_ridle, 1, 2, "[<idletime>]" }, |
| { "login", cmd_rlogin, 2, 3, "<uname> [<password>]" }, |
| { "ls", cmd_rls, 1, 2, "[<dirpath>]" }, |
| { "quit", cmd_rquit, 1, 1, "" }, |
| { "mkdir", cmd_rmkdir, 2, 2, "<directory>" }, |
| { "noop", cmd_rnoop, 1, 1, "" }, |
| { "put", cmd_rput, 2, 4, "[-a|b] <lname> [<rname>]" }, |
| { "pwd", cmd_rpwd, 1, 1, "" }, |
| { "rename", cmd_rrename, 3, 3, "<oldname> <newname>" }, |
| { "rhelp", cmd_rhelp, 1, 2, "[<command>]" }, |
| { "rm", cmd_runlink, 2, 2, "" }, |
| { "rmdir", cmd_rrmdir, 2, 2, "<directory>" }, |
| { "size", cmd_rsize, 2, 2, "<filepath>" }, |
| { "time", cmd_rtime, 2, 2, "<filepath>" }, |
| { "up", cmd_rcdup, 1, 1, "" }, |
| { NULL, NULL, 1, 1, NULL } |
| }; |
| |
| static char g_line[CONFIG_FTPC_LINELEN]; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: cmd_lhelp |
| ****************************************************************************/ |
| |
| static int cmd_lhelp(SESSION handle, int argc, char **argv) |
| { |
| const struct cmdmap_s *ptr; |
| |
| printf("Local FTPC commands:\n"); |
| for (ptr = g_cmdmap; ptr->cmd; ptr++) |
| { |
| if (ptr->usage) |
| { |
| printf(" %s %s\n", ptr->cmd, ptr->usage); |
| } |
| else |
| { |
| printf(" %s\n", ptr->cmd); |
| } |
| } |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: cmd_lunrecognized |
| ****************************************************************************/ |
| |
| static int cmd_lunrecognized(SESSION handle, int argc, char **argv) |
| { |
| printf("Command %s unrecognized\n", argv[0]); |
| return ERROR; |
| } |
| |
| /**************************************************************************** |
| * Name: ftpc_argument |
| ****************************************************************************/ |
| |
| char *ftpc_argument(char **saveptr) |
| { |
| char *pbegin = *saveptr; |
| char *pend = NULL; |
| const char *term; |
| |
| /* Find the beginning of the next token */ |
| |
| for (; |
| *pbegin && strchr(g_delim, *pbegin) != NULL; |
| pbegin++); |
| |
| /* If we are at the end of the string with nothing |
| * but delimiters found, then return NULL. |
| */ |
| |
| if (!*pbegin) |
| { |
| return NULL; |
| } |
| |
| /* Does the token begin with '#' -- comment */ |
| |
| else if (*pbegin == '#') |
| { |
| /* Return NULL meaning that we are at the end of the line */ |
| |
| *saveptr = pbegin; |
| pbegin = NULL; |
| } |
| else |
| { |
| /* Otherwise, we are going to have to parse to find the end of |
| * the token. Does the token begin with '"'? |
| */ |
| |
| if (*pbegin == '"') |
| { |
| /* Yes.. then only another '"' can terminate the string */ |
| |
| pbegin++; |
| term = "\""; |
| } |
| else |
| { |
| /* No, then any of the usual terminators will terminate the argument */ |
| |
| term = g_delim; |
| } |
| |
| /* Find the end of the string */ |
| |
| for (pend = pbegin + 1; |
| *pend && strchr(term, *pend) == NULL; |
| pend++); |
| |
| /* pend either points to the end of the string or to |
| * the first delimiter after the string. |
| */ |
| |
| if (*pend) |
| { |
| /* Turn the delimiter into a null terminator */ |
| |
| *pend++ = '\0'; |
| } |
| |
| /* Save the pointer where we left off */ |
| |
| *saveptr = pend; |
| |
| } |
| |
| /* Return the beginning of the token. */ |
| |
| return pbegin; |
| } |
| |
| /**************************************************************************** |
| * Name: ftpc_execute |
| ****************************************************************************/ |
| |
| static int ftpc_execute(SESSION handle, int argc, char *argv[]) |
| { |
| const struct cmdmap_s *cmdmap; |
| const char *cmd; |
| cmd_t handler = cmd_lunrecognized; |
| int ret; |
| |
| /* The form of argv is: |
| * |
| * argv[0]: The command name. This is argv[0] when the arguments |
| * are, finally, received by the command handler |
| * argv[1]: The beginning of argument (up to FTPC_MAX_ARGUMENTS) |
| * argv[argc]: NULL terminating pointer |
| */ |
| |
| cmd = argv[0]; |
| |
| /* See if the command is one that we understand */ |
| |
| for (cmdmap = g_cmdmap; cmdmap->cmd; cmdmap++) |
| { |
| if (strcmp(cmdmap->cmd, cmd) == 0) |
| { |
| /* Check if a valid number of arguments was provided. We |
| * do this simple, imperfect checking here so that it does |
| * not have to be performed in each command. |
| */ |
| |
| if (argc < cmdmap->minargs) |
| { |
| /* Fewer than the minimum number were provided */ |
| |
| printf("Too few arguments for '%s'\n", cmd); |
| return ERROR; |
| } |
| else if (argc > cmdmap->maxargs) |
| { |
| /* More than the maximum number were provided */ |
| |
| printf("Too many arguments for '%s'\n", cmd); |
| return ERROR; |
| } |
| else |
| { |
| /* A valid number of arguments were provided (this does |
| * not mean they are right). |
| */ |
| |
| handler = cmdmap->handler; |
| break; |
| } |
| } |
| } |
| |
| ret = handler(handle, argc, argv); |
| if (ret < 0) |
| { |
| printf("%s failed: %d\n", cmd, errno); |
| } |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: ftpc_parse |
| ****************************************************************************/ |
| |
| int ftpc_parse(SESSION handle, char *cmdline) |
| { |
| FAR char *argv[FTPC_MAX_ARGUMENTS+1]; |
| FAR char *saveptr; |
| FAR char *cmd; |
| int argc; |
| int ret; |
| |
| /* Initialize parser state */ |
| |
| memset(argv, 0, FTPC_MAX_ARGUMENTS*sizeof(FAR char *)); |
| |
| /* Parse out the command at the beginning of the line */ |
| |
| saveptr = cmdline; |
| cmd = ftpc_argument(&saveptr); |
| |
| /* Check if any command was provided -OR- if command processing is |
| * currently disabled. |
| */ |
| |
| if (!cmd) |
| { |
| /* An empty line is not an error */ |
| |
| return OK; |
| } |
| |
| /* Parse all of the arguments following the command name. */ |
| |
| argv[0] = cmd; |
| for (argc = 1; argc < FTPC_MAX_ARGUMENTS; argc++) |
| { |
| argv[argc] = ftpc_argument(&saveptr); |
| if (!argv[argc]) |
| { |
| break; |
| } |
| } |
| argv[argc] = NULL; |
| |
| /* Check if the maximum number of arguments was exceeded */ |
| |
| if (argc > FTPC_MAX_ARGUMENTS) |
| { |
| printf("Too many arguments\n"); |
| ret = -EINVAL; |
| } |
| else |
| { |
| /* Then execute the command */ |
| |
| ret = ftpc_execute(handle, argc, argv); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_BUILD_KERNEL |
| int main(int argc, FAR char *argv[]) |
| #else |
| int ftpc_main(int argc, char **argv, char **envp) |
| #endif |
| { |
| struct ftpc_connect_s connect = {{0}, 0}; |
| SESSION handle; |
| FAR char *ptr; |
| #ifndef CONFIG_EXAMPLES_FTPC_FGETS |
| int ret; |
| #endif |
| |
| if (argc != 2) |
| { |
| printf("Usage:\n"); |
| printf(" %s xx.xx.xx.xx[:pp]\n", argv[0]); |
| printf("Where\n"); |
| printf(" xx.xx.xx.xx is the IP address of the FTP server\n"); |
| printf(" pp is option port to use with the FTP server\n"); |
| exit(1); |
| } |
| |
| /* Check if the argument includes a port number */ |
| |
| ptr = strchr(argv[1], ':'); |
| if (ptr) |
| { |
| *ptr = '\0'; |
| connect.port = atoi(ptr+1); |
| } |
| |
| /* In any event, we can now extract the IP address from the comman-line */ |
| |
| connect.addr.s_addr = inet_addr(argv[1]); |
| |
| /* Connect to the FTP server */ |
| |
| handle = ftpc_connect(&connect); |
| if (!handle) |
| { |
| printf("Failed to connect to the server: %d\n", errno); |
| exit(1); |
| } |
| |
| /* Present a greeting */ |
| |
| printf("NuttX FTP Client:\n"); |
| FFLUSH(); |
| |
| /* Setting optind to -1 is a non-standard, backdoor way to reinitialize |
| * getopt(). getopt() is not thread safe and we have no idea what state |
| * it is in now! |
| */ |
| |
| optind = -1; |
| |
| /* Then enter the command line parsing loop */ |
| |
| for (;;) |
| { |
| /* Display the prompt string */ |
| |
| fputs("nfc> ", stdout); |
| FFLUSH(); |
| |
| /* Get the next line of input */ |
| |
| #ifdef CONFIG_EXAMPLES_FTPC_FGETS |
| /* fgets returns NULL on end-of-file or any I/O error */ |
| |
| if (fgets(g_line, CONFIG_FTPC_LINELEN, stdin) == NULL) |
| { |
| printf("ERROR: fgets failed: %d\n", errno); |
| return 1; |
| } |
| #else |
| ret = readline(g_line, CONFIG_FTPC_LINELEN, stdin, stdout); |
| |
| /* Readline normally returns the number of characters read, |
| * but will return EOF on end of file or if an error occurs. |
| * Either will cause the session to terminate. |
| */ |
| |
| if (ret == EOF) |
| { |
| printf("ERROR: readline failed: %d\n", errno); |
| return 1; |
| } |
| #endif |
| else |
| { |
| /* Parse and process the command */ |
| |
| (void)ftpc_parse(handle, g_line); |
| FFLUSH(); |
| } |
| } |
| |
| return 0; |
| } |