Simple command line test tool for MTP host.
Change-Id: Ifd13e1ca5d49a5477a9850d94d443a50bbc32ff1 Signed-off-by: Mike Lockwood <lockwood@android.com>
This commit is contained in:
57
media/tests/mtp/Android.mk
Normal file
57
media/tests/mtp/Android.mk
Normal file
@@ -0,0 +1,57 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
mtp.cpp \
|
||||
MtpFile.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
frameworks/base/media/mtp \
|
||||
|
||||
LOCAL_CFLAGS := -DMTP_HOST
|
||||
|
||||
LOCAL_MODULE := mtp
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libmtp libusbhost libutils libcutils
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
ifeq ($(HOST_OS),linux)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
mtp.cpp \
|
||||
MtpFile.cpp \
|
||||
../../../libs/utils/RefBase.cpp \
|
||||
../../../libs/utils/SharedBuffer.cpp \
|
||||
../../../libs/utils/Threads.cpp \
|
||||
../../../libs/utils/VectorImpl.cpp \
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
frameworks/base/media/mtp \
|
||||
|
||||
LOCAL_CFLAGS := -DMTP_HOST -g -O0
|
||||
|
||||
have_readline := $(wildcard /usr/include/readline/readline.h)
|
||||
have_history := $(wildcard /usr/lib/libhistory*)
|
||||
ifneq ($(strip $(have_readline)),)
|
||||
LOCAL_CFLAGS += -DHAVE_READLINE=1
|
||||
endif
|
||||
|
||||
LOCAL_LDLIBS += -lpthread
|
||||
ifneq ($(strip $(have_readline)),)
|
||||
LOCAL_LDLIBS += -lreadline -lncurses
|
||||
endif
|
||||
ifneq ($(strip $(have_history)),)
|
||||
LOCAL_LDLIBS += -lhistory
|
||||
endif
|
||||
|
||||
LOCAL_MODULE := mtp
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libmtp libusbhost libcutils
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
endif
|
||||
187
media/tests/mtp/MtpFile.cpp
Normal file
187
media/tests/mtp/MtpFile.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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 "MtpClient.h"
|
||||
#include "MtpDevice.h"
|
||||
#include "MtpDeviceInfo.h"
|
||||
#include "MtpObjectInfo.h"
|
||||
#include "MtpStorage.h"
|
||||
#include "MtpUtils.h"
|
||||
|
||||
#include "MtpFile.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
MtpClient* MtpFile::sClient = NULL;
|
||||
|
||||
MtpFile::MtpFile(MtpDevice* device)
|
||||
: mDevice(device),
|
||||
mStorage(0),
|
||||
mHandle(0)
|
||||
{
|
||||
}
|
||||
|
||||
MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage)
|
||||
: mDevice(device),
|
||||
mStorage(storage),
|
||||
mHandle(0)
|
||||
{
|
||||
}
|
||||
|
||||
MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle)
|
||||
: mDevice(device),
|
||||
mStorage(storage),
|
||||
mHandle(handle)
|
||||
{
|
||||
}
|
||||
|
||||
MtpFile::MtpFile(MtpFile* file)
|
||||
: mDevice(file->mDevice),
|
||||
mStorage(file->mStorage),
|
||||
mHandle(file->mHandle)
|
||||
{
|
||||
}
|
||||
|
||||
MtpFile::~MtpFile() {
|
||||
}
|
||||
|
||||
void MtpFile::print() {
|
||||
if (mHandle) {
|
||||
|
||||
} else if (mStorage) {
|
||||
printf("%x\n", mStorage);
|
||||
} else {
|
||||
int id = mDevice->getID();
|
||||
MtpDeviceInfo* info = mDevice->getDeviceInfo();
|
||||
if (info)
|
||||
printf("%d\t%s %s %s\n", id, info->mManufacturer, info->mModel, info->mSerial);
|
||||
else
|
||||
printf("%d\t(no device info available)\n", id);
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
|
||||
MtpObjectInfo* MtpFile::getObjectInfo() {
|
||||
return mDevice->getObjectInfo(mHandle);
|
||||
}
|
||||
|
||||
void MtpFile::list() {
|
||||
if (mStorage) {
|
||||
MtpObjectHandleList* handles = mDevice->getObjectHandles(mStorage, 0,
|
||||
(mHandle ? mHandle : -1));
|
||||
if (handles) {
|
||||
for (int i = 0; i < handles->size(); i++) {
|
||||
MtpObjectHandle handle = (*handles)[i];
|
||||
MtpObjectInfo* info = mDevice->getObjectInfo(handle);
|
||||
if (info) {
|
||||
char modified[100];
|
||||
struct tm tm;
|
||||
|
||||
gmtime_r(&info->mDateModified, &tm);
|
||||
strftime(modified, sizeof(modified), "%a %b %e %H:%M:%S GMT %Y", &tm);
|
||||
printf("%s Handle: %d Format: %04X Size: %d Modified: %s\n",
|
||||
info->mName, handle, info->mFormat, info->mCompressedSize, modified);
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
delete handles;
|
||||
}
|
||||
} else {
|
||||
// list storage units for device
|
||||
MtpStorageIDList* storageList = mDevice->getStorageIDs();
|
||||
for (int i = 0; i < storageList->size(); i++) {
|
||||
MtpStorageID storageID = (*storageList)[i];
|
||||
printf("%x\n", storageID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MtpFile::init(MtpClient* client) {
|
||||
sClient = client;
|
||||
}
|
||||
|
||||
MtpFile* MtpFile::parsePath(MtpFile* base, char* path) {
|
||||
MtpDevice* device = NULL;
|
||||
MtpStorageID storage = 0;
|
||||
MtpObjectHandle handle = 0;
|
||||
|
||||
if (path[0] != '/' && base) {
|
||||
device = base->mDevice;
|
||||
storage = base->mStorage;
|
||||
handle = base->mHandle;
|
||||
}
|
||||
|
||||
// parse an absolute path
|
||||
if (path[0] == '/')
|
||||
path++;
|
||||
char* tok = strtok(path, "/");
|
||||
while (tok) {
|
||||
if (storage) {
|
||||
// find child of current handle
|
||||
MtpObjectHandleList* handles = device->getObjectHandles(storage, 0,
|
||||
(handle ? handle : -1));
|
||||
MtpObjectHandle childHandle = 0;
|
||||
|
||||
if (handles) {
|
||||
for (int i = 0; i < handles->size() && !childHandle; i++) {
|
||||
MtpObjectHandle handle = (*handles)[i];
|
||||
MtpObjectInfo* info = device->getObjectInfo(handle);
|
||||
if (info && !strcmp(tok, info->mName))
|
||||
childHandle = handle;
|
||||
delete info;
|
||||
}
|
||||
delete handles;
|
||||
}
|
||||
if (childHandle)
|
||||
handle = childHandle;
|
||||
else
|
||||
return NULL;
|
||||
} else if (device) {
|
||||
unsigned int id;
|
||||
// find storage for the device
|
||||
if (sscanf(tok, "%x", &id) == 1) {
|
||||
MtpStorageIDList* storageList = device->getStorageIDs();
|
||||
bool found = false;
|
||||
for (int i = 0; i < storageList->size(); i++) {
|
||||
if ((*storageList)[i] == id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
storage = id;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// find device
|
||||
unsigned int id;
|
||||
if (sscanf(tok, "%d", &id) == 1)
|
||||
device = sClient->getDevice(id);
|
||||
if (!device)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tok = strtok(NULL, "/");
|
||||
}
|
||||
|
||||
if (device)
|
||||
return new MtpFile(device, storage, handle);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
57
media/tests/mtp/MtpFile.h
Normal file
57
media/tests/mtp/MtpFile.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _MTP_FILE_H
|
||||
#define _MTP_FILE_H
|
||||
|
||||
#include "MtpTypes.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class MtpClient;
|
||||
class MtpDevice;
|
||||
class MtpObjectInfo;
|
||||
|
||||
// File-like abstraction for the interactive shell.
|
||||
// This can be used to represent an MTP device, storage unit or object
|
||||
// (either file or association).
|
||||
class MtpFile {
|
||||
private:
|
||||
MtpDevice* mDevice;
|
||||
MtpStorageID mStorage;
|
||||
MtpObjectHandle mHandle;
|
||||
static MtpClient* sClient;
|
||||
|
||||
public:
|
||||
MtpFile(MtpDevice* device);
|
||||
MtpFile(MtpDevice* device, MtpStorageID storage);
|
||||
MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle);
|
||||
MtpFile(MtpFile* file);
|
||||
virtual ~MtpFile();
|
||||
|
||||
MtpObjectInfo* getObjectInfo();
|
||||
void print();
|
||||
void list();
|
||||
|
||||
inline MtpDevice* getDevice() const { return mDevice; }
|
||||
|
||||
static void init(MtpClient* client);
|
||||
static MtpFile* parsePath(MtpFile* base, char* path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _MTP_DIRECTORY_H
|
||||
370
media/tests/mtp/mtp.cpp
Normal file
370
media/tests/mtp/mtp.cpp
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
Reference in New Issue
Block a user