More small improvements to JimWideToString
[jimtcl.git] / jim-file.c
blob6b834a293a557fa6ad6cc3cff1d5fc3d4788fc9e
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 && path[0] == '.' && path[1] == '.' && path[2] == '\0') {
203 Jim_SetResultString(interp, "..", -1);
204 } else if (!p) {
205 Jim_SetResultString(interp, ".", -1);
207 else if (p == path) {
208 Jim_SetResultString(interp, "/", -1);
210 #if defined(__MINGW32__) || defined(_MSC_VER)
211 else if (p[-1] == ':') {
212 /* z:/dir => z:/ */
213 Jim_SetResultString(interp, path, p - path + 1);
215 #endif
216 else {
217 Jim_SetResultString(interp, path, p - path);
219 return JIM_OK;
222 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
224 const char *path = Jim_String(argv[0]);
225 const char *lastSlash = strrchr(path, '/');
226 const char *p = strrchr(path, '.');
228 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
229 Jim_SetResult(interp, argv[0]);
231 else {
232 Jim_SetResultString(interp, path, p - path);
234 return JIM_OK;
237 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
239 const char *path = Jim_String(argv[0]);
240 const char *lastSlash = strrchr(path, '/');
241 const char *p = strrchr(path, '.');
243 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
244 p = "";
246 Jim_SetResultString(interp, p, -1);
247 return JIM_OK;
250 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
252 const char *path = Jim_String(argv[0]);
253 const char *lastSlash = strrchr(path, '/');
255 if (lastSlash) {
256 Jim_SetResultString(interp, lastSlash + 1, -1);
258 else {
259 Jim_SetResult(interp, argv[0]);
261 return JIM_OK;
264 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
266 #ifdef HAVE_REALPATH
267 const char *path = Jim_String(argv[0]);
268 char *newname = Jim_Alloc(MAXPATHLEN + 1);
270 if (realpath(path, newname)) {
271 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
272 return JIM_OK;
274 else {
275 Jim_Free(newname);
276 Jim_SetResultFormatted(interp, "can't normalize \"%#s\": %s", argv[0], strerror(errno));
277 return JIM_ERR;
279 #else
280 Jim_SetResultString(interp, "Not implemented", -1);
281 return JIM_ERR;
282 #endif
285 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
287 int i;
288 char *newname = Jim_Alloc(MAXPATHLEN + 1);
289 char *last = newname;
291 *newname = 0;
293 /* Simple implementation for now */
294 for (i = 0; i < argc; i++) {
295 int len;
296 const char *part = Jim_GetString(argv[i], &len);
298 if (*part == '/') {
299 /* Absolute component, so go back to the start */
300 last = newname;
302 #if defined(__MINGW32__) || defined(_MSC_VER)
303 else if (strchr(part, ':')) {
304 /* Absolute compontent on mingw, so go back to the start */
305 last = newname;
307 #endif
308 else if (part[0] == '.') {
309 if (part[1] == '/') {
310 part += 2;
311 len -= 2;
313 else if (part[1] == 0 && last != newname) {
314 /* Adding '.' to an existing path does nothing */
315 continue;
319 /* Add a slash if needed */
320 if (last != newname && last[-1] != '/') {
321 *last++ = '/';
324 if (len) {
325 if (last + len - newname >= MAXPATHLEN) {
326 Jim_Free(newname);
327 Jim_SetResultString(interp, "Path too long", -1);
328 return JIM_ERR;
330 memcpy(last, part, len);
331 last += len;
334 /* Remove a slash if needed */
335 if (last > newname + 1 && last[-1] == '/') {
336 *--last = 0;
340 *last = 0;
342 /* Probably need to handle some special cases ... */
344 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
346 return JIM_OK;
349 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
351 const char *path = Jim_String(filename);
352 int rc = access(path, mode);
354 Jim_SetResultBool(interp, rc != -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 /* XXX: X_OK doesn't work under Windows.
375 * In any case, may need to add .exe, etc. so just lie!
377 Jim_SetResultBool(interp, 1);
378 return JIM_OK;
379 #endif
382 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
384 return file_access(interp, argv[0], F_OK);
387 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
389 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
391 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
392 argc++;
393 argv--;
396 while (argc--) {
397 const char *path = Jim_String(argv[0]);
399 if (unlink(path) == -1 && errno != ENOENT) {
400 if (rmdir(path) == -1) {
401 /* Maybe try using the script helper */
402 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
403 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
404 strerror(errno));
405 return JIM_ERR;
409 argv++;
411 return JIM_OK;
414 #ifdef HAVE_MKDIR_ONE_ARG
415 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
416 #else
417 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
418 #endif
421 * Create directory, creating all intermediate paths if necessary.
423 * Returns 0 if OK or -1 on failure (and sets errno)
425 * Note: The path may be modified.
427 static int mkdir_all(char *path)
429 int ok = 1;
431 /* First time just try to make the dir */
432 goto first;
434 while (ok--) {
435 /* Must have failed the first time, so recursively make the parent and try again */
437 char *slash = strrchr(path, '/');
439 if (slash && slash != path) {
440 *slash = 0;
441 if (mkdir_all(path) != 0) {
442 return -1;
444 *slash = '/';
447 first:
448 if (MKDIR_DEFAULT(path) == 0) {
449 return 0;
451 if (errno == ENOENT) {
452 /* Create the parent and try again */
453 continue;
455 /* Maybe it already exists as a directory */
456 if (errno == EEXIST) {
457 struct stat sb;
459 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
460 return 0;
462 /* Restore errno */
463 errno = EEXIST;
465 /* Failed */
466 break;
468 return -1;
471 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
473 while (argc--) {
474 char *path = Jim_StrDup(Jim_String(argv[0]));
475 int rc = mkdir_all(path);
477 Jim_Free(path);
478 if (rc != 0) {
479 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
480 strerror(errno));
481 return JIM_ERR;
483 argv++;
485 return JIM_OK;
488 #ifdef HAVE_MKSTEMP
489 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
491 int fd;
492 char *filename;
493 const char *template = "/tmp/tcl.tmp.XXXXXX";
495 if (argc >= 1) {
496 template = Jim_String(argv[0]);
498 filename = Jim_StrDup(template);
500 fd = mkstemp(filename);
501 if (fd < 0) {
502 Jim_SetResultString(interp, "Failed to create tempfile", -1);
503 return JIM_ERR;
505 close(fd);
507 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, filename, -1));
508 return JIM_OK;
510 #endif
512 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
514 const char *source;
515 const char *dest;
516 int force = 0;
518 if (argc == 3) {
519 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
520 return -1;
522 force++;
523 argv++;
524 argc--;
527 source = Jim_String(argv[0]);
528 dest = Jim_String(argv[1]);
530 if (!force && access(dest, F_OK) == 0) {
531 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
532 argv[1]);
533 return JIM_ERR;
536 if (rename(source, dest) != 0) {
537 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
538 strerror(errno));
539 return JIM_ERR;
542 return JIM_OK;
545 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
547 const char *path = Jim_String(filename);
549 if (stat(path, sb) == -1) {
550 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
551 return JIM_ERR;
553 return JIM_OK;
556 #ifndef HAVE_LSTAT
557 #define lstat stat
558 #endif
560 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
562 const char *path = Jim_String(filename);
564 if (lstat(path, sb) == -1) {
565 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
566 return JIM_ERR;
568 return JIM_OK;
571 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
573 struct stat sb;
575 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
576 return JIM_ERR;
578 Jim_SetResultInt(interp, sb.st_atime);
579 return JIM_OK;
582 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
584 struct stat sb;
586 if (argc == 2) {
587 #ifdef HAVE_UTIMES
588 jim_wide newtime;
589 struct timeval times[2];
591 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
592 return JIM_ERR;
595 times[1].tv_sec = times[0].tv_sec = newtime;
596 times[1].tv_usec = times[0].tv_usec = 0;
598 if (utimes(Jim_String(argv[0]), times) != 0) {
599 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
600 return JIM_ERR;
602 #else
603 Jim_SetResultString(interp, "Not implemented", -1);
604 return JIM_ERR;
605 #endif
607 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
608 return JIM_ERR;
610 Jim_SetResultInt(interp, sb.st_mtime);
611 return JIM_OK;
614 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
616 return Jim_EvalPrefix(interp, "file copy", argc, argv);
619 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
621 struct stat sb;
623 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
624 return JIM_ERR;
626 Jim_SetResultInt(interp, sb.st_size);
627 return JIM_OK;
630 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
632 struct stat sb;
633 int ret = 0;
635 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
636 ret = S_ISDIR(sb.st_mode);
638 Jim_SetResultInt(interp, ret);
639 return JIM_OK;
642 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
644 struct stat sb;
645 int ret = 0;
647 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
648 ret = S_ISREG(sb.st_mode);
650 Jim_SetResultInt(interp, ret);
651 return JIM_OK;
654 #ifdef HAVE_GETEUID
655 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
657 struct stat sb;
658 int ret = 0;
660 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
661 ret = (geteuid() == sb.st_uid);
663 Jim_SetResultInt(interp, ret);
664 return JIM_OK;
666 #endif
668 #if defined(HAVE_READLINK)
669 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
671 const char *path = Jim_String(argv[0]);
672 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
674 int linkLength = readlink(path, linkValue, MAXPATHLEN);
676 if (linkLength == -1) {
677 Jim_Free(linkValue);
678 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
679 return JIM_ERR;
681 linkValue[linkLength] = 0;
682 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
683 return JIM_OK;
685 #endif
687 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
689 struct stat sb;
691 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
692 return JIM_ERR;
694 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
695 return JIM_OK;
698 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
700 struct stat sb;
702 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
703 return JIM_ERR;
705 return StoreStatData(interp, argv[1], &sb);
708 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
710 struct stat sb;
712 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
713 return JIM_ERR;
715 return StoreStatData(interp, argv[1], &sb);
718 static const jim_subcmd_type file_command_table[] = {
719 { "atime",
720 "name",
721 file_cmd_atime,
724 /* Description: Last access time */
726 { "mtime",
727 "name ?time?",
728 file_cmd_mtime,
731 /* Description: Get or set last modification time */
733 { "copy",
734 "?-force? source dest",
735 file_cmd_copy,
738 /* Description: Copy source file to destination file */
740 { "dirname",
741 "name",
742 file_cmd_dirname,
745 /* Description: Directory part of the name */
747 { "rootname",
748 "name",
749 file_cmd_rootname,
752 /* Description: Name without any extension */
754 { "extension",
755 "name",
756 file_cmd_extension,
759 /* Description: Last extension including the dot */
761 { "tail",
762 "name",
763 file_cmd_tail,
766 /* Description: Last component of the name */
768 { "normalize",
769 "name",
770 file_cmd_normalize,
773 /* Description: Normalized path of name */
775 { "join",
776 "name ?name ...?",
777 file_cmd_join,
780 /* Description: Join multiple path components */
782 { "readable",
783 "name",
784 file_cmd_readable,
787 /* Description: Is file readable */
789 { "writable",
790 "name",
791 file_cmd_writable,
794 /* Description: Is file writable */
796 { "executable",
797 "name",
798 file_cmd_executable,
801 /* Description: Is file executable */
803 { "exists",
804 "name",
805 file_cmd_exists,
808 /* Description: Does file exist */
810 { "delete",
811 "?-force|--? name ...",
812 file_cmd_delete,
815 /* Description: Deletes the files or directories (must be empty unless -force) */
817 { "mkdir",
818 "dir ...",
819 file_cmd_mkdir,
822 /* Description: Creates the directories */
824 #ifdef HAVE_MKSTEMP
825 { "tempfile",
826 "?template?",
827 file_cmd_tempfile,
830 /* Description: Creates a temporary filename */
832 #endif
833 { "rename",
834 "?-force? source dest",
835 file_cmd_rename,
838 /* Description: Renames a file */
840 #if defined(HAVE_READLINK)
841 { "readlink",
842 "name",
843 file_cmd_readlink,
846 /* Description: Value of the symbolic link */
848 #endif
849 { "size",
850 "name",
851 file_cmd_size,
854 /* Description: Size of file */
856 { "stat",
857 "name var",
858 file_cmd_stat,
861 /* Description: Stores results of stat in var array */
863 { "lstat",
864 "name var",
865 file_cmd_lstat,
868 /* Description: Stores results of lstat in var array */
870 { "type",
871 "name",
872 file_cmd_type,
875 /* Description: Returns type of the file */
877 #ifdef HAVE_GETEUID
878 { "owned",
879 "name",
880 file_cmd_owned,
883 /* Description: Returns 1 if owned by the current owner */
885 #endif
886 { "isdirectory",
887 "name",
888 file_cmd_isdirectory,
891 /* Description: Returns 1 if name is a directory */
893 { "isfile",
894 "name",
895 file_cmd_isfile,
898 /* Description: Returns 1 if name is a file */
901 NULL
905 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
907 const char *path;
909 if (argc != 2) {
910 Jim_WrongNumArgs(interp, 1, argv, "dirname");
911 return JIM_ERR;
914 path = Jim_String(argv[1]);
916 if (chdir(path) != 0) {
917 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
918 strerror(errno));
919 return JIM_ERR;
921 return JIM_OK;
924 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
926 const int cwd_len = 2048;
927 char *cwd = malloc(cwd_len);
929 if (getcwd(cwd, cwd_len) == NULL) {
930 Jim_SetResultString(interp, "Failed to get pwd", -1);
931 return JIM_ERR;
933 #if defined(__MINGW32__) || defined(_MSC_VER)
935 /* Try to keep backlashes out of paths */
936 char *p = cwd;
937 while ((p = strchr(p, '\\')) != NULL) {
938 *p++ = '/';
941 #endif
943 Jim_SetResultString(interp, cwd, -1);
945 free(cwd);
946 return JIM_OK;
949 int Jim_fileInit(Jim_Interp *interp)
951 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
952 return JIM_ERR;
954 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
955 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
956 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
957 return JIM_OK;