usbmodeswitch: Updated to v.1.2.6 from shibby's branch.
[tomato.git] / release / src / router / usbmodeswitch / jim / jim-file.c
blob7b483685ec996c3fc126ab90f23c072607b0a6ae
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 <unistd.h>
51 #include <errno.h>
52 #include <sys/stat.h>
53 #include <sys/param.h>
54 #include <sys/time.h>
56 #include "jim.h"
57 #include "jimautoconf.h"
58 #include "jim-subcmd.h"
60 # ifndef MAXPATHLEN
61 # define MAXPATHLEN JIM_PATH_LEN
62 # endif
65 *----------------------------------------------------------------------
67 * JimGetFileType --
69 * Given a mode word, returns a string identifying the type of a
70 * file.
72 * Results:
73 * A static text string giving the file type from mode.
75 * Side effects:
76 * None.
78 *----------------------------------------------------------------------
81 static const char *JimGetFileType(int mode)
83 if (S_ISREG(mode)) {
84 return "file";
86 else if (S_ISDIR(mode)) {
87 return "directory";
89 else if (S_ISCHR(mode)) {
90 return "characterSpecial";
92 else if (S_ISBLK(mode)) {
93 return "blockSpecial";
95 else if (S_ISFIFO(mode)) {
96 return "fifo";
97 #ifdef S_ISLNK
99 else if (S_ISLNK(mode)) {
100 return "link";
101 #endif
102 #ifdef S_ISSOCK
104 else if (S_ISSOCK(mode)) {
105 return "socket";
106 #endif
108 return "unknown";
112 *----------------------------------------------------------------------
114 * StoreStatData --
116 * This is a utility procedure that breaks out the fields of a
117 * "stat" structure and stores them in textual form into the
118 * elements of an associative array.
120 * Results:
121 * Returns a standard Tcl return value. If an error occurs then
122 * a message is left in interp->result.
124 * Side effects:
125 * Elements of the associative array given by "varName" are modified.
127 *----------------------------------------------------------------------
130 static int set_array_int_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
131 jim_wide value)
133 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
134 Jim_Obj *valobj = Jim_NewWideObj(interp, value);
136 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
137 Jim_FreeObj(interp, nameobj);
138 Jim_FreeObj(interp, valobj);
139 return JIM_ERR;
141 return JIM_OK;
144 static int set_array_string_value(Jim_Interp *interp, Jim_Obj *container, const char *key,
145 const char *value)
147 Jim_Obj *nameobj = Jim_NewStringObj(interp, key, -1);
148 Jim_Obj *valobj = Jim_NewStringObj(interp, value, -1);
150 if (Jim_SetDictKeysVector(interp, container, &nameobj, 1, valobj, JIM_ERRMSG) != JIM_OK) {
151 Jim_FreeObj(interp, nameobj);
152 Jim_FreeObj(interp, valobj);
153 return JIM_ERR;
155 return JIM_OK;
158 static int StoreStatData(Jim_Interp *interp, Jim_Obj *varName, const struct stat *sb)
160 if (set_array_int_value(interp, varName, "dev", sb->st_dev) != JIM_OK) {
161 Jim_SetResultFormatted(interp, "can't set \"%#s(dev)\": variables isn't array", varName);
162 return JIM_ERR;
164 set_array_int_value(interp, varName, "ino", sb->st_ino);
165 set_array_int_value(interp, varName, "mode", sb->st_mode);
166 set_array_int_value(interp, varName, "nlink", sb->st_nlink);
167 set_array_int_value(interp, varName, "uid", sb->st_uid);
168 set_array_int_value(interp, varName, "gid", sb->st_gid);
169 set_array_int_value(interp, varName, "size", sb->st_size);
170 set_array_int_value(interp, varName, "atime", sb->st_atime);
171 set_array_int_value(interp, varName, "mtime", sb->st_mtime);
172 set_array_int_value(interp, varName, "ctime", sb->st_ctime);
173 set_array_string_value(interp, varName, "type", JimGetFileType((int)sb->st_mode));
175 /* And also return the value */
176 Jim_SetResult(interp, Jim_GetVariable(interp, varName, 0));
178 return JIM_OK;
181 static int file_cmd_dirname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
183 const char *path = Jim_String(argv[0]);
184 const char *p = strrchr(path, '/');
186 if (!p) {
187 Jim_SetResultString(interp, ".", -1);
189 else if (p == path) {
190 Jim_SetResultString(interp, "/", -1);
192 #if defined(__MINGW32__)
193 else if (p[-1] == ':') {
194 /* z:/dir => z:/ */
195 Jim_SetResultString(interp, path, p - path + 1);
197 #endif
198 else {
199 Jim_SetResultString(interp, path, p - path);
201 return JIM_OK;
204 static int file_cmd_rootname(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
206 const char *path = Jim_String(argv[0]);
207 const char *lastSlash = strrchr(path, '/');
208 const char *p = strrchr(path, '.');
210 if (p == NULL || (lastSlash != NULL && lastSlash > p)) {
211 Jim_SetResult(interp, argv[0]);
213 else {
214 Jim_SetResultString(interp, path, p - path);
216 return JIM_OK;
219 static int file_cmd_extension(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
221 const char *path = Jim_String(argv[0]);
222 const char *lastSlash = strrchr(path, '/');
223 const char *p = strrchr(path, '.');
225 if (p == NULL || (lastSlash != NULL && lastSlash >= p)) {
226 p = "";
228 Jim_SetResultString(interp, p, -1);
229 return JIM_OK;
232 static int file_cmd_tail(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
234 const char *path = Jim_String(argv[0]);
235 const char *lastSlash = strrchr(path, '/');
237 if (lastSlash) {
238 Jim_SetResultString(interp, lastSlash + 1, -1);
240 else {
241 Jim_SetResult(interp, argv[0]);
243 return JIM_OK;
246 static int file_cmd_normalize(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
248 #ifdef HAVE_REALPATH
249 const char *path = Jim_String(argv[0]);
250 char *newname = Jim_Alloc(MAXPATHLEN + 1);
252 if (realpath(path, newname)) {
253 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, -1));
255 else {
256 Jim_Free(newname);
257 Jim_SetResult(interp, argv[0]);
259 return JIM_OK;
260 #else
261 Jim_SetResultString(interp, "Not implemented", -1);
262 return JIM_ERR;
263 #endif
266 static int file_cmd_join(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
268 int i;
269 char *newname = Jim_Alloc(MAXPATHLEN + 1);
270 char *last = newname;
272 *newname = 0;
274 /* Simple implementation for now */
275 for (i = 0; i < argc; i++) {
276 int len;
277 const char *part = Jim_GetString(argv[i], &len);
279 if (*part == '/') {
280 /* Absolute component, so go back to the start */
281 last = newname;
283 #if defined(__MINGW32__)
284 else if (strchr(part, ':')) {
285 /* Absolute compontent on mingw, so go back to the start */
286 last = newname;
288 #endif
289 else if (part[0] == '.') {
290 if (part[1] == '/') {
291 part += 2;
292 len -= 2;
294 else if (part[1] == 0 && last != newname) {
295 /* Adding '.' to an existing path does nothing */
296 continue;
300 /* Add a slash if needed */
301 if (last != newname && last[-1] != '/') {
302 *last++ = '/';
305 if (len) {
306 if (last + len - newname >= MAXPATHLEN) {
307 Jim_Free(newname);
308 Jim_SetResultString(interp, "Path too long", -1);
309 return JIM_ERR;
311 memcpy(last, part, len);
312 last += len;
315 /* Remove a slash if needed */
316 if (last > newname + 1 && last[-1] == '/') {
317 *--last = 0;
321 *last = 0;
323 /* Probably need to handle some special cases ... */
325 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, newname, last - newname));
327 return JIM_OK;
330 static int file_access(Jim_Interp *interp, Jim_Obj *filename, int mode)
332 const char *path = Jim_String(filename);
333 int rc = access(path, mode);
335 Jim_SetResultBool(interp, rc != -1);
337 return JIM_OK;
340 static int file_cmd_readable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
342 return file_access(interp, argv[0], R_OK);
345 static int file_cmd_writable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
347 return file_access(interp, argv[0], W_OK);
350 static int file_cmd_executable(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
352 return file_access(interp, argv[0], X_OK);
355 static int file_cmd_exists(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
357 return file_access(interp, argv[0], F_OK);
360 static int file_cmd_delete(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
362 int force = Jim_CompareStringImmediate(interp, argv[0], "-force");
364 if (force || Jim_CompareStringImmediate(interp, argv[0], "--")) {
365 argc++;
366 argv--;
369 while (argc--) {
370 const char *path = Jim_String(argv[0]);
372 if (unlink(path) == -1 && errno != ENOENT) {
373 if (rmdir(path) == -1) {
374 /* Maybe try using the script helper */
375 if (!force || Jim_EvalPrefix(interp, "file delete force", 1, argv) != JIM_OK) {
376 Jim_SetResultFormatted(interp, "couldn't delete file \"%s\": %s", path,
377 strerror(errno));
378 return JIM_ERR;
382 argv++;
384 return JIM_OK;
387 #ifdef HAVE_MKDIR_ONE_ARG
388 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME)
389 #else
390 #define MKDIR_DEFAULT(PATHNAME) mkdir(PATHNAME, 0755)
391 #endif
394 * Create directory, creating all intermediate paths if necessary.
396 * Returns 0 if OK or -1 on failure (and sets errno)
398 * Note: The path may be modified.
400 static int mkdir_all(char *path)
402 int ok = 1;
404 /* First time just try to make the dir */
405 goto first;
407 while (ok--) {
408 /* Must have failed the first time, so recursively make the parent and try again */
409 char *slash = strrchr(path, '/');
411 if (slash && slash != path) {
412 *slash = 0;
413 if (mkdir_all(path) != 0) {
414 return -1;
416 *slash = '/';
418 first:
419 if (MKDIR_DEFAULT(path) == 0) {
420 return 0;
422 if (errno == ENOENT) {
423 /* Create the parent and try again */
424 continue;
426 /* Maybe it already exists as a directory */
427 if (errno == EEXIST) {
428 struct stat sb;
430 if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
431 return 0;
433 /* Restore errno */
434 errno = EEXIST;
436 /* Failed */
437 break;
439 return -1;
442 static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
444 while (argc--) {
445 char *path = Jim_StrDup(Jim_String(argv[0]));
446 int rc = mkdir_all(path);
448 Jim_Free(path);
449 if (rc != 0) {
450 Jim_SetResultFormatted(interp, "can't create directory \"%#s\": %s", argv[0],
451 strerror(errno));
452 return JIM_ERR;
454 argv++;
456 return JIM_OK;
459 #ifdef HAVE_MKSTEMP
460 static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
462 int fd;
463 char *filename;
464 const char *template = "/tmp/tcl.tmp.XXXXXX";
466 if (argc >= 1) {
467 template = Jim_String(argv[0]);
469 filename = Jim_StrDup(template);
471 fd = mkstemp(filename);
472 if (fd < 0) {
473 Jim_SetResultString(interp, "Failed to create tempfile", -1);
474 return JIM_ERR;
476 close(fd);
478 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, filename, -1));
479 return JIM_OK;
481 #endif
483 static int file_cmd_rename(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
485 const char *source;
486 const char *dest;
487 int force = 0;
489 if (argc == 3) {
490 if (!Jim_CompareStringImmediate(interp, argv[0], "-force")) {
491 return -1;
493 force++;
494 argv++;
495 argc--;
498 source = Jim_String(argv[0]);
499 dest = Jim_String(argv[1]);
501 if (!force && access(dest, F_OK) == 0) {
502 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": target exists", argv[0],
503 argv[1]);
504 return JIM_ERR;
507 if (rename(source, dest) != 0) {
508 Jim_SetResultFormatted(interp, "error renaming \"%#s\" to \"%#s\": %s", argv[0], argv[1],
509 strerror(errno));
510 return JIM_ERR;
513 return JIM_OK;
516 static int file_stat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
518 const char *path = Jim_String(filename);
520 if (stat(path, sb) == -1) {
521 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
522 return JIM_ERR;
524 return JIM_OK;
527 #ifndef HAVE_LSTAT
528 #define lstat stat
529 #endif
531 static int file_lstat(Jim_Interp *interp, Jim_Obj *filename, struct stat *sb)
533 const char *path = Jim_String(filename);
535 if (lstat(path, sb) == -1) {
536 Jim_SetResultFormatted(interp, "could not read \"%#s\": %s", filename, strerror(errno));
537 return JIM_ERR;
539 return JIM_OK;
542 static int file_cmd_atime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
544 struct stat sb;
546 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
547 return JIM_ERR;
549 Jim_SetResultInt(interp, sb.st_atime);
550 return JIM_OK;
553 static int file_cmd_mtime(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
555 struct stat sb;
557 if (argc == 2) {
558 #ifdef HAVE_UTIMES
559 jim_wide newtime;
560 struct timeval times[2];
562 if (Jim_GetWide(interp, argv[1], &newtime) != JIM_OK) {
563 return JIM_ERR;
566 times[1].tv_sec = times[0].tv_sec = newtime;
567 times[1].tv_usec = times[0].tv_usec = 0;
569 if (utimes(Jim_String(argv[0]), times) != 0) {
570 Jim_SetResultFormatted(interp, "can't set time on \"%#s\": %s", argv[0], strerror(errno));
571 return JIM_ERR;
573 #else
574 Jim_SetResultString(interp, "Not implemented", -1);
575 return JIM_ERR;
576 #endif
578 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
579 return JIM_ERR;
581 Jim_SetResultInt(interp, sb.st_mtime);
582 return JIM_OK;
585 static int file_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
587 return Jim_EvalPrefix(interp, "file copy", argc, argv);
590 static int file_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
592 struct stat sb;
594 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
595 return JIM_ERR;
597 Jim_SetResultInt(interp, sb.st_size);
598 return JIM_OK;
601 static int file_cmd_isdirectory(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
603 struct stat sb;
604 int ret = 0;
606 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
607 ret = S_ISDIR(sb.st_mode);
609 Jim_SetResultInt(interp, ret);
610 return JIM_OK;
613 static int file_cmd_isfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
615 struct stat sb;
616 int ret = 0;
618 if (file_stat(interp, argv[0], &sb) == JIM_OK) {
619 ret = S_ISREG(sb.st_mode);
621 Jim_SetResultInt(interp, ret);
622 return JIM_OK;
625 #ifdef HAVE_GETEUID
626 static int file_cmd_owned(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 = (geteuid() == sb.st_uid);
634 Jim_SetResultInt(interp, ret);
635 return JIM_OK;
637 #endif
639 #if defined(HAVE_READLINK)
640 static int file_cmd_readlink(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
642 const char *path = Jim_String(argv[0]);
643 char *linkValue = Jim_Alloc(MAXPATHLEN + 1);
645 int linkLength = readlink(path, linkValue, MAXPATHLEN);
647 if (linkLength == -1) {
648 Jim_Free(linkValue);
649 Jim_SetResultFormatted(interp, "couldn't readlink \"%#s\": %s", argv[0], strerror(errno));
650 return JIM_ERR;
652 linkValue[linkLength] = 0;
653 Jim_SetResult(interp, Jim_NewStringObjNoAlloc(interp, linkValue, linkLength));
654 return JIM_OK;
656 #endif
658 static int file_cmd_type(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
660 struct stat sb;
662 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
663 return JIM_ERR;
665 Jim_SetResultString(interp, JimGetFileType((int)sb.st_mode), -1);
666 return JIM_OK;
669 static int file_cmd_lstat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
671 struct stat sb;
673 if (file_lstat(interp, argv[0], &sb) != JIM_OK) {
674 return JIM_ERR;
676 return StoreStatData(interp, argv[1], &sb);
679 static int file_cmd_stat(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
681 struct stat sb;
683 if (file_stat(interp, argv[0], &sb) != JIM_OK) {
684 return JIM_ERR;
686 return StoreStatData(interp, argv[1], &sb);
689 static const jim_subcmd_type file_command_table[] = {
690 { .cmd = "atime",
691 .args = "name",
692 .function = file_cmd_atime,
693 .minargs = 1,
694 .maxargs = 1,
695 .description = "Last access time"
697 { .cmd = "mtime",
698 .args = "name ?time?",
699 .function = file_cmd_mtime,
700 .minargs = 1,
701 .maxargs = 2,
702 .description = "Get or set last modification time"
704 { .cmd = "copy",
705 .args = "?-force? source dest",
706 .function = file_cmd_copy,
707 .minargs = 2,
708 .maxargs = 3,
709 .description = "Copy source file to destination file"
711 { .cmd = "dirname",
712 .args = "name",
713 .function = file_cmd_dirname,
714 .minargs = 1,
715 .maxargs = 1,
716 .description = "Directory part of the name"
718 { .cmd = "rootname",
719 .args = "name",
720 .function = file_cmd_rootname,
721 .minargs = 1,
722 .maxargs = 1,
723 .description = "Name without any extension"
725 { .cmd = "extension",
726 .args = "name",
727 .function = file_cmd_extension,
728 .minargs = 1,
729 .maxargs = 1,
730 .description = "Last extension including the dot"
732 { .cmd = "tail",
733 .args = "name",
734 .function = file_cmd_tail,
735 .minargs = 1,
736 .maxargs = 1,
737 .description = "Last component of the name"
739 { .cmd = "normalize",
740 .args = "name",
741 .function = file_cmd_normalize,
742 .minargs = 1,
743 .maxargs = 1,
744 .description = "Normalized path of name"
746 { .cmd = "join",
747 .args = "name ?name ...?",
748 .function = file_cmd_join,
749 .minargs = 1,
750 .maxargs = -1,
751 .description = "Join multiple path components"
753 { .cmd = "readable",
754 .args = "name",
755 .function = file_cmd_readable,
756 .minargs = 1,
757 .maxargs = 1,
758 .description = "Is file readable"
760 { .cmd = "writable",
761 .args = "name",
762 .function = file_cmd_writable,
763 .minargs = 1,
764 .maxargs = 1,
765 .description = "Is file writable"
767 { .cmd = "executable",
768 .args = "name",
769 .function = file_cmd_executable,
770 .minargs = 1,
771 .maxargs = 1,
772 .description = "Is file executable"
774 { .cmd = "exists",
775 .args = "name",
776 .function = file_cmd_exists,
777 .minargs = 1,
778 .maxargs = 1,
779 .description = "Does file exist"
781 { .cmd = "delete",
782 .args = "?-force|--? name ...",
783 .function = file_cmd_delete,
784 .minargs = 1,
785 .maxargs = -1,
786 .description = "Deletes the files or directories (must be empty unless -force)"
788 { .cmd = "mkdir",
789 .args = "dir ...",
790 .function = file_cmd_mkdir,
791 .minargs = 1,
792 .maxargs = -1,
793 .description = "Creates the directories"
795 #ifdef HAVE_MKSTEMP
796 { .cmd = "tempfile",
797 .args = "?template?",
798 .function = file_cmd_tempfile,
799 .minargs = 0,
800 .maxargs = 1,
801 .description = "Creates a temporary filename"
803 #endif
804 { .cmd = "rename",
805 .args = "?-force? source dest",
806 .function = file_cmd_rename,
807 .minargs = 2,
808 .maxargs = 3,
809 .description = "Renames a file"
811 #if defined(HAVE_READLINK)
812 { .cmd = "readlink",
813 .args = "name",
814 .function = file_cmd_readlink,
815 .minargs = 1,
816 .maxargs = 1,
817 .description = "Value of the symbolic link"
819 #endif
820 { .cmd = "size",
821 .args = "name",
822 .function = file_cmd_size,
823 .minargs = 1,
824 .maxargs = 1,
825 .description = "Size of file"
827 { .cmd = "stat",
828 .args = "name var",
829 .function = file_cmd_stat,
830 .minargs = 2,
831 .maxargs = 2,
832 .description = "Stores results of stat in var array"
834 { .cmd = "lstat",
835 .args = "name var",
836 .function = file_cmd_lstat,
837 .minargs = 2,
838 .maxargs = 2,
839 .description = "Stores results of lstat in var array"
841 { .cmd = "type",
842 .args = "name",
843 .function = file_cmd_type,
844 .minargs = 1,
845 .maxargs = 1,
846 .description = "Returns type of the file"
848 #ifdef HAVE_GETEUID
849 { .cmd = "owned",
850 .args = "name",
851 .function = file_cmd_owned,
852 .minargs = 1,
853 .maxargs = 1,
854 .description = "Returns 1 if owned by the current owner"
856 #endif
857 { .cmd = "isdirectory",
858 .args = "name",
859 .function = file_cmd_isdirectory,
860 .minargs = 1,
861 .maxargs = 1,
862 .description = "Returns 1 if name is a directory"
864 { .cmd = "isfile",
865 .args = "name",
866 .function = file_cmd_isfile,
867 .minargs = 1,
868 .maxargs = 1,
869 .description = "Returns 1 if name is a file"
872 .cmd = 0
876 static int Jim_CdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
878 const char *path;
880 if (argc != 2) {
881 Jim_WrongNumArgs(interp, 1, argv, "dirname");
882 return JIM_ERR;
885 path = Jim_String(argv[1]);
887 if (chdir(path) != 0) {
888 Jim_SetResultFormatted(interp, "couldn't change working directory to \"%s\": %s", path,
889 strerror(errno));
890 return JIM_ERR;
892 return JIM_OK;
895 static int Jim_PwdCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
897 const int cwd_len = 2048;
898 char *cwd = malloc(cwd_len);
900 if (getcwd(cwd, cwd_len) == NULL) {
901 Jim_SetResultString(interp, "Failed to get pwd", -1);
902 return JIM_ERR;
904 #if defined(__MINGW32__)
906 /* Try to keep backlashes out of paths */
907 char *p = cwd;
908 while ((p = strchr(p, '\\')) != NULL) {
909 *p++ = '/';
912 #endif
914 Jim_SetResultString(interp, cwd, -1);
916 free(cwd);
917 return JIM_OK;
920 int Jim_fileInit(Jim_Interp *interp)
922 if (Jim_PackageProvide(interp, "file", "1.0", JIM_ERRMSG))
923 return JIM_ERR;
925 Jim_CreateCommand(interp, "file", Jim_SubCmdProc, (void *)file_command_table, NULL);
926 Jim_CreateCommand(interp, "pwd", Jim_PwdCmd, NULL, NULL);
927 Jim_CreateCommand(interp, "cd", Jim_CdCmd, NULL, NULL);
928 return JIM_OK;