Newer
Older
/****************************************************************************
* Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#if CONFIG_NFILE_DESCRIPTORS > 0
# include <sys/stat.h>
# include <fcntl.h>
# if !defined(CONFIG_DISABLE_MOUNTPOINT)
# ifdef CONFIG_FS_FAT /* Need at least one filesytem in configuration */
# include <sys/mount.h>
# include <nuttx/mkfatfs.h>
# endif
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <limits.h>
#include "nsh.h"
/****************************************************************************
* Definitions
****************************************************************************/
#define LSFLAGS_SIZE 1
#define LSFLAGS_LONG 2
#define LSFLAGS_RECURSIVE 4
/* The size of the I/O buffer may be specified in the
* configs/<board-name>defconfig file -- provided that it is at least as
* large as PATH_MAX.
*/
#if CONFIG_NFILE_DESCRIPTORS > 0
# ifdef CONFIG_EXAMPLES_NSH_FILEIOSIZE
# if CONFIG_EXAMPLES_NSH_FILEIOSIZE > (PATH_MAX + 1)
# define IOBUFFERSIZE CONFIG_EXAMPLES_NSH_FILEIOSIZE
# else
# define IOBUFFERSIZE (PATH_MAX + 1)
# endif
# else
# endif
# else
# define IOBUFFERSIZE (PATH_MAX + 1)
#endif
/****************************************************************************
* Private Types
****************************************************************************/
typedef int (*direntry_handler_t)(FAR struct nsh_vtbl_s *, const char *, struct dirent *, void *);
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/* Common buffer for file I/O. Note the use of this common buffer precludes
* multiple copies of NSH running concurrently. It should be allocated per
* NSH instance and retained in the "vtbl" as is done for the telnet
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: trim_dir
****************************************************************************/
static void trim_dir(char *arg)
{
/* Skip any trailing '/' characters (unless it is also the leading '/') */
int len = strlen(arg) - 1;
while (len > 0 && arg[len] == '/')
{
arg[len] = '\0';
len--;
}
}
/****************************************************************************
****************************************************************************/
static char *nsh_getdirpath(const char *path, const char *file)
g_iobuffer[PATH_MAX] = '\0';
return strdup(g_iobuffer);
}
/****************************************************************************
* Name: foreach_direntry
****************************************************************************/
static int foreach_direntry(FAR struct nsh_vtbl_s *vtbl, const char *cmd, const char *dirpath,
direntry_handler_t handler, void *pvarg)
{
DIR *dirp;
int ret = OK;
/* Trim trailing '/' from directory names */
trim_dir(arg);
#endif
/* Open the directory */
dirp = opendir(dirpath);
if (!dirp)
{
/* Failed to open the directory */
nsh_output(vtbl, g_fmtnosuch, cmd, "directory", dirpath);
return ERROR;
}
/* Read each directory entry */
for (;;)
{
struct dirent *entryp = readdir(dirp);
if (!entryp)
{
/* Finished with this directory */
break;
}
/* Call the handler with this directory entry */
{
/* The handler reported a problem */
ret = ERROR;
break;
}
}
closedir(dirp);
return ret;
}
/****************************************************************************
* Name: ls_handler
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
static int ls_handler(FAR struct nsh_vtbl_s *vtbl, const char *dirpath, struct dirent *entryp, void *pvarg)
{
unsigned int lsflags = (unsigned int)pvarg;
int ret;
/* Check if any options will require that we stat the file */
if ((lsflags & (LSFLAGS_SIZE|LSFLAGS_LONG)) != 0)
{
struct stat buf;
char *fullpath = nsh_getdirpath(dirpath, entryp->d_name);
/* Yes, stat the file */
ret = stat(fullpath, &buf);
free(fullpath);
if (ret != 0)
{
nsh_output(vtbl, g_fmtcmdfailed, "ls", "stat", NSH_ERRNO);
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
}
if ((lsflags & LSFLAGS_LONG) != 0)
{
char details[] = "----------";
if (S_ISDIR(buf.st_mode))
{
details[0]='d';
}
else if (S_ISCHR(buf.st_mode))
{
details[0]='c';
}
else if (S_ISBLK(buf.st_mode))
{
details[0]='b';
}
if ((buf.st_mode & S_IRUSR) != 0)
{
details[1]='r';
}
if ((buf.st_mode & S_IWUSR) != 0)
{
details[2]='w';
}
if ((buf.st_mode & S_IXUSR) != 0)
{
details[3]='x';
}
if ((buf.st_mode & S_IRGRP) != 0)
{
details[4]='r';
}
if ((buf.st_mode & S_IWGRP) != 0)
{
details[5]='w';
}
if ((buf.st_mode & S_IXGRP) != 0)
{
details[6]='x';
}
if ((buf.st_mode & S_IROTH) != 0)
{
details[7]='r';
}
if ((buf.st_mode & S_IWOTH) != 0)
{
details[8]='w';
}
if ((buf.st_mode & S_IXOTH) != 0)
{
details[9]='x';
}
}
}
/* then provide the filename that is common to normal and verbose output */
#endif
if (DIRENT_ISDIRECTORY(entryp->d_type))
{
}
return OK;
}
#endif
/****************************************************************************
* Name: ls_recursive
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
static int ls_recursive(FAR struct nsh_vtbl_s *vtbl, const char *dirpath, struct dirent *entryp, void *pvarg)
/* Is this entry a directory? */
if (DIRENT_ISDIRECTORY(entryp->d_type))
{
/* Yes.. */
char *newpath;
ret = foreach_direntry(vtbl, "ls", newpath, ls_handler, pvarg);
if (ret == 0)
{
/* Then recurse to list each directory within the directory */
ret = foreach_direntry(vtbl, "ls", newpath, ls_recursive, pvarg);
free(newpath);
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: cmd_cat
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
int cmd_cat(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
/* Loop for each file name on the command line */
fullpath = nsh_getfullpath(vtbl, argv[i]);
if (fullpath)
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
}
else
{
/* And just dump it byte for byte into stdout */
/* Check for read errors */
if (nbytesread < 0)
if (errno != EINTR)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "read", NSH_ERRNO);
ret = ERROR;
break;
}
}
while (nbyteswritten < nbytesread)
{
int n = write(1, buffer, nbytesread);
if (n < 0)
/* EINTR is not an error */
if (errno != EINTR)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "write", NSH_ERRNO);
ret = ERROR;
break;
}
}
else
{
nbyteswritten += n;
/* Free the allocated full path */
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_cp
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
char *srcpath = NULL;
char *destpath = NULL;
char *allocpath = NULL;
int oflags = O_WRONLY|O_CREAT|O_TRUNC;
int rdfd;
int wrfd;
/* Get the full path to the source file */
srcpath = nsh_getfullpath(vtbl, argv[1]);
if (!srcpath)
{
goto errout;
}
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
goto errout_with_srcpath;
}
/* Get the full path to the destination file or directory */
destpath = nsh_getfullpath(vtbl, argv[2]);
if (!destpath)
{
goto errout_with_rdfd;
}
/* Check if the destination is a directory */
if (ret == 0)
{
/* Something exists here... is it a directory? */
if (S_ISDIR(buf.st_mode))
{
/* Yes, it is a directory. Remove any trailing '/' characters from the path */
trim_dir(argv[2]);
/* Construct the full path to the new file */
allocpath = nsh_getdirpath(argv[2], basename(argv[1]) );
if (!allocpath)
/* Open then dest for writing */
nsh_freefullpath(destpath);
destpath = allocpath;
}
else if (!S_ISREG(buf.st_mode))
{
/* Maybe it is a driver? */
oflags = O_WRONLY;
}
}
/* Now open the destination */
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
}
/* Now copy the file */
for (;;)
{
int nbytesread;
int nbyteswritten;
do
{
nbytesread = read(rdfd, g_iobuffer, IOBUFFERSIZE);
if (nbytesread == 0)
{
/* End of file */
}
else if (nbytesread < 0 && errno != EINTR)
{
/* Read error */
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "read", NSH_ERRNO);
}
}
while (nbytesread <= 0);
do
{
nbyteswritten = write(wrfd, g_iobuffer, nbytesread);
if (nbyteswritten >= 0)
{
nbytesread -= nbyteswritten;
}
else if (errno != EINTR)
{
/* Read error */
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "write", NSH_ERRNO);
errout_with_destpath:
if (destpath && !allocpath)
{
nsh_freefullpath(destpath);
}
errout_with_rdfd:
errout_with_srcpath:
if (srcpath)
{
nsh_freefullpath(srcpath);
}
errout:
/****************************************************************************
* Name: cmd_ls
****************************************************************************/
#if CONFIG_NFILE_DESCRIPTORS > 0
int cmd_ls(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
int ret;
/* Get the ls options */
int option;
while ((option = getopt(argc, argv, "lRs")) != ERROR)
{
switch (option)
{
case 'l':
lsflags |= (LSFLAGS_SIZE|LSFLAGS_LONG);
break;
case 'R':
lsflags |= LSFLAGS_RECURSIVE;
break;
case 's':
lsflags |= LSFLAGS_SIZE;
break;
case '?':
default:
#ifndef CONFIG_DISABLE_ENVIRON
relpath = nsh_getcwd();
#else
#endif
}
else
{
relpath = argv[optind];
}
/* Get the fullpath to the directory */
fullpath = nsh_getfullpath(vtbl, relpath);
if (!fullpath)
{
return ERROR;
nsh_output(vtbl, "%s:\n", fullpath);
ret = foreach_direntry(vtbl, "ls", fullpath, ls_handler, (void*)lsflags);
if (ret == OK && (lsflags & LSFLAGS_RECURSIVE) != 0)
{
/* Then recurse to list each directory within the directory */
ret = foreach_direntry(vtbl, "ls", fullpath, ls_recursive, (void*)lsflags);
}
#endif
/****************************************************************************
* Name: cmd_mkdir
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
int cmd_mkdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
char *fullpath = nsh_getfullpath(vtbl, argv[1]);
int ret = ERROR;
if (fullpath)
ret = mkdir(fullpath, 0777);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkdir", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_mkfatfs
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0 && defined(CONFIG_FS_FAT)
int cmd_mkfatfs(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
struct fat_format_s fmt = FAT_FORMAT_INITIALIZER;
char *fullpath = nsh_getfullpath(vtbl, argv[1]);
int ret = ERROR;
if (fullpath)
{
ret = mkfatfs(fullpath, &fmt);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkfatfs", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_mkfifo
****************************************************************************/
int cmd_mkfifo(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
char *fullpath = nsh_getfullpath(vtbl, argv[1]);
int ret = ERROR;
if (fullpath)
ret = mkfifo(fullpath, 0777);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mkfifo", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
/****************************************************************************
* Name: cmd_mkrd
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
int cmd_mkrd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
const char *fmt;
ubyte *buffer;
uint32 nsectors;
int sectsize = 512;
int minor = 0;
int ret;
/* Get the mount options */
int option;
while ((option = getopt(argc, argv, ":m:s:")) != ERROR)
{
switch (option)
{
case 'm':
minor = atoi(optarg);
if (minor < 0 || minor > 255)
{
fmt = g_fmtargrange;
goto errout_with_fmt;
}
break;
case 's':
sectsize = atoi(optarg);
if (minor < 0 || minor > 16384)
{
fmt = g_fmtargrange;
goto errout_with_fmt;
}
break;
case ':':
fmt = g_fmtargrequired;
goto errout_with_fmt;
case '?':
default:
fmt = g_fmtarginvalid;
goto errout_with_fmt;
}
}
/* There should be exactly on parameter left on the command-line */
if (optind == argc-1)
{
nsectors = (uint32)atoi(argv[optind]);
}
else if (optind >= argc)
{
fmt = g_fmttoomanyargs;
goto errout_with_fmt;
}
else
{
fmt = g_fmtargrequired;
goto errout_with_fmt;
}
/* Allocate the memory backing up the ramdisk */
buffer = (ubyte*)malloc(sectsize * nsectors);
if (!buffer)
{
fmt = g_fmtcmdoutofmemory;
goto errout_with_fmt;
}
/* Then register the ramdisk */
ret = rd_register(minor, buffer, nsectors, sectsize, TRUE);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "rd_register", NSH_ERRNO_OF(-ret));
free(buffer);
return ERROR;
}
return ret;
errout_with_fmt:
nsh_output(vtbl, fmt, argv[0]);
return ERROR;
}
#endif
/****************************************************************************
* Name: cmd_mount
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
#ifdef CONFIG_FS_FAT /* Need at least one filesytem in configuration */
int cmd_mount(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
/* Get the mount options */
int option;
while ((option = getopt(argc, argv, ":t:")) != ERROR)
{
switch (option)
{
case 't':
filesystem = optarg;
break;
case ':':
}
}
/* There are two required arguments after the options */
if (optind + 2 < argc)
{
/* The source and target pathes might be relative to the current
* working directory.
*/
source = nsh_getfullpath(vtbl, argv[optind]);
if (!source)
{
return ERROR;
}
target = nsh_getfullpath(vtbl, argv[optind+1]);
if (!source)
{
nsh_freefullpath(source);
return ERROR;
}
ret = mount(source, target, filesystem, 0, NULL);
if (ret < 0)
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "mount", NSH_ERRNO);
nsh_freefullpath(source);
nsh_freefullpath(target);
return ret;
/****************************************************************************
* Name: cmd_rm
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
int cmd_rm(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
ret = unlink(fullpath);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "unlink", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
}
#endif
/****************************************************************************
****************************************************************************/
#if !defined(CONFIG_DISABLE_MOUNTPOINT) && CONFIG_NFILE_DESCRIPTORS > 0
int cmd_rmdir(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
ret = rmdir(fullpath);
if (ret < 0)
{
nsh_output(vtbl, g_fmtcmdfailed, argv[0], "rmdir", NSH_ERRNO);
}
nsh_freefullpath(fullpath);
/****************************************************************************
* Name: cmd_sh
****************************************************************************/