version 1.43, 2000/10/18 18:23:02 |
version 1.43.2.4, 2001/05/07 21:09:33 |
|
|
* called by a name other than "ssh" or "Secure Shell". |
* called by a name other than "ssh" or "Secure Shell". |
*/ |
*/ |
/* |
/* |
* Copyright (c) 1999 Theo de Raadt. All rights reserved. |
* Copyright (c) 1999 Theo de Raadt. All rights reserved. |
* Copyright (c) 1999 Aaron Campbell. All rights reserved. |
* Copyright (c) 1999 Aaron Campbell. All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* modification, are permitted provided that the following conditions |
|
|
#include "includes.h" |
#include "includes.h" |
RCSID("$OpenBSD$"); |
RCSID("$OpenBSD$"); |
|
|
#include "ssh.h" |
|
#include "xmalloc.h" |
#include "xmalloc.h" |
|
#include "atomicio.h" |
|
#include "pathnames.h" |
|
#include "log.h" |
|
#include "scp-common.h" |
|
|
#define _PATH_CP "cp" |
|
|
|
/* For progressmeter() -- number of seconds before xfer considered "stalled" */ |
/* For progressmeter() -- number of seconds before xfer considered "stalled" */ |
#define STALLTIME 5 |
#define STALLTIME 5 |
|
|
|
|
static struct timeval start; |
static struct timeval start; |
|
|
/* Number of bytes of current file transferred so far. */ |
/* Number of bytes of current file transferred so far. */ |
volatile unsigned long statbytes; |
volatile off_t statbytes; |
|
|
/* Total size of current file. */ |
/* Total size of current file. */ |
off_t totalbytes = 0; |
off_t totalbytes = 0; |
|
|
/* This is set to non-zero to enable verbose mode. */ |
/* This is set to non-zero to enable verbose mode. */ |
int verbose_mode = 0; |
int verbose_mode = 0; |
|
|
/* This is set to non-zero if compression is desired. */ |
|
int compress = 0; |
|
|
|
/* This is set to zero if the progressmeter is not desired. */ |
/* This is set to zero if the progressmeter is not desired. */ |
int showprogress = 1; |
int showprogress = 1; |
|
|
/* This is the program to execute for the secured connection. ("ssh" or -S) */ |
/* This is the program to execute for the secured connection. ("ssh" or -S) */ |
char *ssh_program = SSH_PROGRAM; |
char *ssh_program = _PATH_SSH_PROGRAM; |
|
|
/* This is the list of arguments that scp passes to ssh */ |
/* This is the list of arguments that scp passes to ssh */ |
struct { |
struct { |
|
|
return 0; |
return 0; |
} |
} |
|
|
void |
|
fatal(const char *fmt,...) |
|
{ |
|
va_list ap; |
|
char buf[1024]; |
|
|
|
va_start(ap, fmt); |
|
vsnprintf(buf, sizeof(buf), fmt, ap); |
|
va_end(ap); |
|
fprintf(stderr, "%s\n", buf); |
|
exit(255); |
|
} |
|
|
|
typedef struct { |
typedef struct { |
int cnt; |
int cnt; |
char *buf; |
char *buf; |
} BUF; |
} BUF; |
|
|
extern int iamremote; |
|
|
|
BUF *allocbuf(BUF *, int, int); |
BUF *allocbuf(BUF *, int, int); |
char *colon(char *); |
|
void lostconn(int); |
void lostconn(int); |
void nospace(void); |
void nospace(void); |
int okname(char *); |
int okname(char *); |
|
|
addargs("-oFallBackToRsh no"); |
addargs("-oFallBackToRsh no"); |
|
|
fflag = tflag = 0; |
fflag = tflag = 0; |
while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:")) != EOF) |
while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S:o:")) != -1) |
switch (ch) { |
switch (ch) { |
/* User-visible flags. */ |
/* User-visible flags. */ |
case '4': |
case '4': |
|
|
iamremote = 1; |
iamremote = 1; |
tflag = 1; |
tflag = 1; |
break; |
break; |
case '?': |
|
default: |
default: |
usage(); |
usage(); |
} |
} |
|
|
remin = STDIN_FILENO; |
remin = STDIN_FILENO; |
remout = STDOUT_FILENO; |
remout = STDOUT_FILENO; |
|
|
if (fflag) { |
if (fflag) { |
/* Follow "protocol", send data. */ |
/* Follow "protocol", send data. */ |
(void) response(); |
(void) response(); |
source(argc, argv); |
source(argc, argv); |
|
|
|
|
remin = remout = -1; |
remin = remout = -1; |
/* Command to be executed on remote system using "ssh". */ |
/* Command to be executed on remote system using "ssh". */ |
(void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", |
(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s", |
|
verbose_mode ? " -v" : "", |
iamrecursive ? " -r" : "", pflag ? " -p" : "", |
iamrecursive ? " -r" : "", pflag ? " -p" : "", |
targetshouldbedirectory ? " -d" : ""); |
targetshouldbedirectory ? " -d" : ""); |
|
|
|
|
exit(errs != 0); |
exit(errs != 0); |
} |
} |
|
|
char * |
|
cleanhostname(host) |
|
char *host; |
|
{ |
|
if (*host == '[' && host[strlen(host) - 1] == ']') { |
|
host[strlen(host) - 1] = '\0'; |
|
return (host + 1); |
|
} else |
|
return host; |
|
} |
|
|
|
void |
void |
toremote(targ, argc, argv) |
toremote(targ, argc, argv) |
char *targ, *argv[]; |
char *targ, *argv[]; |
|
|
suser = pwd->pw_name; |
suser = pwd->pw_name; |
else if (!okname(suser)) |
else if (!okname(suser)) |
continue; |
continue; |
(void) sprintf(bp, |
snprintf(bp, len, |
"%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", |
"%s%s -x -o'FallBackToRsh no' -n " |
ssh_program, verbose_mode ? " -v" : "", |
"-l %s %s %s %s '%s%s%s:%s'", |
suser, host, cmd, src, |
ssh_program, verbose_mode ? " -v" : "", |
tuser ? tuser : "", tuser ? "@" : "", |
suser, host, cmd, src, |
thost, targ); |
tuser ? tuser : "", tuser ? "@" : "", |
|
thost, targ); |
} else { |
} else { |
host = cleanhostname(argv[i]); |
host = cleanhostname(argv[i]); |
(void) sprintf(bp, |
snprintf(bp, len, |
"exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", |
"exec %s%s -x -o'FallBackToRsh no' -n %s " |
ssh_program, verbose_mode ? " -v" : "", |
"%s %s '%s%s%s:%s'", |
host, cmd, src, |
ssh_program, verbose_mode ? " -v" : "", |
tuser ? tuser : "", tuser ? "@" : "", |
host, cmd, src, |
thost, targ); |
tuser ? tuser : "", tuser ? "@" : "", |
|
thost, targ); |
} |
} |
if (verbose_mode) |
if (verbose_mode) |
fprintf(stderr, "Executing: %s\n", bp); |
fprintf(stderr, "Executing: %s\n", bp); |
|
|
if (remin == -1) { |
if (remin == -1) { |
len = strlen(targ) + CMDNEEDS + 20; |
len = strlen(targ) + CMDNEEDS + 20; |
bp = xmalloc(len); |
bp = xmalloc(len); |
(void) sprintf(bp, "%s -t %s", cmd, targ); |
(void) snprintf(bp, len, "%s -t %s", cmd, targ); |
host = cleanhostname(thost); |
host = cleanhostname(thost); |
if (do_cmd(host, tuser, bp, &remin, |
if (do_cmd(host, tuser, bp, &remin, |
&remout, argc) < 0) |
&remout, argc) < 0) |
|
|
len = strlen(_PATH_CP) + strlen(argv[i]) + |
len = strlen(_PATH_CP) + strlen(argv[i]) + |
strlen(argv[argc - 1]) + 20; |
strlen(argv[argc - 1]) + 20; |
bp = xmalloc(len); |
bp = xmalloc(len); |
(void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, |
(void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP, |
iamrecursive ? " -r" : "", pflag ? " -p" : "", |
iamrecursive ? " -r" : "", pflag ? " -p" : "", |
argv[i], argv[argc - 1]); |
argv[i], argv[argc - 1]); |
if (verbose_mode) |
if (verbose_mode) |
|
|
host = cleanhostname(host); |
host = cleanhostname(host); |
len = strlen(src) + CMDNEEDS + 20; |
len = strlen(src) + CMDNEEDS + 20; |
bp = xmalloc(len); |
bp = xmalloc(len); |
(void) sprintf(bp, "%s -f %s", cmd, src); |
(void) snprintf(bp, len, "%s -f %s", cmd, src); |
if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { |
if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) { |
(void) xfree(bp); |
(void) xfree(bp); |
++errs; |
++errs; |
|
|
struct stat stb; |
struct stat stb; |
static BUF buffer; |
static BUF buffer; |
BUF *bp; |
BUF *bp; |
off_t i; |
off_t i, amt, result; |
int amt, fd, haderr, indx, result; |
int fd, haderr, indx; |
char *last, *name, buf[2048]; |
char *last, *name, buf[2048]; |
|
int len; |
|
|
for (indx = 0; indx < argc; ++indx) { |
for (indx = 0; indx < argc; ++indx) { |
name = argv[indx]; |
name = argv[indx]; |
statbytes = 0; |
statbytes = 0; |
|
len = strlen(name); |
|
while (len > 1 && name[len-1] == '/') |
|
name[--len] = '\0'; |
if ((fd = open(name, O_RDONLY, 0)) < 0) |
if ((fd = open(name, O_RDONLY, 0)) < 0) |
goto syserr; |
goto syserr; |
if (fstat(fd, &stb) < 0) { |
if (fstat(fd, &stb) < 0) { |
|
|
* Make it compatible with possible future |
* Make it compatible with possible future |
* versions expecting microseconds. |
* versions expecting microseconds. |
*/ |
*/ |
(void) sprintf(buf, "T%lu 0 %lu 0\n", |
(void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n", |
(unsigned long) stb.st_mtime, |
(u_long) stb.st_mtime, |
(unsigned long) stb.st_atime); |
(u_long) stb.st_atime); |
(void) atomicio(write, remout, buf, strlen(buf)); |
(void) atomicio(write, remout, buf, strlen(buf)); |
if (response() < 0) |
if (response() < 0) |
goto next; |
goto next; |
} |
} |
#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) |
#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) |
(void) sprintf(buf, "C%04o %lu %s\n", |
snprintf(buf, sizeof buf, "C%04o %lld %s\n", |
(unsigned int) (stb.st_mode & FILEMODEMASK), |
(u_int) (stb.st_mode & FILEMODEMASK), |
(unsigned long) stb.st_size, |
(long long)stb.st_size, last); |
last); |
|
if (verbose_mode) { |
if (verbose_mode) { |
fprintf(stderr, "Sending file modes: %s", buf); |
fprintf(stderr, "Sending file modes: %s", buf); |
fflush(stderr); |
fflush(stderr); |
|
|
else |
else |
last++; |
last++; |
if (pflag) { |
if (pflag) { |
(void) sprintf(path, "T%lu 0 %lu 0\n", |
(void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n", |
(unsigned long) statp->st_mtime, |
(u_long) statp->st_mtime, |
(unsigned long) statp->st_atime); |
(u_long) statp->st_atime); |
(void) atomicio(write, remout, path, strlen(path)); |
(void) atomicio(write, remout, path, strlen(path)); |
if (response() < 0) { |
if (response() < 0) { |
closedir(dirp); |
closedir(dirp); |
return; |
return; |
} |
} |
} |
} |
(void) sprintf(path, "D%04o %d %.1024s\n", |
(void) snprintf(path, sizeof path, "D%04o %d %.1024s\n", |
(unsigned int) (statp->st_mode & FILEMODEMASK), 0, last); |
(u_int) (statp->st_mode & FILEMODEMASK), 0, last); |
if (verbose_mode) |
if (verbose_mode) |
fprintf(stderr, "Entering directory: %s", path); |
fprintf(stderr, "Entering directory: %s", path); |
(void) atomicio(write, remout, path, strlen(path)); |
(void) atomicio(write, remout, path, strlen(path)); |
|
|
closedir(dirp); |
closedir(dirp); |
return; |
return; |
} |
} |
while ((dp = readdir(dirp))) { |
while ((dp = readdir(dirp)) != NULL) { |
if (dp->d_ino == 0) |
if (dp->d_ino == 0) |
continue; |
continue; |
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) |
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) |
|
|
run_err("%s/%s: name too long", name, dp->d_name); |
run_err("%s/%s: name too long", name, dp->d_name); |
continue; |
continue; |
} |
} |
(void) sprintf(path, "%s/%s", name, dp->d_name); |
(void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name); |
vect[0] = path; |
vect[0] = path; |
source(1, vect); |
source(1, vect); |
} |
} |
|
|
off_t size; |
off_t size; |
int setimes, targisdir, wrerrno = 0; |
int setimes, targisdir, wrerrno = 0; |
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
int dummy_usec; |
|
struct timeval tv[2]; |
struct timeval tv[2]; |
|
|
|
#define atime tv[0] |
|
#define mtime tv[1] |
#define SCREWUP(str) { why = str; goto screwup; } |
#define SCREWUP(str) { why = str; goto screwup; } |
|
|
setimes = targisdir = 0; |
setimes = targisdir = 0; |
|
|
if (buf[0] == '\01' || buf[0] == '\02') { |
if (buf[0] == '\01' || buf[0] == '\02') { |
if (iamremote == 0) |
if (iamremote == 0) |
(void) atomicio(write, STDERR_FILENO, |
(void) atomicio(write, STDERR_FILENO, |
buf + 1, strlen(buf + 1)); |
buf + 1, strlen(buf + 1)); |
if (buf[0] == '\02') |
if (buf[0] == '\02') |
exit(1); |
exit(1); |
++errs; |
++errs; |
|
|
if (ch == '\n') |
if (ch == '\n') |
*--cp = 0; |
*--cp = 0; |
|
|
#define getnum(t) (t) = 0; \ |
|
while (*cp >= '0' && *cp <= '9') (t) = (t) * 10 + (*cp++ - '0'); |
|
cp = buf; |
cp = buf; |
if (*cp == 'T') { |
if (*cp == 'T') { |
setimes++; |
setimes++; |
cp++; |
cp++; |
getnum(tv[1].tv_sec); |
mtime.tv_sec = strtol(cp, &cp, 10); |
if (*cp++ != ' ') |
if (!cp || *cp++ != ' ') |
SCREWUP("mtime.sec not delimited"); |
SCREWUP("mtime.sec not delimited"); |
getnum(dummy_usec); |
mtime.tv_usec = strtol(cp, &cp, 10); |
tv[1].tv_usec = 0; |
if (!cp || *cp++ != ' ') |
if (*cp++ != ' ') |
|
SCREWUP("mtime.usec not delimited"); |
SCREWUP("mtime.usec not delimited"); |
getnum(tv[0].tv_sec); |
atime.tv_sec = strtol(cp, &cp, 10); |
if (*cp++ != ' ') |
if (!cp || *cp++ != ' ') |
SCREWUP("atime.sec not delimited"); |
SCREWUP("atime.sec not delimited"); |
getnum(dummy_usec); |
atime.tv_usec = strtol(cp, &cp, 10); |
tv[0].tv_usec = 0; |
if (!cp || *cp++ != '\0') |
if (*cp++ != '\0') |
|
SCREWUP("atime.usec not delimited"); |
SCREWUP("atime.usec not delimited"); |
(void) atomicio(write, remout, "", 1); |
(void) atomicio(write, remout, "", 1); |
continue; |
continue; |
|
|
if (*cp++ != ' ') |
if (*cp++ != ' ') |
SCREWUP("mode not delimited"); |
SCREWUP("mode not delimited"); |
|
|
for (size = 0; *cp >= '0' && *cp <= '9';) |
for (size = 0; isdigit(*cp);) |
size = size * 10 + (*cp++ - '0'); |
size = size * 10 + (*cp++ - '0'); |
if (*cp++ != ' ') |
if (*cp++ != ' ') |
SCREWUP("size not delimited"); |
SCREWUP("size not delimited"); |
|
|
size_t need; |
size_t need; |
|
|
need = strlen(targ) + strlen(cp) + 250; |
need = strlen(targ) + strlen(cp) + 250; |
if (need > cursize) |
if (need > cursize) { |
|
if (namebuf) |
|
xfree(namebuf); |
namebuf = xmalloc(need); |
namebuf = xmalloc(need); |
(void) sprintf(namebuf, "%s%s%s", targ, |
cursize = need; |
|
} |
|
(void) snprintf(namebuf, need, "%s%s%s", targ, |
*targ ? "/" : "", cp); |
*targ ? "/" : "", cp); |
np = namebuf; |
np = namebuf; |
} else |
} else |
|
|
if (mkdir(np, mode | S_IRWXU) < 0) |
if (mkdir(np, mode | S_IRWXU) < 0) |
goto bad; |
goto bad; |
} |
} |
vect[0] = np; |
vect[0] = xstrdup(np); |
sink(1, vect); |
sink(1, vect); |
if (setimes) { |
if (setimes) { |
setimes = 0; |
setimes = 0; |
if (utimes(np, tv) < 0) |
if (utimes(vect[0], tv) < 0) |
run_err("%s: set times: %s", |
run_err("%s: set times: %s", |
np, strerror(errno)); |
vect[0], strerror(errno)); |
} |
} |
if (mod_flag) |
if (mod_flag) |
(void) chmod(np, mode); |
(void) chmod(vect[0], mode); |
|
if (vect[0]) |
|
xfree(vect[0]); |
continue; |
continue; |
} |
} |
omode = mode; |
omode = mode; |
|
|
continue; |
continue; |
} else if (j <= 0) { |
} else if (j <= 0) { |
run_err("%s", j ? strerror(errno) : |
run_err("%s", j ? strerror(errno) : |
"dropped connection"); |
"dropped connection"); |
exit(1); |
exit(1); |
} |
} |
amt -= j; |
amt -= j; |
|
|
if (exists || omode != mode) |
if (exists || omode != mode) |
if (fchmod(ofd, omode)) |
if (fchmod(ofd, omode)) |
run_err("%s: set mode: %s", |
run_err("%s: set mode: %s", |
np, strerror(errno)); |
np, strerror(errno)); |
} else { |
} else { |
if (!exists && omode != mode) |
if (!exists && omode != mode) |
if (fchmod(ofd, omode & ~mask)) |
if (fchmod(ofd, omode & ~mask)) |
run_err("%s: set mode: %s", |
run_err("%s: set mode: %s", |
np, strerror(errno)); |
np, strerror(errno)); |
} |
} |
if (close(ofd) == -1) { |
if (close(ofd) == -1) { |
wrerr = YES; |
wrerr = YES; |
|
|
setimes = 0; |
setimes = 0; |
if (utimes(np, tv) < 0) { |
if (utimes(np, tv) < 0) { |
run_err("%s: set times: %s", |
run_err("%s: set times: %s", |
np, strerror(errno)); |
np, strerror(errno)); |
wrerr = DISPLAYED; |
wrerr = DISPLAYED; |
} |
} |
} |
} |
|
|
usage() |
usage() |
{ |
{ |
(void) fprintf(stderr, "usage: scp " |
(void) fprintf(stderr, "usage: scp " |
"[-pqrvC46] [-S ssh] [-P port] [-c cipher] [-i identity] f1 f2; or:\n" |
"[-pqrvBC46] [-S ssh] [-P port] [-c cipher] [-i identity] f1 f2\n" |
" scp [options] f1 ... fn directory\n"); |
" or: scp [options] f1 ... fn directory\n"); |
exit(1); |
exit(1); |
} |
} |
|
|
|
|
va_end(ap); |
va_end(ap); |
} |
} |
|
|
char * |
|
colon(cp) |
|
char *cp; |
|
{ |
|
int flag = 0; |
|
|
|
if (*cp == ':') /* Leading colon is part of file name. */ |
|
return (0); |
|
if (*cp == '[') |
|
flag = 1; |
|
|
|
for (; *cp; ++cp) { |
|
if (*cp == '@' && *(cp+1) == '[') |
|
flag = 1; |
|
if (*cp == ']' && *(cp+1) == ':' && flag) |
|
return (cp+1); |
|
if (*cp == ':' && !flag) |
|
return (cp); |
|
if (*cp == '/') |
|
return (0); |
|
} |
|
return (0); |
|
} |
|
|
|
void |
void |
verifydir(cp) |
verifydir(cp) |
char *cp; |
char *cp; |
|
|
c = *cp; |
c = *cp; |
if (c & 0200) |
if (c & 0200) |
goto bad; |
goto bad; |
if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-' && c != '.') |
if (!isalpha(c) && !isdigit(c) && |
|
c != '_' && c != '-' && c != '.' && c != '+') |
goto bad; |
goto bad; |
} while (*++cp); |
} while (*++cp); |
return (1); |
return (1); |
|
|
} |
} |
|
|
int |
int |
foregroundproc() |
foregroundproc(void) |
{ |
{ |
static pid_t pgrp = -1; |
static pid_t pgrp = -1; |
int ctty_pgrp; |
int ctty_pgrp; |
|
|
i++; |
i++; |
abbrevsize >>= 10; |
abbrevsize >>= 10; |
} |
} |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5llu %c%c ", |
(quad_t) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : |
(unsigned long long) abbrevsize, prefixes[i], |
'B'); |
prefixes[i] == ' ' ? ' ' : 'B'); |
|
|
timersub(&now, &lastupdate, &wait); |
timersub(&now, &lastupdate, &wait); |
if (cursize > lastsize) { |
if (cursize > lastsize) { |
|
|
timersub(&now, &start, &td); |
timersub(&now, &start, &td); |
elapsed = td.tv_sec + (td.tv_usec / 1000000.0); |
elapsed = td.tv_sec + (td.tv_usec / 1000000.0); |
|
|
if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { |
if (flag != 1 && |
|
(statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes)) { |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
" --:-- ETA"); |
" --:-- ETA"); |
} else if (wait.tv_sec >= STALLTIME) { |
} else if (wait.tv_sec >= STALLTIME) { |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
" - stalled -"); |
" - stalled -"); |
} else { |
} else { |
if (flag != 1) |
if (flag != 1) |
remaining = |
remaining = (int)(totalbytes / (statbytes / elapsed) - |
(int)(totalbytes / (statbytes / elapsed) - elapsed); |
elapsed); |
else |
else |
remaining = elapsed; |
remaining = elapsed; |
|
|