strop_upper(), strop_lower(): shared code version caused to much of a
performance hit.  Urg.  Reverted.

strop_joinfields(): re-instate optimizations for lists and tuples, but
support arbitrary other kinds of sequences as well.
diff --git a/Modules/stropmodule.c b/Modules/stropmodule.c
index 193d998..d84b55f 100644
--- a/Modules/stropmodule.c
+++ b/Modules/stropmodule.c
@@ -169,52 +169,73 @@
 	PyObject *args;
 {
 	PyObject *seq, *item, *res;
-	char *sep, *p;
-	int seplen, seqlen, reslen, itemlen, i;
+	char *p, *sep = NULL;
+	int seqlen, reslen, itemlen, i, seplen = 0;
+	int ownrefs = 0;
+	PyObject* (*getitemfunc) Py_PROTO((PyObject*, int));
 
-	sep = NULL;
-	seplen = 0;
 	if (!PyArg_ParseTuple(args, "O|s#", &seq, &sep, &seplen))
 		return NULL;
 	if (sep == NULL) {
 		sep = " ";
 		seplen = 1;
 	}
-	if (!PySequence_Check(seq)) {
+	/* do it this way to optimize for the common cases, but also
+	 * support the more general sequence protocol
+	 */
+	if (PyList_Check(seq)) {
+		getitemfunc = PyList_GetItem;
+		seqlen = PyList_Size(seq);
+	}
+	else if (PyTuple_Check(seq)) {
+		getitemfunc = PyTuple_GetItem;
+		seqlen = PyTuple_Size(seq);
+	}
+	else if (PySequence_Check(seq)) {
+		getitemfunc = PySequence_GetItem;
+		seqlen = PySequence_Length(seq);
+		ownrefs = 1;
+	}
+	else {
 		PyErr_SetString(PyExc_TypeError,
 				"first argument must be a sequence");
 		return NULL;
 	}
-	seqlen = PySequence_Length(seq);
 	if (seqlen < 0 && PyErr_Occurred())
 		return NULL;
 
 	reslen = 0;
 	for (i = 0; i < seqlen; i++) {
-		if (!(item = PySequence_GetItem(seq, i)))
+		if (!(item = getitemfunc(seq, i)))
 			return NULL;
 
 		if (!PyString_Check(item)) {
 			PyErr_SetString(PyExc_TypeError,
 				 "first argument must be sequence of strings");
-			Py_DECREF(item);
+			if (ownrefs)
+				Py_DECREF(item);
 			return NULL;
 		}
 		if (i > 0)
 			reslen += seplen;
 		reslen += PyString_Size(item);
-		Py_DECREF(item);
+		if (ownrefs)
+			Py_DECREF(item);
 	}
-	if (seqlen == 1)
+	if (seqlen == 1) {
 		/* Optimization if there's only one item */
-		return PySequence_GetItem(seq, 0);
+		item = getitemfunc(seq, 0);
+		if (!ownrefs)
+			Py_XINCREF(item);
+		return item;
+	}
 
 	res = PyString_FromStringAndSize((char *)NULL, reslen);
 	if (res == NULL)
 		return NULL;
 	p = PyString_AsString(res);
 	for (i = 0; i < seqlen; i++) {
-		if (!(item = PySequence_GetItem(seq, i))) {
+		if (!(item = getitemfunc(seq, i))) {
 			Py_DECREF(res);
 			return NULL;
 		}
@@ -225,7 +246,8 @@
 		itemlen = PyString_Size(item);
 		memcpy(p, PyString_AsString(item), itemlen);
 		p += itemlen;
-		Py_DECREF(item);
+		if (ownrefs)
+			Py_DECREF(item);
 	}
 	if (p != PyString_AsString(res) + reslen) {
 		PyErr_SetString(PyExc_SystemError,
@@ -356,11 +378,9 @@
 
 
 static PyObject *
-do_casechange(self, args, test, conv)
+strop_lower(self, args)
 	PyObject *self; /* Not used */
 	PyObject *args;
-	int (*test) Py_PROTO((int));
-	int (*conv) Py_PROTO((int));
 {
 	char *s, *s_new;
 	int i, n;
@@ -376,9 +396,9 @@
 	changed = 0;
 	for (i = 0; i < n; i++) {
 		int c = Py_CHARMASK(*s++);
-		if (test(c)) {
+		if (isupper(c)) {
 			changed = 1;
-			*s_new = conv(c);
+			*s_new = tolower(c);
 		} else
 			*s_new = c;
 		s_new++;
@@ -391,21 +411,39 @@
 	return new;
 }
 
-static PyObject *
-strop_lower(self, args)
-	PyObject *self; /* Not used */
-	PyObject *args;
-{
-	return do_casechange(self, args, isupper, tolower);
-}
-
 
 static PyObject *
 strop_upper(self, args)
 	PyObject *self; /* Not used */
 	PyObject *args;
 {
-	return do_casechange(self, args, islower, toupper);
+	char *s, *s_new;
+	int i, n;
+	PyObject *new;
+	int changed;
+
+	if (!PyArg_Parse(args, "s#", &s, &n))
+		return NULL;
+	new = PyString_FromStringAndSize(NULL, n);
+	if (new == NULL)
+		return NULL;
+	s_new = PyString_AsString(new);
+	changed = 0;
+	for (i = 0; i < n; i++) {
+		int c = Py_CHARMASK(*s++);
+		if (islower(c)) {
+			changed = 1;
+			*s_new = toupper(c);
+		} else
+			*s_new = c;
+		s_new++;
+	}
+	if (!changed) {
+		Py_DECREF(new);
+		Py_INCREF(args);
+		return args;
+	}
+	return new;
 }