docs: update documentation for recent changes
[jimtcl.git] / jim-file.c
blob2fd051f93a98a3ac293a35629e1736091de940b5
1 /*
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
8 * are met:
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.
46 #include <limits.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <stdio.h>
50 #include <errno.h>
51 #include <sys/stat.h>
53 #include <jimautoconf.h>
54 #include <jim-subcmd.h>
56 #ifdef HAVE_UTIMES
57 #include <sys/time.h>
58 #endif
59 #ifdef HAVE_UNISTD_H
60 #include <unistd.h>
61 #elif defined(_MSC_VER)
62 #include <direct.h>
63 #define F_OK 0
64 #define W_OK 2
65 #define R_OK 4
66 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
67 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
68 #endif
70 # ifndef MAXPATHLEN
71 # define MAXPATHLEN JIM_PATH_LEN
72 # endif
75 *----------------------------------------------------------------------
77 * JimGetFileType --
79 * Given a mode word, returns a string identifying the type of a
80 * file.
82 * Results:
83 * A static text string giving the file type from mode.
85 * Side effects:
86 * None.
88 *----------------------------------------------------------------------
91 static const char *JimGetFileType(int mode)
93 if (S_ISREG(mode)) {
94 return "file";
96 else if (S_ISDIR(mode)) {
97 return "directory";
99 #ifdef S_ISCHR
100 else if (S_ISCHR(mode)) {
101 return "characterSpecial";
103 #endif
104 #ifdef S_ISBLK
105 else if (S_ISBLK(mode)) {
106 return "blockSpecial";
108 #endif
109 #ifdef S_ISFIFO
110 else if (S_ISFIFO(mode)) {
111 return "fifo";
113 #endif
114 #ifdef S_ISLNK
115 else if (S_ISLNK(mode)) {
116 return "link";
118 #endif
119 #ifdef S_ISSOCK
120 else if (S_ISSOCK(mode)) {
121 return "socket";
123 #endif
124 return "unknown";
128 *----------------------------------------------------------------------
130 * StoreStatData --
132 * This is a utility procedure that breaks out the fields of a
133 * "stat" structure and stores them in textual form into the
134 * elements of an associative array.
136 * Results:
137 * Returns a standard Tcl return value. If an error occurs then
138 * a message is left in interp->result.
140 * Side effects:
141 * Elements of the associative array given by "varName" are modified.
143 *----------------------------------------------------------------------
145 static void AppendStatElement(Jim_Interp *interp, Jim_Obj *listObj, const char *key, jim_wide value)
147 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, key, -1));
148 Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
151 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
153 /* Just use a list to store the data */
154 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0);
156 AppendStatElement(interp, listObj, "dev", sb->st_dev);
157 AppendStatElement(interp, listObj, "ino", sb->st_ino);
158 AppendStatElement(interp, listObj, "mode", sb->st_mode);
159 AppendStatElement(interp, listObj, "nlink", sb->st_nlink);
160 AppendStatElement(interp, listObj, "uid", sb->st_uid);
161 AppendStatElement(interp, listObj, "gid", sb->st_gid);
162 AppendStatElement(interp, listObj, "size", sb->st_size);
163 AppendStatElement(interp, listObj, "atime", sb->st_atime);
164 AppendStatElement(interp, listObj, "mtime", sb->st_mtime);
165 AppendStatElement(interp, listObj, "ctime", sb->st_ctime);
166 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, "type", -1));
167 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, JimGetFileType((int)sb->st_mode), -1));
169 /* Was a variable specified? */
170 if (varName) {
171 Jim_Obj *objPtr = Jim_GetVariable(interp, varName, JIM_NONE);
172 if (objPtr) {
173 if (Jim_DictSize(interp, objPtr) < 0) {
174 /* This message matches the one from Tcl */
175 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
176 Jim_FreeNewObj(interp, listObj);
177 return JIM_ERR;
180 if (Jim_IsShared(objPtr))
181 objPtr = Jim_DuplicateObj(interp, objPtr);
183 /* Just cheat here and append as a list and convert to a dict */
184 Jim_ListAppendList(interp, objPtr, listObj);
185 Jim_DictSize(interp, objPtr);
186 Jim_InvalidateStringRep(objPtr);
188 Jim_FreeNewObj(interp, listObj);
189 listObj = objPtr;
191 Jim_SetVariable(interp, varName, listObj);
194 /* And also return the value */
195 Jim_SetResult(interp, listObj);
197 return JIM_OK;
200 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
202 const char *path = Jim_String(argv[0]);
203 const char *p = strrchr(path, '/');
205 if (!p && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
206 Jim_SetResultString(interp, "..", -1);
207 } else if (!p) {
208 Jim_SetResultString(interp, ".", -1);
210 else if (p == path) {
211 Jim_SetResultString(interp, "/", -1);
213 #if defined(__MINGW32__) || defined(_MSC_VER)
214 else if (p[-1] == ':') {
215 /* z:/dir => z:/ */
216 Jim_SetResultString(interp, path, p - path + 1);
218 #endif
219 else {
220 Jim_SetResultString(interp, path, p - path);
222 return JIM_OK;
225 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
227 const char *path = Jim_String(argv[0]);
228 const char *lastSlash = strrchr(path, '/');
229 const char *p = strrchr(path, '.');
231 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
232 Jim_SetResult(interp, argv[0]);
234 else {
235 Jim_SetResultString(interp, path, p - path);
237 return JIM_OK;
240 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
242 const char *path = Jim_String(argv[0]);
243 const char *lastSlash = strrchr(path, '/');
244 const char *p = strrchr(path, '.');
246 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
247 p = "";
249 Jim_SetResultString(interp, p, -1);
250 return JIM_OK;
253 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
255 const char *path = Jim_String(argv[0]);
256 const char *lastSlash = strrchr(path, '/');
258 if (lastSlash) {
259 Jim_SetResultString(interp, lastSlash + 1, -1);
261 else {
262 Jim_SetResult(interp, argv[0]);
264 return JIM_OK;
267 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
269 #ifdef HAVE_REALPATH
270 const char *path = Jim_String(argv[0]);
271 char *newname = Jim_Alloc(MAXPATHLEN + 1);
273 if (realpath(path, newname)) {
274 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
275 return JIM_OK;
277 else {
278 Jim_Free(newname);
279 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
280 return JIM_ERR;
282 #else
283 Jim_SetResultString(interp, "Not implemented", -1);
284 return JIM_ERR;
285 #endif
288 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
290 int i;
291 char *newname = Jim_Alloc(MAXPATHLEN + 1);
292 char *last = newname;
294 *newname = 0;
296 /* Simple implementation for now */
297 for (i = 0; i < argc; i++) {
298 int len;
299 const char *part = Jim_GetString(argv[i], &len);
301 if (*part == '/') {
302 /* Absolute component, so go back to the start */
303 last = newname;
305 #if defined(__MINGW32__) || defined(_MSC_VER)
306 else if (strchr(part, ':')) {
307 /* Absolute compontent on mingw, so go back to the start */
308 last = newname;
310 #endif
311 else if (part[0] == '.') {
312 if (part[1] == '/') {
313 part += 2;
314 len -= 2;
316 else if (part[1] == 0 && last != newname) {
317 /* Adding '.' to an existing path does nothing */
318 continue;
322 /* Add a slash if needed */
323 if (last != newname && last[-1] != '/') {
324 *last++ = '/';
327 if (len) {
328 if (last + len - newname >= MAXPATHLEN) {
329 Jim_Free(newname);
330 Jim_SetResultString(interp, "Path too long", -1);
331 return JIM_ERR;
333 memcpy(last, part, len);
334 last += len;
337 /* Remove a slash if needed */
338 if (last > newname + 1 && last[-1] == '/') {
339 *--last = 0;
343 *last = 0;
345 /* Probably need to handle some special cases ... */
347 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
349 return JIM_OK;
352 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
354 Jim_SetResultBool(interp, access(Jim_String(filename), mode) != -1);
356 return JIM_OK;
359 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
361 return file_access(interp, argv[0], R_OK);
364 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
366 return file_access(interp, argv[0], W_OK);
369 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
371 #ifdef X_OK
372 return file_access(interp, argv[0], X_OK);
373 #else
374 /* If no X_OK, just assume true. */
375 Jim_SetResultBool(interp, 1);
376 return JIM_OK;
377 #endif
380 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
382 return file_access(interp, argv[0], F_OK);
385 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
387 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
389 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
390 argc++;
391 argv--;
394 while (argc--) {
395 const char *path = Jim_String(argv[0]);
397 if (unlink(path) == -1 && errno != ENOENT) {
398 if (rmdir(path) == -1) {
399 /* Maybe try using the script helper */
400 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
401 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
402 strerror(errno));
403 return JIM_ERR;
407 argv++;
409 return JIM_OK;
412 #ifdef HAVE_MKDIR_ONE_ARG
413 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
414 #else
415 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
416 #endif
419 * Create directory, creating all intermediate paths if necessary.
421 * Returns 0 if OK or -1 on failure (and sets errno)
423 * Note: The path may be modified.
425 static int mkdir_all(char *path)
427 int ok = 1;
429 /* First time just try to make the dir */
430 goto first;
432 while (ok--) {
433 /* Must have failed the first time, so recursively make the parent and try again */
435 char *slash = strrchr(path, '/');
437 if (slash && slash != path) {
438 *slash = 0;
439 if (mkdir_all(path) != 0) {
440 return -1;
442 *slash = '/';
445 first:
446 if (MKDIR_DEFAULT(path) == 0) {
447 return 0;
449 if (errno == ENOENT) {
450 /* Create the parent and try again */
451 continue;
453 /* Maybe it already exists as a directory */
454 if (errno == EEXIST) {
455 struct stat sb;
457 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
458 return 0;
460 /* Restore errno */
461 errno = EEXIST;
463 /* Failed */
464 break;
466 return -1;
469 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
471 while (argc--) {
472 char *path = Jim_StrDup(Jim_String(argv[0]));
473 int rc = mkdir_all(path);
475 Jim_Free(path);
476 if (rc != 0) {
477 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
478 strerror(errno));
479 return JIM_ERR;
481 argv++;
483 return JIM_OK;
486 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
488 int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL);
490 if (fd < 0) {
491 return JIM_ERR;
493 close(fd);
495 return JIM_OK;
498 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
500 const char *source;
501 const char *dest;
502 int force = 0;
504 if (argc == 3) {
505 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
506 return -1;
508 force++;
509 argv++;
510 argc--;
513 source = Jim_String(argv[0]);
514 dest = Jim_String(argv[1]);
516 if (!force && access(dest, F_OK) == 0) {
517 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
518 argv[1]);
519 return JIM_ERR;
522 if (rename(source, dest) != 0) {
523 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
524 strerror(errno));
525 return JIM_ERR;
528 return JIM_OK;
531 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
532 static int file_cmd_link(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
534 int ret;
535 const char *source;
536 const char *dest;
537 static const char * const options[] = { "-hard", "-symbolic", NULL };
538 enum { OPT_HARD, OPT_SYMBOLIC, };
539 int option = OPT_HARD;
541 if (argc == 3) {
542 if (Jim_GetEnum(interp, argv[0], options, &option, NULL, JIM_ENUM_ABBREV | JIM_ERRMSG) != JIM_OK) {
543 return JIM_ERR;
545 argv++;
546 argc--;
549 dest = Jim_String(argv[0]);
550 source = Jim_String(argv[1]);
552 if (option == OPT_HARD) {
553 ret = link(source, dest);
555 else {
556 ret = symlink(source, dest);
559 if (ret != 0) {
560 Jim_SetResultFormatted(interp, "error linking \"%#s\" to \"%#s\": %s", argv[0], argv[1],
561 strerror(errno));
562 return JIM_ERR;
565 return JIM_OK;
567 #endif
569 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
571 const char *path = Jim_String(filename);
573 if (stat(path, sb) == -1) {
574 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
575 return JIM_ERR;
577 return JIM_OK;
580 #ifdef HAVE_LSTAT
581 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
583 const char *path = Jim_String(filename);
585 if (lstat(path, sb) == -1) {
586 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
587 return JIM_ERR;
589 return JIM_OK;
591 #else
592 #define file_lstat file_stat
593 #endif
595 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
597 struct stat sb;
599 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
600 return JIM_ERR;
602 Jim_SetResultInt(interp, sb.st_atime);
603 return JIM_OK;
606 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
608 struct stat sb;
610 if (argc == 2) {
611 #ifdef HAVE_UTIMES
612 jim_wide newtime;
613 struct timeval times[2];
615 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
616 return JIM_ERR;
619 times[1].tv_sec = times[0].tv_sec = newtime;
620 times[1].tv_usec = times[0].tv_usec = 0;
622 if (utimes(Jim_String(argv[0]), times) != 0) {
623 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
624 return JIM_ERR;
626 #else
627 Jim_SetResultString(interp, "Not implemented", -1);
628 return JIM_ERR;
629 #endif
631 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
632 return JIM_ERR;
634 Jim_SetResultInt(interp, sb.st_mtime);
635 return JIM_OK;
638 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
640 return Jim_EvalPrefix(interp, "file copy", argc, argv);
643 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
645 struct stat sb;
647 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
648 return JIM_ERR;
650 Jim_SetResultInt(interp, sb.st_size);
651 return JIM_OK;
654 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
656 struct stat sb;
657 int ret = 0;
659 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
660 ret = S_ISDIR(sb.st_mode);
662 Jim_SetResultInt(interp, ret);
663 return JIM_OK;
666 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
668 struct stat sb;
669 int ret = 0;
671 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
672 ret = S_ISREG(sb.st_mode);
674 Jim_SetResultInt(interp, ret);
675 return JIM_OK;
678 #ifdef HAVE_GETEUID
679 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
681 struct stat sb;
682 int ret = 0;
684 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
685 ret = (geteuid() == sb.st_uid);
687 Jim_SetResultInt(interp, ret);
688 return JIM_OK;
690 #endif
692 #if defined(HAVE_READLINK)
693 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
695 const char *path = Jim_String(argv[0]);
696 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
698 int linkLength = readlink(path, linkValue, MAXPATHLEN);
700 if (linkLength == -1) {
701 Jim_Free(linkValue);
702 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
703 return JIM_ERR;
705 linkValue[linkLength] = 0;
706 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
707 return JIM_OK;
709 #endif
711 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
713 struct stat sb;
715 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
716 return JIM_ERR;
718 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
719 return JIM_OK;
722 #ifdef HAVE_LSTAT
723 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
725 struct stat sb;
727 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
728 return JIM_ERR;
730 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
732 #else
733 #define file_cmd_lstat file_cmd_stat
734 #endif
736 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
738 struct stat sb;
740 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
741 return JIM_ERR;
743 return StoreStatData(interp, argc == 2 ? argv[1] : NULL, &sb);
746 static const jim_subcmd_type file_command_table[] = {
747 { "atime",
748 "name",
749 file_cmd_atime,
752 /* Description: Last access time */
754 { "mtime",
755 "name ?time?",
756 file_cmd_mtime,
759 /* Description: Get or set last modification time */
761 { "copy",
762 "?-force? source dest",
763 file_cmd_copy,
766 /* Description: Copy source file to destination file */
768 { "dirname",
769 "name",
770 file_cmd_dirname,
773 /* Description: Directory part of the name */
775 { "rootname",
776 "name",
777 file_cmd_rootname,
780 /* Description: Name without any extension */
782 { "extension",
783 "name",
784 file_cmd_extension,
787 /* Description: Last extension including the dot */
789 { "tail",
790 "name",
791 file_cmd_tail,
794 /* Description: Last component of the name */
796 { "normalize",
797 "name",
798 file_cmd_normalize,
801 /* Description: Normalized path of name */
803 { "join",
804 "name ?name ...?",
805 file_cmd_join,
808 /* Description: Join multiple path components */
810 { "readable",
811 "name",
812 file_cmd_readable,
815 /* Description: Is file readable */
817 { "writable",
818 "name",
819 file_cmd_writable,
822 /* Description: Is file writable */
824 { "executable",
825 "name",
826 file_cmd_executable,
829 /* Description: Is file executable */
831 { "exists",
832 "name",
833 file_cmd_exists,
836 /* Description: Does file exist */
838 { "delete",
839 "?-force|--? name ...",
840 file_cmd_delete,
843 /* Description: Deletes the files or directories (must be empty unless -force) */
845 { "mkdir",
846 "dir ...",
847 file_cmd_mkdir,
850 /* Description: Creates the directories */
852 { "tempfile",
853 "?template?",
854 file_cmd_tempfile,
857 /* Description: Creates a temporary filename */
859 { "rename",
860 "?-force? source dest",
861 file_cmd_rename,
864 /* Description: Renames a file */
866 #if defined(HAVE_LINK) && defined(HAVE_SYMLINK)
867 { "link",
868 "?-symbolic|-hard? newname target",
869 file_cmd_link,
872 /* Description: Creates a hard or soft link */
874 #endif
875 #if defined(HAVE_READLINK)
876 { "readlink",
877 "name",
878 file_cmd_readlink,
881 /* Description: Value of the symbolic link */
883 #endif
884 { "size",
885 "name",
886 file_cmd_size,
889 /* Description: Size of file */
891 { "stat",
892 "name ?var?",
893 file_cmd_stat,
896 /* Description: Returns results of stat, and may store in var array */
898 { "lstat",
899 "name ?var?",
900 file_cmd_lstat,
903 /* Description: Returns results of lstat, and may store in var array */
905 { "type",
906 "name",
907 file_cmd_type,
910 /* Description: Returns type of the file */
912 #ifdef HAVE_GETEUID
913 { "owned",
914 "name",
915 file_cmd_owned,
918 /* Description: Returns 1 if owned by the current owner */
920 #endif
921 { "isdirectory",
922 "name",
923 file_cmd_isdirectory,
926 /* Description: Returns 1 if name is a directory */
928 { "isfile",
929 "name",
930 file_cmd_isfile,
933 /* Description: Returns 1 if name is a file */
936 NULL
940 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
942 const char *path;
944 if (argc != 2) {
945 Jim_WrongNumArgs(interp, 1, argv, "dirname");
946 return JIM_ERR;
949 path = Jim_String(argv[1]);
951 if (chdir(path) != 0) {
952 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
953 strerror(errno));
954 return JIM_ERR;
956 return JIM_OK;
959 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
961 char *cwd = Jim_Alloc(MAXPATHLEN);
963 if (getcwd(cwd, MAXPATHLEN) == NULL) {
964 Jim_SetResultString(interp, "Failed to get pwd", -1);
965 Jim_Free(cwd);
966 return JIM_ERR;
968 #if defined(__MINGW32__) || defined(_MSC_VER)
970 /* Try to keep backlashes out of paths */
971 char *p = cwd;
972 while ((p = strchr(p, '\\')) != NULL) {
973 *p++ = '/';
976 #endif
978 Jim_SetResultString(interp, cwd, -1);
980 Jim_Free(cwd);
981 return JIM_OK;
984 int Jim_fileInit(Jim_Interp *interp)
986 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
987 return JIM_ERR;
989 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
990 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
991 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
992 return JIM_OK;