DNA.
wyrm-pref
Version.
1.0.9
Namespace.
::wyrm
Command.
::wyrm::pref
Language.
c
Manpage.
pref (1WY)
Manpage.
wyrm_preference (3WY)
Testbase.
Test Script
Test Report
Import.
Interface.
wyrmwif
Export.
Implementation.
wyrm-pref.c
Interface.
wyrm-pref.h
Object.
wyrm-pref.o
System.
wyrm-pref.sys
wyrm-pref.sys

Application Preferences in System-Specific Format

Sections.
Get or Set Preference Variable
Error handling
Unix Implementation
Macintosh Implementation
pref Command
Make.
Object.
compile -c -o [
  export object
] [
  export implementation
] -- -list [import interface] [export interface]
   
top

1 :: Copyright (C) 2002 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

#ifndef WYRM_PREF_H
#define WYRM_PREF_H

	//	wyrm-pref - Copyright (C) 2002 SM Ryan.  All rights reserved.

	#include "wyrmwif.h"
	
	<Declare wyrm_preference>
	<Declare wyrm_preferenceDelete>
	<Declare wyrm_preferenceCommandInit>

#endif

		
   
   
top

static const char COPYRIGHT[] = "wyrm-pref - Copyright (C) 2002 SM Ryan.  All rights reserved.";

#include "wyrm-pref.h"
#include "wyrm-pref.sys"

<Declare onError>
<pref command>
<pref command initialiser>

		
   
   

Get or Set Preference Variable

   
top
 
section    top
proc wyrm_preference—Get or set preference value; returns the old preference, or an empty string if none, or NULL if error.
Memory. New object with reference count 1, or NULL.
output intr—Receives possible error messages.
input group—Name of the group of variables.
io (group)—The preference group in some system-defined format is read and possibly updated.
input variable—Name of the variable.
input value—The new value if not NULL; if NULL, nothing is changed.
Obj wyrm_preference(Intr intr,chars group,chars variable,chars value);
 
section    top
 
section    top
proc wyrm_preferenceDelete—Delete preference group.
output intr—Receives possible error messages.
input group—Name of the group of variables.
output (group)—Deleted.
int wyrm_preferenceDelete(Intr intr,chars group);
 
section    top
   
   

Error handling

   
top
proc onError—Determine if an error occurred, and format error messages into the interpretter result.
input ec—System specific indication of an error code.
output intr— Receives error message unless NULL
input where—Additional information added to possible error messages.
static bool onError(int ec,Intr intr,chars where);
		
   
   

Unix Implementation

   
top

#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>

<Error handling (Unix)>
<Get or set a preference variable (Unix)>
<Delete a variable group (Unix)>
			
		
 
section    top
 
section    top

Obj	prefPath = 0;
chars home;

		
 
section    top

home = getenv("HOME");

prefPath = incr(Tcl_NewObj());
if (home) Tcl_AppendToObj(prefPath,home,-1);
Tcl_AppendToObj(prefPath,"/.",-1);
Tcl_AppendToObj(prefPath,group,-1);
Tcl_AppendToObj(prefPath,"rc",-1);

		
 
section    top

Obj	tempPath = 0;

		
 
section    top

tempPath = incr(Tcl_NewObj());
if (home) Tcl_AppendToObj(tempPath,home,-1);
Tcl_AppendToObj(tempPath,"/.",-1);
Tcl_AppendToObj(tempPath,group,-1);
Tcl_AppendToObj(tempPath,".tmp",-1);
tempChan = Tcl_OpenFileChannel(intr,Tcl_GetStringFromObj(tempPath,0),"w",0644);

		
 
section    top

Tcl_Channel prefChan = 0, tempChan = 0;

		
 
section    top
{
	chars fn = Tcl_GetStringFromObj(prefPath,0); int fd; ClientData FD;
	prefChan = Tcl_OpenFileChannel(intr,fn,"r",0);
	if (prefChan) {
		FD = &fd;
		if (onError(Tcl_GetChannelHandle(prefChan,TCL_READABLE,&FD)==TCL_ERROR,intr,fn)) goto exit;
		fd = (int)FD;
		if (onError(flock(fd,LOCK_EX)<0,intr,fn)) goto exit;
	}

	if (value) {
		fn = Tcl_GetStringFromObj(tempPath,0);
		tempChan = Tcl_OpenFileChannel(intr,fn,"w",0644);
		if (onError(!tempChan,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
		FD = &fd;
		if (onError(Tcl_GetChannelHandle(tempChan,TCL_WRITABLE,&FD)==TCL_ERROR,intr,fn)) goto exit;
		fd = (int)FD;
		if (onError(flock(fd,LOCK_EX)<0,intr,fn)) goto exit;
	}
}
		
 
section    top

int n = strlen(variable);
Obj line = incr(Tcl_NewObj());

		
 
section    top

for (;;) {
	int m; chars s,s1;
	if (!prefChan) break;
	Tcl_SetObjLength(line,0);
	if (Tcl_GetsObj(prefChan,line)<=0) {
		if (onError(!Tcl_Eof(prefChan),intr,Tcl_GetStringFromObj(prefPath,0))) goto exit;
		break;
	}
	s = Tcl_GetStringFromObj(line,&m);
	while (n>0 && isspace(*s)) {s++; n--;}
	if (*s!='#' && m>n && s[n]=='=' && memcmp(s,variable,n)==0) {
		r = incr(Tcl_NewStringObj(s+n+1,m-n-1));
		if (tempChan) {
			if (onError(Tcl_Write(tempChan,variable,-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
			if (onError(Tcl_Write(tempChan,"=",-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
			if (onError(Tcl_Write(tempChan,value,-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
			if (onError(Tcl_Write(tempChan,"\n",-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
		}
		break;
	}else {
		if (tempChan) {
			if (onError(Tcl_Write(tempChan,s,m)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
			if (onError(Tcl_Write(tempChan,"\n",-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
		}
	}
}
if (tempChan && !r) {
	if (onError(Tcl_Write(tempChan,variable,-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
	if (onError(Tcl_Write(tempChan,"=",-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
	if (*value) {
		if (onError(Tcl_Write(tempChan,value,-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
	}
	if (onError(Tcl_Write(tempChan,"\n",-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
}
if (!r)
	r = incr(Tcl_NewObj());

		
 
section    top

if (tempChan && prefChan && !Tcl_Eof(prefChan)) {
	for (;;) {
		int m; chars s;
		if (!prefChan) break;
		Tcl_SetObjLength(line,0);
		if (Tcl_GetsObj(prefChan,line)<=0) {
			if (onError(!Tcl_Eof(prefChan),intr,Tcl_GetStringFromObj(prefPath,0))) goto exit;
			break;
		}
		s = Tcl_GetStringFromObj(line,&m);
		if (onError(Tcl_Write(tempChan,s,m)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
		if (onError(Tcl_Write(tempChan,"\n",-1)<=0,intr,Tcl_GetStringFromObj(tempPath,0))) goto exit;
	}
}

		
 
section    top

chars prefName,tempName;

		
 
section    top

prefName = Tcl_GetStringFromObj(prefPath,0);
tempName = Tcl_GetStringFromObj(tempPath,0);
unlink(prefName);
if (onError(link(tempName,prefName)<0,intr,"wyrm_preference: link new prefs")) goto exit;
unlink(tempName);

		
 
section    top

exit:
	decr(prefPath);
	decr(tempPath);
	if (prefChan) Tcl_Close(intr,prefChan);
	if (tempChan) Tcl_Close(intr,tempChan);
	decr(line);
	return r;

		
 
section    top

int wyrm_preferenceDelete(Intr intr,chars group) {
	int	rc; chars prefName;

	
	<File path variables (Unix)>
	
	<Determine preference file path (Unix)>
	


	prefName = Tcl_GetStringFromObj(prefPath,0);
	rc = unlink(prefName);
	if (rc<0 && errno==ENOENT) {
		rc = TCL_OK;
	}else if (onError(rc<0,intr,"wyrm_preferenceDelete unlink")) {
		rc = TCL_ERROR;
	}else {
		rc = TCL_OK;
	}
	decr(prefPath);
	return rc;
}

		
 
section    top

static bool onError(int ec,Intr intr,chars where) {
	if (ec) {
		if (intr) {
			Tcl_ResetResult(intr);
			Tcl_AppendResult(intr,where,": ",strerror(errno),0);
		}
		return true;
	}else
		return false;
}

		
   
   

Macintosh Implementation

   
top

#include <Errors.h>
#include <Files.h>
#include <Finder.h>
#include <Folders.h>
#include <Resources.h>
#include <TextUtils.h>

//
//	CopyCStringToPascal converts the source C string to a destination
//	pascal string as it copies. The dest string will
//	be truncated to fit into an Str255 if necessary.
// If the C String pointer is NULL, the pascal string's length is set to zero
//
static void CopyCStringToPascal(const char* src, Str255 dst)
{
	short	length = 0;

	// handle case of overlapping strings
	if ( (void*)src == (void*)dst )
	{
		unsigned char*		curdst = &dst[1];
		unsigned char		thisChar;

		thisChar = *(const unsigned char*)src++;
		while ( thisChar != '\0' )
		{
			unsigned char	nextChar;

			// use nextChar so we don't overwrite what we are about to read
			nextChar = *(const unsigned char*)src++;
			*curdst++ = thisChar;
			thisChar = nextChar;

			if ( ++length >= 255 )
				break;
		}
	}
	else if ( src != NULL )
	{
		unsigned char*		curdst = &dst[1];
		short				overflow = 255;		// count down so test it loop is faster
		register char		temp;

		// Can't do the K&R C thing of while (*s++ = *t++) because it will copy trailing zero

		// which might overrun pascal buffer. Instead we use a temp variable.
		while ( (temp = *src++) != 0 )
		{
			*(char*)curdst++ = temp;

			if ( --overflow <= 0 )
				break;
		}
		length = 255 - overflow;
	}
	dst[0] = length;
}

<Error handling (Macintosh)>
<Get or set a preference variable (Macintosh)>
<Delete a variable group (Macintosh)>
			
		
 
section    top
 
section    top

short	vol;
long	dir;
Str255	pgroup;
FSSpec	spec;

		
 
section    top

if (!onError(rc=FindFolder(kOnSystemDisk,'pref',kCreateFolder,&vol,&dir),intr,"find pref folder")) {
	CopyCStringToPascal(group,pgroup);
	rc = FSMakeFSSpec(vol,dir,pgroup,&spec);
}

		
 
section    top

enum {invalidfref = -1};
int fn = invalidfref;

		
 
section    top

if (rc==fnfErr) {
	if (value) {
		FSpCreateResFile(&spec,'RSED','rsrc',smSystemScript);
		rc = ResError();
	}else {
		r = incr(Tcl_NewObj());
		goto exit;
	}
}
if (onError(rc,intr,"wyrm_preference find pref folder")) goto exit;
fn = FSpOpenResFile(&spec,value ? fsRdWrPerm : fsRdPerm);
if (fn==invalidfref && onError(ResError(),intr,group)) goto exit;

		
 
section    top

bytes	*h;

		
 
section    top

h = (bytes*)get1namedresource('STR ',variable);
if (!h) {
	rc = ResError();
	if (rc!=resNotFound) {
		if (onError(rc,intr,"wyrm_preference: reading old value")) goto exit;
	}
	r = incr(Tcl_NewObj());
}else {
	r = incr(Tcl_NewStringObj((chars)(*h + 1),**h));
}

		
 
section    top

if (!h) {
	h = (bytes*)NewHandle(strlen(value)+1);
	if (onError(MemError(),intr,"wyrm_preference writing new value")) goto exit;
	CopyCStringToPascal(value,*h);
	addresource((chars*)h,'STR ',UniqueID('STR '),variable);
	if (onError(ResError(),intr,"wyrm_preference: writing new value")) goto exit;
}else {
	SetHandleSize((chars*)h,strlen(value)+1);
	if (onError(MemError(),intr,"wyrm_preference: writing new value")) goto exit;
	CopyCStringToPascal(value,*h);
	ChangedResource((chars*)h);
	if (onError(ResError(),intr,"wyrm_preference: writing new value")) goto exit;
}

		
 
section    top

exit:
	if (fn!=invalidfref) CloseResFile(fn);
	return r;

		
 
section    top

int wyrm_preferenceDelete(Intr intr,chars group) {
	int	rc;
	<File path variables (Macintosh)>
	<Determine preference file path (Macintosh)>
	if (rc!=fnfErr) {
		if (onError(rc,intr,"wyrm_preferenceDelete find pref folder")) return TCL_ERROR;
		if (onError(FSpDelete(&spec),intr,"wyrm_preferenceDelete delete file")) return TCL_ERROR;
	}
	return TCL_OK;
}

		
 
section    top

static bool onError(int ec,Intr intr,chars where) {
	if (ec<0) {
		if (intr) {
			char b[20]; sprintf(b,"%d",ec);
			Tcl_ResetResult(intr);
			Tcl_AppendResult(intr,where,": error code ",b,0);
		}
		return true;
	}else
		return false;
}

		
   
   

pref Command

   
top
 
section    top
proc wyrm_preferenceCommand—Get and maybe set the preference, and return TCL_OK or TCL_ERROR.
output intr—Command result or error message. For getting/setting a variable, the result is the old variable value (or an empty string). For deleting a group, the result is an empty string.
input N—Number of command parameters.
input N,P—Command parameters.

static int wyrm_preferenceCommand(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
	chars group,variable,value;
	int rc = TCL_OK;
	if (N!=3 && N!=4) {usage:
		Tcl_SetResult(intr,"usage: pref <group> <variable>|-delete [<value>]",TCL_STATIC);
		return TCL_ERROR;
	}

	group = Tcl_GetStringFromObj(P[1],0);
	variable = Tcl_GetStringFromObj(P[2],0);
	value = N==4 ? Tcl_GetStringFromObj(P[3],0) : 0;

	if (streq(variable,"-delete")) {
		if (value) goto usage;
		rc = wyrm_preferenceDelete(intr,group);
		if (rc==TCL_OK) Tcl_ResetResult(intr);
	}else {
		Obj r = wyrm_preference(intr,group,variable,value);
		if (r) {
			Tcl_SetObjResult(intr,r);
			decr(r);
		}else {
			rc = TCL_ERROR;
		}
	}
	return rc;
}

		
 
section    top
proc wyrm_preferenceCommandInit—Define the pref command.
output intr—The pref command is defined.
int wyrm_preferenceCommandInit(Intr intr);
		
 
section    top

int wyrm_preferenceCommandInit(Intr intr) {
	char package[] = "namespace eval ::wyrm {namespace export pref}\n";
	Tcl_CreateObjCommand(intr,"::wyrm::pref",wyrm_preferenceCommand,0,0);
	return Tcl_Eval(intr,package);
}

		
 
section    top
    PRF Application preferences.
      PRF000
      Nonexistent group.
        PRF001
        PRF002
        PRF003
      Create a group and variable.
        PRF010
        PRF011
      Nonexistent variable.
        PRF020
        PRF021
      Update a variable.
        PRF030
        PRF031
      Another variable.
        PRF040
        PRF041
        PRF042
        PRF043
      Delete group.
        PRF050
        PRF051
      Command usage.
        PRF500
        PRF501
      Test cleanup.
        PRF999