diff options
Diffstat (limited to 'core/fs/pxe/ftp_readdir.c')
-rw-r--r-- | core/fs/pxe/ftp_readdir.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/core/fs/pxe/ftp_readdir.c b/core/fs/pxe/ftp_readdir.c new file mode 100644 index 00000000..6b87f77e --- /dev/null +++ b/core/fs/pxe/ftp_readdir.c @@ -0,0 +1,141 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2011 Intel Corporation; author: H. Peter Anvin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * ftp_readdir.c + */ +#include <inttypes.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <dprintf.h> +#include "pxe.h" + +static int dirtype(char type) +{ + switch (type) { + case 'f': + return DT_FIFO; + case 'c': + return DT_CHR; + case 'd': + return DT_DIR; + case 'b': + return DT_BLK; + case '-': + case '0' ... '9': /* Some DOS FTP stacks */ + return DT_REG; + case 'l': + return DT_LNK; + case 's': + return DT_SOCK; + default: + return DT_UNKNOWN; + } +} + +int ftp_readdir(struct inode *inode, struct dirent *dirent) +{ + char bufs[2][FILENAME_MAX + 1]; + int nbuf = 0; + char *buf = bufs[nbuf]; + char *p = buf; + char *name = NULL; + char type; + int c; + int dt; + bool was_cr = false; + bool first = true; + + for (;;) { + type = 0; + + for (;;) { + c = pxe_getc(inode); + if (c == -1) + return -1; /* Nothing else there */ + + if (c == '\r') { + was_cr = true; + continue; + } + if (was_cr) { + if (c == '\n') { + if (!name) { + *p = '\0'; + name = buf; + } + break; /* End of line */ + } + else if (c == '\0') + c = '\r'; + } + was_cr = false; + + if (c == ' ' || c == '\t') { + if (!name) { + *p = '\0'; + if (first) { + if (p == buf) { + /* Name started with whitespace - skip line */ + name = buf; + } else if ((p = strchr(buf, ';'))) { + /* VMS/Multinet format */ + if (p > buf+4 && !memcmp(p-4, ".DIR", 4)) { + type = 'd'; + p -= 4; + } else { + type = 'f'; + } + *p = '\0'; + name = buf; + } else { + type = buf[0]; + } + first = false; + } else { + /* Not the first word */ + if ((type >= '0' && type <= '9') && + !strcmp(buf, "<DIR>")) { + /* Some DOS FTP servers */ + type = 'd'; + } else if (type == 'l' && !strcmp(buf, "->")) { + /* The name was the previous word */ + name = bufs[nbuf ^ 1]; + } + } + nbuf ^= 1; + p = buf = bufs[nbuf]; + } + } else { + if (!name && p < buf + FILENAME_MAX) + *p++ = c; + } + } + + dt = dirtype(type); + if (dt != DT_UNKNOWN) { + size_t len = strlen(name); + + if (len <= NAME_MAX) { + dirent->d_type = dt; + dirent->d_ino = 0; /* Not applicable */ + dirent->d_off = 0; /* Not applicable */ + dirent->d_reclen = offsetof(struct dirent, d_name) + len+1; + memcpy(dirent->d_name, name, len+1); + return 0; + } + } + + /* Otherwise try the next line... */ + } +} |