📄 parse.c
字号:
* rare. */#define check_trailing_char(string, trailchar) \ ((string)->length > 0 \ && (string)->source[(string)->length - 1] == (trailchar)) switch (info->type) { case FTP_FILE_DIRECTORY: /* Check for trailing `/' */ if (check_trailing_char(&info->name, '/')) info->name.length--; break; case FTP_FILE_SYMLINK: /* If the file is a symbolic link, it should * have a ` -> ' somewhere. */ while (pos && pos + 3 < end) { if (!memcmp(pos, " -> ", 4)) { info->symlink.source = pos + 4; info->symlink.length = end - pos - 4; info->name.length = pos - src; break; } pos = memchr(pos, ' ', end - pos); } if (!info->symlink.source) return NULL; /* Check for trailing `@' on link and trailing * `/' on the link target if it's a directory */ if (check_trailing_char(&info->name, '@')) info->name.length--; if (check_trailing_char(&info->symlink, '/')) info->symlink.length--; break; case FTP_FILE_PLAINFILE: /* Check for trailing `*' on files which are * executable. */ if ((info->permissions & 0111) && check_trailing_char(&info->name, '*')) info->name.length--; default: break; } if (mtime.tm_year == 0) { /* Get the current time. */ time_t timenow = time(NULL); struct tm *now = localtime(&timenow); mtime.tm_year = now->tm_year; /* Some listings will not specify the year if it * is "obvious" that the file was from the * previous year. E.g. if today is 97-01-12, and * you see a file of Dec 15th, its year is 1996, * not 1997. Thanks to Vladimir Volovich for * mentioning this! */ if (mtime.tm_mon > now->tm_mon) mtime.tm_year--; } info->mtime = mktime(&mtime); /* store the time-stamp */ info->local_time_zone = 1; return info; } skip_space_end(pos, end); } return NULL;}/* Parser for VMS-style MultiNet (some spaces removed from examples): */#ifdef DEBUG_FTP_PARSERstatic unsigned char ftp_vms_responses[] = "00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)\r\n" "CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)\r\n" /* And non-MutliNet VMS: */ "CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)\r\n" /* A garbage line which should fail: */ "EA95_0PS.GZ;1 No privilege for attempted operation\r\n";#endif/* Converts VMS symbolic permissions to number-style ones, e.g. string * RWED,RWE,RE to 755. "D" (delete) is taken to be equal to "W" (write). * Inspired by a patch of Stoyan Lekov <lekov@eda.bg>. */static intparse_ftp_vms_permissions(const unsigned char *src, int len){ int perms = 0; int pos; for (pos = 0; pos < len; pos++) { switch (src[pos]) { case ',': perms <<= 3; break; case 'R': perms |= 4; break; case 'W': case 'D': perms |= 2; break; case 'E': perms |= 1; break; default: /* Wrong VMS permissons! */ return 0; } } return perms;}static struct ftp_file_info *parse_ftp_vms_response(struct ftp_file_info *info, unsigned char *src, int len){ unsigned char *end = src + len; unsigned char *pos; /* First column: Name. A bit of black magic again. The name maybe either * ABCD.EXT or ABCD.EXT;NUM and it might be on a separate line. * Therefore we will first try to get the complete name until the first * space character; if it fails, we assume that the name occupies the * whole line. After that we search for the version separator ";", we * remove it and check the extension of the file; extension .DIR denotes * directory. */ pos = memchr(src, ';', end - src); if (!pos) return NULL; info->name.source = src; info->name.length = pos - src; /* If the name ends on .DIR or .DIR;#, it's a directory. We also * set the file size to zero as the listing does tell us only * the size in filesystem blocks - for an integrity check (when * mirroring, for example) we would need the size in bytes. */ if (info->name.length > 4 && !memcmp(&pos[-4], ".DIR", 4)) { info->type = FTP_FILE_DIRECTORY; info->name.length -= 4; } else { info->type = FTP_FILE_PLAINFILE; } skip_nonspace_end(pos, end); skip_space_end(pos, end); src = pos; /* Second column, if exists, or the first column of the next line * contain file size in blocks. We will skip it. */ if (src >= end) { /* FIXME: Handle multi-lined views. */ return NULL; } skip_nonspace_end(src, end); skip_space_end(src, end); if (src >= end) return NULL; /* Third/Second column: Date DD-MMM-YYYY and * Fourth/Third column: Time hh:mm[:ss] */ /* If the server produces garbage like * 'EA95_0PS.GZ;1 No privilege for attempted operation' * parse_date() will fail. */ info->mtime = parse_date(&src, end, 1, 0); if (info->mtime == 0) return NULL; /* Be more tolerant from here on ... */ /* Skip the fifth column */ skip_space_end(src, end); skip_nonspace_end(src, end); skip_space_end(src, end); if (src >= end) return info; /* Sixth column: Permissions */ src = memchr(src, '(', end - src); if (!src || src >= end) return info; src++; pos = memchr(src, ')', end - src); if (!pos) return info; /* Permissons have the format "RWED,RWED,RE" */ info->permissions = parse_ftp_vms_permissions(src, pos - src); return info;}/* Parser for the MSDOS-style format: */#ifdef DEBUG_FTP_PARSERstatic unsigned char ftp_winnt_responses[] = "04-27-00 09:09PM <DIR> licensed\r\n" "07-18-00 10:16AM <DIR> pub\r\n" "04-14-00 03:47PM 589 readme.htm\r\n";#endifstruct ftp_file_info *parse_ftp_winnt_response(struct ftp_file_info *info, unsigned char *src, int len){ struct tm mtime; unsigned char *end = src + len; /* Extracting name is a bit of black magic and we have to do it * before `strtok' inserted extra \0 characters in the line * string. For the moment let us just suppose that the name starts at * column 39 of the listing. This way we could also recognize * filenames that begin with a series of space characters (but who * really wants to use such filenames anyway?). */ if (len <= 39) return NULL; info->name.source = src + 39; info->name.length = end - src - 39; /* First column: mm-dd-yy. Should number parsing of the month fail, * january will be assumed. */ memset(&mtime, 0, sizeof(mtime)); mtime.tm_isdst = -1; mtime.tm_mon = parse_ftp_number(&src, end, 1, 12); if (src + 2 >= end || *src != '-') return NULL; src++; mtime.tm_mday = parse_day((const unsigned char **) &src, end); if (src + 2 >= end || *src != '-') return NULL; src++; mtime.tm_year = parse_year((const unsigned char **) &src, end); if (src >= end || mtime.tm_year == -1) return NULL; skip_space_end(src, end); if (src >= end) return NULL; /* Second column: hh:mm[AP]M, listing does not contain value for * seconds */ if (!parse_time((const unsigned char **) &src, &mtime, end)) return NULL; /* Store the time-stamp. */ info->mtime = mktime(&mtime); skip_nonspace_end(src, end); skip_space_end(src, end); if (src >= end) return NULL; /* Third column: Either file length, or <DIR>. We also set the * permissions (guessed as 0644 for plain files and 0755 for directories * as the listing does not give us a clue) and filetype here. */ if (*src == '<') { info->type = FTP_FILE_DIRECTORY; info->permissions = 0755; } else if (isdigit(*src)) { info->type = FTP_FILE_PLAINFILE; info->size = parse_ftp_number(&src, end, 0, LONG_MAX); info->permissions = 0644; } else { info->type = FTP_FILE_UNKNOWN; } return info;}#ifdef DEBUG_FTP_PARSERunsigned char *get_ftp_debug_parse_responses(unsigned char *buffer, int buflen){ struct string response; if (!init_string(&response)) return NULL; add_to_string(&response, ftp_eplf_responses); add_to_string(&response, ftp_unix_responses); add_to_string(&response, ftp_vms_responses); add_to_string(&response, ftp_winnt_responses); add_bytes_to_string(&response, buffer, buflen); return response.source;}#endifstruct ftp_file_info *parse_ftp_file_info(struct ftp_file_info *info, unsigned char *src, int len){ assert(info && src && len > 0); if_assert_failed return NULL; switch (*src) { case '+': return parse_ftp_eplf_response(info, src, len); case 'b': case 'c': case 'd': case 'l': case 'p': case 's': case '-': break; default: if (memchr(src, ';', len)) return parse_ftp_vms_response(info, src, len); if (isdigit(*src)) return parse_ftp_winnt_response(info, src, len); } return parse_ftp_unix_response(info, src, len);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -