Return codes > 7 should be caught by default
[jimtcl.git] / jim-file.c
blob5c962ac853f608a6b28cbedb4e62146fb1dabf6e
1 /*
2 * Implements the file command for jim
4 * (c) 2008 Steve Bennett <steveb@workware.net.au>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
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 <unistd.h>
51 #include <errno.h>
52 #include <sys/stat.h>
53 #include <sys/param.h>
55 #include "jim.h"
56 #include "jimautoconf.h"
57 #include "jim-subcmd.h"
59 # ifndef MAXPATHLEN
60 # define MAXPATHLEN JIM_PATH_LEN
61 # endif
64 *----------------------------------------------------------------------
66 * JimGetFileType --
68 * Given a mode word, returns a string identifying the type of a
69 * file.
71 * Results:
72 * A static text string giving the file type from mode.
74 * Side effects:
75 * None.
77 *----------------------------------------------------------------------
80 static const char *JimGetFileType(int mode)
82 if (S_ISREG(mode)) {
83 return "file";
85 else if (S_ISDIR(mode)) {
86 return "directory";
88 else if (S_ISCHR(mode)) {
89 return "characterSpecial";
91 else if (S_ISBLK(mode)) {
92 return "blockSpecial";
94 else if (S_ISFIFO(mode)) {
95 return "fifo";
96 #ifdef S_ISLNK
98 else if (S_ISLNK(mode)) {
99 return "link";
100 #endif
101 #ifdef S_ISSOCK
103 else if (S_ISSOCK(mode)) {
104 return "socket";
105 #endif
107 return "unknown";
111 *----------------------------------------------------------------------
113 * StoreStatData --
115 * This is a utility procedure that breaks out the fields of a
116 * "stat" structure and stores them in textual form into the
117 * elements of an associative array.
119 * Results:
120 * Returns a standard Tcl return value. If an error occurs then
121 * a message is left in interp->result.
123 * Side effects:
124 * Elements of the associative array given by "varName" are modified.
126 *----------------------------------------------------------------------
129 static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
130 jim_wide value)
132 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
133 Jim_Obj *valobj = Jim_NewWideObj(interp, value);
135 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj) != JIM_OK) {
136 Jim_FreeObj(interp, nameobj);
137 Jim_FreeObj(interp, valobj);
138 return JIM_ERR;
140 return JIM_OK;
143 static int set_array_string_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
144 const char *value)
146 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
147 Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1);
149 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj) != JIM_OK) {
150 Jim_FreeObj(interp, nameobj);
151 Jim_FreeObj(interp, valobj);
152 return JIM_ERR;
154 return JIM_OK;
157 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
159 if (set_array_int_value(interp, varName, "dev", sb->st_dev) != JIM_OK) {
160 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variables isn't array", varName);
161 return JIM_ERR;
163 set_array_int_value(interp, varName, "ino", sb->st_ino);
164 set_array_int_value(interp, varName, "mode", sb->st_mode);
165 set_array_int_value(interp, varName, "nlink", sb->st_nlink);
166 set_array_int_value(interp, varName, "uid", sb->st_uid);
167 set_array_int_value(interp, varName, "gid", sb->st_gid);
168 set_array_int_value(interp, varName, "size", sb->st_size);
169 set_array_int_value(interp, varName, "atime", sb->st_atime);
170 set_array_int_value(interp, varName, "mtime", sb->st_mtime);
171 set_array_int_value(interp, varName, "ctime", sb->st_ctime);
172 set_array_string_value(interp, varName, "type", JimGetFileType((int)sb->st_mode));
174 /* And also return the value */
175 Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0));
177 return JIM_OK;
180 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
182 const char *path = Jim_String(argv[0]);
183 const char *p = strrchr(path, '/');
185 if (!p) {
186 Jim_SetResultString(interp, ".", -1);
188 else if (p == path) {
189 Jim_SetResultString(interp, "/", -1);
191 #if defined(__MINGW32__)
192 else if (p[-1] == ':') {
193 /* z:/dir => z:/ */
194 Jim_SetResultString(interp, path, p - path + 1);
196 #endif
197 else {
198 Jim_SetResultString(interp, path, p - path);
200 return JIM_OK;
203 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
205 const char *path = Jim_String(argv[0]);
206 const char *lastSlash = strrchr(path, '/');
207 const char *p = strrchr(path, '.');
209 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
210 Jim_SetResult(interp, argv[0]);
212 else {
213 Jim_SetResultString(interp, path, p - path);
215 return JIM_OK;
218 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
220 const char *path = Jim_String(argv[0]);
221 const char *lastSlash = strrchr(path, '/');
222 const char *p = strrchr(path, '.');
224 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
225 p = "";
227 Jim_SetResultString(interp, p, -1);
228 return JIM_OK;
231 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
233 const char *path = Jim_String(argv[0]);
234 const char *lastSlash = strrchr(path, '/');
236 if (lastSlash) {
237 Jim_SetResultString(interp, lastSlash + 1, -1);
239 else {
240 Jim_SetResult(interp, argv[0]);
242 return JIM_OK;
245 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
247 #ifdef HAVE_REALPATH
248 const char *path = Jim_String(argv[0]);
249 char *newname = Jim_Alloc(MAXPATHLEN + 1);
251 if (realpath(path, newname)) {
252 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
254 else {
255 Jim_Free(newname);
256 Jim_SetResult(interp, argv[0]);
258 return JIM_OK;
259 #else
260 Jim_SetResultString(interp, "Not implemented", -1);
261 return JIM_ERR;
262 #endif
265 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
267 int i;
268 char *newname = Jim_Alloc(MAXPATHLEN + 1);
269 char *last = newname;
271 *newname = 0;
273 /* Simple implementation for now */
274 for (i = 0; i < argc; i++) {
275 int len;
276 const char *part = Jim_GetString(argv[i], &len);
278 if (*part == '/') {
279 /* Absolute component, so go back to the start */
280 last = newname;
282 #if defined(__MINGW32__)
283 else if (strchr(part, ':')) {
284 /* Absolute compontent on mingw, so go back to the start */
285 last = newname;
287 #endif
288 else if (part[0] == '.') {
289 if (part[1] == '/') {
290 part += 2;
291 len -= 2;
293 else if (part[1] == 0 && last != newname) {
294 /* Adding '.' to an existing path does nothing */
295 continue;
299 /* Add a slash if needed */
300 if (last != newname && last[-1] != '/') {
301 *last++ = '/';
304 if (len) {
305 if (last + len - newname >= MAXPATHLEN) {
306 Jim_Free(newname);
307 Jim_SetResultString(interp, "Path too long", -1);
308 return JIM_ERR;
310 memcpy(last, part, len);
311 last += len;
314 /* Remove a slash if needed */
315 if (last > newname + 1 && last[-1] == '/') {
316 *--last = 0;
320 *last = 0;
322 /* Probably need to handle some special cases ... */
324 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
326 return JIM_OK;
329 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
331 const char *path = Jim_String(filename);
332 int rc = access(path, mode);
334 Jim_SetResultBool(interp, rc != -1);
336 return JIM_OK;
339 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
341 return file_access(interp, argv[0], R_OK);
344 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
346 return file_access(interp, argv[0], W_OK);
349 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
351 return file_access(interp, argv[0], X_OK);
354 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
356 return file_access(interp, argv[0], F_OK);
359 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
361 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
363 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
364 argc++;
365 argv--;
368 while (argc--) {
369 const char *path = Jim_String(argv[0]);
371 if (unlink(path) == -1 && errno != ENOENT) {
372 if (rmdir(path) == -1) {
373 /* Maybe try using the script helper */
374 if (!force || Jim_EvalObjPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
375 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
376 strerror(errno));
377 return JIM_ERR;
381 argv++;
383 return JIM_OK;
386 #ifdef HAVE_MKDIR_ONE_ARG
387 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
388 #else
389 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
390 #endif
393 * Create directory, creating all intermediate paths if necessary.
395 * Returns 0 if OK or -1 on failure (and sets errno)
397 * Note: The path may be modified.
399 static int mkdir_all(char *path)
401 int ok = 1;
403 /* First time just try to make the dir */
404 goto first;
406 while (ok--) {
407 /* Must have failed the first time, so recursively make the parent and try again */
408 char *slash = strrchr(path, '/');
410 if (slash && slash != path) {
411 *slash = 0;
412 if (mkdir_all(path) != 0) {
413 return -1;
415 *slash = '/';
417 first:
418 if (MKDIR_DEFAULT(path) == 0) {
419 return 0;
421 if (errno == ENOENT) {
422 /* Create the parent and try again */
423 continue;
425 /* Maybe it already exists as a directory */
426 if (errno == EEXIST) {
427 struct stat sb;
429 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
430 return 0;
432 /* Restore errno */
433 errno = EEXIST;
435 /* Failed */
436 break;
438 return -1;
441 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
443 while (argc--) {
444 char *path = Jim_StrDup(Jim_String(argv[0]));
445 int rc = mkdir_all(path);
447 Jim_Free(path);
448 if (rc != 0) {
449 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
450 strerror(errno));
451 return JIM_ERR;
453 argv++;
455 return JIM_OK;
458 #ifdef HAVE_MKSTEMP
459 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
461 int fd;
462 char *filename;
463 const char *template = "/tmp/tcl.tmp.XXXXXX";
465 if (argc >= 1) {
466 template = Jim_String(argv[0]);
468 filename = Jim_StrDup(template);
470 fd = mkstemp(filename);
471 if (fd < 0) {
472 Jim_SetResultString(interp, "Failed to create tempfile", -1);
473 return JIM_ERR;
475 close(fd);
477 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, filename, -1));
478 return JIM_OK;
480 #endif
482 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
484 const char *source;
485 const char *dest;
486 int force = 0;
488 if (argc == 3) {
489 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
490 return -1;
492 force++;
493 argv++;
494 argc--;
497 source = Jim_String(argv[0]);
498 dest = Jim_String(argv[1]);
500 if (!force && access(dest, F_OK) == 0) {
501 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
502 argv[1]);
503 return JIM_ERR;
506 if (rename(source, dest) != 0) {
507 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
508 strerror(errno));
509 return JIM_ERR;
512 return JIM_OK;
515 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
517 const char *path = Jim_String(filename);
519 if (stat(path, sb) == -1) {
520 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
521 return JIM_ERR;
523 return JIM_OK;
526 #ifndef HAVE_LSTAT
527 #define lstat stat
528 #endif
530 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
532 const char *path = Jim_String(filename);
534 if (lstat(path, sb) == -1) {
535 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
536 return JIM_ERR;
538 return JIM_OK;
541 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
543 struct stat sb;
545 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
546 return JIM_ERR;
548 Jim_SetResultInt(interp, sb.st_atime);
549 return JIM_OK;
552 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
554 struct stat sb;
556 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
557 return JIM_ERR;
559 Jim_SetResultInt(interp, sb.st_mtime);
560 return JIM_OK;
563 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
565 return Jim_EvalObjPrefix(interp, "file copy", argc, argv);
568 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
570 struct stat sb;
572 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
573 return JIM_ERR;
575 Jim_SetResultInt(interp, sb.st_size);
576 return JIM_OK;
579 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
581 struct stat sb;
582 int ret = 0;
584 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
585 ret = S_ISDIR(sb.st_mode);
587 Jim_SetResultInt(interp, ret);
588 return JIM_OK;
591 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
593 struct stat sb;
594 int ret = 0;
596 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
597 ret = S_ISREG(sb.st_mode);
599 Jim_SetResultInt(interp, ret);
600 return JIM_OK;
603 #ifdef HAVE_GETEUID
604 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
606 struct stat sb;
607 int ret = 0;
609 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
610 ret = (geteuid() == sb.st_uid);
612 Jim_SetResultInt(interp, ret);
613 return JIM_OK;
615 #endif
617 #if defined(HAVE_READLINK)
618 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
620 const char *path = Jim_String(argv[0]);
621 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
623 int linkLength = readlink(path, linkValue, MAXPATHLEN);
625 if (linkLength == -1) {
626 Jim_Free(linkValue);
627 Jim_SetResultFormatted(interp, "couldn't readlink \"%s\": %s", argv[0], strerror(errno));
628 return JIM_ERR;
630 linkValue[linkLength] = 0;
631 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
632 return JIM_OK;
634 #endif
636 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
638 struct stat sb;
640 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
641 return JIM_ERR;
643 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
644 return JIM_OK;
647 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
649 struct stat sb;
651 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
652 return JIM_ERR;
654 return StoreStatData(interp, argv[1], &sb);
657 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
659 struct stat sb;
661 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
662 return JIM_ERR;
664 return StoreStatData(interp, argv[1], &sb);
667 static const jim_subcmd_type file_command_table[] = {
668 { .cmd = "atime",
669 .args = "name",
670 .function = file_cmd_atime,
671 .minargs = 1,
672 .maxargs = 1,
673 .description = "Last access time"
675 { .cmd = "mtime",
676 .args = "name",
677 .function = file_cmd_mtime,
678 .minargs = 1,
679 .maxargs = 1,
680 .description = "Last modification time"
682 { .cmd = "copy",
683 .args = "?-force? source dest",
684 .function = file_cmd_copy,
685 .minargs = 2,
686 .maxargs = 3,
687 .description = "Copy source file to destination file"
689 { .cmd = "dirname",
690 .args = "name",
691 .function = file_cmd_dirname,
692 .minargs = 1,
693 .maxargs = 1,
694 .description = "Directory part of the name"
696 { .cmd = "rootname",
697 .args = "name",
698 .function = file_cmd_rootname,
699 .minargs = 1,
700 .maxargs = 1,
701 .description = "Name without any extension"
703 { .cmd = "extension",
704 .args = "name",
705 .function = file_cmd_extension,
706 .minargs = 1,
707 .maxargs = 1,
708 .description = "Last extension including the dot"
710 { .cmd = "tail",
711 .args = "name",
712 .function = file_cmd_tail,
713 .minargs = 1,
714 .maxargs = 1,
715 .description = "Last component of the name"
717 { .cmd = "normalize",
718 .args = "name",
719 .function = file_cmd_normalize,
720 .minargs = 1,
721 .maxargs = 1,
722 .description = "Normalized path of name"
724 { .cmd = "join",
725 .args = "name ?name ...?",
726 .function = file_cmd_join,
727 .minargs = 1,
728 .maxargs = -1,
729 .description = "Join multiple path components"
731 { .cmd = "readable",
732 .args = "name",
733 .function = file_cmd_readable,
734 .minargs = 1,
735 .maxargs = 1,
736 .description = "Is file readable"
738 { .cmd = "writable",
739 .args = "name",
740 .function = file_cmd_writable,
741 .minargs = 1,
742 .maxargs = 1,
743 .description = "Is file writable"
745 { .cmd = "executable",
746 .args = "name",
747 .function = file_cmd_executable,
748 .minargs = 1,
749 .maxargs = 1,
750 .description = "Is file executable"
752 { .cmd = "exists",
753 .args = "name",
754 .function = file_cmd_exists,
755 .minargs = 1,
756 .maxargs = 1,
757 .description = "Does file exist"
759 { .cmd = "delete",
760 .args = "?-force|--? name ...",
761 .function = file_cmd_delete,
762 .minargs = 1,
763 .maxargs = -1,
764 .description = "Deletes the files or directories (must be empty unless -force)"
766 { .cmd = "mkdir",
767 .args = "dir ...",
768 .function = file_cmd_mkdir,
769 .minargs = 1,
770 .maxargs = -1,
771 .description = "Creates the directories"
773 #ifdef HAVE_MKSTEMP
774 { .cmd = "tempfile",
775 .args = "?template?",
776 .function = file_cmd_tempfile,
777 .minargs = 0,
778 .maxargs = 1,
779 .description = "Creates a temporary filename"
781 #endif
782 { .cmd = "rename",
783 .args = "?-force? source dest",
784 .function = file_cmd_rename,
785 .minargs = 2,
786 .maxargs = 3,
787 .description = "Renames a file"
789 #if defined(HAVE_READLINK)
790 { .cmd = "readlink",
791 .args = "name",
792 .function = file_cmd_readlink,
793 .minargs = 1,
794 .maxargs = 1,
795 .description = "Value of the symbolic link"
797 #endif
798 { .cmd = "size",
799 .args = "name",
800 .function = file_cmd_size,
801 .minargs = 1,
802 .maxargs = 1,
803 .description = "Size of file"
805 { .cmd = "stat",
806 .args = "name var",
807 .function = file_cmd_stat,
808 .minargs = 2,
809 .maxargs = 2,
810 .description = "Stores results of stat in var array"
812 { .cmd = "lstat",
813 .args = "name var",
814 .function = file_cmd_lstat,
815 .minargs = 2,
816 .maxargs = 2,
817 .description = "Stores results of lstat in var array"
819 { .cmd = "type",
820 .args = "name",
821 .function = file_cmd_type,
822 .minargs = 1,
823 .maxargs = 1,
824 .description = "Returns type of the file"
826 #ifdef HAVE_GETEUID
827 { .cmd = "owned",
828 .args = "name",
829 .function = file_cmd_owned,
830 .minargs = 1,
831 .maxargs = 1,
832 .description = "Returns 1 if owned by the current owner"
834 #endif
835 { .cmd = "isdirectory",
836 .args = "name",
837 .function = file_cmd_isdirectory,
838 .minargs = 1,
839 .maxargs = 1,
840 .description = "Returns 1 if name is a directory"
842 { .cmd = "isfile",
843 .args = "name",
844 .function = file_cmd_isfile,
845 .minargs = 1,
846 .maxargs = 1,
847 .description = "Returns 1 if name is a file"
850 .cmd = 0
854 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
856 const char *path;
858 if (argc != 2) {
859 Jim_WrongNumArgs(interp, 1, argv, "dirname");
860 return JIM_ERR;
863 path = Jim_String(argv[1]);
865 if (chdir(path) != 0) {
866 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
867 strerror(errno));
868 return JIM_ERR;
870 return JIM_OK;
873 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
875 const int cwd_len = 2048;
876 char *cwd = malloc(cwd_len);
878 if (getcwd(cwd, cwd_len) == NULL) {
879 Jim_SetResultString(interp, "Failed to get pwd", -1);
880 return JIM_ERR;
882 #if defined(__MINGW32__)
884 /* Try to keep backlashes out of paths */
885 char *p = cwd;
886 while ((p = strchr(p, '\\')) != NULL) {
887 *p++ = '/';
890 #endif
892 Jim_SetResultString(interp, cwd, -1);
894 free(cwd);
895 return JIM_OK;
898 int Jim_fileInit(Jim_Interp *interp)
900 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
901 return JIM_ERR;
903 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
904 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
905 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
906 return JIM_OK;