Add a general purpose hashtable pattern matcher
[jimtcl.git] / jim-file.c
blobf56db025b8fad14a3b7f02e5392a07976f0d37c2
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 "jim.h"
54 #include "jimautoconf.h"
55 #include "jim-subcmd.h"
57 #ifdef HAVE_UTIMES
58 #include <sys/time.h>
59 #endif
60 #ifdef HAVE_UNISTD_H
61 #include <unistd.h>
62 #elif defined(_MSC_VER)
63 #include <direct.h>
64 #define F_OK 0
65 #define W_OK 2
66 #define R_OK 4
67 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
68 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
69 #endif
71 # ifndef MAXPATHLEN
72 # define MAXPATHLEN JIM_PATH_LEN
73 # endif
76 *----------------------------------------------------------------------
78 * JimGetFileType --
80 * Given a mode word, returns a string identifying the type of a
81 * file.
83 * Results:
84 * A static text string giving the file type from mode.
86 * Side effects:
87 * None.
89 *----------------------------------------------------------------------
92 static const char *JimGetFileType(int mode)
94 if (S_ISREG(mode)) {
95 return "file";
97 else if (S_ISDIR(mode)) {
98 return "directory";
100 #ifdef S_ISCHR
101 else if (S_ISCHR(mode)) {
102 return "characterSpecial";
104 #endif
105 #ifdef S_ISBLK
106 else if (S_ISBLK(mode)) {
107 return "blockSpecial";
109 #endif
110 #ifdef S_ISFIFO
111 else if (S_ISFIFO(mode)) {
112 return "fifo";
114 #endif
115 #ifdef S_ISLNK
116 else if (S_ISLNK(mode)) {
117 return "link";
119 #endif
120 #ifdef S_ISSOCK
121 else if (S_ISSOCK(mode)) {
122 return "socket";
124 #endif
125 return "unknown";
129 *----------------------------------------------------------------------
131 * StoreStatData --
133 * This is a utility procedure that breaks out the fields of a
134 * "stat" structure and stores them in textual form into the
135 * elements of an associative array.
137 * Results:
138 * Returns a standard Tcl return value. If an error occurs then
139 * a message is left in interp->result.
141 * Side effects:
142 * Elements of the associative array given by "varName" are modified.
144 *----------------------------------------------------------------------
147 static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
148 jim_wide value)
150 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
151 Jim_Obj *valobj = Jim_NewWideObj(interp, value);
153 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
154 Jim_FreeObj(interp, nameobj);
155 Jim_FreeObj(interp, valobj);
156 return JIM_ERR;
158 return JIM_OK;
161 static int set_array_string_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
162 const char *value)
164 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
165 Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1);
167 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
168 Jim_FreeObj(interp, nameobj);
169 Jim_FreeObj(interp, valobj);
170 return JIM_ERR;
172 return JIM_OK;
175 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
177 if (set_array_int_value(interp, varName, "dev", sb->st_dev) != JIM_OK) {
178 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variable isn't array", varName);
179 return JIM_ERR;
181 set_array_int_value(interp, varName, "ino", sb->st_ino);
182 set_array_int_value(interp, varName, "mode", sb->st_mode);
183 set_array_int_value(interp, varName, "nlink", sb->st_nlink);
184 set_array_int_value(interp, varName, "uid", sb->st_uid);
185 set_array_int_value(interp, varName, "gid", sb->st_gid);
186 set_array_int_value(interp, varName, "size", sb->st_size);
187 set_array_int_value(interp, varName, "atime", sb->st_atime);
188 set_array_int_value(interp, varName, "mtime", sb->st_mtime);
189 set_array_int_value(interp, varName, "ctime", sb->st_ctime);
190 set_array_string_value(interp, varName, "type", JimGetFileType((int)sb->st_mode));
192 /* And also return the value */
193 Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0));
195 return JIM_OK;
198 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
200 const char *path = Jim_String(argv[0]);
201 const char *p = strrchr(path, '/');
203 if (!p) {
204 Jim_SetResultString(interp, ".", -1);
206 else if (p == path) {
207 Jim_SetResultString(interp, "/", -1);
209 #if defined(__MINGW32__) || defined(_MSC_VER)
210 else if (p[-1] == ':') {
211 /* z:/dir => z:/ */
212 Jim_SetResultString(interp, path, p - path + 1);
214 #endif
215 else {
216 Jim_SetResultString(interp, path, p - path);
218 return JIM_OK;
221 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
223 const char *path = Jim_String(argv[0]);
224 const char *lastSlash = strrchr(path, '/');
225 const char *p = strrchr(path, '.');
227 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
228 Jim_SetResult(interp, argv[0]);
230 else {
231 Jim_SetResultString(interp, path, p - path);
233 return JIM_OK;
236 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
238 const char *path = Jim_String(argv[0]);
239 const char *lastSlash = strrchr(path, '/');
240 const char *p = strrchr(path, '.');
242 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
243 p = "";
245 Jim_SetResultString(interp, p, -1);
246 return JIM_OK;
249 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
251 const char *path = Jim_String(argv[0]);
252 const char *lastSlash = strrchr(path, '/');
254 if (lastSlash) {
255 Jim_SetResultString(interp, lastSlash + 1, -1);
257 else {
258 Jim_SetResult(interp, argv[0]);
260 return JIM_OK;
263 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
265 #ifdef HAVE_REALPATH
266 const char *path = Jim_String(argv[0]);
267 char *newname = Jim_Alloc(MAXPATHLEN + 1);
269 if (realpath(path, newname)) {
270 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
272 else {
273 Jim_Free(newname);
274 Jim_SetResult(interp, argv[0]);
276 return JIM_OK;
277 #else
278 Jim_SetResultString(interp, "Not implemented", -1);
279 return JIM_ERR;
280 #endif
283 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
285 int i;
286 char *newname = Jim_Alloc(MAXPATHLEN + 1);
287 char *last = newname;
289 *newname = 0;
291 /* Simple implementation for now */
292 for (i = 0; i < argc; i++) {
293 int len;
294 const char *part = Jim_GetString(argv[i], &len);
296 if (*part == '/') {
297 /* Absolute component, so go back to the start */
298 last = newname;
300 #if defined(__MINGW32__) || defined(_MSC_VER)
301 else if (strchr(part, ':')) {
302 /* Absolute compontent on mingw, so go back to the start */
303 last = newname;
305 #endif
306 else if (part[0] == '.') {
307 if (part[1] == '/') {
308 part += 2;
309 len -= 2;
311 else if (part[1] == 0 && last != newname) {
312 /* Adding '.' to an existing path does nothing */
313 continue;
317 /* Add a slash if needed */
318 if (last != newname && last[-1] != '/') {
319 *last++ = '/';
322 if (len) {
323 if (last + len - newname >= MAXPATHLEN) {
324 Jim_Free(newname);
325 Jim_SetResultString(interp, "Path too long", -1);
326 return JIM_ERR;
328 memcpy(last, part, len);
329 last += len;
332 /* Remove a slash if needed */
333 if (last > newname + 1 && last[-1] == '/') {
334 *--last = 0;
338 *last = 0;
340 /* Probably need to handle some special cases ... */
342 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
344 return JIM_OK;
347 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
349 const char *path = Jim_String(filename);
350 int rc = access(path, mode);
352 Jim_SetResultBool(interp, rc != -1);
354 return JIM_OK;
357 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
359 return file_access(interp, argv[0], R_OK);
362 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
364 return file_access(interp, argv[0], W_OK);
367 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
369 #ifdef X_OK
370 return file_access(interp, argv[0], X_OK);
371 #else
372 /* XXX: X_OK doesn't work under Windows.
373 * In any case, may need to add .exe, etc. so just lie!
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 */
434 char *slash = strrchr(path, '/');
436 if (slash && slash != path) {
437 *slash = 0;
438 if (mkdir_all(path) != 0) {
439 return -1;
441 *slash = '/';
443 first:
444 if (MKDIR_DEFAULT(path) == 0) {
445 return 0;
447 if (errno == ENOENT) {
448 /* Create the parent and try again */
449 continue;
451 /* Maybe it already exists as a directory */
452 if (errno == EEXIST) {
453 struct stat sb;
455 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
456 return 0;
458 /* Restore errno */
459 errno = EEXIST;
461 /* Failed */
462 break;
464 return -1;
467 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
469 while (argc--) {
470 char *path = Jim_StrDup(Jim_String(argv[0]));
471 int rc = mkdir_all(path);
473 Jim_Free(path);
474 if (rc != 0) {
475 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
476 strerror(errno));
477 return JIM_ERR;
479 argv++;
481 return JIM_OK;
484 #ifdef HAVE_MKSTEMP
485 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
487 int fd;
488 char *filename;
489 const char *template = "/tmp/tcl.tmp.XXXXXX";
491 if (argc >= 1) {
492 template = Jim_String(argv[0]);
494 filename = Jim_StrDup(template);
496 fd = mkstemp(filename);
497 if (fd < 0) {
498 Jim_SetResultString(interp, "Failed to create tempfile", -1);
499 return JIM_ERR;
501 close(fd);
503 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, filename, -1));
504 return JIM_OK;
506 #endif
508 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
510 const char *source;
511 const char *dest;
512 int force = 0;
514 if (argc == 3) {
515 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
516 return -1;
518 force++;
519 argv++;
520 argc--;
523 source = Jim_String(argv[0]);
524 dest = Jim_String(argv[1]);
526 if (!force && access(dest, F_OK) == 0) {
527 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
528 argv[1]);
529 return JIM_ERR;
532 if (rename(source, dest) != 0) {
533 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
534 strerror(errno));
535 return JIM_ERR;
538 return JIM_OK;
541 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
543 const char *path = Jim_String(filename);
545 if (stat(path, sb) == -1) {
546 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
547 return JIM_ERR;
549 return JIM_OK;
552 #ifndef HAVE_LSTAT
553 #define lstat stat
554 #endif
556 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
558 const char *path = Jim_String(filename);
560 if (lstat(path, sb) == -1) {
561 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
562 return JIM_ERR;
564 return JIM_OK;
567 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
569 struct stat sb;
571 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
572 return JIM_ERR;
574 Jim_SetResultInt(interp, sb.st_atime);
575 return JIM_OK;
578 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
580 struct stat sb;
582 if (argc == 2) {
583 #ifdef HAVE_UTIMES
584 jim_wide newtime;
585 struct timeval times[2];
587 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
588 return JIM_ERR;
591 times[1].tv_sec = times[0].tv_sec = newtime;
592 times[1].tv_usec = times[0].tv_usec = 0;
594 if (utimes(Jim_String(argv[0]), times) != 0) {
595 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
596 return JIM_ERR;
598 #else
599 Jim_SetResultString(interp, "Not implemented", -1);
600 return JIM_ERR;
601 #endif
603 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
604 return JIM_ERR;
606 Jim_SetResultInt(interp, sb.st_mtime);
607 return JIM_OK;
610 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
612 return Jim_EvalPrefix(interp, "file copy", argc, argv);
615 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
617 struct stat sb;
619 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
620 return JIM_ERR;
622 Jim_SetResultInt(interp, sb.st_size);
623 return JIM_OK;
626 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
628 struct stat sb;
629 int ret = 0;
631 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
632 ret = S_ISDIR(sb.st_mode);
634 Jim_SetResultInt(interp, ret);
635 return JIM_OK;
638 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
640 struct stat sb;
641 int ret = 0;
643 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
644 ret = S_ISREG(sb.st_mode);
646 Jim_SetResultInt(interp, ret);
647 return JIM_OK;
650 #ifdef HAVE_GETEUID
651 static int file_cmd_owned(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
653 struct stat sb;
654 int ret = 0;
656 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
657 ret = (geteuid() == sb.st_uid);
659 Jim_SetResultInt(interp, ret);
660 return JIM_OK;
662 #endif
664 #if defined(HAVE_READLINK)
665 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
667 const char *path = Jim_String(argv[0]);
668 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
670 int linkLength = readlink(path, linkValue, MAXPATHLEN);
672 if (linkLength == -1) {
673 Jim_Free(linkValue);
674 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
675 return JIM_ERR;
677 linkValue[linkLength] = 0;
678 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
679 return JIM_OK;
681 #endif
683 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
685 struct stat sb;
687 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
688 return JIM_ERR;
690 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
691 return JIM_OK;
694 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
696 struct stat sb;
698 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
699 return JIM_ERR;
701 return StoreStatData(interp, argv[1], &sb);
704 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
706 struct stat sb;
708 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
709 return JIM_ERR;
711 return StoreStatData(interp, argv[1], &sb);
714 static const jim_subcmd_type file_command_table[] = {
715 { "atime",
716 "name",
717 file_cmd_atime,
720 /* Description: Last access time */
722 { "mtime",
723 "name ?time?",
724 file_cmd_mtime,
727 /* Description: Get or set last modification time */
729 { "copy",
730 "?-force? source dest",
731 file_cmd_copy,
734 /* Description: Copy source file to destination file */
736 { "dirname",
737 "name",
738 file_cmd_dirname,
741 /* Description: Directory part of the name */
743 { "rootname",
744 "name",
745 file_cmd_rootname,
748 /* Description: Name without any extension */
750 { "extension",
751 "name",
752 file_cmd_extension,
755 /* Description: Last extension including the dot */
757 { "tail",
758 "name",
759 file_cmd_tail,
762 /* Description: Last component of the name */
764 { "normalize",
765 "name",
766 file_cmd_normalize,
769 /* Description: Normalized path of name */
771 { "join",
772 "name ?name ...?",
773 file_cmd_join,
776 /* Description: Join multiple path components */
778 { "readable",
779 "name",
780 file_cmd_readable,
783 /* Description: Is file readable */
785 { "writable",
786 "name",
787 file_cmd_writable,
790 /* Description: Is file writable */
792 { "executable",
793 "name",
794 file_cmd_executable,
797 /* Description: Is file executable */
799 { "exists",
800 "name",
801 file_cmd_exists,
804 /* Description: Does file exist */
806 { "delete",
807 "?-force|--? name ...",
808 file_cmd_delete,
811 /* Description: Deletes the files or directories (must be empty unless -force) */
813 { "mkdir",
814 "dir ...",
815 file_cmd_mkdir,
818 /* Description: Creates the directories */
820 #ifdef HAVE_MKSTEMP
821 { "tempfile",
822 "?template?",
823 file_cmd_tempfile,
826 /* Description: Creates a temporary filename */
828 #endif
829 { "rename",
830 "?-force? source dest",
831 file_cmd_rename,
834 /* Description: Renames a file */
836 #if defined(HAVE_READLINK)
837 { "readlink",
838 "name",
839 file_cmd_readlink,
842 /* Description: Value of the symbolic link */
844 #endif
845 { "size",
846 "name",
847 file_cmd_size,
850 /* Description: Size of file */
852 { "stat",
853 "name var",
854 file_cmd_stat,
857 /* Description: Stores results of stat in var array */
859 { "lstat",
860 "name var",
861 file_cmd_lstat,
864 /* Description: Stores results of lstat in var array */
866 { "type",
867 "name",
868 file_cmd_type,
871 /* Description: Returns type of the file */
873 #ifdef HAVE_GETEUID
874 { "owned",
875 "name",
876 file_cmd_owned,
879 /* Description: Returns 1 if owned by the current owner */
881 #endif
882 { "isdirectory",
883 "name",
884 file_cmd_isdirectory,
887 /* Description: Returns 1 if name is a directory */
889 { "isfile",
890 "name",
891 file_cmd_isfile,
894 /* Description: Returns 1 if name is a file */
897 NULL
901 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
903 const char *path;
905 if (argc != 2) {
906 Jim_WrongNumArgs(interp, 1, argv, "dirname");
907 return JIM_ERR;
910 path = Jim_String(argv[1]);
912 if (chdir(path) != 0) {
913 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
914 strerror(errno));
915 return JIM_ERR;
917 return JIM_OK;
920 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
922 const int cwd_len = 2048;
923 char *cwd = malloc(cwd_len);
925 if (getcwd(cwd, cwd_len) == NULL) {
926 Jim_SetResultString(interp, "Failed to get pwd", -1);
927 return JIM_ERR;
929 #if defined(__MINGW32__) || defined(_MSC_VER)
931 /* Try to keep backlashes out of paths */
932 char *p = cwd;
933 while ((p = strchr(p, '\\')) != NULL) {
934 *p++ = '/';
937 #endif
939 Jim_SetResultString(interp, cwd, -1);
941 free(cwd);
942 return JIM_OK;
945 int Jim_fileInit(Jim_Interp *interp)
947 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
948 return JIM_ERR;
950 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
951 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
952 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
953 return JIM_OK;