DNA.
wyrm-unix
Version.
1.0.9
System-requirement.
platform=unix
Namespace.
::wyrm::unix, ::wyrm::wif::unix
Language.
c
Manpage.
unix-fork (1WY)
Manpage.
unix-exit (1WY)
Manpage.
unix-wait (1WY)
Manpage.
unix-pid (1WY)
Manpage.
unix-nice (1WY)
Manpage.
unix-exec (1WY)
Manpage.
unix-passwd (1WY)
Manpage.
unix-group (1WY)
Manpage.
unix-grent (1WY)
Manpage.
unix-pipe (1WY)
Manpage.
unix-dup (1WY)
Manpage.
unix-dev-null (1WY)
Manpage.
unix-reader (1WY)
Manpage.
unix-writer (1WY)
Manpage.
unix-until (1WY)
Manpage.
unix-lock (1WY)
Manpage.
unix-truncate (1WY)
Manpage.
unix-touch (1WY)
Manpage.
unix-mountpoint (1WY)
Manpage.
unix-signal (1WY)
Manpage.
unix-sigblock (1WY)
Manpage.
unix-kill (1WY)
Manpage.
unix-daemon (1WY)
Manpage.
unix-residentevil (1WY)
Manpage.
unix-run (1WY)
Testbase.
Test Script
Test Report
Import.
Interface.
wyrmwif
wyrm-io
Package.
wyrmwif
Export.
Implementation.
wyrm-unix.c
Package.
wyrmunix.dylib
System.
wyrm-unix.sys
wyrm-unix.sys
wyrm-unix.sys

Unix extensions for Tcl

Sections.
Process Control
User and Group
Files
Signal Processing
Additional Protocols
Package Initialisation
Make.
Package.
if {[string equal $::tcl_platform(platform) unix]} {
  switch $::tcl_platform(os) {
    Darwin {set setpgrp setpgrp(0,0)}
    Linux {set setpgrp setpgrp()}
    IRIX64 {set setpgrp setpgrp()}
    default {error "do not how to setpgrp for $tcl_platform(os)"}
  }
  compile -cc -ld '-DSETPGRP=$setpgrp' -o [export package] [
    export implementation
  ] -- -list [
    import interface
  ]
} else {
  puts stderr "wyrm-unix is for Unix only."
}
Script.
rule clean :: {} "
  -rm $test/wyrm-unix.TESTING
"
   
top

1 :: Provide unix capabilities as Tcl commands that are not otherwise available. This package is specify for Unix and will not compile for other systems.

Copyright (C) 2004 SM Ryan

Wyrmwif Tcl extensions. For non-profit uses only, provided this copyright is preserved on all copies, this work may be freely copied, modified, redistributed, compiled, and incorporated in other works. This work is distributed with no warranty of any kind; no author or distributor accepts any responsibility for the consequences of using it, or for whether it serves any particular purpose or works at all, unless he or she says so in writing.

   
   
top

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

#include "wyrm-io.h"
#include "wyrm-unix.sys"

#define objeq(string,object) streq(string,Tcl_GetString((object)))

		
   
   

Process Control

   
top

static void flushAll(Intr intr) {
	int N; Obj L = 0,*P;
	if (Tcl_GetChannelNames(intr)==TCL_OK
			&& Tcl_ListObjGetElements(0,(L=incr(Tcl_GetObjResult(intr))),&N,&P)==TCL_OK
	) {
		while (N-->0) {
			int mood; Tcl_Channel channel = Tcl_GetChannel(intr,Tcl_GetString(*P),&mood);
			if (channel && (mood&TCL_WRITABLE)) Tcl_Flush(channel);
		}
	}
	decr(L);
}

static int forkcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	flushAll(intr);
	if (N!=1 && N!=3) {
		Tcl_WrongNumArgs(intr,1,P,"[parent-script child-script]");
		return TCL_ERROR;
	}
	int child = fork();
	if (child<0) {
		Tcl_SetErrno(errno);
		rprintf(intr,"fork failed: %s",Tcl_PosixError(intr));
		return TCL_ERROR;
	}else {
		if (N==3) {
			Obj cn = incr(Tcl_NewIntObj(child));
			Tcl_SetVar2Ex(intr,"child",0,cn,0);
			decr(cn);
			if (Tcl_EvalObjEx(intr,child?P[1]:P[2],0)!=TCL_OK) return TCL_ERROR;
		}
		Tcl_SetObjResult(intr,Tcl_NewIntObj(child));
		return TCL_OK;
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::fork",forkcommand,0,0);

		
 
section    top
 
section    top

static int exitcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	int ec = 0;
	if (N>1) Tcl_GetIntFromObj(0,P[1],&ec);
	_exit(ec);
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::exit",exitcommand,0,0);

		
 
section    top
 
section    top

static Tcl_ThreadDataKey waitKey;

static void wakeup(ClientData clientData) {
	bool *waiting = Tcl_GetThreadData(&waitKey,sizeof(bool));
	*waiting = false;
}

static void waitForSumpin(void) {
	bool *waiting = Tcl_GetThreadData(&waitKey,sizeof(bool));
	if (!Tcl_DoOneEvent(TCL_DONT_WAIT)==0) {
		if (*waiting) {
			Tcl_DoOneEvent(0);
		}else {
			Tcl_TimerToken token = Tcl_CreateTimerHandler(250,wakeup,0);
			Tcl_DoOneEvent(0);
			if (*waiting) Tcl_DeleteTimerHandler(token);
		}
	}
}

static int waitcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	int pid,status,rc; Obj E[3];
	if (N>3) {
		Tcl_WrongNumArgs(intr,1,P,"[child]");
		return TCL_ERROR;
	}
	if (N==1) pid = -1;
	else if (N==2 && objeq("any",P[1])) pid = -1;
	else if (N==2 && objeq("pgrp",P[1])) pid = 0;
	else if (N==2 && Tcl_GetIntFromObj(intr,P[1],&pid)==TCL_OK) ;
	else if (N==3 && objeq("pgrp",P[1]) && Tcl_GetIntFromObj(intr,P[2],&pid)==TCL_OK) {
		pid = -pid;
	}else {
		return rprintf(intr,"%!unparseable process identifier: %{y}s",TCL_ERROR,P[1]);
	}
	while ((rc=waitpid(pid,&status,WNOHANG))==0) waitForSumpin();
	if (rc<0) {
		Tcl_SetErrno(errno);
		rprintf(intr,"wait failed: %s",Tcl_PosixError(intr));
		return TCL_ERROR;
	}else if (WIFEXITED(status)) {
		E[1] = Tcl_NewStringObj("exit",-1);
		E[2] = Tcl_NewIntObj(WEXITSTATUS(status));
	}else if (WIFSIGNALED(status)) {
		E[1] = Tcl_NewStringObj("signal",-1);
		E[2] = Tcl_NewIntObj(WTERMSIG(status));
	}else if (WIFSTOPPED(status)) {
		E[1] = Tcl_NewStringObj("stop",-1);
		E[2] = Tcl_NewIntObj(WTERMSIG(status));
	}else {
		E[1] = Tcl_NewStringObj("??",-1);
		E[2] = Tcl_NewIntObj(status);
	}
	E[0] = Tcl_NewIntObj(rc);
	Tcl_SetObjResult(intr,Tcl_NewListObj(3,E));
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::wait",waitcommand,0,0);

		
 
section    top
 
section    top
		
static int pidcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	if (N!=1) {
		Tcl_WrongNumArgs(intr,1,P,0);
		return TCL_ERROR;
	}
	Tcl_SetObjResult(intr,Tcl_NewIntObj(getpid()));
	return TCL_OK;
}

static int ppidcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	if (N!=1) {
		Tcl_WrongNumArgs(intr,1,P,0);
		return TCL_ERROR;
	}
	Tcl_SetObjResult(intr,Tcl_NewIntObj(getppid()));
	return TCL_OK;
}

static int pgrpcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	if (N==1) {
		Tcl_SetObjResult(intr,Tcl_NewIntObj(getpgrp()));
		return TCL_OK;
	}else if (N==2) {
		int pid,pgrp;
		if (Tcl_GetIntFromObj(intr,P[1],&pid)!=TCL_OK) return TCL_ERROR;
		if ((pgrp=getpgid(pid))<0) {
			Tcl_SetErrno(errno);
			rprintf(intr,"pgid failed: %s",Tcl_PosixError(intr));
			return TCL_ERROR;
		}
		Tcl_SetObjResult(intr,Tcl_NewIntObj(pgrp));
		return TCL_OK;
	}else {
		Tcl_WrongNumArgs(intr,1,P,"[pid]");
		return TCL_ERROR;
	}
}

static int sidcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	if (N==1) {
		Tcl_SetObjResult(intr,Tcl_NewIntObj(getsid(0)));
		return TCL_OK;
	}else if (N==2) {
		int pid,sid;
		if (Tcl_GetIntFromObj(intr,P[1],&pid)!=TCL_OK) return TCL_ERROR;
		if ((sid=getsid(pid))<0) {
			Tcl_SetErrno(errno);
			rprintf(intr,"sid failed: %s",Tcl_PosixError(intr));
			return TCL_ERROR;
		}
		Tcl_SetObjResult(intr,Tcl_NewIntObj(sid));
		return TCL_OK;
	}else {
		Tcl_WrongNumArgs(intr,1,P,"[pid]");
		return TCL_ERROR;
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::pid",pidcommand,0,0);
Tcl_CreateObjCommand(intr,"::wyrm::unix::ppid",ppidcommand,0,0);
Tcl_CreateObjCommand(intr,"::wyrm::unix::pgrp",pgrpcommand,0,0);
Tcl_CreateObjCommand(intr,"::wyrm::unix::sid",sidcommand,0,0);

		
 
section    top
 
section    top
		
static int nicecommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	if (N==1) {
		Tcl_SetObjResult(intr,Tcl_NewIntObj(getpriority(PRIO_PROCESS,0)));
		return TCL_OK;
	}else if (N==2) {
		int prio;
		if (Tcl_GetIntFromObj(intr,P[1],&prio)!=TCL_OK) return TCL_ERROR;
		if (setpriority(PRIO_PROCESS,0,prio)) {
			Tcl_SetErrno(errno);
			rprintf(intr,"nice failed: %s",Tcl_PosixError(intr));
			return TCL_ERROR;
		}
		Tcl_ResetResult(intr);
		return TCL_OK;
	}else {
		Tcl_WrongNumArgs(intr,1,P,"[priority]");
		return TCL_ERROR;
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::nice",nicecommand,0,0);

		
 
section    top
 
section    top
		
static int execcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	Obj *P0 = P,env = 0,E = 0; int n; Obj *e;
	char *exec,**envp = 0,**argv = 0; int i;
	if (N>=5 && objeq("-env",P[1])) {env = P[2]; N -= 2; P += 2;}
	if (N>=4 && objeq("--",P[1])) {N -= 1; P += 1;}
	if (N<3) {
		Tcl_WrongNumArgs(intr,1,P0,"[environment] executable arguments...");
		return TCL_ERROR;
	}
	if (Tcl_VarEval(intr,"array get ::env",0)!=TCL_OK) E = incr(Tcl_NewObj());
	else if (env) E = incr(Tcl_DuplicateObj(Tcl_GetObjResult(intr)));
	else E = incr(Tcl_GetObjResult(intr));
	if (env && Tcl_ListObjAppendList(intr,E,env)!=TCL_OK) goto error;
	if (Tcl_ListObjGetElements(intr,E,&n,&e)!=TCL_OK) goto error;
	if (n%2!=0) {rprintf(intr,"environmental definitions are not name/value pairs"); goto error;}
	envp = nheap(n/2+1,char*); zero(n/2+1,char*,envp);
	argv = nheap(N-1,char*);
	exec = Tcl_GetString(P[1]); N -= 2; P += 2;
	for (i=0; N>0; i++,N--,P++) argv[i] = Tcl_GetString(*P);
	argv[i] = 0;
	for (i=0; n>0; i++,n-=2,e+=2) {
		char *var = Tcl_GetString(e[0]);
		char *val = Tcl_GetString(e[1]);
		envp[i] = nheap(strlen(var)+strlen(val)+2,char);
		sprintf(envp[i],"%s=%s",var,val);
	}
	envp[i] = 0;
	if (execve(exec,argv,envp)<0) {
		Tcl_SetErrno(errno);
		rprintf(intr,"exec failed: %s",Tcl_PosixError(intr));
	}else {
		rprintf(intr,"execve returned but indicated no error");
	}
error:
	dispose(argv);
	for (i=0; envp[i]; i++) dispose(envp[i]);
	dispose(envp);
	decr(E);
	return TCL_ERROR;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::exec",execcommand,0,0);

		
 
section    top
   
   

User and Group

   
top
		
static int usercommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	int o = 0; bool eff = false; int uid; struct passwd *pwd = 0;
	if (N==1) ;
	else if (objeq("-effective",P[1])) {eff = true; o = 1;}
	else if (objeq("--",P[1])) {eff = false; o = 1;}
	if (N-o==1) {
		uid = eff ? geteuid() : getuid();
	}else if (N-o==2) {
		if (Tcl_GetIntFromObj(intr,P[1+o],&uid)!=TCL_OK) {
			pwd = getpwnam(Tcl_GetString(P[1+o]));
			if (pwd) uid = pwd->pw_uid;
			else return rprintf(intr,"%!unknown user: %{y}s",TCL_ERROR,P[1+o]);
		}
		if ((eff ? seteuid(uid) : setuid(uid))<0) {
			Tcl_SetErrno(errno);
			rprintf(intr,"setuid failed: %s",Tcl_PosixError(intr));
			return TCL_ERROR;
		}
	}else {
		Tcl_WrongNumArgs(intr,1+o,P,"[uid|username]");
		return TCL_ERROR;
	}
	if (!pwd) pwd = getpwuid(uid);
	if (pwd) Tcl_SetObjResult(intr,Tcl_NewStringObj(pwd->pw_name,-1));
	else Tcl_SetObjResult(intr,Tcl_NewIntObj(uid));
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::user",usercommand,0,0);

		
 
section    top
 
section    top

static void showpasswd(Tcl_DString *S,struct passwd *pwd) {
	char B[50];
	Tcl_DStringAppendElement(S,"username"); Tcl_DStringAppendElement(S,pwd->pw_name);
	Tcl_DStringAppendElement(S,"password"); Tcl_DStringAppendElement(S,pwd->pw_passwd);
	sprintf(B,"%d",pwd->pw_uid); Tcl_DStringAppendElement(S,"uid"); Tcl_DStringAppendElement(S,B);
	sprintf(B,"%d",pwd->pw_gid); Tcl_DStringAppendElement(S,"gid"); Tcl_DStringAppendElement(S,B);
	Tcl_DStringAppendElement(S,"comments"); Tcl_DStringAppendElement(S,pwd->pw_gecos);
	Tcl_DStringAppendElement(S,"home"); Tcl_DStringAppendElement(S,pwd->pw_dir);
	Tcl_DStringAppendElement(S,"shell"); Tcl_DStringAppendElement(S,pwd->pw_shell);
		Tcl_DStringAppendElement(S,B);
}
		
static int passwdcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	Tcl_DString S;
	Tcl_DStringInit(&S);
	if (N==2 && objeq("-all",P[1])) {
		struct passwd *pwd;
		endpwent();
		while ((pwd=getpwent())) {
			Tcl_DStringAppendElement(&S,pwd->pw_name);
			Tcl_DStringStartSublist(&S);
			showpasswd(&S,pwd);
			Tcl_DStringEndSublist(&S);
		}
		endpwent();
	}else if (N>2) {
		Tcl_WrongNumArgs(intr,1,P,"[-all|uid|username]");
		return TCL_ERROR;
	}else {
		struct passwd *pwd;int uid;
		if (N==1) pwd = getpwuid(getuid());
		else if (Tcl_GetIntFromObj(intr,P[1],&uid)!=TCL_OK || !(pwd=getpwuid(uid))) {
			pwd = getpwnam(Tcl_GetString(P[1]));
		}
		if (!pwd) return rprintf(intr,"%!unknown user: %{y}s",TCL_ERROR,P[1]);
		showpasswd(&S,pwd);
	}
	Tcl_DStringResult(intr,&S);
	Tcl_DStringFree(&S);
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::passwd",passwdcommand,0,0);

		
 
section    top
 
section    top

static int groupcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	int o = 0; bool eff = false; int gid; struct group *grp = 0;
	if (N==1) ;
	else if (objeq("-effective",P[1])) {eff = true; o = 1;}
	else if (objeq("--",P[1])) {eff = false; o = 1;}
	if (N-o==1) {
		gid = eff ? getegid() : getgid();
	}else if (N-o==2) {
		if (Tcl_GetIntFromObj(intr,P[1+o],&gid)!=TCL_OK) {
			grp = getgrnam(Tcl_GetString(P[1+o]));
			if (grp) gid = grp->gr_gid;
			else return rprintf(intr,"%!unknown group: %{y}s",TCL_ERROR,P[1+o]);
		}
		if ((eff ? setegid(gid) : setgid(gid))<0) {
			Tcl_SetErrno(errno);
			rprintf(intr,"setgid failed: %s",Tcl_PosixError(intr));
			return TCL_ERROR;
		}
	}else {
		Tcl_WrongNumArgs(intr,1+o,P,"[gid|groupname]");
		return TCL_ERROR;
	}
	if (!grp) grp = getgrgid(gid);
	if (grp) Tcl_SetObjResult(intr,Tcl_NewStringObj(grp->gr_name,-1));
	else Tcl_SetObjResult(intr,Tcl_NewIntObj(gid));
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::group",groupcommand,0,0);

		
 
section    top
 
section    top

static void showgrent(Tcl_DString *S,struct group *grp) {
	char B[50],**m;
	Tcl_DStringAppendElement(S,"group"); Tcl_DStringAppendElement(S,grp->gr_name);
	Tcl_DStringAppendElement(S,"password"); Tcl_DStringAppendElement(S,grp->gr_passwd);
	sprintf(B,"%d",grp->gr_gid); Tcl_DStringAppendElement(S,"gid"); Tcl_DStringAppendElement(S,B);
	Tcl_DStringAppendElement(S,"members"); Tcl_DStringStartSublist(S);
	for (m=grp->gr_mem; *m; m++) Tcl_DStringAppendElement(S,*m);
	Tcl_DStringEndSublist(S);
}
		
static int grentcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	Tcl_DString S;
	Tcl_DStringInit(&S);
	if (N==2 && objeq("-all",P[1])) {
		struct group *grp;
		endgrent();
		while ((grp=getgrent())) {
			Tcl_DStringAppendElement(&S,grp->gr_name);
			Tcl_DStringStartSublist(&S);
			showgrent(&S,grp);
			Tcl_DStringEndSublist(&S);
		}
		endgrent();
	}else if (N>2) {
		Tcl_WrongNumArgs(intr,1,P,"[-all|gid|groupname]");
		return TCL_ERROR;
	}else {
		struct group *grp; int gid;
		if (N==1) grp = getgrgid(getgid());
		else if (Tcl_GetIntFromObj(intr,P[1],&gid)!=TCL_OK || !(grp=getgrgid(gid))) {
			grp = getgrnam(Tcl_GetString(P[1]));
		}
		if (!grp) return rprintf(intr,"%!unknown group: %{y}s",TCL_ERROR,P[1]);
		showgrent(&S,grp);
	}
	Tcl_DStringResult(intr,&S);
	Tcl_DStringFree(&S);
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::grent",grentcommand,0,0);

		
 
section    top
   
   

Files

   
top
		
static int pipecommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	int p[2];
	if (N!=1) {
		Tcl_WrongNumArgs(intr,1,P,0);
		return TCL_ERROR;
	}
	if (pipe(p)<0) {
		Tcl_SetErrno(errno);
		rprintf(intr,"fork failed: %s",Tcl_PosixError(intr));
		return TCL_ERROR;
	}else {
		Tcl_Channel reader = Tcl_MakeFileChannel((ClientData)p[0],TCL_READABLE);
		Tcl_Channel writer = Tcl_MakeFileChannel((ClientData)p[1],TCL_WRITABLE);
		Obj E[2];
		Tcl_RegisterChannel(intr,reader);
		Tcl_RegisterChannel(intr,writer);
		E[0] = Tcl_NewStringObj(Tcl_GetChannelName(reader),-1);
		E[1] = Tcl_NewStringObj(Tcl_GetChannelName(writer),-1);
		Tcl_SetObjResult(intr,Tcl_NewListObj(2,E));
		return TCL_OK;
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::pipe",pipecommand,0,0);

		
 
section    top
 
section    top
		
static int dupcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	Tcl_Channel duplicated,replaced; int mood1,mood2; int p,q,r;
	if (N!=2 && N!=3) {
		Tcl_WrongNumArgs(intr,1,P,"duplicated-channel [replaced-channel]");
		return TCL_ERROR;
	}
	duplicated = Tcl_GetChannel(intr,Tcl_GetString(P[1]),&mood1);
	if (!duplicated) return rprintf(intr,"%!not a channel: %{y}s",TCL_ERROR,P[1]);
	if (Tcl_GetChannelHandle(duplicated,TCL_READABLE,(ClientData*)(&q))==TCL_ERROR
			&& Tcl_GetChannelHandle(duplicated,TCL_WRITABLE,(ClientData*)(&q))==TCL_ERROR)
		return rprintf(intr,"%!could not get the file descriptor: %{y}s",TCL_ERROR,P[1]);
	if (N==2) {
		p = dup(q);
	}else {
		replaced = Tcl_GetChannel(intr,Tcl_GetString(P[2]),&mood2);
		if (!replaced) return rprintf(intr,"%!not a channel: %{y}s",TCL_ERROR,P[1]);
		if (Tcl_GetChannelHandle(replaced,TCL_READABLE,(ClientData*)(&r))==TCL_ERROR
				&& Tcl_GetChannelHandle(replaced,TCL_WRITABLE,(ClientData*)(&r))==TCL_ERROR)
			return rprintf(intr,"%!could not get the file descriptor: %{y}s",TCL_ERROR,P[2]);
		p = dup2(q,r);
	}
	if (p<0) {
		Tcl_SetErrno(errno);
		rprintf(intr,"dup failed: %s",Tcl_PosixError(intr));
		return TCL_ERROR;
	}else if (N==2) {
		duplicated = Tcl_MakeFileChannel((ClientData)p,mood1);
		Tcl_RegisterChannel(intr,duplicated);
		Tcl_SetObjResult(intr,Tcl_NewStringObj(Tcl_GetChannelName(duplicated),-1));
		return TCL_OK;
	}else {
		Tcl_SetObjResult(intr,P[2]);
		return TCL_OK;
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::dup",dupcommand,0,0);

		
 
section    top
 
section    top
		
static int devnullcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	Tcl_Channel channel; int mood; int p,q;
	if (N!=2) {
		Tcl_WrongNumArgs(intr,1,P,"redirected-channel");
		return TCL_ERROR;
	}
	channel = Tcl_GetChannel(intr,Tcl_GetString(P[1]),&mood);
	if (!channel) return rprintf(intr,"%!not a channel: %{y}s",TCL_ERROR,P[1]);
	if (Tcl_GetChannelHandle(channel,TCL_READABLE,(ClientData*)(&q))==TCL_ERROR
			&& Tcl_GetChannelHandle(channel,TCL_WRITABLE,(ClientData*)(&q))==TCL_ERROR)
		return rprintf(intr,"%!could not get the file descriptor: %{y}s",TCL_ERROR,P[1]);
	p = open("/dev/null",O_RDWR);
	if (p<0) {
		Tcl_SetErrno(errno);
		rprintf(intr,"open /dev/null failed: %s",Tcl_PosixError(intr));
		return TCL_ERROR;
	}
	if (dup2(p,q)<0) {
		Tcl_SetErrno(errno);
		rprintf(intr,"dup failed: %s",Tcl_PosixError(intr));
		close(p);
		return TCL_ERROR;
	}else {
		Tcl_SetObjResult(intr,P[1]);
		close(p);
		return TCL_OK;
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::/dev/null",devnullcommand,0,0);

		
 
section    top
 
section    top


static Tcl_ThreadDataKey untilKey;
typedef struct ChannelHandler ChannelHandler;
struct ChannelHandler {
	Intr intr; Tcl_Channel channel; Obj script;
	ChannelHandler *pre,*post;
};

static void channelClose(ClientData clientData);
static void channelEvent(ClientData clientData,int mood);

static void channelClose(ClientData clientData) {
	ChannelHandler **untilRing = Tcl_GetThreadData(&untilKey,sizeof(ChannelHandler*));
	ChannelHandler *c = (ChannelHandler*)clientData;
	Tcl_Release(c->intr);
	Tcl_DeleteChannelHandler(c->channel,channelEvent,clientData);
	Tcl_DeleteCloseHandler(c->channel,channelClose,clientData);
	if (!c->pre) {
		;
	}else if (c==c->post) {
		(*untilRing) = 0;
	}else {
		(*untilRing) = c->pre;
		c->post->pre = (*untilRing);
		(*untilRing)->post = c->post;
	}
	decr(c->script); dispose(c);
}

static void channelEvent(ClientData clientData,int mood) {
	ChannelHandler *c = (ChannelHandler*)clientData;
	if (Tcl_InterpDeleted(c->intr)) return;
	Obj script = c->script,cn; int rc = TCL_OK;
	Intr intr = c->intr;
	Tcl_Preserve(c->intr);
	Tcl_SavedResult state;
	Tcl_SaveResult(intr,&state);
	if (rc==TCL_OK) rc = Tcl_EvalObjEx(intr,script,TCL_EVAL_GLOBAL);
	if (rc!=TCL_OK) Tcl_BackgroundError(intr);
	Tcl_RestoreResult(intr,&state);
	Tcl_Release(intr);
}

static int readercommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	ChannelHandler **untilRing = Tcl_GetThreadData(&untilKey,sizeof(ChannelHandler*));
	Tcl_Channel channel; int mood; ChannelHandler *c;
	if (N!=3 && N!=4) {
		Tcl_WrongNumArgs(intr,1,P,"channel script [until]");
		return TCL_ERROR;
	}
	if (N==4 && !objeq("until",P[3]))
		return rprintf(intr,"%!expected 'until': %{y}s",TCL_ERROR,P[3]);
	channel = Tcl_GetChannel(intr,Tcl_GetString(P[1]),&mood);
	if (!channel) return rprintf(intr,"%!not a channel: %{y}s",TCL_ERROR,P[1]);
	if (!(mood&TCL_READABLE)) return rprintf(intr,"%!not a readable channel: %{y}s",TCL_ERROR,P[1]);
	if (Tcl_SetChannelOption(intr,channel,"-blocking","0")!=TCL_OK) return TCL_ERROR;
	c = heap(ChannelHandler);
	Tcl_Preserve(intr); c->intr = intr; c->channel = channel; c->script = incr(P[2]);
	if (N==3) {
		c->pre = c->post = 0;
	}else if ((*untilRing)==0) {
		(*untilRing) = c->pre = c->post = c;
	}else {
		c->pre = (*untilRing)->pre;
		c->pre->post = c;
		c->post = (*untilRing);
		(*untilRing)->pre = c;
	}
	Tcl_CreateCloseHandler(channel,channelClose,(ClientData)c);
	Tcl_CreateChannelHandler(channel,TCL_EXCEPTION|TCL_READABLE,channelEvent,(ClientData)c);
	Tcl_SetObjResult(intr,P[1]);
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::reader",readercommand,0,0);

		
 
section    top
 
section    top

static int writercommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	ChannelHandler **untilRing = Tcl_GetThreadData(&untilKey,sizeof(ChannelHandler*));
	Tcl_Channel channel; int mood; ChannelHandler *c;
	if (N!=3 && N!=4) {
		Tcl_WrongNumArgs(intr,1,P,"channel script [until]");
		return TCL_ERROR;
	}
	if (N==4 && !objeq("until",P[3]))
		return rprintf(intr,"%!expected 'until': %{y}s",TCL_ERROR,P[3]);
	channel = Tcl_GetChannel(intr,Tcl_GetString(P[1]),&mood);
	if (!channel) return rprintf(intr,"%!not a channel: %{y}s",TCL_ERROR,P[1]);
	if (!(mood&TCL_WRITABLE)) return rprintf(intr,"%!not a writable channel: %{y}s",TCL_ERROR,P[1]);
	if (Tcl_SetChannelOption(intr,channel,"-blocking","0")!=TCL_OK) return TCL_ERROR;
	c = heap(ChannelHandler);
	Tcl_Preserve(intr); c->intr = intr; c->channel = channel; c->script = incr(P[2]);
	if (N==3) {
		c->pre = c->post = 0;
	}else if ((*untilRing)==0) {
		(*untilRing) = c->pre = c->post = c;
	}else {
		c->pre = (*untilRing)->pre;
		c->pre->post = c;
		c->post = (*untilRing);
		(*untilRing)->post = c;
	}
	Tcl_CreateCloseHandler(channel,channelClose,(ClientData)c);
	Tcl_CreateChannelHandler(channel,TCL_EXCEPTION|TCL_WRITABLE,channelEvent,(ClientData)c);
	Tcl_SetObjResult(intr,P[1]);
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::writer",writercommand,0,0);

		
 
section    top
 
section    top

static int untilcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	ChannelHandler **untilRing = Tcl_GetThreadData(&untilKey,sizeof(ChannelHandler*));
	Tcl_Channel channel; int mood; ChannelHandler *c;
	if (N!=1) {
		Tcl_WrongNumArgs(intr,1,P,0);
		return TCL_ERROR;
	}
	while ((*untilRing)) {
		waitForSumpin();
	}
	Tcl_ResetResult(intr);
	return TCL_OK;
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::until",untilcommand,0,0);

		
 
section    top
 
section    top
51. unix::lock ::

NAME

unix::lock — Lock or unlock a file.

synopsis

wyrm::unix::lock create filepath

wyrm::unix::lock sh|ex|un [ nb ] channel

wyrm::unix::lock w|r|u [ nb|get ] channel [ set|curr|end offset length ]

description

Provides for different kinds of file locks.

create filepath
Create and open the file and return the channel to it. If the file already exists, it is an error.
sh|ex|un [ nb ] channel
Locks or unlocks the entire file with flock. If nb is specified and lock fails, it returns immediately. Otherwise the command enters the event loop and retries until it completes. Returns a true value if locked, and false if not.
w|r|u [ nb ] channel [ set|curr|end offset length ]
Locks or unlocks all or part of the file with fcntl. If nb is specified and lock fails, it returns immediately. Otherwise the command enters the event loop and retries until it completes. Returns a true value if locked, and false if not.
w|r get channel [ set|curr|end offset end ]
Identifies the first conflicting lock, if any. The returned value is a list w|r|u (u indicates no conflict), set|curr|end, offset, length, and owner-pid.

		
static int lockcommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	if (N==3 && objeq("create",P[1])) {
		int fd = open(Tcl_GetString(P[2]),O_RDWR|O_CREAT|O_EXCL,0600);
		if (fd<0) {
			Tcl_SetErrno(errno);
			rprintf(intr,"create failed: %s",Tcl_PosixError(intr));
			return TCL_ERROR;
		}else {
			Tcl_Channel channel = Tcl_MakeFileChannel((ClientData)fd,TCL_READABLE|TCL_WRITABLE);
			Tcl_RegisterChannel(intr,channel);
			Tcl_SetObjResult(intr,Tcl_NewStringObj(Tcl_GetChannelName(channel),-1));
			return TCL_OK;
		}
	}else if ((N==3 || N==4 && objeq("nb",P[2]))
				&& (objeq("sh",P[1]) || objeq("ex",P[1]) || objeq("un",P[1]) && N==3)
	) {
		int op = objeq("sh",P[1]) ? LOCK_SH|LOCK_NB : objeq("ex",P[1]) ? LOCK_EX|LOCK_NB : LOCK_UN;
		int fd,mood; Tcl_Channel channel = Tcl_GetChannel(intr,Tcl_GetString(P[N-1]),&mood);
		if (!channel) return rprintf(intr,"%!not a channel: %{y}s",TCL_ERROR,P[N-1]);
		if (Tcl_GetChannelHandle(channel,TCL_READABLE,(ClientData*)(&fd))==TCL_ERROR
				&& Tcl_GetChannelHandle(channel,TCL_WRITABLE,(ClientData*)(&fd))==TCL_ERROR)
			return rprintf(intr,"%!could not get the file descriptor: %{y}s",TCL_ERROR,P[N-1]);
		int r;
		while ((r=flock(fd,op))<0) {
			if (errno==EWOULDBLOCK) {
				if (N==4) {
					Tcl_SetObjResult(intr,Tcl_NewBooleanObj(0));
					return TCL_OK;
				}else {
					waitForSumpin();
				}
			}else {
				Tcl_SetErrno(errno);
				rprintf(intr,"create failed: %s",Tcl_PosixError(intr));
				return TCL_ERROR;
			}
		}
		Tcl_SetObjResult(intr,Tcl_NewBooleanObj(1));
		return TCL_OK;
	}else if ((N==3 || N==6 || (N==4 ||N==7) && (objeq("nb",P[2]) || objeq("get",P[2])))
				&& (objeq("w",P[1]) || objeq("r",P[1]) || objeq("u",P[1]))
				&& (N<6 || objeq("set",P[N-3]) || objeq("curr",P[N-3]) || objeq("end",P[N-3]))
	) {
		struct flock l; bool nb = N==4 || N==7; bool get = nb && objeq("get",P[2]);
		int CN = 2 + (N==4 || N==7);
		int fd,mood; Tcl_Channel channel = Tcl_GetChannel(intr,Tcl_GetString(P[CN]),&mood);
		if (!channel) return rprintf(intr,"%!not a channel: %{y}s",TCL_ERROR,P[CN]);
		if (Tcl_GetChannelHandle(channel,TCL_READABLE,(ClientData*)(&fd))==TCL_ERROR
				&& Tcl_GetChannelHandle(channel,TCL_WRITABLE,(ClientData*)(&fd))==TCL_ERROR)
			return rprintf(intr,"%!could not get the file descriptor: %{y}s",TCL_ERROR,P[CN]);
		l.l_pid = 0;
		l.l_type = objeq("w",P[1]) ? F_WRLCK : objeq("r",P[1]) ? F_RDLCK : F_UNLCK;
		if (N<6) {
			l.l_start = 0; l.l_len = 0; l.l_whence = SEEK_SET;
		}else {
			long long o;
			if (Tcl_GetWideIntFromObj(intr,P[N-2],&o)!=TCL_OK) return TCL_ERROR; l.l_start = o;
			if (Tcl_GetWideIntFromObj(intr,P[N-1],&o)!=TCL_OK) return TCL_ERROR; l.l_len = o;
			l.l_whence = objeq("set",P[N-3]) ? SEEK_SET : objeq("curr",P[N-3]) ? SEEK_CUR : SEEK_END;
		}
		if (get) {
			if (fcntl(fd,F_GETLK,&l)<0) {
				Tcl_SetErrno(errno);
				rprintf(intr,"fcntl F_GETLK failed: %s",Tcl_PosixError(intr));
				return TCL_ERROR;
			}else {
				Obj E[5];
				E[0] = Tcl_NewStringObj(l.l_type==F_WRLCK ? "w" : l.l_type==F_RDLCK ? "r" : "u",1);
				E[1] = Tcl_NewStringObj(l.l_whence==SEEK_SET ? "set"
								: l.l_whence==SEEK_CUR ? "curr"
								: "end",-1);
				E[2] = Tcl_NewWideIntObj(l.l_start);
				E[3] = Tcl_NewWideIntObj(l.l_len);
				E[4] = Tcl_NewIntObj(l.l_pid);
				Tcl_SetObjResult(intr,Tcl_NewListObj(5,E));
				return TCL_OK;
			}
		}else {
			while (fcntl(fd,F_SETLK,&l)<0) {
				if (errno==EACCES || errno==EAGAIN) {
					if (nb) {
						Tcl_SetObjResult(intr,Tcl_NewBooleanObj(0));
						return TCL_OK;
					}else {
						waitForSumpin();
					}
				}else {
					Tcl_SetErrno(errno);
					rprintf(intr,"partial lock failed: %s",Tcl_PosixError(intr));
					return TCL_ERROR;
				}
			}
			Tcl_SetObjResult(intr,Tcl_NewBooleanObj(1));
			return TCL_OK;
		}
	}else {
		return rprintf(intr,"%!unrecognised lock request",TCL_ERROR);
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::lock",lockcommand,0,0);

		
 
section    top
 
section    top
		
static int truncatecommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	if (N!=2 && N!=3) {
		Tcl_WrongNumArgs(intr,1,P,"file [length]");
		return TCL_ERROR;
	}else {
		long long length = 0;
		if (N==3 && Tcl_GetWideIntFromObj(intr,P[2],&length)!=TCL_OK) return TCL_ERROR;
		if (truncate(Tcl_GetString(P[1]),length)<0) {
			Tcl_SetErrno(errno);
			rprintf(intr,"truncate failed: %s",Tcl_PosixError(intr));
			return TCL_ERROR;
		}else {
			Tcl_ResetResult(intr);
			return TCL_OK;
		}
	}
}

		
 
section    top

Tcl_CreateObjCommand(intr,"::wyrm::unix::truncate",truncatecommand,0,0);

		
 
section    top