Weblog

All | CMT | General | NUMA | OpenSolaris | Perl | Photo | Programmers Desk | STREAMS
« I want to understand... | Main | NUMA Observability... »
20050819 Friday August 19, 2005

STREAMS: tr a-z A-Z

STREAMS: tr a-z A-Z

In my earlier entry I discussed the issues with flow control. This time I want to look at the module which actually looks at the data being passed and will convert all output to upper case letters. Basically, it will do the same thing as tr a-z A-Z command does, but using STREAMS

The core part of the module is the upmod_upcase() function that examines all M_DATA messages in the mblk and converts every symbol to upper case:

#define	islower(x)	(((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
#define	isupper(x)	(((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
#define	toupper(x)	(isupper(x) ? (x) : (unsigned)(x) - 'a' + 'A')

/*
 * Convert all ASCII chars in data blocks to upper case
 */
static mblk_t *
upmod_upcase(mblk_t *passed_mp)
{
	mblk_t *mp = passed_mp;

	for (; mp != NULL; mp = mp->b_cont) {
		if ((DB_TYPE(mp) == M_DATA) && (MBLKL(mp) > 0)) {
			unsigned char *p;

			for (p = mp->b_rptr; p < mp->b_wptr; p++)
				if (islower(*p))
					*p = toupper(*p);
		}
	}
	return (passed_mp);
}

The DB_TYPE(mp) macro simply returns mp->b_datab->db_type value and MBLKL(mp) is the amount of data between the read and write pointers . These macros together with some other useful definitions are defined in sys/strsun.h file:

#define	DB_BASE(mp)	((mp)->b_datap->db_base)
#define	DB_LIM(mp)	((mp)->b_datap->db_lim)
#define	DB_REF(mp)	((mp)->b_datap->db_ref)
#define	DB_TYPE(mp)	((mp)->b_datap->db_type)

#define	MBLKL(mp)	((mp)->b_wptr - (mp)->b_rptr)
#define	MBLKSIZE(mp)	((mp)->b_datap->db_lim - (mp)->b_datap->db_base)
#define	MBLKHEAD(mp)	((mp)->b_rptr - (mp)->b_datap->db_base)
#define	MBLKTAIL(mp)	((mp)->b_datap->db_lim - (mp)->b_wptr)
#define	MBLKIN(mp, off, len) (((off) <= MBLKL(mp)) && \
			(((mp)->b_rptr + (off) + (len)) <= (mp)->b_wptr))

Now we can modify the read-side put procedure to call upmod_upcase for each mblock seen on input. static void upmodrput(queue_t *q, mblk_t *mp) { upmodput(q, upmod_upcase(mp)); }

Here is the full example:

/*
 * This example demonstrates a minimum STREAMS module that honors flow control.
 * It converts all data bytes on the read side to the upper case.
 */

/*
 * Required include files.
 */
#include	
#include	
#include	
#include	
#include	
#include	
#include	

/*
 * Function prototypes.
 */
static int	upmodopen(queue_t *, dev_t *, int, int, cred_t *);
static int	upmodclose(queue_t *);

static void	upmodput(queue_t *, mblk_t *);
static void	upmodrput(queue_t *, mblk_t *);
static void	upmodsrv(queue_t *);
static mblk_t	*upmod_upcase(mblk_t *mp);

/*
 * Module linkage data
 */
static struct module_info	upmod_minfo = {
	2,		/* mi_idnum */
	"upmod",	/* mi_idname */
	0,		/* mi_minpsz */
	INFPSZ,		/* mi_maxpsz */
	4096,		/* mi_hiwat */
	512		/* mi_lowat */
};

static struct qinit	upmod_rinit = {
	(int (*)())upmodput,	/* qi_putp */
	(int (*)())upmodsrv,	/* qi_srvp  */
	upmodopen,	/* qi_qopen */
	upmodclose,	/* qi_qclose */
	NULL,		/* qi_qadmin */
	&upmod_minfo,	/* qi_minfo */
};

static struct qinit	upmod_winit = {
	(int (*)())upmodrput,	/* qi_putp */
	(int (*)())upmodsrv,	/* qi_srvp */
	NULL,		/* qi_qopen */
	NULL,		/* qi_qclose */
	NULL,		/* qi_qadmin */
	&upmod_minfo,	/* qi_minfo */
};

static struct streamtab	upmod_info = {
	&upmod_rinit,	/* st_rdinit */
	&upmod_winit,	/* st_wrinit */
};

static struct fmodsw fsw = {
	"upmod",
	&upmod_info,
	D_MP | D_MTPERQ
};

/*
 * Module linkage information for the kernel.
 */
struct mod_ops mod_strmodops;

static struct modlstrmod modlstrmod = {
	&mod_strmodops, "Example up-through module 1.0", &fsw
};

static struct modlinkage modlinkage = {
	MODREV_1, (void *)&modlstrmod, NULL
};

/*
 * Standard module entry points.
 */
int
_init(void)
{
	return (mod_install(&modlinkage));
}

int
_fini(void)
{
	return (mod_remove(&modlinkage));
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}


/*
 * Actual module code.
 */

/*
 * STREAMS entry points.
 */

/* ARGSUSED */
static int
upmodopen(queue_t *rq, dev_t *dev, int oflag, int sflag, cred_t *crp)
{
	if (sflag != MODOPEN)
		return (EINVAL);

	/* Prevent duplicate opens */
	if (rq->q_ptr != NULL)
		return (0);

	rq->q_ptr = WR(rq)->q_ptr = (void *)1;

	qprocson(rq);
	/*
	 * At this point module is linked in the STREAM and can send/receive
	 * messages. Its put/service procedures may execute at any time.
	 */
	return (0);
}

static int
upmodclose(queue_t *rq)
{
	qprocsoff(rq);
	rq->q_ptr = WR(rq)->q_ptr = NULL;
	/*
	 * At this point module is disconnected from the STREAM and can
	 * no longer receive messages. Its put or service procedures are not
	 * running.
	 */
	return (0);
}

/*
 * Support routines.
 */

/* Put procedure */
static void
upmodput(queue_t *q, mblk_t *mp)
{
	/*
	 * If the message is a high-priority message or there is no flow control
	 * and there are no messages in the queue already, pass it forward,
	 * otherwise queue.
	 */
	if (queclass(mp) == QPCTL ||
	    ((q->q_first == NULL) && canputnext(q)))
		putnext(q, mp);
	else
		(void) putq(q, mp);
}


/*
 * Support routines.
 */
static void
upmodrput(queue_t *q, mblk_t *mp)
{
	upmodput(q, upmod_upcase(mp));
}

/* Read/write side service routine */
static void
upmodsrv(queue_t *q)
{
	mblk_t *mp;

	/*
	 * Get messages from the service queue and pass them forward until flow
	 * controlled.
	 */
	while ((mp = getq(q)) != NULL) {
		if (canputnext(q)) {
			putnext(q, mp);
		} else {
			(void) putbq(q, mp);
			break;
		}
	}
}

#ifndef islower
#define	islower(x)	(((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
#endif

#ifndef isupper
#define	isupper(x)	(((unsigned)(x) >= 'A') && ((unsigned)(x) <= 'Z'))
#endif

#ifndef toupper
#define	toupper(x)	(isupper(x) ? (x) : (unsigned)(x) - 'a' + 'A')
#endif

/*
 * Convert all ASCII chars in data blocks to upper case
 */
static mblk_t *
upmod_upcase(mblk_t *passed_mp)
{
	mblk_t *mp = passed_mp;

	for (; mp != NULL; mp = mp->b_cont) {
		if ((DB_TYPE(mp) == M_DATA) && (MBLKL(mp) > 0)) {
			uchar_t *p;

			for (p = mp->b_rptr; p < mp->b_wptr; p++)
				if (islower(*p))
					*p = toupper(*p);
		}
	}

	return (passed_mp);
}

Let us save it in file upmod.c, and compile it:


$ /usr/sfw/bin/gcc -c -m64 -o upmod.o -I/usr/include \
    -O -D_KERNEL  -D_SYSCALL32 -D_SYSCALL32_IMPL upmod.c
$ ld -r -o upmod upmod.o

Now we can install it. For example, on sparc system:


$ su
# cp upmod /kernel/strmod/sparcv9
# exit
$ strchg -h upmod
$ UPTIME
  4:53PM  UP 35 DAY(S),  4:38,  2 USERS,  LOAD AVERAGE: 0.01, 0.01, 0.00
USER     TTY           LOGIN@  IDLE   JCPU   PCPU  WHAT
USER    PTS/1         8AUG0510DAYS      8         BASH
USER    PTS/2         4:11PM           12      1  W
$ STRCHG -P
$ w
   4:55pm  up 35 day(s),  4:39,  2 users,  load average: 0.00, 0.01, 0.00
User     tty           login@  idle   JCPU   PCPU  what
user    pts/1         8Aug0510days      8         bash
user    pts/2         4:11pm           12      1  w

The output reminds me of the old Russian-made mainframes ES 1045 (Soviet clone of IBM 360/370 series) which could only output in all caps. Interestingly, at these times Russian-made computers usually used capital letters for English and lower letters for Russian. This was a precursor for KOI8-r encoding.


Technorati Tag:
Technorati Tag:
Technorati Tag:

( Aug 19 2005, 05:02:41 PM PDT ) Permalink Comments [0]

Trackback URL: http://blogs.sun.com/akolb/entry/streams_tr_a_z_a
Comments:

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: NOT allowed

Calendar

RSS Feeds

Search

Links

Navigation

Referers