blob: 2c5e23bf660c9e184d463e270e33be03acf11db9 [file] [log] [blame]
/*
* 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);
}