diff options
author | Miles Bader <miles@gnu.org> | 1997-05-07 21:01:07 +0000 |
---|---|---|
committer | Miles Bader <miles@gnu.org> | 1997-05-07 21:01:07 +0000 |
commit | 44340c896186ee1265520855d2b8f74883ba8829 (patch) | |
tree | 9fc6bf01cf48fb461a1db22b47576a3e3f66c637 /libftpconn/names.c | |
parent | 6abfa49b04ea7b5d4721871296959ad620be0c80 (diff) |
Initial checkin
Diffstat (limited to 'libftpconn/names.c')
-rw-r--r-- | libftpconn/names.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/libftpconn/names.c b/libftpconn/names.c new file mode 100644 index 00000000..ced741f6 --- /dev/null +++ b/libftpconn/names.c @@ -0,0 +1,209 @@ +/* Fetch directory file names + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + 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; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include <ftpconn.h> + +struct get_names_state +{ + char *name; /* Last read (maybe partial) name. */ + size_t name_len; /* Valid length of NAME, *not including* '\0'. */ + size_t name_alloced; /* Allocated size of NAME (>= NAME_LEN). */ + int name_partial; /* True if NAME isn't complete. */ + + size_t buf_len; /* Length of contents in BUF. */ + char buf[7000]; +}; + +/* Start an operation to get a list of filenames in the directory NAME, and + return a file-descriptor for reading on, and a state structure in STATE + suitable for passing to cont_get_names. */ +error_t +ftp_conn_start_get_names (struct ftp_conn *conn, + const char *name, int *fd, void **state) +{ + error_t err; + struct get_names_state *s = malloc (sizeof (struct get_names_state)); + + if (! s) + return ENOMEM; + + err = ftp_conn_start_list (conn, name, fd); + + if (err) + free (s); + else + { + s->name = 0; + s->name_len = s->name_alloced = 0; + s->name_partial = 0; + s->buf_len = 0; + *state = s; + } + + return err; +} + +/* Read filenames from FD, calling ADD_NAME for each new NAME (HOOK is passed + to ADD_NAME). FD and STATE should be returned from start_get_names. If + this function returns EAGAIN, then it should be called again to finish the + job (possibly after calling select on FD); if it returns 0, then it is + finishe,d and FD and STATE are deallocated. */ +error_t +ftp_conn_cont_get_names (struct ftp_conn *conn, int fd, void *state, + ftp_conn_add_name_fun_t add_name, void *hook) +{ + char *p, *nl; + ssize_t rd; + size_t name_len; + error_t err = 0; + struct get_names_state *s = state; + + /* We always consume full lines, so we know that we have to read more when + we first get called. */ + rd = read (fd, s->buf + s->buf_len, sizeof (s->buf) - s->buf_len); + if (rd < 0) + { + err = errno; + goto finished; + } + + if (rd == 0) + /* EOF */ + if (s->buf_len == 0) + /* We're done! Clean up and return the result in NAMES. */ + goto finished; + else + /* Partial line at end of file? */ + nl = s->buf + s->buf_len; + else + /* Look for a new line in what we read (we know that there weren't any in + the buffer before that). */ + { + nl = memchr (s->buf + s->buf_len, '\n', rd); + s->buf_len += rd; + } + + if (!nl && s->buf_len < sizeof (s->buf)) + /* We didn't find any newlines (which implies we didn't hit EOF), and we + still have room to grow the buffer, so just wait until next time to do + anything. */ + return EAGAIN; + + /* Where we start parsing. */ + p = s->buf; + + do + { + /* Fill in S->name, possibly extending it from a previous buffer. */ + name_len = (nl ? nl - p : s->buf + s->buf_len - p); + if (name_len > 0 && p[name_len - 1] == '\r') + name_len--; + if (name_len > 0) + /* Extending s->name. */ + { + size_t old_len = s->name_len; + size_t total_len = old_len + name_len + 1; + + if (total_len > s->name_alloced) + { + char *new_name = realloc (s->name, total_len); + if (! new_name) + goto enomem; + s->name = new_name; + s->name_alloced = total_len; + } + + strncpy (s->name + old_len, p, name_len); + s->name[old_len + name_len] = '\0'; + s->name_len = total_len - 1; + } + + if (nl) + { + /* Call the callback function to process the current entry. */ + err = (*add_name) (s->name, hook); + if (err) + goto finished; + + s->name_len = 0; + s->name_partial = 0; + + p = nl + 1; + nl = memchr (p, '\n', s->buf + s->buf_len - p); + } + else + /* We found no newline, so the name extends past what we read; we'll + try to read more next time. */ + { + s->name_partial = 1; + /* Skip over the partial name for the next iteration. */ + p += name_len; + } + } + while (nl); + + /* Move any remaining characters in the buffer to the beginning for the + next call. */ + s->buf_len -= (p - s->buf); + if (s->buf_len > 0) + memmove (s->buf, p, s->buf_len); + + /* Try again later. */ + return EAGAIN; + +enomem: + /* Some memory allocation failed. */ + err = ENOMEM; + +finished: + /* We're finished (with an error if ERR != 0), deallocate everything & + return. */ + if (s->name) + free (s->name); + free (s); + close (fd); + + return err; +} + +/* Get a list of names in the directory NAME, calling ADD_NAME for each one + (HOOK is passed to ADD_NAME). This function may block. */ +error_t +ftp_conn_get_names (struct ftp_conn *conn, const char *name, + ftp_conn_add_name_fun_t add_name, void *hook) +{ + int fd; + void *state; + error_t err = ftp_conn_start_get_names (conn, name, &fd, &state); + + if (err) + return err; + + do + err = ftp_conn_cont_get_names (conn, fd, state, add_name, hook); + while (err == EAGAIN); + + return err; +} + |