1*21606Sdist /* 2*21606Sdist * Copyright (c) 1983 Regents of the University of California. 3*21606Sdist * All rights reserved. The Berkeley software License Agreement 4*21606Sdist * specifies the terms and conditions for redistribution. 5*21606Sdist */ 69666Slinton 7*21606Sdist #ifndef lint 8*21606Sdist static char sccsid[] = "@(#)library.c 5.1 (Berkeley) 05/31/85"; 9*21606Sdist #endif not lint 1016611Ssam 1118221Slinton static char rcsid[] = "$Header: library.c,v 1.5 84/12/26 10:39:52 linton Exp $"; 1218221Slinton 139666Slinton /* 149666Slinton * General purpose routines. 159666Slinton */ 169666Slinton 179666Slinton #include <stdio.h> 189666Slinton #include <errno.h> 199666Slinton #include <signal.h> 209666Slinton 219666Slinton #define public 229666Slinton #define private static 239666Slinton #define and && 249666Slinton #define or || 259666Slinton #define not ! 269666Slinton #define ord(enumcon) ((int) enumcon) 279666Slinton #define nil(type) ((type) 0) 289666Slinton 2916611Ssam typedef int integer; 3016611Ssam typedef enum { FALSE, TRUE } boolean; 319666Slinton typedef char *String; 329666Slinton typedef FILE *File; 339666Slinton typedef String Filename; 349666Slinton 359666Slinton #undef FILE 369666Slinton 379666Slinton String cmdname; /* name of command for error messages */ 389666Slinton Filename errfilename; /* current file associated with error */ 399666Slinton short errlineno; /* line number associated with error */ 409666Slinton 419666Slinton /* 429666Slinton * Definitions for doing memory allocation. 439666Slinton */ 449666Slinton 459666Slinton extern char *malloc(); 469666Slinton 479666Slinton #define alloc(n, type) ((type *) malloc((unsigned) (n) * sizeof(type))) 489666Slinton #define dispose(p) { free((char *) p); p = 0; } 499666Slinton 509666Slinton /* 519666Slinton * Macros for doing freads + fwrites. 529666Slinton */ 539666Slinton 549666Slinton #define get(fp, var) fread((char *) &(var), sizeof(var), 1, fp) 559666Slinton #define put(fp, var) fwrite((char *) &(var), sizeof(var), 1, fp) 569666Slinton 579666Slinton /* 589666Slinton * String definitions. 599666Slinton */ 609666Slinton 619666Slinton extern String strcpy(), index(), rindex(); 629666Slinton extern int strlen(); 639666Slinton 649666Slinton #define strdup(s) strcpy(malloc((unsigned) strlen(s) + 1), s) 659666Slinton #define streq(s1, s2) (strcmp(s1, s2) == 0) 669666Slinton 679666Slinton typedef int INTFUNC(); 689666Slinton 699666Slinton typedef struct { 709666Slinton INTFUNC *func; 719666Slinton } ERRINFO; 729666Slinton 739666Slinton #define ERR_IGNORE ((INTFUNC *) 0) 749666Slinton #define ERR_CATCH ((INTFUNC *) 1) 759666Slinton 769666Slinton /* 779666Slinton * Call a program. 789666Slinton * 799666Slinton * Four entries: 809666Slinton * 819666Slinton * call, callv - call a program and wait for it, returning status 829666Slinton * back, backv - call a program and don't wait, returning process id 839666Slinton * 849666Slinton * The command's standard input and output are passed as FILE's. 859666Slinton */ 869666Slinton 879666Slinton 8816611Ssam #define MAXNARGS 1000 /* unchecked upper limit on max num of arguments */ 899666Slinton #define BADEXEC 127 /* exec fails */ 909666Slinton 919666Slinton #define ischild(pid) ((pid) == 0) 929666Slinton 939666Slinton /* VARARGS3 */ 949666Slinton public int call(name, in, out, args) 959666Slinton String name; 969666Slinton File in; 979666Slinton File out; 989666Slinton String args; 999666Slinton { 1009666Slinton String *ap, *argp; 1019666Slinton String argv[MAXNARGS]; 1029666Slinton 1039666Slinton argp = &argv[0]; 1049666Slinton *argp++ = name; 1059666Slinton ap = &args; 1069666Slinton while (*ap != nil(String)) { 1079666Slinton *argp++ = *ap++; 1089666Slinton } 1099666Slinton *argp = nil(String); 1109666Slinton return callv(name, in, out, argv); 1119666Slinton } 1129666Slinton 1139666Slinton /* VARARGS3 */ 1149666Slinton public int back(name, in, out, args) 1159666Slinton String name; 1169666Slinton File in; 1179666Slinton File out; 1189666Slinton String args; 1199666Slinton { 1209666Slinton String *ap, *argp; 1219666Slinton String argv[MAXNARGS]; 1229666Slinton 1239666Slinton argp = &argv[0]; 1249666Slinton *argp++ = name; 1259666Slinton ap = &args; 1269666Slinton while (*ap != nil(String)) { 1279666Slinton *argp++ = *ap++; 1289666Slinton } 1299666Slinton *argp = nil(String); 1309666Slinton return backv(name, in, out, argv); 1319666Slinton } 1329666Slinton 1339666Slinton public int callv(name, in, out, argv) 1349666Slinton String name; 1359666Slinton File in; 1369666Slinton File out; 1379666Slinton String *argv; 1389666Slinton { 1399666Slinton int pid, status; 1409666Slinton 1419666Slinton pid = backv(name, in, out, argv); 1429666Slinton pwait(pid, &status); 1439666Slinton return status; 1449666Slinton } 1459666Slinton 1469666Slinton public int backv(name, in, out, argv) 1479666Slinton String name; 1489666Slinton File in; 1499666Slinton File out; 1509666Slinton String *argv; 1519666Slinton { 1529666Slinton int pid; 1539666Slinton 1549666Slinton fflush(stdout); 1559666Slinton if (ischild(pid = fork())) { 1569666Slinton fswap(0, fileno(in)); 1579666Slinton fswap(1, fileno(out)); 1589666Slinton onsyserr(EACCES, ERR_IGNORE); 1599666Slinton execvp(name, argv); 1609666Slinton _exit(BADEXEC); 1619666Slinton } 1629666Slinton return pid; 1639666Slinton } 1649666Slinton 1659666Slinton /* 1669666Slinton * Swap file numbers so as to redirect standard input and output. 1679666Slinton */ 1689666Slinton 1699666Slinton private fswap(oldfd, newfd) 1709666Slinton int oldfd; 1719666Slinton int newfd; 1729666Slinton { 1739666Slinton if (oldfd != newfd) { 1749666Slinton close(oldfd); 1759666Slinton dup(newfd); 1769666Slinton close(newfd); 1779666Slinton } 1789666Slinton } 1799666Slinton 1809666Slinton /* 1819666Slinton * Invoke a shell on a command line. 1829666Slinton */ 1839666Slinton 1849666Slinton #define DEF_SHELL "csh" 1859666Slinton 1869666Slinton public shell(s) 1879666Slinton String s; 1889666Slinton { 1899666Slinton extern String getenv(); 1909666Slinton String sh; 1919666Slinton 1929666Slinton if ((sh = getenv("SHELL")) == nil(String)) { 1939666Slinton sh = DEF_SHELL; 1949666Slinton } 1959666Slinton if (s != nil(String) and *s != '\0') { 1969666Slinton call(sh, stdin, stdout, "-c", s, 0); 1979666Slinton } else { 1989666Slinton call(sh, stdin, stdout, 0); 1999666Slinton } 2009666Slinton } 2019666Slinton 2029666Slinton /* 2039666Slinton * Wait for a process the right way. We wait for a particular 2049666Slinton * process and if any others come along in between, we remember them 2059666Slinton * in case they are eventually waited for. 2069666Slinton * 2079666Slinton * This routine is not very efficient when the number of processes 2089666Slinton * to be remembered is large. 20914394Slinton * 21014394Slinton * To deal with a kernel idiosyncrasy, we keep a list on the side 21114394Slinton * of "traced" processes, and do not notice them when waiting for 21214394Slinton * another process. 2139666Slinton */ 2149666Slinton 2159666Slinton typedef struct pidlist { 2169666Slinton int pid; 2179666Slinton int status; 2189666Slinton struct pidlist *next; 2199666Slinton } Pidlist; 2209666Slinton 22114394Slinton private Pidlist *pidlist, *ptrclist, *pfind(); 2229666Slinton 22314394Slinton public ptraced(pid) 22414394Slinton int pid; 22514394Slinton { 22614394Slinton Pidlist *p; 22714394Slinton 22814394Slinton p = alloc(1, Pidlist); 22914394Slinton p->pid = pid; 23014394Slinton p->next = ptrclist; 23114394Slinton ptrclist = p; 23214394Slinton } 23314394Slinton 23414394Slinton public unptraced(pid) 23514394Slinton int pid; 23614394Slinton { 23714394Slinton register Pidlist *p, *prev; 23814394Slinton 23914394Slinton prev = nil(Pidlist *); 24014394Slinton p = ptrclist; 24114394Slinton while (p != nil(Pidlist *) and p->pid != pid) { 24214394Slinton prev = p; 24314394Slinton p = p->next; 24414394Slinton } 24514394Slinton if (p != nil(Pidlist *)) { 24614394Slinton if (prev == nil(Pidlist *)) { 24714394Slinton ptrclist = p->next; 24814394Slinton } else { 24914394Slinton prev->next = p->next; 25014394Slinton } 25114394Slinton dispose(p); 25214394Slinton } 25314394Slinton } 25414394Slinton 25516611Ssam private boolean isptraced(pid) 25614394Slinton int pid; 25714394Slinton { 25814394Slinton register Pidlist *p; 25914394Slinton 26014394Slinton p = ptrclist; 26114394Slinton while (p != nil(Pidlist *) and p->pid != pid) { 26214394Slinton p = p->next; 26314394Slinton } 26416611Ssam return (boolean) (p != nil(Pidlist *)); 26514394Slinton } 26614394Slinton 2679666Slinton public pwait(pid, statusp) 2689666Slinton int pid, *statusp; 2699666Slinton { 27014393Slinton Pidlist *p; 27114393Slinton int pnum, status; 2729666Slinton 27314393Slinton p = pfind(pid); 27414393Slinton if (p != nil(Pidlist *)) { 27514393Slinton *statusp = p->status; 27614393Slinton dispose(p); 27714394Slinton } else { 27814394Slinton pnum = wait(&status); 27914394Slinton while (pnum != pid and pnum >= 0) { 28014394Slinton if (not isptraced(pnum)) { 28114394Slinton p = alloc(1, Pidlist); 28214394Slinton p->pid = pnum; 28314394Slinton p->status = status; 28414394Slinton p->next = pidlist; 28514394Slinton pidlist = p; 28614394Slinton } 28714394Slinton pnum = wait(&status); 2889666Slinton } 28914394Slinton if (pnum < 0) { 29014394Slinton p = pfind(pid); 29114394Slinton if (p == nil(Pidlist *)) { 29214394Slinton panic("pwait: pid %d not found", pid); 29314394Slinton } 29414394Slinton *statusp = p->status; 29514394Slinton dispose(p); 29614394Slinton } else { 29714394Slinton *statusp = status; 29814394Slinton } 29914393Slinton } 3009666Slinton } 3019666Slinton 3029666Slinton /* 3039666Slinton * Look for the given process id on the pidlist. 3049666Slinton * 3059666Slinton * Unlink it from list if found. 3069666Slinton */ 3079666Slinton 3089666Slinton private Pidlist *pfind(pid) 3099666Slinton int pid; 3109666Slinton { 3119666Slinton register Pidlist *p, *prev; 3129666Slinton 3139666Slinton prev = nil(Pidlist *); 3149666Slinton for (p = pidlist; p != nil(Pidlist *); p = p->next) { 3159666Slinton if (p->pid == pid) { 3169666Slinton break; 3179666Slinton } 3189666Slinton prev = p; 3199666Slinton } 3209666Slinton if (p != nil(Pidlist *)) { 3219666Slinton if (prev == nil(Pidlist *)) { 3229666Slinton pidlist = p->next; 3239666Slinton } else { 3249666Slinton prev->next = p->next; 3259666Slinton } 3269666Slinton } 3279666Slinton return p; 3289666Slinton } 3299666Slinton 3309666Slinton /* 3319666Slinton * System call error handler. 3329666Slinton * 3339666Slinton * The syserr routine is called when a system call is about to 3349666Slinton * set the c-bit to report an error. Certain errors are caught 3359666Slinton * and cause the process to print a message and immediately exit. 3369666Slinton */ 3379666Slinton 3389666Slinton extern int sys_nerr; 3399666Slinton extern char *sys_errlist[]; 3409666Slinton 3419666Slinton /* 3429666Slinton * Before calling syserr, the integer errno is set to contain the 3439666Slinton * number of the error. The routine "_mycerror" is a dummy which 3449666Slinton * is used to force the loader to get my version of cerror rather 3459666Slinton * than the usual one. 3469666Slinton */ 3479666Slinton 3489666Slinton extern int errno; 3499666Slinton extern _mycerror(); 3509666Slinton 3519666Slinton /* 35216611Ssam * Initialize error information, setting defaults for handling errors. 3539666Slinton */ 3549666Slinton 35516611Ssam private ERRINFO *errinfo; 3569666Slinton 35716611Ssam private initErrInfo () 35816611Ssam { 35916611Ssam integer i; 36016611Ssam 36116611Ssam errinfo = alloc(sys_nerr, ERRINFO); 36216611Ssam for (i = 0; i < sys_nerr; i++) { 36316611Ssam errinfo[i].func = ERR_CATCH; 36416611Ssam } 36516611Ssam errinfo[0].func = ERR_IGNORE; 36616611Ssam errinfo[EPERM].func = ERR_IGNORE; 36716611Ssam errinfo[ENOENT].func = ERR_IGNORE; 36816611Ssam errinfo[ESRCH].func = ERR_IGNORE; 36916611Ssam errinfo[EBADF].func = ERR_IGNORE; 37016611Ssam errinfo[ENOTTY].func = ERR_IGNORE; 37116611Ssam errinfo[EOPNOTSUPP].func = ERR_IGNORE; 37216611Ssam } 37316611Ssam 3749666Slinton public syserr() 3759666Slinton { 3769666Slinton ERRINFO *e; 3779666Slinton 37816611Ssam if (errno < 0 or errno > sys_nerr) { 37916611Ssam fatal("errno %d", errno); 38016611Ssam } else { 38116611Ssam if (errinfo == nil(ERRINFO *)) { 38216611Ssam initErrInfo(); 38316611Ssam } 38416611Ssam e = &(errinfo[errno]); 38516611Ssam if (e->func == ERR_CATCH) { 3869666Slinton fatal(sys_errlist[errno]); 38716611Ssam } else if (e->func != ERR_IGNORE) { 38816611Ssam (*e->func)(); 3899666Slinton } 3909666Slinton } 3919666Slinton } 3929666Slinton 3939666Slinton /* 39416611Ssam * Catcherrs' purpose is to initialize the errinfo table, get this module 39516611Ssam * loaded, and make sure my cerror is loaded (only applicable when this is 39616611Ssam * in a library). 3979666Slinton */ 3989666Slinton 3999666Slinton public catcherrs() 4009666Slinton { 4019666Slinton _mycerror(); 40216611Ssam initErrInfo(); 4039666Slinton } 4049666Slinton 4059666Slinton /* 40618221Slinton * Turn off the error catching mechanism completely by having all errors 40718221Slinton * ignored. This is most useful between a fork and an exec. 40818221Slinton */ 40918221Slinton 41018221Slinton public nocatcherrs() 41118221Slinton { 41218221Slinton integer i; 41318221Slinton 41418221Slinton for (i = 0; i < sys_nerr; i++) { 41518221Slinton errinfo[i].func = ERR_IGNORE; 41618221Slinton } 41718221Slinton } 41818221Slinton 41918221Slinton /* 4209666Slinton * Change the action on receipt of an error. 4219666Slinton */ 4229666Slinton 4239666Slinton public onsyserr(n, f) 4249666Slinton int n; 4259666Slinton INTFUNC *f; 4269666Slinton { 42716611Ssam if (errinfo == nil(ERRINFO *)) { 42816611Ssam initErrInfo(); 42916611Ssam } 4309666Slinton errinfo[n].func = f; 4319666Slinton } 4329666Slinton 4339666Slinton /* 4349666Slinton * Print the message associated with the given signal. 4359666Slinton * Like a "perror" for signals. 4369666Slinton */ 4379666Slinton 4389666Slinton public int sys_nsig = NSIG; 4399666Slinton 44016611Ssam public psignal(s, n) 4419666Slinton String s; 44216611Ssam integer n; 4439666Slinton { 44416611Ssam String msg; 44516611Ssam integer len; 44618538Sralph extern String sys_siglist[]; 4479666Slinton 44816611Ssam if (n >= 0 and n < sys_nsig) { 44916611Ssam msg = sys_siglist[n]; 45016611Ssam } else { 45116611Ssam msg = "Unknown signal"; 4529666Slinton } 45316611Ssam len = strlen(s); 45416611Ssam if (len > 0) { 45516611Ssam write(2, s, len); 4569666Slinton write(2, ": ", 2); 4579666Slinton } 45816611Ssam write(2, msg, strlen(msg)); 4599666Slinton write(2, "\n", 1); 4609666Slinton } 4619666Slinton 4629666Slinton /* 4639666Slinton * Standard error handling routines. 4649666Slinton */ 4659666Slinton 4669666Slinton private short nerrs; 4679666Slinton private short nwarnings; 4689666Slinton 4699666Slinton /* 4709666Slinton * Main driver of error message reporting. 4719666Slinton */ 4729666Slinton 4739666Slinton /* VARARGS2 */ 4749666Slinton private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) 4759666Slinton String errname; 47616611Ssam boolean shouldquit; 4779666Slinton String s; 4789666Slinton { 4799666Slinton fflush(stdout); 4809666Slinton if (shouldquit and cmdname != nil(String)) { 4819666Slinton fprintf(stderr, "%s: ", cmdname); 4829666Slinton } 4839666Slinton if (errfilename != nil(Filename)) { 4849666Slinton fprintf(stderr, "%s: ", errfilename); 4859666Slinton } 4869666Slinton if (errlineno > 0) { 4879666Slinton fprintf(stderr, "%d: ", errlineno); 4889666Slinton } 4899666Slinton if (errname != nil(String)) { 4909666Slinton fprintf(stderr, "%s: ", errname); 4919666Slinton } 4929666Slinton fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 4939666Slinton putc('\n', stderr); 4949666Slinton if (shouldquit) { 4959666Slinton quit(1); 4969666Slinton } 4979666Slinton } 4989666Slinton 4999666Slinton /* 5009666Slinton * For when printf isn't sufficient for printing the error message ... 5019666Slinton */ 5029666Slinton 5039666Slinton public beginerrmsg() 5049666Slinton { 5059666Slinton fflush(stdout); 5069666Slinton if (errfilename != nil(String)) { 5079666Slinton fprintf(stderr, "%s: ", errfilename); 5089666Slinton } 5099666Slinton if (errlineno > 0) { 5109666Slinton fprintf(stderr, "%d: ", errlineno); 5119666Slinton } 5129666Slinton } 5139666Slinton 5149666Slinton public enderrmsg() 5159666Slinton { 5169666Slinton putc('\n', stderr); 5179666Slinton erecover(); 5189666Slinton } 5199666Slinton 5209666Slinton /* 5219666Slinton * The messages are listed in increasing order of seriousness. 5229666Slinton * 5239666Slinton * First are warnings. 5249666Slinton */ 5259666Slinton 5269666Slinton /* VARARGS1 */ 5279666Slinton public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 5289666Slinton String s; 5299666Slinton { 5309666Slinton nwarnings++; 5319666Slinton errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 5329666Slinton } 5339666Slinton 5349666Slinton /* 5359666Slinton * Errors are a little worse, they mean something is wrong, 5369666Slinton * but not so bad that processing can't continue. 5379666Slinton * 5389666Slinton * The routine "erecover" is called to recover from the error, 5399666Slinton * a default routine is provided that does nothing. 5409666Slinton */ 5419666Slinton 5429666Slinton /* VARARGS1 */ 5439666Slinton public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 5449666Slinton String s; 5459666Slinton { 5469666Slinton extern erecover(); 5479666Slinton 5489666Slinton nerrs++; 5499666Slinton errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 5509666Slinton erecover(); 5519666Slinton } 5529666Slinton 5539666Slinton /* 5549666Slinton * Non-recoverable user error. 5559666Slinton */ 5569666Slinton 5579666Slinton /* VARARGS1 */ 5589666Slinton public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 5599666Slinton String s; 5609666Slinton { 5619666Slinton errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 5629666Slinton } 5639666Slinton 5649666Slinton /* 5659666Slinton * Panics indicate an internal program error. 5669666Slinton */ 5679666Slinton 5689666Slinton /* VARARGS1 */ 5699666Slinton public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 5709666Slinton String s; 5719666Slinton { 5729666Slinton errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 5739666Slinton } 5749666Slinton 5759666Slinton short numerrors() 5769666Slinton { 5779666Slinton short r; 5789666Slinton 5799666Slinton r = nerrs; 5809666Slinton nerrs = 0; 5819666Slinton return r; 5829666Slinton } 5839666Slinton 5849666Slinton short numwarnings() 5859666Slinton { 5869666Slinton short r; 5879666Slinton 5889666Slinton r = nwarnings; 5899666Slinton nwarnings = 0; 5909666Slinton return r; 5919666Slinton } 5929666Slinton 5939666Slinton /* 5949666Slinton * Recover from an error. 5959666Slinton * 5969666Slinton * This is the default routine which we aren't using since we have our own. 5979666Slinton * 5989666Slinton public erecover() 5999666Slinton { 6009666Slinton } 6019666Slinton * 6029666Slinton */ 6039666Slinton 6049666Slinton /* 6059666Slinton * Default way to quit from a program is just to exit. 6069666Slinton * 6079666Slinton public quit(r) 6089666Slinton int r; 6099666Slinton { 6109666Slinton exit(r); 6119666Slinton } 6129666Slinton * 6139666Slinton */ 6149666Slinton 6159666Slinton /* 6169666Slinton * Compare n-byte areas pointed to by s1 and s2 6179666Slinton * if n is 0 then compare up until one has a null byte. 6189666Slinton */ 6199666Slinton 6209666Slinton public int cmp(s1, s2, n) 6219666Slinton register char *s1, *s2; 6229666Slinton register unsigned int n; 6239666Slinton { 6249666Slinton if (s1 == nil(char *) || s2 == nil(char *)) { 6259666Slinton panic("cmp: nil pointer"); 6269666Slinton } 6279666Slinton if (n == 0) { 6289666Slinton while (*s1 == *s2++) { 6299666Slinton if (*s1++ == '\0') { 6309666Slinton return(0); 6319666Slinton } 6329666Slinton } 6339666Slinton return(*s1 - *(s2-1)); 6349666Slinton } else { 6359666Slinton for (; n != 0; n--) { 6369666Slinton if (*s1++ != *s2++) { 6379666Slinton return(*(s1-1) - *(s2-1)); 6389666Slinton } 6399666Slinton } 6409666Slinton return(0); 6419666Slinton } 6429666Slinton } 6439666Slinton 6449666Slinton /* 6459666Slinton * Move n bytes from src to dest. 6469666Slinton * If n is 0 move until a null is found. 6479666Slinton */ 6489666Slinton 6499666Slinton public mov(src, dest, n) 6509666Slinton register char *src, *dest; 6519666Slinton register unsigned int n; 6529666Slinton { 6539666Slinton if (src == nil(char *)) 6549666Slinton panic("mov: nil source"); 6559666Slinton if (dest == nil(char *)) 6569666Slinton panic("mov: nil destination"); 6579666Slinton if (n != 0) { 6589666Slinton for (; n != 0; n--) { 6599666Slinton *dest++ = *src++; 6609666Slinton } 6619666Slinton } else { 6629666Slinton while ((*dest++ = *src++) != '\0'); 6639666Slinton } 6649666Slinton } 665