static const char COPYRIGHT[] = "wyrm-sieve.dna - Copyright (C) 2004 SM Ryan. All rights reserved.";
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "wyrm-sieve.h"
#include "wyrm-io.h"
static char *number(int n) {
char b[50],*c;
sprintf(b,"%d",n);
c = nheap(strlen(b)+1,char);
strcpy(c,b);
return c;
}
static char *increment(char *n) {
char *digit = n+strlen(n); int carry = 1;
while (carry) {
if (digit==n) {
char *n1 = nheap(1+strlen(n)+1,char);
*n1 = '1'; strcpy(n1+1,n);
carry = 0;
dispose(n); n = n1;
}else if (*--digit=='9') {
*digit = '0';
carry = 1;
}else {
*digit += 1;
carry = 0;
}
}
return n;
}
static int decrement(Intr intr,char *n) {
char *digit = n+strlen(n); int borrow = 1;
int zero = 1;
while (borrow) {
if (digit==n) {
rprintf(intr,"Attempted to decrement zero."); return TCL_ERROR;
}else if (*--digit=='0') {
*digit = '9';
borrow = 1;
}else {
*digit -= 1;
borrow = 0;
}
}
while (*n && zero) zero = *n++=='0';
return zero ? TCL_BREAK : TCL_CONTINUE;
}
static char *readline(Tcl_Channel A) {
char *buffer = 0; int n = 0,m = 0,ch;
for (;;) {
int ch = cgetc(A);
switch (ch) {
case EOF: case ERR: if (buffer==0) return 0;
case '\n': ch = 0;
}
if (n+1>=m) {m = 2*(n+1); buffer = reheap(m,char,buffer);}
buffer[n++] = ch;
if (!ch) break;
}
return buffer;
}
static void writeline(Tcl_Channel B,char *n) {
cputs(B,n); cputc(B,'\n');
}
static int sieveStep(Intr intr,Tcl_Channel A,Tcl_Channel B) {
int isprime = 1;
while (1) {
char *prime = readline(A);
char *counter = readline(A);
if (!prime) break;
if (prime && !counter) return -1;
writeline(B,prime);
switch (decrement(intr,counter)) {
case TCL_BREAK:
writeline(B,prime);
isprime = 0;
break;
case TCL_CONTINUE:
writeline(B,counter);
break;
default:
dispose(prime); dispose(counter);
return TCL_ERROR;
}
dispose(prime); dispose(counter);
}
return isprime ? TCL_BREAK : TCL_CONTINUE;
}
static void addPrime(char *current,Tcl_Channel PR,Tcl_Channel B) {
if (PR) writeline(PR,current);
writeline(B,current);
writeline(B,current);
}
static int sieveCommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
chars limit = 0; bool restart = false; int rc;
Tcl_Channel A = 0,B = 0,PR = chstdout;
Obj tmp = 0;
Obj Apath = incr(Tcl_NewStringObj("A.sve",-1));
Obj Bpath = incr(Tcl_NewStringObj("B.sve",-1));
chars current = 0;
while (++P,--N>0) {
chars p = Tcl_GetString(*P);
if (isdigit(p[0])) {
limit = p;
limit = strcpy(nheap(strlen(limit)+1,char),limit);
}else if (strbegins("-t",p) && N>0) {
tmp = P[1]; P++,N--;
}else if (strbegins("-o",p) && N>0) {
chars pr = Tcl_GetString(P[1]); P++,N--;
if (streq(pr,"none")) {
PR = 0;
}else {
int mood; PR = Tcl_GetChannel(intr,pr,&mood);
if (!PR) {
rprintf(intr,"not a channel: %{y}s",pr);
goto error;
}
if (!(TCL_WRITABLE&mood)) {
rprintf(intr,"not a writable channel: %{y}s",pr);
goto error;
}
}
}else {
rprintf(intr,"unrecognised option: %s",p);
return TCL_ERROR;
}
}
if (tmp) {
Obj L,E[2];
E[0] = tmp; E[1] = Apath; L = incr(Tcl_NewListObj(2,E)); decr(Apath);
Apath = incr(Tcl_FSJoinPath(L,-1)); decr(L);
E[0] = tmp; E[1] = Bpath; L = incr(Tcl_NewListObj(2,E)); decr(Bpath);
Bpath = incr(Tcl_FSJoinPath(L,-1)); decr(L);
}
if (Tcl_FSAccess(Apath,0)<0 ||Tcl_FSAccess(Bpath,0)==0) {
Tcl_FSRenameFile(Bpath,Apath);
}
A = Tcl_FSOpenFileChannel(intr,Apath,"r",0);
if (A) {
current = readline(A);
if (current && limit) {
chars t = strcpy(nheap(strlen(limit)+1,char),limit);
current = increment(current);
for (restart=true; restart; ) {
char *prime = readline(A);
char *counter = readline(A);
if (!prime) break;
dispose(prime);
if (prime && !counter) restart = false;
dispose(counter);
switch (decrement(intr,t)) {
case TCL_BREAK: restart = false;
case TCL_CONTINUE: break;
case TCL_ERROR: dispose(t);
goto error;
}
}
if (restart) {dispose(limit); limit = t;}
else {dispose(t);}
}else {
restart = true;
}
if (!restart) dispose(current);
Tcl_Close(intr,A); A = 0;
}
if (!restart) {
Tcl_ResetResult(intr);
A = Tcl_FSOpenFileChannel(intr,Apath,"w",0600);
if (!A) {
dispose(current); dispose(limit);
return TCL_ERROR;
}
current = number(2);
writeline(A,current);
Tcl_Close(intr,A); A = 0;
}
while (1) {
A = Tcl_FSOpenFileChannel(intr,Apath,"r",0); if (!A) goto error;
B = Tcl_FSOpenFileChannel(intr,Bpath,"w",0600); if (!B) goto error;
dispose(readline(A)); writeline(B,current);
switch (sieveStep(intr,A,B)) {
case TCL_BREAK:
Tcl_SetResult(intr,current,TCL_VOLATILE);
addPrime(current,PR,B);
if (limit) {
switch (decrement(intr,limit)) {
case TCL_BREAK: goto okay;
case TCL_ERROR:
goto error;
}
}
break;
case TCL_ERROR:
goto error;
}
current = increment(current);
Tcl_Close(intr,A); A = 0;
Tcl_Close(intr,B); B = 0;
if (Tcl_FSDeleteFile(Apath)<0) {
rprintf(intr,"failed to delete %{y}s",Apath);
goto error;
}
if (Tcl_FSRenameFile(Bpath,Apath)<0) {
rprintf(intr,"failed to rename %{y}s to %{y}s",Bpath,Apath);
goto error;
}
}
okay:
rc = TCL_OK;
exit:
if (A) Tcl_Close(intr,A);
if (B) Tcl_Close(intr,B);
dispose(current); dispose(limit);
decr(Apath); decr(Bpath);
return rc;
error:
rc = TCL_ERROR;
goto exit;
}
int Wyrmsieve_Init(Intr intr) {
Tcl_PkgProvide(intr,"wyrmsieve",VERSION);
Tcl_PkgRequire(intr,"wyrmwif","1",0);
Tcl_CreateObjCommand(intr,"::wyrm::sieve",sieveCommand,0,0);
return Tcl_VarEval(intr,
"namespace eval ::wyrm {namespace export sieve}\n",
0);
}
int Wyrmsieve_SafeInit(Intr intr) {
return rprintf(intr,"%!package not available in a safe interpretter",TCL_ERROR);
}