2 * Implements the file command for jim
4 * (c) 2008 Steve Bennett <steveb@workware.net.au>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and documentation
31 * are those of the authors and should not be interpreted as representing
32 * official policies, either expressed or implied, of the Jim Tcl Project.
34 * Based on code originally from Tcl 6.7:
36 * Copyright 1987-1991 Regents of the University of California
37 * Permission to use, copy, modify, and distribute this
38 * software and its documentation for any purpose and without
39 * fee is hereby granted, provided that the above copyright
40 * notice appear in all copies. The University of California
41 * makes no representations about the suitability of this
42 * software for any purpose. It is provided "as is" without
43 * express or implied warranty.
52 #include <jimautoconf.h>
53 #include <jim-subcmd.h>
54 #include <jimiocompat.h>
61 #elif defined(_MSC_VER)
66 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
67 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
71 # define MAXPATHLEN JIM_PATH_LEN
74 #if defined(__MINGW32__) || defined(__MSYS__) || defined(_MSC_VER)
80 /* extract nanosecond resolution mtime from struct stat */
81 #if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC)
82 #define STAT_MTIME_US(STAT) ((STAT).st_mtimespec.tv_sec * 1000000ll + (STAT).st_mtimespec.tv_nsec / 1000)
83 #elif defined(HAVE_STRUCT_STAT_ST_MTIM)
84 #define STAT_MTIME_US(STAT) ((STAT).st_mtim.tv_sec * 1000000ll + (STAT).st_mtim.tv_nsec / 1000)
88 *----------------------------------------------------------------------
92 * Given a mode word, returns a string identifying the type of a
96 * A static text string giving the file type from mode.
101 *----------------------------------------------------------------------
104 static const char *JimGetFileType(int mode
)
109 else if (S_ISDIR(mode
)) {
113 else if (S_ISCHR(mode
)) {
114 return "characterSpecial";
118 else if (S_ISBLK(mode
)) {
119 return "blockSpecial";
123 else if (S_ISFIFO(mode
)) {
128 else if (S_ISLNK(mode
)) {
133 else if (S_ISSOCK(mode
)) {
141 *----------------------------------------------------------------------
145 * This is a utility procedure that breaks out the fields of a
146 * "stat" structure and stores them in textual form into the
147 * elements of an associative array.
150 * Returns a standard Tcl return value. If an error occurs then
151 * a message is left in interp->result.
154 * Elements of the associative array given by "varName" are modified.
156 *----------------------------------------------------------------------
158 static void AppendStatElement(Jim_Interp
*interp
, Jim_Obj
*listObj
, const char *key
, jim_wide value
)
160 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, key
, -1));
161 Jim_ListAppendElement(interp
, listObj
, Jim_NewIntObj(interp
, value
));
164 static int StoreStatData(Jim_Interp
*interp
, Jim_Obj
*varName
, const jim_stat_t
*sb
)
166 /* Just use a list to store the data */
167 Jim_Obj
*listObj
= Jim_NewListObj(interp
, NULL
, 0);
169 AppendStatElement(interp
, listObj
, "dev", sb
->st_dev
);
170 AppendStatElement(interp
, listObj
, "ino", sb
->st_ino
);
171 AppendStatElement(interp
, listObj
, "mode", sb
->st_mode
);
172 AppendStatElement(interp
, listObj
, "nlink", sb
->st_nlink
);
173 AppendStatElement(interp
, listObj
, "uid", sb
->st_uid
);
174 AppendStatElement(interp
, listObj
, "gid", sb
->st_gid
);
175 AppendStatElement(interp
, listObj
, "size", sb
->st_size
);
176 AppendStatElement(interp
, listObj
, "atime", sb
->st_atime
);
177 AppendStatElement(interp
, listObj
, "mtime", sb
->st_mtime
);
178 AppendStatElement(interp
, listObj
, "ctime", sb
->st_ctime
);
180 AppendStatElement(interp
, listObj
, "mtimeus", STAT_MTIME_US(*sb
));
182 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, "type", -1));
183 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, JimGetFileType((int)sb
->st_mode
), -1));
185 /* Was a variable specified? */
188 objPtr
= Jim_GetVariable(interp
, varName
, JIM_NONE
);
196 objPtr
= Jim_DictMerge(interp
, 2, objv
);
197 if (objPtr
== NULL
) {
198 /* This message matches the one from Tcl */
199 Jim_SetResultFormatted(interp
, "can't set \"%#s(dev)\": variable isn't array", varName
);
200 Jim_FreeNewObj(interp
, listObj
);
204 Jim_InvalidateStringRep(objPtr
);
206 Jim_FreeNewObj(interp
, listObj
);
209 Jim_SetVariable(interp
, varName
, listObj
);
212 /* And also return the value */
213 Jim_SetResult(interp
, listObj
);
219 * Give a path of length 'len', returns the length of the path
220 * with any trailing slashes removed.
222 static int JimPathLenNoTrailingSlashes(const char *path
, int len
)
225 for (i
= len
; i
> 1 && path
[i
- 1] == '/'; i
--) {
226 /* Trailing slash, so remove it */
227 if (ISWINDOWS
&& path
[i
- 2] == ':') {
228 /* But on windows, we won't remove the trailing slash from c:/ */
236 * Give a path in objPtr, returns a new path with any trailing slash removed.
237 * Use Jim_DecrRefCount() on the returned object (which may be identical to objPtr).
239 static Jim_Obj
*JimStripTrailingSlashes(Jim_Interp
*interp
, Jim_Obj
*objPtr
)
241 int len
= Jim_Length(objPtr
);
242 const char *path
= Jim_String(objPtr
);
243 int i
= JimPathLenNoTrailingSlashes(path
, len
);
245 objPtr
= Jim_NewStringObj(interp
, path
, i
);
247 Jim_IncrRefCount(objPtr
);
251 static int file_cmd_dirname(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
253 Jim_Obj
*objPtr
= JimStripTrailingSlashes(interp
, argv
[0]);
254 const char *path
= Jim_String(objPtr
);
255 const char *p
= strrchr(path
, '/');
258 Jim_SetResultString(interp
, ".", -1);
260 else if (p
[1] == 0) {
261 /* Trailing slash so do nothing */
262 Jim_SetResult(interp
, objPtr
);
264 else if (p
== path
) {
265 Jim_SetResultString(interp
, "/", -1);
267 else if (ISWINDOWS
&& p
[-1] == ':') {
269 Jim_SetResultString(interp
, path
, p
- path
+ 1);
272 /* Strip any trailing slashes from the result */
273 int len
= JimPathLenNoTrailingSlashes(path
, p
- path
);
274 Jim_SetResultString(interp
, path
, len
);
276 Jim_DecrRefCount(interp
, objPtr
);
280 static int file_cmd_split(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
282 Jim_Obj
*listObj
= Jim_NewListObj(interp
, NULL
, 0);
283 const char *path
= Jim_String(argv
[0]);
286 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, "/", 1));
290 /* Remove leading slashes */
291 while (*path
== '/') {
295 const char *pt
= strchr(path
, '/');
297 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, path
, pt
- path
));
301 Jim_ListAppendElement(interp
, listObj
, Jim_NewStringObj(interp
, path
, -1));
305 Jim_SetResult(interp
, listObj
);
309 static int file_cmd_rootname(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
311 const char *path
= Jim_String(argv
[0]);
312 const char *lastSlash
= strrchr(path
, '/');
313 const char *p
= strrchr(path
, '.');
315 if (p
== NULL
|| (lastSlash
!= NULL
&& lastSlash
> p
)) {
316 Jim_SetResult(interp
, argv
[0]);
319 Jim_SetResultString(interp
, path
, p
- path
);
324 static int file_cmd_extension(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
326 Jim_Obj
*objPtr
= JimStripTrailingSlashes(interp
, argv
[0]);
327 const char *path
= Jim_String(objPtr
);
328 const char *lastSlash
= strrchr(path
, '/');
329 const char *p
= strrchr(path
, '.');
331 if (p
== NULL
|| (lastSlash
!= NULL
&& lastSlash
>= p
)) {
334 Jim_SetResultString(interp
, p
, -1);
335 Jim_DecrRefCount(interp
, objPtr
);
339 static int file_cmd_tail(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
341 Jim_Obj
*objPtr
= JimStripTrailingSlashes(interp
, argv
[0]);
342 const char *path
= Jim_String(objPtr
);
343 const char *lastSlash
= strrchr(path
, '/');
346 Jim_SetResultString(interp
, lastSlash
+ 1, -1);
349 Jim_SetResult(interp
, objPtr
);
351 Jim_DecrRefCount(interp
, objPtr
);
355 static int file_cmd_normalize(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
358 const char *path
= Jim_String(argv
[0]);
359 char *newname
= Jim_Alloc(MAXPATHLEN
+ 1);
361 if (realpath(path
, newname
)) {
362 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, newname
, -1));
367 Jim_SetResultFormatted(interp
, "can't normalize \"%#s\": %s", argv
[0], strerror(errno
));
371 Jim_SetResultString(interp
, "Not implemented", -1);
376 static int file_cmd_join(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
379 char *newname
= Jim_Alloc(MAXPATHLEN
+ 1);
380 char *last
= newname
;
384 /* Simple implementation for now */
385 for (i
= 0; i
< argc
; i
++) {
387 const char *part
= Jim_GetString(argv
[i
], &len
);
390 /* Absolute component, so go back to the start */
393 else if (ISWINDOWS
&& strchr(part
, ':')) {
394 /* Absolute component on mingw, so go back to the start */
397 else if (part
[0] == '.') {
398 if (part
[1] == '/') {
402 else if (part
[1] == 0 && last
!= newname
) {
403 /* Adding '.' to an existing path does nothing */
408 /* Add a slash if needed */
409 if (last
!= newname
&& last
[-1] != '/') {
414 if (last
+ len
- newname
>= MAXPATHLEN
) {
416 Jim_SetResultString(interp
, "Path too long", -1);
419 memcpy(last
, part
, len
);
423 /* Remove a slash if needed */
424 if (last
> newname
+ 1 && last
[-1] == '/') {
425 /* but on on Windows, leave the trailing slash on "c:/ " */
426 if (!ISWINDOWS
|| !(last
> newname
+ 2 && last
[-2] == ':')) {
434 /* Probably need to handle some special cases ... */
436 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, newname
, last
- newname
));
441 static int file_access(Jim_Interp
*interp
, Jim_Obj
*filename
, int mode
)
443 Jim_SetResultBool(interp
, access(Jim_String(filename
), mode
) != -1);
448 static int file_cmd_readable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
450 return file_access(interp
, argv
[0], R_OK
);
453 static int file_cmd_writable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
455 return file_access(interp
, argv
[0], W_OK
);
458 static int file_cmd_executable(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
461 return file_access(interp
, argv
[0], X_OK
);
463 /* If no X_OK, just assume true. */
464 Jim_SetResultBool(interp
, 1);
469 static int file_cmd_exists(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
471 return file_access(interp
, argv
[0], F_OK
);
474 static int file_cmd_delete(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
476 int force
= Jim_CompareStringImmediate(interp
, argv
[0], "-force");
478 if (force
|| Jim_CompareStringImmediate(interp
, argv
[0], "--")) {
484 const char *path
= Jim_String(argv
[0]);
486 if (unlink(path
) == -1 && errno
!= ENOENT
) {
487 if (rmdir(path
) == -1) {
488 /* Maybe try using the script helper */
489 if (!force
|| Jim_EvalPrefix(interp
, "file delete force", 1, argv
) != JIM_OK
) {
490 Jim_SetResultFormatted(interp
, "couldn't delete file \"%s\": %s", path
,
501 #ifdef HAVE_MKDIR_ONE_ARG
502 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
504 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
508 * Create directory, creating all intermediate paths if necessary.
510 * Returns 0 if OK or -1 on failure (and sets errno)
512 * Note: The path may be modified.
514 static int mkdir_all(char *path
)
518 /* First time just try to make the dir */
522 /* Must have failed the first time, so recursively make the parent and try again */
524 char *slash
= strrchr(path
, '/');
526 if (slash
&& slash
!= path
) {
528 if (mkdir_all(path
) != 0) {
535 if (MKDIR_DEFAULT(path
) == 0) {
538 if (errno
== ENOENT
) {
539 /* Create the parent and try again */
542 /* Maybe it already exists as a directory */
543 if (errno
== EEXIST
) {
546 if (Jim_Stat(path
, &sb
) == 0 && S_ISDIR(sb
.st_mode
)) {
558 static int file_cmd_mkdir(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
561 char *path
= Jim_StrDup(Jim_String(argv
[0]));
562 int rc
= mkdir_all(path
);
566 Jim_SetResultFormatted(interp
, "can't create directory \"%#s\": %s", argv
[0],
575 static int file_cmd_tempfile(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
577 int fd
= Jim_MakeTempFile(interp
, (argc
>= 1) ? Jim_String(argv
[0]) : NULL
, 0);
587 static int file_cmd_rename(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
594 if (!Jim_CompareStringImmediate(interp
, argv
[0], "-force")) {
602 source
= Jim_String(argv
[0]);
603 dest
= Jim_String(argv
[1]);
605 if (!force
&& access(dest
, F_OK
) == 0) {
606 Jim_SetResultFormatted(interp
, "error renaming \"%#s\" to \"%#s\": target exists", argv
[0],
611 if (access(dest
, F_OK
) == 0) {
612 /* Windows won't rename over an existing file */
616 if (rename(source
, dest
) != 0) {
617 Jim_SetResultFormatted(interp
, "error renaming \"%#s\" to \"%#s\": %s", argv
[0], argv
[1],
625 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
626 static int file_cmd_link(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
631 static const char * const options
[] = { "-hard", "-symbolic", NULL
};
632 enum { OPT_HARD
, OPT_SYMBOLIC
, };
633 int option
= OPT_HARD
;
636 if (Jim_GetEnum(interp
, argv
[0], options
, &option
, NULL
, JIM_ENUM_ABBREV
| JIM_ERRMSG
) != JIM_OK
) {
643 dest
= Jim_String(argv
[0]);
644 source
= Jim_String(argv
[1]);
646 if (option
== OPT_HARD
) {
647 ret
= link(source
, dest
);
650 ret
= symlink(source
, dest
);
654 Jim_SetResultFormatted(interp
, "error linking \"%#s\" to \"%#s\": %s", argv
[0], argv
[1],
663 static int file_stat(Jim_Interp
*interp
, Jim_Obj
*filename
, jim_stat_t
*sb
)
665 const char *path
= Jim_String(filename
);
667 if (Jim_Stat(path
, sb
) == -1) {
668 Jim_SetResultFormatted(interp
, "could not read \"%#s\": %s", filename
, strerror(errno
));
675 static int file_lstat(Jim_Interp
*interp
, Jim_Obj
*filename
, jim_stat_t
*sb
)
677 const char *path
= Jim_String(filename
);
679 if (lstat(path
, sb
) == -1) {
680 Jim_SetResultFormatted(interp
, "could not read \"%#s\": %s", filename
, strerror(errno
));
686 #define file_lstat file_stat
689 static int file_cmd_atime(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
693 if (file_stat(interp
, argv
[0], &sb
) != JIM_OK
) {
696 Jim_SetResultInt(interp
, sb
.st_atime
);
701 * Set file atime/mtime to the given time in microseconds since the epoch.
703 static int JimSetFileTimes(Jim_Interp
*interp
, const char *filename
, jim_wide us
)
706 struct timeval times
[2];
708 times
[1].tv_sec
= times
[0].tv_sec
= us
/ 1000000;
709 times
[1].tv_usec
= times
[0].tv_usec
= us
% 1000000;
711 if (utimes(filename
, times
) != 0) {
712 Jim_SetResultFormatted(interp
, "can't set time on \"%s\": %s", filename
, strerror(errno
));
717 Jim_SetResultString(interp
, "Not implemented", -1);
722 static int file_cmd_mtime(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
728 if (Jim_GetWide(interp
, argv
[1], &secs
) != JIM_OK
) {
731 return JimSetFileTimes(interp
, Jim_String(argv
[0]), secs
* 1000000);
733 if (file_stat(interp
, argv
[0], &sb
) != JIM_OK
) {
736 Jim_SetResultInt(interp
, sb
.st_mtime
);
741 static int file_cmd_mtimeus(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
747 if (Jim_GetWide(interp
, argv
[1], &us
) != JIM_OK
) {
750 return JimSetFileTimes(interp
, Jim_String(argv
[0]), us
);
752 if (file_stat(interp
, argv
[0], &sb
) != JIM_OK
) {
755 Jim_SetResultInt(interp
, STAT_MTIME_US(sb
));
760 static int file_cmd_copy(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
762 return Jim_EvalPrefix(interp
, "file copy", argc
, argv
);
765 static int file_cmd_size(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
769 if (file_stat(interp
, argv
[0], &sb
) != JIM_OK
) {
772 Jim_SetResultInt(interp
, sb
.st_size
);
776 static int file_cmd_isdirectory(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
781 if (file_stat(interp
, argv
[0], &sb
) == JIM_OK
) {
782 ret
= S_ISDIR(sb
.st_mode
);
784 Jim_SetResultInt(interp
, ret
);
788 static int file_cmd_isfile(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
793 if (file_stat(interp
, argv
[0], &sb
) == JIM_OK
) {
794 ret
= S_ISREG(sb
.st_mode
);
796 Jim_SetResultInt(interp
, ret
);
801 static int file_cmd_owned(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
806 if (file_stat(interp
, argv
[0], &sb
) == JIM_OK
) {
807 ret
= (geteuid() == sb
.st_uid
);
809 Jim_SetResultInt(interp
, ret
);
814 #if defined(HAVE_READLINK)
815 static int file_cmd_readlink(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
817 const char *path
= Jim_String(argv
[0]);
818 char *linkValue
= Jim_Alloc(MAXPATHLEN
+ 1);
820 int linkLength
= readlink(path
, linkValue
, MAXPATHLEN
);
822 if (linkLength
== -1) {
824 Jim_SetResultFormatted(interp
, "could not read link \"%#s\": %s", argv
[0], strerror(errno
));
827 linkValue
[linkLength
] = 0;
828 Jim_SetResult(interp
, Jim_NewStringObjNoAlloc(interp
, linkValue
, linkLength
));
833 static int file_cmd_type(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
837 if (file_lstat(interp
, argv
[0], &sb
) != JIM_OK
) {
840 Jim_SetResultString(interp
, JimGetFileType((int)sb
.st_mode
), -1);
845 static int file_cmd_lstat(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
849 if (file_lstat(interp
, argv
[0], &sb
) != JIM_OK
) {
852 return StoreStatData(interp
, argc
== 2 ? argv
[1] : NULL
, &sb
);
855 #define file_cmd_lstat file_cmd_stat
858 static int file_cmd_stat(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
862 if (file_stat(interp
, argv
[0], &sb
) != JIM_OK
) {
865 return StoreStatData(interp
, argc
== 2 ? argv
[1] : NULL
, &sb
);
868 static const jim_subcmd_type file_command_table
[] = {
874 /* Description: Last access time */
881 /* Description: Get or set last modification time */
889 /* Description: Get or set last modification time in microseconds */
893 "?-force? source dest",
897 /* Description: Copy source file to destination file */
904 /* Description: Directory part of the name */
911 /* Description: Name without any extension */
918 /* Description: Last extension including the dot */
925 /* Description: Last component of the name */
932 /* Description: Split path into components as a list */
939 /* Description: Normalized path of name */
946 /* Description: Join multiple path components */
953 /* Description: Is file readable */
960 /* Description: Is file writable */
967 /* Description: Is file executable */
974 /* Description: Does file exist */
977 "?-force|--? name ...",
981 /* Description: Deletes the files or directories (must be empty unless -force) */
988 /* Description: Creates the directories */
995 /* Description: Creates a temporary filename */
998 "?-force? source dest",
1002 /* Description: Renames a file */
1004 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
1006 "?-symbolic|-hard? newname target",
1010 /* Description: Creates a hard or soft link */
1013 #if defined(HAVE_READLINK)
1019 /* Description: Value of the symbolic link */
1027 /* Description: Size of file */
1034 /* Description: Returns results of stat, and may store in var array */
1041 /* Description: Returns results of lstat, and may store in var array */
1048 /* Description: Returns type of the file */
1056 /* Description: Returns 1 if owned by the current owner */
1061 file_cmd_isdirectory
,
1064 /* Description: Returns 1 if name is a directory */
1071 /* Description: Returns 1 if name is a file */
1078 static int Jim_CdCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1083 Jim_WrongNumArgs(interp
, 1, argv
, "dirname");
1087 path
= Jim_String(argv
[1]);
1089 if (chdir(path
) != 0) {
1090 Jim_SetResultFormatted(interp
, "couldn't change working directory to \"%s\": %s", path
,
1097 static int Jim_PwdCmd(Jim_Interp
*interp
, int argc
, Jim_Obj
*const *argv
)
1099 char *cwd
= Jim_Alloc(MAXPATHLEN
);
1101 if (getcwd(cwd
, MAXPATHLEN
) == NULL
) {
1102 Jim_SetResultString(interp
, "Failed to get pwd", -1);
1106 else if (ISWINDOWS
) {
1107 /* Try to keep backslashes out of paths */
1109 while ((p
= strchr(p
, '\\')) != NULL
) {
1114 Jim_SetResultString(interp
, cwd
, -1);
1120 int Jim_fileInit(Jim_Interp
*interp
)
1122 if (Jim_PackageProvide(interp
, "file", "1.0", JIM_ERRMSG
))
1125 Jim_CreateCommand(interp
, "file", Jim_SubCmdProc
, (void *)file_command_table
, NULL
);
1126 Jim_CreateCommand(interp
, "pwd", Jim_PwdCmd
, NULL
, NULL
);
1127 Jim_CreateCommand(interp
, "cd", Jim_CdCmd
, NULL
, NULL
);