diff options
author | Guido van Rossum <guido@python.org> | 1994-09-29 09:42:55 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1994-09-29 09:42:55 (GMT) |
commit | fe3f1a256bc4f0c359ab843d5de517946916218f (patch) | |
tree | a8b42e97bb6a3f7e851f4763384b72ef5f3feaf0 /Python/getargs.c | |
parent | 6989e54ebf020ce9b3b2ecd327afa650e0e27995 (diff) | |
download | cpython-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.c | 587 |
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; +} |