📄 parse.c
字号:
/* Parsing of FTP `ls' directory output. *//* $Id: parse.c,v 1.38.2.1 2005/04/06 09:30:31 jonas Exp $ *//* Parts of this file was part of GNU Wget * Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc. */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <errno.h>#include <stdio.h>#include <stdlib.h>#ifdef HAVE_STRING_H# include <string.h>#else# include <strings.h>#endif#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <sys/stat.h>#include <sys/types.h>#include "elinks.h"#include "osdep/ascii.h"#include "protocol/date.h"#include "protocol/ftp/parse.h"#include "util/conv.h"#include "util/string.h"#include "util/ttime.h"#define skip_space_end(src, end) \ do { while ((src) < (end) && isspace(*(src))) (src)++; } while (0)#define skip_nonspace_end(src, end) \ do { while ((src) < (end) && !isspace(*(src))) (src)++; } while (0)static longparse_ftp_number(unsigned char **src, unsigned char *end, long from, long to){ long number = 0; unsigned char *pos = *src; for (; pos < end && isdigit(*pos); pos++) number = (*pos - '0') + 10 * number; *src = pos; if (number < from || to < number) return -1; return number;}/* Parser for the EPLF format (see http://pobox.com/~djb/proto/eplf.txt). * * Lines end with \r\n (CR-LF), but that is handled elsewhere. * * Some example EPLF response, with the filename separator (tab) displayed as a * space: */#ifdef DEBUG_FTP_PARSERstatic unsigned char ftp_eplf_responses[] = "+i8388621.48594,m825718503,r,s280,\tdjb.html\r\n" "+i8388621.50690,m824255907,/,\t514\r\n" "+i8388621.48598,m824253270,r,s612,\t514.html\r\n";#endifenum ftp_eplf { FTP_EPLF_FILENAME = ASCII_TAB, /* Filename follows */ FTP_EPLF_PLAINFILE = 'r', /* RETR is possible */ FTP_EPLF_DIRECTORY = '/', /* CWD is possible */ FTP_EPLF_SIZE = 's', /* File size follows */ FTP_EPLF_MTIME = 'm', /* Modification time follows */ FTP_EPLF_ID = 'i', /* Unique file id follows */};static struct ftp_file_info *parse_ftp_eplf_response(struct ftp_file_info *info, unsigned char *src, int len){ /* Skip the '+'-char which starts the line. */ unsigned char *end = src + len; unsigned char *pos = src++; /* Handle the series of facts about the file. */ for (; src < end && pos; src = pos + 1) { /* Find the end of the current fact. */ pos = memchr(src, ',', end - src); if (!pos) pos = end; switch (*src++) { case FTP_EPLF_FILENAME: if (src >= end) break; info->name.source = src; info->name.length = end - src; return info; case FTP_EPLF_DIRECTORY: info->type = FTP_FILE_DIRECTORY; break; case FTP_EPLF_PLAINFILE: info->type = FTP_FILE_PLAINFILE; break; case FTP_EPLF_SIZE: if (src >= pos) break; info->size = parse_ftp_number(&src, pos, 0, LONG_MAX); break; case FTP_EPLF_MTIME: if (src >= pos) break; info->mtime = (time_t) parse_ftp_number(&src, pos, 0, LONG_MAX); break; case FTP_EPLF_ID: /* Not used */ break; } } return NULL;}/* Parser for UNIX-style listing: */#ifdef DEBUG_FTP_PARSERstatic unsigned char ftp_unix_responses[] = /* UNIX-style listing, without inum and without blocks: */ "-rw-r--r-- 1 root other 531 Jan 29 03:26 README\r\n" "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc\r\n" "dr-xr-xr-x 2 root 512 Apr 8 1994 et\r\nc" "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin\r\n" /* Also produced by Microsoft's FTP servers for Windows: */ "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z\r\n" "d--------- 1 owner group 0 May 9 19:45 Softlib\r\n" /* Also WFTPD for MSDOS: */ "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp\r\n" /* Also NetWare: */ "d [R----F--] supervisor 512 Jan 16 18:53 login\r\n" "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe\r\n" /* Also NetPresenz for the Mac: */ "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit\r\n" "drwxrwxr-x folder 2 May 10 1996 network\r\n";#endifenum ftp_unix { FTP_UNIX_PERMISSIONS, FTP_UNIX_SIZE, FTP_UNIX_DAY, FTP_UNIX_TIME, FTP_UNIX_NAME};/* Converts Un*x-style symbolic permissions to number-style ones, e.g. string * rwxr-xr-x to 755. * Borrowed from lftp source code by Alexander V. Lukyanov. * On parse error, it returns 0. */static intparse_ftp_unix_permissions(const unsigned char *src, int len){ int perms = 0; if (len != 9 && !(len == 10 && src[9] == '+')) /* ACL tag */ return 0; /* User permissions */ switch (src[0]) { case('r'): perms |= S_IRUSR; break; case('-'): break; default: return 0; } switch (src[1]) { case('w'): perms |= S_IWUSR; break; case('-'): break; default: return 0; } switch (src[2]) { case('S'): perms |= S_ISUID; break; case('s'): perms |= S_ISUID; /* fall-through */ case('x'): perms |= S_IXUSR; break; case('-'): break; default: return 0; } /* Group permissions */ switch (src[3]) { case('r'): perms |= S_IRGRP; break; case('-'): break; default: return 0; } switch (src[4]) { case('w'): perms |= S_IWGRP; break; case('-'): break; default: return 0; } switch (src[5]) { case('S'): perms |= S_ISGID; break; case('s'): perms |= S_ISGID; /* fall-through */ case('x'): perms |= S_IXGRP; break; case('-'): break; default: return 0; } /* Others permissions */ switch (src[6]) { case('r'): perms |= S_IROTH; break; case('-'): break; default: return 0; } switch (src[7]) { case('w'): perms |= S_IWOTH; break; case('-'): break; default: return 0; } switch (src[8]) { case('T'): perms |= S_ISVTX; break; case('t'): perms |= S_ISVTX; /* fall-through */ case('x'): perms |= S_IXOTH; break; case('l'): case('L'): perms |= S_ISGID; perms &= ~S_IXGRP; break; case('-'): break; default: return 0; } return perms;}static struct ftp_file_info *parse_ftp_unix_response(struct ftp_file_info *info, unsigned char *src, int len){ unsigned char *end = src + len; enum ftp_file_type type = *src++; unsigned char *pos = src; struct tm mtime; enum ftp_unix fact; /* Decide the file type. */ switch (type) { case FTP_FILE_PLAINFILE: case FTP_FILE_DIRECTORY: case FTP_FILE_SYMLINK: info->type = type; break; default: info->type = FTP_FILE_UNKNOWN; } memset(&mtime, 0, sizeof(mtime)); mtime.tm_isdst = -1; skip_space_end(src, end); fact = FTP_UNIX_PERMISSIONS; for (pos = src; src < end; src = pos) { skip_nonspace_end(pos, end); switch (fact) { case FTP_UNIX_PERMISSIONS: /* We wanna know permissions as well! And I decided to * completely ignore the NetWare perms, they are very * rare and of some nonstandart format. If you want * them, though, I'll accept patch enabling them. * --pasky */ if (pos - src == 9) /* 9 is length of "rwxrwxrwx". */ info->permissions = parse_ftp_unix_permissions(src, 9); fact = FTP_UNIX_SIZE; break; case FTP_UNIX_SIZE: /* Search for the size and month name combo: */ if (info->size != FTP_SIZE_UNKNOWN && pos - src == 3) { int month = parse_month((const unsigned char **) &src, pos); if (month != -1) { fact = FTP_UNIX_DAY; mtime.tm_mon = month; break; } } if (!isdigit(*src)) { info->size = FTP_SIZE_UNKNOWN; break; } info->size = parse_ftp_number(&src, pos, 0, LONG_MAX); break; case FTP_UNIX_DAY: mtime.tm_mday = parse_day((const unsigned char **) &src, pos); fact = FTP_UNIX_TIME; break; case FTP_UNIX_TIME: /* This ought to be either the time, or the * year. Let's be flexible! */ fact = FTP_UNIX_NAME; /* We must deal with digits. */ if (!isdigit (*src)) break; /* If we have a number x, it's a year. If we have x:y, * it's hours and minutes. */ if (!memchr(src, ':', pos - src)) { mtime.tm_year = parse_year((const unsigned char **) &src, pos); break; } if (!parse_time((const unsigned char **) &src, &mtime, pos)) { mtime.tm_hour = mtime.tm_min = mtime.tm_sec = 0; } break; case FTP_UNIX_NAME: /* Since the file name may contain spaces use @end as the * token ending and not @pos. */ info->name.source = src; info->name.length = end - src; /* Some FTP sites choose to have ls -F as their default * LIST output, which marks the symlinks with a trailing * `@', directory names with a trailing `/' and * executables with a trailing `*'. This is no problem * unless encountering a symbolic link ending with `@', * or an executable ending with `*' on a server without * default -F output. I believe these cases are very
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -