Change-Id: Ifd13e1ca5d49a5477a9850d94d443a50bbc32ff1 Signed-off-by: Mike Lockwood <lockwood@android.com>
371 lines
8.9 KiB
C++
371 lines
8.9 KiB
C++
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#if HAVE_READLINE
|
|
#include <readline/readline.h>
|
|
#include <readline/history.h>
|
|
#endif
|
|
|
|
#include "MtpClient.h"
|
|
#include "MtpDevice.h"
|
|
#include "MtpObjectInfo.h"
|
|
|
|
#include "MtpFile.h"
|
|
|
|
#define PROMPT "mtp> "
|
|
|
|
using namespace android;
|
|
|
|
static MtpClient* sClient = NULL;
|
|
|
|
// current working directory information for interactive shell
|
|
static MtpFile* sCurrentDirectory = NULL;
|
|
|
|
static MtpFile* parse_path(char* path) {
|
|
return MtpFile::parsePath(sCurrentDirectory, path);
|
|
}
|
|
|
|
class MyClient : public MtpClient {
|
|
private:
|
|
virtual void deviceAdded(MtpDevice *device) {
|
|
}
|
|
|
|
virtual void deviceRemoved(MtpDevice *device) {
|
|
}
|
|
|
|
public:
|
|
};
|
|
|
|
static void init() {
|
|
sClient = new MyClient;
|
|
sClient->start();
|
|
MtpFile::init(sClient);
|
|
}
|
|
|
|
static int set_cwd(int argc, char* argv[]) {
|
|
if (argc != 1) {
|
|
fprintf(stderr, "cd should have one argument\n");
|
|
return -1;
|
|
}
|
|
if (!strcmp(argv[0], "/")) {
|
|
delete sCurrentDirectory;
|
|
sCurrentDirectory = NULL;
|
|
}
|
|
else {
|
|
MtpFile* file = parse_path(argv[0]);
|
|
if (file) {
|
|
delete sCurrentDirectory;
|
|
sCurrentDirectory = file;
|
|
} else {
|
|
fprintf(stderr, "could not find %s\n", argv[0]);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void list_devices() {
|
|
// TODO - need to make sure the list will not change while iterating
|
|
MtpDeviceList& devices = sClient->getDeviceList();
|
|
for (int i = 0; i < devices.size(); i++) {
|
|
MtpDevice* device = devices[i];
|
|
MtpFile* file = new MtpFile(device);
|
|
file->print();
|
|
delete file;
|
|
}
|
|
}
|
|
|
|
static int list(int argc, char* argv[]) {
|
|
if (argc == 0) {
|
|
// list cwd
|
|
if (sCurrentDirectory) {
|
|
sCurrentDirectory->list();
|
|
} else {
|
|
list_devices();
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
char* path = argv[i];
|
|
if (!strcmp(path, "/")) {
|
|
list_devices();
|
|
} else {
|
|
MtpFile* file = parse_path(path);
|
|
if (!file) {
|
|
fprintf(stderr, "could not find %s\n", path);
|
|
return -1;
|
|
}
|
|
file->list();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_file(int argc, char* argv[]) {
|
|
int ret = -1;
|
|
int srcFD = -1;
|
|
int destFD = -1;
|
|
MtpFile* srcFile = NULL;
|
|
MtpObjectInfo* info = NULL;
|
|
char* dest;
|
|
|
|
if (argc < 1) {
|
|
fprintf(stderr, "not enough arguments\n");
|
|
return -1;
|
|
} else if (argc > 2) {
|
|
fprintf(stderr, "too many arguments\n");
|
|
return -1;
|
|
}
|
|
|
|
// find source object
|
|
char* src = argv[0];
|
|
srcFile = parse_path(src);
|
|
if (!srcFile) {
|
|
fprintf(stderr, "could not find %s\n", src);
|
|
return -1;
|
|
}
|
|
info = srcFile->getObjectInfo();
|
|
if (!info) {
|
|
fprintf(stderr, "could not find object info for %s\n", src);
|
|
goto fail;
|
|
}
|
|
if (info->mFormat == MTP_FORMAT_ASSOCIATION) {
|
|
fprintf(stderr, "copying directories not implemented yet\n");
|
|
goto fail;
|
|
}
|
|
|
|
dest = (argc > 1 ? argv[1] : info->mName);
|
|
destFD = open(dest, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
|
if (destFD < 0) {
|
|
fprintf(stderr, "could not create %s\n", dest);
|
|
goto fail;
|
|
}
|
|
srcFD = srcFile->getDevice()->readObject(info->mHandle, info->mCompressedSize);
|
|
if (srcFD < 0)
|
|
goto fail;
|
|
|
|
char buffer[65536];
|
|
while (1) {
|
|
int count = read(srcFD, buffer, sizeof(buffer));
|
|
if (count <= 0)
|
|
break;
|
|
write(destFD, buffer, count);
|
|
}
|
|
// FIXME - error checking and reporting
|
|
ret = 0;
|
|
|
|
fail:
|
|
delete srcFile;
|
|
delete info;
|
|
if (srcFD >= 0)
|
|
close(srcFD);
|
|
if (destFD >= 0)
|
|
close(destFD);
|
|
return ret;
|
|
}
|
|
|
|
static int put_file(int argc, char* argv[]) {
|
|
int ret = -1;
|
|
int srcFD = -1;
|
|
MtpFile* destFile = NULL;
|
|
MtpObjectInfo* srcInfo = NULL;
|
|
MtpObjectInfo* destInfo = NULL;
|
|
MtpObjectHandle handle;
|
|
struct stat statbuf;
|
|
const char* lastSlash;
|
|
|
|
if (argc < 1) {
|
|
fprintf(stderr, "not enough arguments\n");
|
|
return -1;
|
|
} else if (argc > 2) {
|
|
fprintf(stderr, "too many arguments\n");
|
|
return -1;
|
|
}
|
|
const char* src = argv[0];
|
|
srcFD = open(src, O_RDONLY);
|
|
if (srcFD < 0) {
|
|
fprintf(stderr, "could not open %s\n", src);
|
|
goto fail;
|
|
}
|
|
if (argc == 2) {
|
|
char* dest = argv[1];
|
|
destFile = parse_path(dest);
|
|
if (!destFile) {
|
|
fprintf(stderr, "could not find %s\n", dest);
|
|
goto fail;
|
|
}
|
|
} else {
|
|
if (!sCurrentDirectory) {
|
|
fprintf(stderr, "current working directory not set\n");
|
|
goto fail;
|
|
}
|
|
destFile = new MtpFile(sCurrentDirectory);
|
|
}
|
|
|
|
destInfo = destFile->getObjectInfo();
|
|
if (!destInfo) {
|
|
fprintf(stderr, "could not find object info destination directory\n");
|
|
goto fail;
|
|
}
|
|
if (destInfo->mFormat != MTP_FORMAT_ASSOCIATION) {
|
|
fprintf(stderr, "destination not a directory\n");
|
|
goto fail;
|
|
}
|
|
|
|
if (fstat(srcFD, &statbuf))
|
|
goto fail;
|
|
|
|
srcInfo = new MtpObjectInfo(0);
|
|
srcInfo->mStorageID = destInfo->mStorageID;
|
|
srcInfo->mFormat = MTP_FORMAT_EXIF_JPEG; // FIXME
|
|
srcInfo->mCompressedSize = statbuf.st_size;
|
|
srcInfo->mParent = destInfo->mHandle;
|
|
lastSlash = strrchr(src, '/');
|
|
srcInfo->mName = strdup(lastSlash ? lastSlash + 1 : src);
|
|
srcInfo->mDateModified = statbuf.st_mtime;
|
|
handle = destFile->getDevice()->sendObjectInfo(srcInfo);
|
|
if (handle <= 0) {
|
|
printf("sendObjectInfo returned %04X\n", handle);
|
|
goto fail;
|
|
}
|
|
if (destFile->getDevice()->sendObject(srcInfo, srcFD))
|
|
ret = 0;
|
|
|
|
fail:
|
|
delete destFile;
|
|
delete srcInfo;
|
|
delete destInfo;
|
|
if (srcFD >= 0)
|
|
close(srcFD);
|
|
printf("returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
typedef int (* command_func)(int argc, char* argv[]);
|
|
|
|
struct command_table_entry {
|
|
const char* name;
|
|
command_func func;
|
|
};
|
|
|
|
const command_table_entry command_list[] = {
|
|
{ "cd", set_cwd },
|
|
{ "ls", list },
|
|
{ "get", get_file },
|
|
{ "put", put_file },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
|
|
static int do_command(int argc, char* argv[]) {
|
|
const command_table_entry* command = command_list;
|
|
const char* name = *argv++;
|
|
argc--;
|
|
|
|
while (command->name) {
|
|
if (!strcmp(command->name, name))
|
|
return command->func(argc, argv);
|
|
else
|
|
command++;
|
|
}
|
|
fprintf(stderr, "unknown command %s\n", name);
|
|
return -1;
|
|
}
|
|
|
|
static int shell() {
|
|
int argc;
|
|
int result = 0;
|
|
#define MAX_ARGS 100
|
|
char* argv[MAX_ARGS];
|
|
|
|
#if HAVE_READLINE
|
|
using_history();
|
|
#endif
|
|
|
|
while (1) {
|
|
#if HAVE_READLINE
|
|
char* line = readline(PROMPT);
|
|
if (!line) {
|
|
printf("\n");
|
|
exit(0);
|
|
}
|
|
#else
|
|
char buffer[1000];
|
|
printf("%s", PROMPT);
|
|
char* line = NULL;
|
|
size_t length = 0;
|
|
|
|
buffer[0] = 0;
|
|
fgets(buffer, sizeof(buffer), stdin);
|
|
int count = strlen(buffer);
|
|
if (count > 0 && buffer[0] == EOF) {
|
|
printf("\n");
|
|
exit(0);
|
|
}
|
|
if (count > 0 && line[count - 1] == '\n')
|
|
line[count - 1] == 0;
|
|
#endif
|
|
char* tok = strtok(line, " \t\n\r");
|
|
if (!tok)
|
|
continue;
|
|
if (!strcmp(tok, "quit") || !strcmp(tok, "exit")) {
|
|
exit(0);
|
|
}
|
|
#if HAVE_READLINE
|
|
add_history(line);
|
|
#endif
|
|
argc = 0;
|
|
while (tok) {
|
|
if (argc + 1 == MAX_ARGS) {
|
|
fprintf(stderr, "too many arguments\n");
|
|
result = -1;
|
|
goto bottom_of_loop;
|
|
}
|
|
|
|
argv[argc++] = strdup(tok);
|
|
tok = strtok(NULL, " \t\n\r");
|
|
}
|
|
|
|
result = do_command(argc, argv);
|
|
|
|
bottom_of_loop:
|
|
for (int i = 0; i < argc; i++)
|
|
free(argv[i]);
|
|
free(line);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
init();
|
|
|
|
if (argc == 1)
|
|
return shell();
|
|
else
|
|
return do_command(argc - 1, argv + 1);
|
|
}
|