summaryrefslogtreecommitdiffstats
path: root/Python/getargs.c
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1994-09-29 09:42:55 (GMT)
committerGuido van Rossum <guido@python.org>1994-09-29 09:42:55 (GMT)
commitfe3f1a256bc4f0c359ab843d5de517946916218f (patch)
treea8b42e97bb6a3f7e851f4763384b72ef5f3feaf0 /Python/getargs.c
parent6989e54ebf020ce9b3b2ecd327afa650e0e27995 (diff)
downloadcpython-fe3f1a256bc4f0c359ab843d5de517946916218f.zip
cpython-fe3f1a256bc4f0c359ab843d5de517946916218f.tar.gz
cpython-fe3f1a256bc4f0c359ab843d5de517946916218f.tar.bz2
* Python/{modsupport.c,getargs.c,Makefile.in},
Include/modsupport.h: moved getargs() to its own file and re-implemented it entirely to support optional arguments, multiple arguments without surrounding parentheses (when called as newgetargs()), and better error messages
Diffstat (limited to 'Python/getargs.c')
-rw-r--r--Python/getargs.c587
1 files changed, 587 insertions, 0 deletions
diff --git a/Python/getargs.c b/Python/getargs.c
new file mode 100644
index 0000000..605cb01
--- /dev/null
+++ b/Python/getargs.c
@@ -0,0 +1,587 @@
+/***********************************************************
+Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum,
+Amsterdam, The Netherlands.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/* New getargs implementation */
+
+/* XXX There are several unchecked sprintf or strcat calls in this file.
+ XXX The only way these can become a danger is if some C code in the
+ XXX Python source (or in an extension) uses ridiculously long names
+ XXX or riduculously deep nesting in format strings. */
+
+#include "allobjects.h"
+
+
+int getargs PROTO((object *, char *, ...));
+int newgetargs PROTO((object *, char *, ...));
+int vgetargs PROTO((object *, char *, va_list));
+
+
+/* Forward */
+static int vgetargs1 PROTO((object *, char *, va_list, int));
+static void seterror PROTO((int, char *, int *, char *, char *));
+static char *convertitem PROTO((object *, char **, va_list *, int *, char *));
+static char *converttuple PROTO((object *, char **, va_list *,
+ int *, char *, int));
+static char *convertsimple PROTO((object *, char **, va_list *, char *));
+static char *convertsimple1 PROTO((object *, char **, va_list *));
+
+
+#ifdef HAVE_STDARG_PROTOTYPES
+/* VARARGS2 */
+int getargs(object *args, char *format, ...)
+#else
+/* VARARGS */
+int getargs(va_alist) va_dcl
+#endif
+{
+ int retval;
+ va_list va;
+#ifdef HAVE_STDARG_PROTOTYPES
+
+ va_start(va, format);
+#else
+ object *args;
+ char *format;
+
+ va_start(va);
+ args = va_arg(va, object *);
+ format = va_arg(va, char *);
+#endif
+ retval = vgetargs1(args, format, va, 1);
+ va_end(va);
+ return retval;
+}
+
+
+#ifdef HAVE_STDARG_PROTOTYPES
+/* VARARGS2 */
+int newgetargs(object *args, char *format, ...)
+#else
+/* VARARGS */
+int newgetargs(va_alist) va_dcl
+#endif
+{
+ int retval;
+ va_list va;
+#ifdef HAVE_STDARG_PROTOTYPES
+
+ va_start(va, format);
+#else
+ object *args;
+ char *format;
+
+ va_start(va);
+ args = va_arg(va, object *);
+ format = va_arg(va, char *);
+#endif
+ retval = vgetargs1(args, format, va, 0);
+ va_end(va);
+ return retval;
+}
+
+
+int
+vgetargs(args, format, va)
+ object *args;
+ char *format;
+ va_list va;
+{
+ return vgetargs1(args, format, va, 0);
+}
+
+
+static int
+vgetargs1(args, format, va, compat)
+ object *args;
+ char *format;
+ va_list va;
+ int compat;
+{
+ char msgbuf[256];
+ int levels[32];
+ char *fname = NULL;
+ char *message = NULL;
+ int min = -1;
+ int max = 0;
+ int level = 0;
+ char *formatsave = format;
+ int i, len;
+ char *msg;
+
+ for (;;) {
+ int c = *format++;
+ if (c == '(' /* ')' */) {
+ if (level == 0)
+ max++;
+ level++;
+ }
+ else if (/* '(' */ c == ')') {
+ if (level == 0)
+ fatal(/* '(' */
+ "excess ')' in getargs format");
+ else
+ level--;
+ }
+ else if (c == '\0')
+ break;
+ else if (c == ':') {
+ fname = format;
+ break;
+ }
+ else if (c == ';') {
+ message = format;
+ break;
+ }
+ else if (level != 0)
+ ; /* Pass */
+ else if (isalpha(c))
+ max++;
+ else if (c == '|')
+ min = max;
+ }
+
+ if (level != 0)
+ fatal(/* '(' */ "missing ')' in getargs format");
+
+ if (min < 0)
+ min = max;
+
+ format = formatsave;
+
+ if (compat) {
+ if (max == 0) {
+ if (args == NULL)
+ return 1;
+ sprintf(msgbuf, "%s requires no arguments",
+ fname==NULL ? "function" : fname);
+ err_setstr(TypeError, msgbuf);
+ return 0;
+ }
+ else if (min == 1 && max == 1) {
+ msg = convertitem(args, &format, &va, levels, msgbuf);
+ if (msg == NULL)
+ return 1;
+ seterror(levels[0], msg, levels+1, fname, message);
+ return 0;
+ }
+ else {
+ err_setstr(SystemError,
+ "old style getargs format uses new features");
+ return 0;
+ }
+ }
+
+ if (!is_tupleobject(args)) {
+ err_setstr(SystemError,
+ "new style getargs format but argument is not a tuple");
+ return 0;
+ }
+
+ len = gettuplesize(args);
+
+ if (len < min || max < len) {
+ if (message == NULL) {
+ sprintf(msgbuf,
+ "%s requires %s %d argument%s; %d given",
+ fname==NULL ? "function" : fname,
+ min==max ? "exactly"
+ : len < min ? "at least" : "at most",
+ len < min ? min : max,
+ (len < min ? min : max) == 1 ? "" : "s",
+ len);
+ message = msgbuf;
+ }
+ err_setstr(TypeError, message);
+ return 0;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (*format == '|')
+ format++;
+ msg = convertitem(gettupleitem(args, i), &format, &va,
+ levels, msgbuf);
+ if (msg) {
+ seterror(i+1, msg, levels, fname, message);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+
+static void
+seterror(iarg, msg, levels, fname, message)
+ int iarg;
+ char *msg;
+ int *levels;
+ char *fname;
+ char *message;
+{
+ char buf[256];
+ int i;
+ char *p = buf;
+
+ if (iarg == 0 && message == NULL)
+ message = msg;
+ else if (message == NULL) {
+ if (fname != NULL) {
+ sprintf(p, "%s, ", fname);
+ p += strlen(p);
+ }
+ sprintf(p, "argument %d", iarg);
+ i = 0;
+ p += strlen(p);
+ while (levels[i] > 0) {
+ sprintf(p, ", item %d", levels[i]-1);
+ p += strlen(p);
+ i++;
+ }
+ sprintf(p, ": expected %s found", msg);
+ message = buf;
+ }
+ err_setstr(TypeError, message);
+}
+
+
+/* Convert a tuple argument.
+ On entry, *p_format points to the character _after_ the opening '('.
+ On successful exit, *p_format points to the closing ')'.
+ If successful:
+ *p_format and *p_va are updated,
+ *levels and *msgbuf are untouched,
+ and NULL is returned.
+ If the argument is invalid:
+ *p_format is unchanged,
+ *p_va is undefined,
+ *levels is a 0-terminated list of item numbers,
+ *msgbuf contains an error message, whose format is:
+ "<typename1>, <typename2>", where:
+ <typename1> is the name of the expected type, and
+ <typename2> is the name of the actual type,
+ (so you can surround it by "expected ... found"),
+ and msgbuf is returned.
+*/
+
+static char *
+converttuple(arg, p_format, p_va, levels, msgbuf, toplevel)
+ object *arg;
+ char **p_format;
+ va_list *p_va;
+ int *levels;
+ char *msgbuf;
+ int toplevel;
+{
+ int level = 0;
+ int n = 0;
+ char *format = *p_format;
+ int i;
+
+ for (;;) {
+ int c = *format++;
+ if (c == '(') {
+ if (level == 0)
+ n++;
+ level++;
+ }
+ else if (c == ')') {
+ if (level == 0)
+ break;
+ level--;
+ }
+ else if (c == ':' || c == ';' || c == '\0')
+ break;
+ else if (level == 0 && isalpha(c))
+ n++;
+ }
+
+ if (!is_tupleobject(arg)) {
+ levels[0] = 0;
+ sprintf(msgbuf,
+ toplevel ? "%d arguments, %s" : "%d-tuple, %s",
+ n, arg == None ? "None" : arg->ob_type->tp_name);
+ return msgbuf;
+ }
+
+ if ((i = gettuplesize(arg)) != n) {
+ levels[0] = 0;
+ sprintf(msgbuf,
+ toplevel ? "%d arguments, %d" : "%d-tuple, %d-tuple",
+ n, i);
+ return msgbuf;
+ }
+
+ format = *p_format;
+ for (i = 0; i < n; i++) {
+ char *msg;
+ msg = convertitem(gettupleitem(arg, i), &format, p_va,
+ levels+1, msgbuf);
+ if (msg != NULL) {
+ levels[0] = i+1;
+ return msg;
+ }
+ }
+
+ *p_format = format;
+ return NULL;
+}
+
+
+/* Convert a single item. */
+
+static char *
+convertitem(arg, p_format, p_va, levels, msgbuf)
+ object *arg;
+ char **p_format;
+ va_list *p_va;
+ int *levels;
+ char *msgbuf;
+{
+ char *msg;
+ char *format = *p_format;
+
+ if (*format == '(' /* ')' */) {
+ format++;
+ msg = converttuple(arg, &format, p_va, levels, msgbuf, 0);
+ if (msg == NULL)
+ format++;
+ }
+ else {
+ msg = convertsimple(arg, &format, p_va, msgbuf);
+ if (msg != NULL)
+ levels[0] = 0;
+ }
+ if (msg == NULL)
+ *p_format = format;
+ return msg;
+}
+
+
+/* Convert a non-tuple argument. Adds to convertsimple1 functionality
+ by appending ", <actual argument type>" to error message. */
+
+static char *
+convertsimple(arg, p_format, p_va, msgbuf)
+ object *arg;
+ char **p_format;
+ va_list *p_va;
+ char *msgbuf;
+{
+ char *msg = convertsimple1(arg, p_format, p_va);
+ if (msg != NULL) {
+ sprintf(msgbuf, "%.50s, %.50s", msg,
+ arg == None ? "None" : arg->ob_type->tp_name);
+ msg = msgbuf;
+ }
+ return msg;
+}
+
+
+/* Convert a non-tuple argument. Return NULL if conversion went OK,
+ or a string representing the expected type if the conversion failed.
+ When failing, an exception may or may not have been raised.
+ Don't call if a tuple is expected. */
+
+static char *
+convertsimple1(arg, p_format, p_va)
+ object *arg;
+ char **p_format;
+ va_list *p_va;
+{
+ char *format = *p_format;
+ char c = *format++;
+
+ switch (c) {
+
+ case 'b': /* byte -- very short int */
+ {
+ char *p = va_arg(*p_va, char *);
+ long ival = getintvalue(arg);
+ if (ival == -1 && err_occurred())
+ return "integer<b>";
+ else
+ *p = ival;
+ break;
+ }
+
+ case 'h': /* short int */
+ {
+ short *p = va_arg(*p_va, short *);
+ long ival = getintvalue(arg);
+ if (ival == -1 && err_occurred())
+ return "integer<h>";
+ else
+ *p = ival;
+ break;
+ }
+
+ case 'i': /* int */
+ {
+ int *p = va_arg(*p_va, int *);
+ long ival = getintvalue(arg);
+ if (ival == -1 && err_occurred())
+ return "integer<i>";
+ else
+ *p = ival;
+ break;
+ }
+
+ case 'l': /* long int */
+ {
+ long *p = va_arg(*p_va, long *);
+ long ival = getintvalue(arg);
+ if (ival == -1 && err_occurred())
+ return "integer<l>";
+ else
+ *p = ival;
+ break;
+ }
+
+ case 'f': /* float */
+ {
+ float *p = va_arg(*p_va, float *);
+ double dval = getfloatvalue(arg);
+ if (err_occurred())
+ return "float<f>";
+ else
+ *p = dval;
+ break;
+ }
+
+ case 'd': /* double */
+ {
+ double *p = va_arg(*p_va, double *);
+ double dval = getfloatvalue(arg);
+ if (err_occurred())
+ return "float<d>";
+ else
+ *p = dval;
+ break;
+ }
+
+ case 'c': /* char */
+ {
+ char *p = va_arg(*p_va, char *);
+ if (is_stringobject(arg) && getstringsize(arg) == 1)
+ *p = getstringvalue(arg)[0];
+ else
+ return "char";
+ break;
+ }
+
+ case 's': /* string */
+ {
+ char **p = va_arg(*p_va, char **);
+ if (is_stringobject(arg))
+ *p = getstringvalue(arg);
+ else
+ return "string";
+ if (*format == '#') {
+ int *q = va_arg(*p_va, int *);
+ *q = getstringsize(arg);
+ format++;
+ }
+ else if (strlen(*p) != getstringsize(arg))
+ return "string without null bytes";
+ break;
+ }
+
+ case 'z': /* string, may be NULL (None) */
+ {
+ char **p = va_arg(*p_va, char **);
+ if (arg == None)
+ *p = 0;
+ else if (is_stringobject(arg))
+ *p = getstringvalue(arg);
+ else
+ return "None or string";
+ if (*format == '#') {
+ int *q = va_arg(*p_va, int *);
+ if (arg == None)
+ *q = 0;
+ else
+ *q = getstringsize(arg);
+ format++;
+ }
+ else if (*p != NULL && strlen(*p) != getstringsize(arg))
+ return "None or string without null bytes";
+ break;
+ }
+
+ case 'S': /* string object */
+ {
+ object **p = va_arg(*p_va, object **);
+ if (is_stringobject(arg))
+ *p = arg;
+ else
+ return "string";
+ break;
+ }
+
+ case 'O': /* object */
+ {
+ typeobject *type;
+ object **p;
+ if (*format == '!') {
+ format++;
+ type = va_arg(*p_va, typeobject*);
+ if (arg->ob_type != type)
+ return type->tp_name;
+ else {
+ p = va_arg(*p_va, object **);
+ *p = arg;
+ }
+ }
+ else if (*format == '?') {
+ inquiry pred = va_arg(*p_va, inquiry);
+ format++;
+ if ((*pred)(arg)) {
+ p = va_arg(*p_va, object **);
+ *p = arg;
+ }
+ }
+ else if (*format == '&') {
+ typedef int (*converter) PROTO((object *, void *));
+ converter convert = va_arg(*p_va, converter);
+ void *addr = va_arg(*p_va, void *);
+ format++;
+ if (! (*convert)(arg, addr))
+ return "";
+ }
+ else {
+ p = va_arg(*p_va, object **);
+ *p = arg;
+ }
+ break;
+ }
+
+ default:
+ return "impossible<bad format char>";
+
+ }
+
+ *p_format = format;
+ return NULL;
+}