sqlite3: Add -Wall and fix a compiler warning
[jimtcl.git] / jim-file.c
blob725cc8a1e40d155a7ce427b8d190c83fb20e08fe
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 *----------------------------------------------------------------------
146 static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
147 jim_wide value)
149 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
150 Jim_Obj *valobj = Jim_NewWideObj(interp, value);
152 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
153 Jim_FreeObj(interp, nameobj);
154 Jim_FreeObj(interp, valobj);
155 return JIM_ERR;
157 return JIM_OK;
160 static int set_array_string_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
161 const char *value)
163 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
164 Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1);
166 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
167 Jim_FreeObj(interp, nameobj);
168 Jim_FreeObj(interp, valobj);
169 return JIM_ERR;
171 return JIM_OK;
174 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
176 if (set_array_int_value(interp, varName, "dev", sb->st_dev) != JIM_OK) {
177 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
178 return JIM_ERR;
180 set_array_int_value(interp, varName, "ino", sb->st_ino);
181 set_array_int_value(interp, varName, "mode", sb->st_mode);
182 set_array_int_value(interp, varName, "nlink", sb->st_nlink);
183 set_array_int_value(interp, varName, "uid", sb->st_uid);
184 set_array_int_value(interp, varName, "gid", sb->st_gid);
185 set_array_int_value(interp, varName, "size", sb->st_size);
186 set_array_int_value(interp, varName, "atime", sb->st_atime);
187 set_array_int_value(interp, varName, "mtime", sb->st_mtime);
188 set_array_int_value(interp, varName, "ctime", sb->st_ctime);
189 set_array_string_value(interp, varName, "type", JimGetFileType((int)sb->st_mode));
191 /* And also return the value */
192 Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0));
194 return JIM_OK;
197 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
199 const char *path = Jim_String(argv[0]);
200 const char *p = strrchr(path, '/');
202 if (!p) {
203 Jim_SetResultString(interp, ".", -1);
205 else if (p == path) {
206 Jim_SetResultString(interp, "/", -1);
208 #if defined(__MINGW32__) || defined(_MSC_VER)
209 else if (p[-1] == ':') {
210 /* z:/dir => z:/ */
211 Jim_SetResultString(interp, path, p - path + 1);
213 #endif
214 else {
215 Jim_SetResultString(interp, path, p - path);
217 return JIM_OK;
220 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
222 const char *path = Jim_String(argv[0]);
223 const char *lastSlash = strrchr(path, '/');
224 const char *p = strrchr(path, '.');
226 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
227 Jim_SetResult(interp, argv[0]);
229 else {
230 Jim_SetResultString(interp, path, p - path);
232 return JIM_OK;
235 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
237 const char *path = Jim_String(argv[0]);
238 const char *lastSlash = strrchr(path, '/');
239 const char *p = strrchr(path, '.');
241 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
242 p = "";
244 Jim_SetResultString(interp, p, -1);
245 return JIM_OK;
248 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
250 const char *path = Jim_String(argv[0]);
251 const char *lastSlash = strrchr(path, '/');
253 if (lastSlash) {
254 Jim_SetResultString(interp, lastSlash + 1, -1);
256 else {
257 Jim_SetResult(interp, argv[0]);
259 return JIM_OK;
262 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
264 #ifdef HAVE_REALPATH
265 const char *path = Jim_String(argv[0]);
266 char *newname = Jim_Alloc(MAXPATHLEN + 1);
268 if (realpath(path, newname)) {
269 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
271 else {
272 Jim_Free(newname);
273 Jim_SetResult(interp, argv[0]);
275 return JIM_OK;
276 #else
277 Jim_SetResultString(interp, "Not implemented", -1);
278 return JIM_ERR;
279 #endif
282 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
284 int i;
285 char *newname = Jim_Alloc(MAXPATHLEN + 1);
286 char *last = newname;
288 *newname = 0;
290 /* Simple implementation for now */
291 for (i = 0; i < argc; i++) {
292 int len;
293 const char *part = Jim_GetString(argv[i], &len);
295 if (*part == '/') {
296 /* Absolute component, so go back to the start */
297 last = newname;
299 #if defined(__MINGW32__) || defined(_MSC_VER)
300 else if (strchr(part, ':')) {
301 /* Absolute compontent on mingw, so go back to the start */
302 last = newname;
304 #endif
305 else if (part[0] == '.') {
306 if (part[1] == '/') {
307 part += 2;
308 len -= 2;
310 else if (part[1] == 0 && last != newname) {
311 /* Adding '.' to an existing path does nothing */
312 continue;
316 /* Add a slash if needed */
317 if (last != newname && last[-1] != '/') {
318 *last++ = '/';
321 if (len) {
322 if (last + len - newname >= MAXPATHLEN) {
323 Jim_Free(newname);
324 Jim_SetResultString(interp, "Path too long", -1);
325 return JIM_ERR;
327 memcpy(last, part, len);
328 last += len;
331 /* Remove a slash if needed */
332 if (last > newname + 1 && last[-1] == '/') {
333 *--last = 0;
337 *last = 0;
339 /* Probably need to handle some special cases ... */
341 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
343 return JIM_OK;
346 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
348 const char *path = Jim_String(filename);
349 int rc = access(path, mode);
351 Jim_SetResultBool(interp, rc != -1);
353 return JIM_OK;
356 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
358 return file_access(interp, argv[0], R_OK);
361 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
363 return file_access(interp, argv[0], W_OK);
366 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
368 #ifdef X_OK
369 return file_access(interp, argv[0], X_OK);
370 #else
371 /* XXX: X_OK doesn't work under Windows.
372 * In any case, may need to add .exe, etc. so just lie!
374 Jim_SetResultBool(interp, 1);
375 return JIM_OK;
376 #endif
379 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
381 return file_access(interp, argv[0], F_OK);
384 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
386 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
388 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
389 argc++;
390 argv--;
393 while (argc--) {
394 const char *path = Jim_String(argv[0]);
396 if (unlink(path) == -1 && errno != ENOENT) {
397 if (rmdir(path) == -1) {
398 /* Maybe try using the script helper */
399 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
400 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
401 strerror(errno));
402 return JIM_ERR;
406 argv++;
408 return JIM_OK;
411 #ifdef HAVE_MKDIR_ONE_ARG
412 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
413 #else
414 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
415 #endif
418 * Create directory, creating all intermediate paths if necessary.
420 * Returns 0 if OK or -1 on failure (and sets errno)
422 * Note: The path may be modified.
424 static int mkdir_all(char *path)
426 int ok = 1;
428 /* First time just try to make the dir */
429 goto first;
431 while (ok--) {
432 /* Must have failed the first time, so recursively make the parent and try again */
433 char *slash = strrchr(path, '/');
435 if (slash && slash != path) {
436 *slash = 0;
437 if (mkdir_all(path) != 0) {
438 return -1;
440 *slash = '/';
442 first:
443 if (MKDIR_DEFAULT(path) == 0) {
444 return 0;
446 if (errno == ENOENT) {
447 /* Create the parent and try again */
448 continue;
450 /* Maybe it already exists as a directory */
451 if (errno == EEXIST) {
452 struct stat sb;
454 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
455 return 0;
457 /* Restore errno */
458 errno = EEXIST;
460 /* Failed */
461 break;
463 return -1;
466 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
468 while (argc--) {
469 char *path = Jim_StrDup(Jim_String(argv[0]));
470 int rc = mkdir_all(path);
472 Jim_Free(path);
473 if (rc != 0) {
474 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
475 strerror(errno));
476 return JIM_ERR;
478 argv++;
480 return JIM_OK;
483 #ifdef HAVE_MKSTEMP
484 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
486 int fd;
487 char *filename;
488 const char *template = "/tmp/tcl.tmp.XXXXXX";
490 if (argc >= 1) {
491 template = Jim_String(argv[0]);
493 filename = Jim_StrDup(template);
495 fd = mkstemp(filename);
496 if (fd < 0) {
497 Jim_SetResultString(interp, "Failed to create tempfile", -1);
498 return JIM_ERR;
500 close(fd);
502 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, filename, -1));
503 return JIM_OK;
505 #endif
507 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
509 const char *source;
510 const char *dest;
511 int force = 0;
513 if (argc == 3) {
514 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
515 return -1;
517 force++;
518 argv++;
519 argc--;
522 source = Jim_String(argv[0]);
523 dest = Jim_String(argv[1]);
525 if (!force && access(dest, F_OK) == 0) {
526 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
527 argv[1]);
528 return JIM_ERR;
531 if (rename(source, dest) != 0) {
532 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
533 strerror(errno));
534 return JIM_ERR;
537 return JIM_OK;
540 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
542 const char *path = Jim_String(filename);
544 if (stat(path, sb) == -1) {
545 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
546 return JIM_ERR;
548 return JIM_OK;
551 #ifndef HAVE_LSTAT
552 #define lstat stat
553 #endif
555 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
557 const char *path = Jim_String(filename);
559 if (lstat(path, sb) == -1) {
560 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
561 return JIM_ERR;
563 return JIM_OK;
566 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
568 struct stat sb;
570 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
571 return JIM_ERR;
573 Jim_SetResultInt(interp, sb.st_atime);
574 return JIM_OK;
577 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
579 struct stat sb;
581 if (argc == 2) {
582 #ifdef HAVE_UTIMES
583 jim_wide newtime;
584 struct timeval times[2];
586 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
587 return JIM_ERR;
590 times[1].tv_sec = times[0].tv_sec = newtime;
591 times[1].tv_usec = times[0].tv_usec = 0;
593 if (utimes(Jim_String(argv[0]), times) != 0) {
594 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
595 return JIM_ERR;
597 #else
598 Jim_SetResultString(interp, "Not implemented", -1);
599 return JIM_ERR;
600 #endif
602 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
603 return JIM_ERR;
605 Jim_SetResultInt(interp, sb.st_mtime);
606 return JIM_OK;
609 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
611 return Jim_EvalPrefix(interp, "file copy", argc, argv);
614 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
616 struct stat sb;
618 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
619 return JIM_ERR;
621 Jim_SetResultInt(interp, sb.st_size);
622 return JIM_OK;
625 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
627 struct stat sb;
628 int ret = 0;
630 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
631 ret = S_ISDIR(sb.st_mode);
633 Jim_SetResultInt(interp, ret);
634 return JIM_OK;
637 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
639 struct stat sb;
640 int ret = 0;
642 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
643 ret = S_ISREG(sb.st_mode);
645 Jim_SetResultInt(interp, ret);
646 return JIM_OK;
649 #ifdef HAVE_GETEUID
650 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
652 struct stat sb;
653 int ret = 0;
655 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
656 ret = (geteuid() == sb.st_uid);
658 Jim_SetResultInt(interp, ret);
659 return JIM_OK;
661 #endif
663 #if defined(HAVE_READLINK)
664 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
666 const char *path = Jim_String(argv[0]);
667 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
669 int linkLength = readlink(path, linkValue, MAXPATHLEN);
671 if (linkLength == -1) {
672 Jim_Free(linkValue);
673 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
674 return JIM_ERR;
676 linkValue[linkLength] = 0;
677 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
678 return JIM_OK;
680 #endif
682 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
684 struct stat sb;
686 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
687 return JIM_ERR;
689 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
690 return JIM_OK;
693 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
695 struct stat sb;
697 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
698 return JIM_ERR;
700 return StoreStatData(interp, argv[1], &sb);
703 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
705 struct stat sb;
707 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
708 return JIM_ERR;
710 return StoreStatData(interp, argv[1], &sb);
713 static const jim_subcmd_type file_command_table[] = {
714 { "atime",
715 "name",
716 file_cmd_atime,
719 /* Description: Last access time */
721 { "mtime",
722 "name ?time?",
723 file_cmd_mtime,
726 /* Description: Get or set last modification time */
728 { "copy",
729 "?-force? source dest",
730 file_cmd_copy,
733 /* Description: Copy source file to destination file */
735 { "dirname",
736 "name",
737 file_cmd_dirname,
740 /* Description: Directory part of the name */
742 { "rootname",
743 "name",
744 file_cmd_rootname,
747 /* Description: Name without any extension */
749 { "extension",
750 "name",
751 file_cmd_extension,
754 /* Description: Last extension including the dot */
756 { "tail",
757 "name",
758 file_cmd_tail,
761 /* Description: Last component of the name */
763 { "normalize",
764 "name",
765 file_cmd_normalize,
768 /* Description: Normalized path of name */
770 { "join",
771 "name ?name ...?",
772 file_cmd_join,
775 /* Description: Join multiple path components */
777 { "readable",
778 "name",
779 file_cmd_readable,
782 /* Description: Is file readable */
784 { "writable",
785 "name",
786 file_cmd_writable,
789 /* Description: Is file writable */
791 { "executable",
792 "name",
793 file_cmd_executable,
796 /* Description: Is file executable */
798 { "exists",
799 "name",
800 file_cmd_exists,
803 /* Description: Does file exist */
805 { "delete",
806 "?-force|--? name ...",
807 file_cmd_delete,
810 /* Description: Deletes the files or directories (must be empty unless -force) */
812 { "mkdir",
813 "dir ...",
814 file_cmd_mkdir,
817 /* Description: Creates the directories */
819 #ifdef HAVE_MKSTEMP
820 { "tempfile",
821 "?template?",
822 file_cmd_tempfile,
825 /* Description: Creates a temporary filename */
827 #endif
828 { "rename",
829 "?-force? source dest",
830 file_cmd_rename,
833 /* Description: Renames a file */
835 #if defined(HAVE_READLINK)
836 { "readlink",
837 "name",
838 file_cmd_readlink,
841 /* Description: Value of the symbolic link */
843 #endif
844 { "size",
845 "name",
846 file_cmd_size,
849 /* Description: Size of file */
851 { "stat",
852 "name var",
853 file_cmd_stat,
856 /* Description: Stores results of stat in var array */
858 { "lstat",
859 "name var",
860 file_cmd_lstat,
863 /* Description: Stores results of lstat in var array */
865 { "type",
866 "name",
867 file_cmd_type,
870 /* Description: Returns type of the file */
872 #ifdef HAVE_GETEUID
873 { "owned",
874 "name",
875 file_cmd_owned,
878 /* Description: Returns 1 if owned by the current owner */
880 #endif
881 { "isdirectory",
882 "name",
883 file_cmd_isdirectory,
886 /* Description: Returns 1 if name is a directory */
888 { "isfile",
889 "name",
890 file_cmd_isfile,
893 /* Description: Returns 1 if name is a file */
896 NULL
900 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
902 const char *path;
904 if (argc != 2) {
905 Jim_WrongNumArgs(interp, 1, argv, "dirname");
906 return JIM_ERR;
909 path = Jim_String(argv[1]);
911 if (chdir(path) != 0) {
912 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
913 strerror(errno));
914 return JIM_ERR;
916 return JIM_OK;
919 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
921 const int cwd_len = 2048;
922 char *cwd = malloc(cwd_len);
924 if (getcwd(cwd, cwd_len) == NULL) {
925 Jim_SetResultString(interp, "Failed to get pwd", -1);
926 return JIM_ERR;
928 #if defined(__MINGW32__) || defined(_MSC_VER)
930 /* Try to keep backlashes out of paths */
931 char *p = cwd;
932 while ((p = strchr(p, '\\')) != NULL) {
933 *p++ = '/';
936 #endif
938 Jim_SetResultString(interp, cwd, -1);
940 free(cwd);
941 return JIM_OK;
944 int Jim_fileInit(Jim_Interp *interp)
946 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
947 return JIM_ERR;
949 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
950 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
951 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
952 return JIM_OK;