diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/afcclient.c | 250 |
1 files changed, 164 insertions, 86 deletions
diff --git a/tools/afcclient.c b/tools/afcclient.c index 9bcd77b..826ad5b 100644 --- a/tools/afcclient.c +++ b/tools/afcclient.c | |||
@@ -89,6 +89,16 @@ static idevice_subscription_context_t context = NULL; | |||
89 | static char* curdir = NULL; | 89 | static char* curdir = NULL; |
90 | static size_t curdir_len = 0; | 90 | static size_t curdir_len = 0; |
91 | 91 | ||
92 | static int is_directory(const char* path) | ||
93 | { | ||
94 | struct stat tst; | ||
95 | #ifdef WIN32 | ||
96 | return (stat(path, &tst) == 0) && S_ISDIR(tst.st_mode); | ||
97 | #else | ||
98 | return (lstat(path, &tst) == 0) && S_ISDIR(tst.st_mode); | ||
99 | #endif | ||
100 | } | ||
101 | |||
92 | static void print_usage(int argc, char **argv, int is_error) | 102 | static void print_usage(int argc, char **argv, int is_error) |
93 | { | 103 | { |
94 | char *name = strrchr(argv[0], '/'); | 104 | char *name = strrchr(argv[0], '/'); |
@@ -173,7 +183,7 @@ static void handle_help(afc_client_t afc, int argc, char** argv) | |||
173 | printf("rm PATH - remove item at PATH\n"); | 183 | printf("rm PATH - remove item at PATH\n"); |
174 | printf("get PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n"); | 184 | printf("get PATH [LOCALPATH] - transfer file at PATH from device to LOCALPATH\n"); |
175 | printf("put LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n"); | 185 | printf("put LOCALPATH [PATH] - transfer local file at LOCALPATH to device at PATH\n"); |
176 | printf("\n"); | 186 | printf("\n"); |
177 | } | 187 | } |
178 | 188 | ||
179 | static const char* path_get_basename(const char* path) | 189 | static const char* path_get_basename(const char* path) |
@@ -200,7 +210,7 @@ static int timeval_subtract(struct timeval *result, struct timeval *x, struct ti | |||
200 | result->tv_sec = x->tv_sec - y->tv_sec; | 210 | result->tv_sec = x->tv_sec - y->tv_sec; |
201 | result->tv_usec = x->tv_usec - y->tv_usec; | 211 | result->tv_usec = x->tv_usec - y->tv_usec; |
202 | /* Return 1 if result is negative. */ | 212 | /* Return 1 if result is negative. */ |
203 | return x->tv_sec < y->tv_sec; | 213 | return x->tv_sec < y->tv_sec; |
204 | } | 214 | } |
205 | 215 | ||
206 | struct str_item { | 216 | struct str_item { |
@@ -674,110 +684,178 @@ static void handle_remove(afc_client_t afc, int argc, char** argv) | |||
674 | } | 684 | } |
675 | } | 685 | } |
676 | 686 | ||
677 | static void handle_get(afc_client_t afc, int argc, char** argv) | 687 | static uint8_t get_single_file(afc_client_t afc, const char *srcpath, const char *dstpath, uint64_t file_size) |
678 | { | 688 | { |
679 | if (argc < 1 || argc > 2) { | 689 | uint64_t fh = 0; |
680 | printf("Error: Invalid number of arguments\n"); | 690 | afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh); |
681 | return; | 691 | if (err != AFC_E_SUCCESS) { |
692 | printf("Error: Failed to open file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); | ||
693 | return 0; | ||
682 | } | 694 | } |
683 | char *srcpath = NULL; | 695 | FILE *f = fopen(dstpath, "wb"); |
684 | char* dstpath = NULL; | 696 | if (!f) { |
685 | if (argc == 1) { | 697 | printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno)); |
686 | srcpath = get_absolute_path(argv[0]); | 698 | return 0; |
687 | dstpath = strdup(path_get_basename(argv[0])); | 699 | } |
688 | } else { | 700 | struct timeval t1; |
689 | srcpath = get_absolute_path(argv[0]); | 701 | struct timeval t2; |
690 | dstpath = strdup(argv[1]); | 702 | struct timeval tdiff; |
703 | size_t bufsize = 0x100000; | ||
704 | char *buf = malloc(bufsize); | ||
705 | size_t total = 0; | ||
706 | int progress = 0; | ||
707 | int lastprog = 0; | ||
708 | if (file_size > 0x400000) { | ||
709 | progress = 1; | ||
710 | gettimeofday(&t1, NULL); | ||
711 | } | ||
712 | uint8_t succeed = 1; | ||
713 | while (err == AFC_E_SUCCESS) { | ||
714 | uint32_t bytes_read = 0; | ||
715 | size_t chunk = 0; | ||
716 | err = afc_file_read(afc, fh, buf, bufsize, &bytes_read); | ||
717 | if (bytes_read == 0) { | ||
718 | break; | ||
719 | } | ||
720 | while (chunk < bytes_read) { | ||
721 | size_t wr = fwrite(buf + chunk, 1, bytes_read - chunk, f); | ||
722 | if (wr == 0) { | ||
723 | if (progress) { | ||
724 | printf("\n"); | ||
725 | } | ||
726 | printf("Error: Failed to write to local file\n"); | ||
727 | succeed = 0; | ||
728 | break; | ||
729 | } | ||
730 | chunk += wr; | ||
731 | } | ||
732 | total += chunk; | ||
733 | if (progress) { | ||
734 | int prog = (int) ((double) total / (double) file_size * 100.0f); | ||
735 | if (prog > lastprog) { | ||
736 | gettimeofday(&t2, NULL); | ||
737 | timeval_subtract(&tdiff, &t2, &t1); | ||
738 | double time_in_sec = (double) tdiff.tv_sec + (double) tdiff.tv_usec / 1000000; | ||
739 | printf("\r%d%% (%0.1f MB/s) ", prog, (double) total / 1048576.0f / time_in_sec); | ||
740 | fflush(stdout); | ||
741 | lastprog = prog; | ||
742 | } | ||
743 | } | ||
691 | } | 744 | } |
745 | if (progress) { | ||
746 | printf("\n"); | ||
747 | } | ||
748 | if (err != AFC_E_SUCCESS) { | ||
749 | printf("Error: Failed to read from file '%s': %s (%d)\n", srcpath, afc_strerror(err), err); | ||
750 | succeed = 0; | ||
751 | } | ||
752 | free(buf); | ||
753 | fclose(f); | ||
754 | afc_file_close(afc, fh); | ||
755 | return succeed; | ||
756 | } | ||
692 | 757 | ||
758 | static uint8_t get_file(afc_client_t afc, const char *srcpath, const char *dstpath) | ||
759 | { | ||
693 | char **info = NULL; | 760 | char **info = NULL; |
694 | uint64_t file_size = 0; | 761 | uint64_t file_size = 0; |
695 | afc_get_file_info(afc, srcpath, &info); | 762 | afc_get_file_info(afc, srcpath, &info); |
763 | uint8_t is_dir = 0; | ||
696 | if (info) { | 764 | if (info) { |
697 | char **p = info; | 765 | char **p = info; |
698 | while (p && *p) { | 766 | while (p && *p) { |
699 | if (!strcmp(*p, "st_size")) { | 767 | if (!strcmp(*p, "st_size")) { |
700 | p++; | 768 | p++; |
701 | file_size = (uint64_t)strtoull(*p, NULL, 10); | 769 | file_size = (uint64_t) strtoull(*p, NULL, 10); |
770 | } | ||
771 | if (!strcmp(*p, "st_ifmt")) { | ||
772 | p++; | ||
773 | is_dir = !strcmp(*p, "S_IFDIR"); | ||
774 | } | ||
775 | p++; | ||
776 | } | ||
777 | afc_dictionary_free(info); | ||
778 | } | ||
779 | uint8_t succeed = 1; | ||
780 | if (is_dir) { | ||
781 | char **entries = NULL; | ||
782 | afc_error_t err = afc_read_directory(afc, srcpath, &entries); | ||
783 | if (err != AFC_E_SUCCESS) { | ||
784 | printf("Error: Failed to list '%s': %s (%d)\n", srcpath, afc_strerror(err), err); | ||
785 | return 0; | ||
786 | } | ||
787 | char **p = entries; | ||
788 | size_t srcpath_len = strlen(srcpath); | ||
789 | int srcpath_is_root = strcmp(srcpath, "/") == 0; | ||
790 | if (!is_directory(dstpath) && mkdir(dstpath, 0777) != 0) { | ||
791 | printf("Error: Failed to create folder '%s': %s\n", dstpath, strerror(errno)); | ||
792 | afc_dictionary_free(entries); | ||
793 | return 0; | ||
794 | } | ||
795 | while (p && *p) { | ||
796 | if (strcmp(".", *p) == 0 || strcmp("..", *p) == 0) { | ||
797 | p++; | ||
798 | continue; | ||
799 | } | ||
800 | size_t len = srcpath_len + 1 + strlen(*p) + 1; | ||
801 | char *testpath = (char *) malloc(len); | ||
802 | if (srcpath_is_root) { | ||
803 | snprintf(testpath, len, "/%s", *p); | ||
804 | } else { | ||
805 | snprintf(testpath, len, "%s/%s", srcpath, *p); | ||
806 | } | ||
807 | size_t dst_len = strlen(dstpath) + 1 + strlen(*p) + 1; | ||
808 | char *newdst = (char *) malloc(dst_len); | ||
809 | snprintf(newdst, dst_len, "%s/%s", dstpath, *p); | ||
810 | if (!get_file(afc, testpath, newdst)) { | ||
811 | succeed = 0; | ||
702 | break; | 812 | break; |
703 | } | 813 | } |
814 | free(testpath); | ||
815 | free(newdst); | ||
704 | p++; | 816 | p++; |
705 | } | 817 | } |
818 | afc_dictionary_free(entries); | ||
819 | } else { | ||
820 | succeed = get_single_file(afc, srcpath, dstpath, file_size); | ||
706 | } | 821 | } |
707 | uint64_t fh = 0; | 822 | return succeed; |
708 | afc_error_t err = afc_file_open(afc, srcpath, AFC_FOPEN_RDONLY, &fh); | 823 | } |
709 | if (err != AFC_E_SUCCESS) { | 824 | |
710 | free(srcpath); | 825 | static void handle_get(afc_client_t afc, int argc, char **argv) |
711 | free(dstpath); | 826 | { |
712 | printf("Error: Failed to open file '%s': %s (%d)\n", argv[0], afc_strerror(err), err); | 827 | if (argc < 1 || argc > 2) { |
828 | printf("Error: Invalid number of arguments\n"); | ||
713 | return; | 829 | return; |
714 | } | 830 | } |
715 | FILE *f = fopen(dstpath, "wb"); | 831 | char *srcpath = NULL; |
716 | if (!f && errno == EISDIR) { | 832 | char *dstpath = NULL; |
717 | const char* basen = path_get_basename(argv[0]); | 833 | size_t src_len = strlen(argv[0]); |
834 | if (strcmp(argv[0], "/") != 0 && argv[0][src_len - 1] == '/') { | ||
835 | argv[0][src_len - 1] = '\0'; | ||
836 | } | ||
837 | if (argc == 1) { | ||
838 | srcpath = get_absolute_path(argv[0]); | ||
839 | dstpath = strdup(path_get_basename(argv[0])); | ||
840 | } else { | ||
841 | srcpath = get_absolute_path(argv[0]); | ||
842 | dstpath = strdup(argv[1]); | ||
843 | } | ||
844 | |||
845 | if (is_directory(dstpath)) { | ||
846 | const char *basen = path_get_basename(argv[0]); | ||
718 | size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; | 847 | size_t len = strlen(dstpath) + 1 + strlen(basen) + 1; |
719 | char* newdst = (char*)malloc(len); | 848 | char *newdst = (char *) malloc(len); |
720 | snprintf(newdst, len, "%s/%s", dstpath, basen); | 849 | snprintf(newdst, len, "%s/%s", dstpath, basen); |
721 | f = fopen(newdst, "wb"); | 850 | get_file(afc, srcpath, newdst); |
851 | free(srcpath); | ||
722 | free(newdst); | 852 | free(newdst); |
723 | } | 853 | free(dstpath); |
724 | if (f) { | ||
725 | struct timeval t1; | ||
726 | struct timeval t2; | ||
727 | struct timeval tdiff; | ||
728 | size_t bufsize = 0x100000; | ||
729 | char* buf = malloc(bufsize); | ||
730 | size_t total = 0; | ||
731 | int progress = 0; | ||
732 | int lastprog = 0; | ||
733 | if (file_size > 0x400000) { | ||
734 | progress = 1; | ||
735 | gettimeofday(&t1, NULL); | ||
736 | } | ||
737 | while (err == AFC_E_SUCCESS) { | ||
738 | uint32_t bytes_read = 0; | ||
739 | size_t chunk = 0; | ||
740 | err = afc_file_read(afc, fh, buf, bufsize, &bytes_read); | ||
741 | if (bytes_read == 0) { | ||
742 | break; | ||
743 | } | ||
744 | while (chunk < bytes_read) { | ||
745 | size_t wr = fwrite(buf+chunk, 1, bytes_read-chunk, f); | ||
746 | if (wr == 0) { | ||
747 | if (progress) { | ||
748 | printf("\n"); | ||
749 | } | ||
750 | printf("Error: Failed to write to local file\n"); | ||
751 | break; | ||
752 | } | ||
753 | chunk += wr; | ||
754 | } | ||
755 | total += chunk; | ||
756 | if (progress) { | ||
757 | int prog = (int)((double)total / (double)file_size * 100.0f); | ||
758 | if (prog > lastprog) { | ||
759 | gettimeofday(&t2, NULL); | ||
760 | timeval_subtract(&tdiff, &t2, &t1); | ||
761 | double time_in_sec = (double)tdiff.tv_sec + (double)tdiff.tv_usec/1000000; | ||
762 | printf("\r%d%% (%0.1f MB/s) ", prog, (double)total/1048576.0f / time_in_sec); | ||
763 | fflush(stdout); | ||
764 | lastprog = prog; | ||
765 | } | ||
766 | } | ||
767 | } | ||
768 | if (progress) { | ||
769 | printf("\n"); | ||
770 | } | ||
771 | if (err != AFC_E_SUCCESS) { | ||
772 | printf("Error: Failed to read from file '%s': %s (%d)\n", argv[0], afc_strerror(err), err); | ||
773 | } | ||
774 | free(buf); | ||
775 | fclose(f); | ||
776 | } else { | 854 | } else { |
777 | printf("Error: Failed to open local file '%s': %s\n", dstpath, strerror(errno)); | 855 | get_file(afc, srcpath, dstpath); |
856 | free(srcpath); | ||
857 | free(dstpath); | ||
778 | } | 858 | } |
779 | afc_file_close(afc, fh); | ||
780 | free(srcpath); | ||
781 | } | 859 | } |
782 | 860 | ||
783 | static void handle_put(afc_client_t afc, int argc, char** argv) | 861 | static void handle_put(afc_client_t afc, int argc, char** argv) |
@@ -975,8 +1053,8 @@ static void parse_cmdline(int* p_argc, char*** p_argv, const char* cmdline) | |||
975 | } else if (*pos == '"' || *pos == '\'') { | 1053 | } else if (*pos == '"' || *pos == '\'') { |
976 | if (!qpos) { | 1054 | if (!qpos) { |
977 | qpos = pos; | 1055 | qpos = pos; |
978 | } else { | 1056 | } else { |
979 | qpos = NULL; | 1057 | qpos = NULL; |
980 | } | 1058 | } |
981 | pos++; | 1059 | pos++; |
982 | } else if (*pos == '\0' || (!qpos && isspace(*pos))) { | 1060 | } else if (*pos == '\0' || (!qpos && isspace(*pos))) { |
@@ -1043,7 +1121,7 @@ static int process_args(afc_client_t afc, int argc, char** argv) | |||
1043 | handle_file_info(afc, argc-1, argv+1); | 1121 | handle_file_info(afc, argc-1, argv+1); |
1044 | } | 1122 | } |
1045 | else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) { | 1123 | else if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "list")) { |
1046 | handle_list(afc, argc-1, argv+1); | 1124 | handle_list(afc, argc-1, argv+1); |
1047 | } | 1125 | } |
1048 | else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) { | 1126 | else if (!strcmp(argv[0], "mv") || !strcmp(argv[0], "rename")) { |
1049 | handle_rename(afc, argc-1, argv+1); | 1127 | handle_rename(afc, argc-1, argv+1); |
@@ -1113,7 +1191,7 @@ static void start_cmdline(afc_client_t afc) | |||
1113 | break; | 1191 | break; |
1114 | } | 1192 | } |
1115 | } | 1193 | } |
1116 | } | 1194 | } |
1117 | } | 1195 | } |
1118 | 1196 | ||
1119 | static void device_event_cb(const idevice_event_t* event, void* userdata) | 1197 | static void device_event_cb(const idevice_event_t* event, void* userdata) |