diff options
-rw-r--r-- | Xi/xiproperty.c | 11 | ||||
-rw-r--r-- | config/hal.c | 9 | ||||
-rw-r--r-- | config/udev.c | 10 | ||||
-rw-r--r-- | configure.ac | 22 | ||||
-rw-r--r-- | dix/main.c | 2 | ||||
-rw-r--r-- | hw/xfree86/common/xf86Xinput.c | 101 | ||||
-rw-r--r-- | hw/xfree86/doc/man/xorg.conf.man.pre | 33 | ||||
-rw-r--r-- | hw/xfree86/parser/InputClass.c | 80 | ||||
-rw-r--r-- | hw/xfree86/parser/scan.c | 26 | ||||
-rw-r--r-- | hw/xfree86/parser/xf86Parser.h | 7 | ||||
-rw-r--r-- | hw/xfree86/parser/xf86tokens.h | 1 | ||||
-rw-r--r-- | include/input.h | 1 | ||||
-rw-r--r-- | include/misc.h | 3 | ||||
-rw-r--r-- | include/xserver-properties.h | 11 | ||||
-rw-r--r-- | os/utils.c | 40 |
15 files changed, 305 insertions, 52 deletions
diff --git a/Xi/xiproperty.c b/Xi/xiproperty.c index ea66c54c6..be0783107 100644 --- a/Xi/xiproperty.c +++ b/Xi/xiproperty.c @@ -93,6 +93,17 @@ static struct dev_properties {0, AXIS_LABEL_PROP_ABS_TILT_Y}, {0, AXIS_LABEL_PROP_ABS_TOOL_WIDTH}, {0, AXIS_LABEL_PROP_ABS_VOLUME}, + {0, AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR}, + {0, AXIS_LABEL_PROP_ABS_MT_TOUCH_MINOR}, + {0, AXIS_LABEL_PROP_ABS_MT_WIDTH_MAJOR}, + {0, AXIS_LABEL_PROP_ABS_MT_WIDTH_MINOR}, + {0, AXIS_LABEL_PROP_ABS_MT_ORIENTATION}, + {0, AXIS_LABEL_PROP_ABS_MT_POSITION_X}, + {0, AXIS_LABEL_PROP_ABS_MT_POSITION_Y}, + {0, AXIS_LABEL_PROP_ABS_MT_TOOL_TYPE}, + {0, AXIS_LABEL_PROP_ABS_MT_BLOB_ID}, + {0, AXIS_LABEL_PROP_ABS_MT_TRACKING_ID}, + {0, AXIS_LABEL_PROP_ABS_MT_PRESSURE}, {0, AXIS_LABEL_PROP_ABS_MISC}, {0, BTN_LABEL_PROP}, diff --git a/config/hal.c b/config/hal.c index 1b01eccaa..d3daa84cd 100644 --- a/config/hal.c +++ b/config/hal.c @@ -164,6 +164,7 @@ device_added(LibHalContext *hal_ctx, const char *udi) attrs.product = xstrdup(name); attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor"); + attrs.tags = xstrtokenize(get_prop_string(hal_ctx, udi, "input.tags"), ","); if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL)) attrs.flags |= ATTR_KEYBOARD; @@ -391,6 +392,14 @@ unwind: xfree(attrs.product); xfree(attrs.vendor); xfree(attrs.device); + if (attrs.tags) { + char **tag = attrs.tags; + while (*tag) { + xfree(*tag); + tag++; + } + xfree(attrs.tags); + } if (xkb_opts.layout) xfree(xkb_opts.layout); diff --git a/config/udev.c b/config/udev.c index 3ef0d7fb3..432ab85e9 100644 --- a/config/udev.c +++ b/config/udev.c @@ -84,6 +84,7 @@ device_added(struct udev_device *udev_device) add_option(&options, "path", path); add_option(&options, "device", path); attrs.device = path; + attrs.tags = xstrtokenize(udev_device_get_property_value(udev_device, "ID_INPUT.tags"), ","); config_info = Xprintf("udev:%s", syspath); if (!config_info) @@ -150,6 +151,15 @@ device_added(struct udev_device *udev_device) xfree(tmpo); } + if (attrs.tags) { + char **tag = attrs.tags; + while (*tag) { + xfree(*tag); + tag++; + } + xfree(attrs.tags); + } + return; } diff --git a/configure.ac b/configure.ac index bab6aee90..e3280b4d7 100644 --- a/configure.ac +++ b/configure.ac @@ -507,19 +507,23 @@ XORG_FONTSUBDIR(FONT75DPIDIR, font75dpidir, 75dpi) XORG_FONTSUBDIR(FONT100DPIDIR, font100dpidir, 100dpi) dnl Uses --default-font-path if set, otherwise checks for /etc/X11/fontpath.d, -dnl otherwise uses standard subdirectories of FONTROOTDIR -AC_CHECK_FILE([${sysconfdir}/X11/fontpath.d], - [DEFAULT_FONT_PATH='catalogue:${sysconfdir}/X11/fontpath.d'], - [ - DEFAULT_FONT_PATH="${FONTMISCDIR}/,${FONTTTFDIR}/,${FONTOTFDIR}/,${FONTTYPE1DIR}/,${FONT100DPIDIR}/,${FONT75DPIDIR}/" - case $host_os in - darwin*) DEFAULT_FONT_PATH="${DEFAULT_FONT_PATH},/Library/Fonts,/System/Library/Fonts" ;; - esac - ]) +dnl otherwise uses standard subdirectories of FONTROOTDIR. When cross +dnl compiling, assume default font path uses standard FONTROOTDIR directories. +DEFAULT_FONT_PATH="${FONTMISCDIR}/,${FONTTTFDIR}/,${FONTOTFDIR}/,${FONTTYPE1DIR}/,${FONT100DPIDIR}/,${FONT75DPIDIR}/" +if test "$cross_compiling" != yes; then + AC_CHECK_FILE([${sysconfdir}/X11/fontpath.d], + [DEFAULT_FONT_PATH='catalogue:${sysconfdir}/X11/fontpath.d'], + [case $host_os in + darwin*) DEFAULT_FONT_PATH="${DEFAULT_FONT_PATH},/Library/Fonts,/System/Library/Fonts" ;; + esac]) +fi AC_ARG_WITH(default-font-path, AS_HELP_STRING([--with-default-font-path=PATH], [Comma separated list of font dirs]), [ FONTPATH="$withval" ], [ FONTPATH="${DEFAULT_FONT_PATH}" ]) +AC_MSG_CHECKING([for default font path]) +AC_MSG_RESULT([$FONTPATH]) + AC_ARG_WITH(xkb-path, AS_HELP_STRING([--with-xkb-path=PATH], [Path to XKB base dir (default: ${datadir}/X11/xkb)]), [ XKBPATH="$withval" ], [ XKBPATH="${datadir}/X11/xkb" ]) diff --git a/dix/main.c b/dix/main.c index d4db90c75..da910fe4b 100644 --- a/dix/main.c +++ b/dix/main.c @@ -255,9 +255,9 @@ int main(int argc, char *argv[], char *envp[]) InitRootWindow(WindowTable[i]); InitCoreDevices(); - config_init(); InitInput(argc, argv); InitAndStartDevices(); + config_init(); dixSaveScreens(serverClient, SCREEN_SAVER_FORCER, ScreenSaverReset); diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c index fb0ee9c3f..c2d9f49de 100644 --- a/hw/xfree86/common/xf86Xinput.c +++ b/hw/xfree86/common/xf86Xinput.c @@ -502,20 +502,67 @@ AddOtherInputDevices(void) static Bool InputClassMatches(XF86ConfInputClassPtr iclass, InputAttributes *attrs) { - if (iclass->match_product && - (!attrs->product || !strstr(attrs->product, iclass->match_product))) - return FALSE; - if (iclass->match_vendor && - (!attrs->vendor || !strstr(attrs->vendor, iclass->match_vendor))) - return FALSE; - if (iclass->match_device && + char **cur; + Bool match; + + if (iclass->match_product) { + if (!attrs->product) + return FALSE; + /* see if any of the values match */ + for (cur = iclass->match_product, match = FALSE; *cur; cur++) + if (strstr(attrs->product, *cur)) { + match = TRUE; + break; + } + if (!match) + return FALSE; + } + if (iclass->match_vendor) { + if (!attrs->vendor) + return FALSE; + /* see if any of the values match */ + for (cur = iclass->match_vendor, match = FALSE; *cur; cur++) + if (strstr(attrs->vendor, *cur)) { + match = TRUE; + break; + } + if (!match) + return FALSE; + } + if (iclass->match_device) { + if (!attrs->device) + return FALSE; + /* see if any of the values match */ + for (cur = iclass->match_device, match = FALSE; *cur; cur++) #ifdef HAVE_FNMATCH_H - (!attrs->device || - fnmatch(iclass->match_device, attrs->device, 0) != 0)) + if (fnmatch(*cur, attrs->device, FNM_PATHNAME) == 0) { #else - (!attrs->device || !strstr(attrs->device, iclass->match_device))) + if (strstr(attrs->device, *cur)) { #endif - return FALSE; + match = TRUE; + break; + } + if (!match) + return FALSE; + } + if (iclass->match_tag) { + if (!attrs->tags) + return FALSE; + + for (cur = iclass->match_tag, match = FALSE; *cur && !match; cur++) { + const char *tag; + for(tag = *attrs->tags; *tag; tag++) { + if (!strcmp(tag, *cur)) { + match = TRUE; + break; + } + } + } + + if (!match) + return FALSE; + } + if (iclass->is_keyboard.set && iclass->is_keyboard.val != !!(attrs->flags & ATTR_KEYBOARD)) return FALSE; @@ -538,9 +585,9 @@ InputClassMatches(XF86ConfInputClassPtr iclass, InputAttributes *attrs) } /* - * Merge in any InputClass configurations. Each InputClass section can - * add to the original device configuration as well as any previous - * InputClass sections. + * Merge in any InputClass configurations. Options in each InputClass + * section have less priority than the original device configuration as + * well as any previous InputClass sections. */ static int MergeInputClasses(IDevPtr idev, InputAttributes *attrs) @@ -574,6 +621,27 @@ MergeInputClasses(IDevPtr idev, InputAttributes *attrs) return Success; } +static Bool +IgnoreInputClass(IDevPtr idev, InputAttributes *attrs) +{ + XF86ConfInputClassPtr cl; + Bool ignore; + + for (cl = xf86configptr->conf_inputclass_lst; cl; cl = cl->list.next) { + if (!InputClassMatches(cl, attrs)) + continue; + if (xf86findOption(cl->option_lst, "Ignore")) { + ignore = xf86CheckBoolOption(cl->option_lst, "Ignore", FALSE); + if (ignore) + xf86Msg(X_CONFIG, + "%s: Ignoring device from InputClass \"%s\"\n", + idev->identifier, cl->identifier); + return ignore; + } + } + return FALSE; +} + /** * Create a new input device, activate and enable it. * @@ -736,6 +804,11 @@ NewInputDeviceRequest (InputOption *options, InputAttributes *attrs, /* Apply InputClass settings */ if (attrs) { + if (IgnoreInputClass(idev, attrs)) { + rval = BadIDChoice; + goto unwind; + } + rval = MergeInputClasses(idev, attrs); if (rval != Success) goto unwind; diff --git a/hw/xfree86/doc/man/xorg.conf.man.pre b/hw/xfree86/doc/man/xorg.conf.man.pre index 222530b41..c8a3c3ac2 100644 --- a/hw/xfree86/doc/man/xorg.conf.man.pre +++ b/hw/xfree86/doc/man/xorg.conf.man.pre @@ -1021,7 +1021,7 @@ The entry specifies the name of the driver to use for this input device. After all classes have been examined, the .RI \*q inputdriver \*q -module from the final +module from the first .B Driver entry will be enabled when using the loadable server. .PP @@ -1039,17 +1039,29 @@ The allowed matching entries are shown below. .BI "MatchProduct \*q" matchproduct \*q This entry can be used to check if the substring .RI \*q matchproduct \*q -occurs in the device's product name. +occurs in the device's product name. Multiple substrings can be matched by +separating arguments with a '|' character. .TP 7 .BI "MatchVendor \*q" matchvendor \*q This entry can be used to check if the substring .RI \*q matchvendor \*q -occurs in the device's vendor name. +occurs in the device's vendor name. Multiple substrings can be matched by +separating arguments with a '|' character. .TP 7 .BI "MatchDevicePath \*q" matchdevice \*q This entry can be used to check if the device file matches the .RI \*q matchdevice \*q -pathname pattern. +pathname pattern. Multiple patterns can be matched by separating arguments +with a '|' character. +.TP 7 +.BI "MatchTag \*q" matchtag \*q +This entry can be used to check if tags assigned by the config backend +matches the +.RI \*q matchtag \*q +pattern. Multiple patterns can be matched by separating arguments +with a '|' character. A match is found if at least one of the tags given in +.RI \*q matchtag \*q +matches at least one of the tags assigned by the backend. .TP 7 .BI "MatchIsKeyboard \*q" bool \*q .TP 7 @@ -1070,11 +1082,20 @@ When an input device has been matched to the .B InputClass section, any .B Option -entries are applied to the device. See the +entries are applied to the device. One +.B InputClass +specific +.B Option +is recognized. See the .B InputDevice -section above for a description of the various +section above for a description of the remaining .B Option entries. +.TP 7 +.BI "Option \*qIgnore\*q \*q" boolean \*q +This optional entry specifies that the device should be ignored entirely, +and not added to the server. This can be useful when the device is handled +by another program and no X events should be generated. .SH "DEVICE SECTION" The config file may have multiple .B Device diff --git a/hw/xfree86/parser/InputClass.c b/hw/xfree86/parser/InputClass.c index 1c9816012..7fb2866cd 100644 --- a/hw/xfree86/parser/InputClass.c +++ b/hw/xfree86/parser/InputClass.c @@ -29,6 +29,8 @@ #include <xorg-config.h> #endif +#include <string.h> +#include "os.h" #include "xf86Parser.h" #include "xf86tokens.h" #include "Configint.h" @@ -45,6 +47,7 @@ xf86ConfigSymTabRec InputClassTab[] = {MATCH_PRODUCT, "matchproduct"}, {MATCH_VENDOR, "matchvendor"}, {MATCH_DEVICE_PATH, "matchdevicepath"}, + {MATCH_TAG, "matchtag"}, {MATCH_IS_KEYBOARD, "matchiskeyboard"}, {MATCH_IS_POINTER, "matchispointer"}, {MATCH_IS_JOYSTICK, "matchisjoystick"}, @@ -56,6 +59,8 @@ xf86ConfigSymTabRec InputClassTab[] = #define CLEANUP xf86freeInputClassList +#define TOKEN_SEP "|" + XF86ConfInputClassPtr xf86parseInputClassSection(void) { @@ -91,17 +96,22 @@ xf86parseInputClassSection(void) case MATCH_PRODUCT: if (xf86getSubToken(&(ptr->comment)) != STRING) Error(QUOTE_MSG, "MatchProduct"); - ptr->match_product = val.str; + ptr->match_product = xstrtokenize(val.str, TOKEN_SEP); break; case MATCH_VENDOR: if (xf86getSubToken(&(ptr->comment)) != STRING) Error(QUOTE_MSG, "MatchVendor"); - ptr->match_vendor = val.str; + ptr->match_vendor = xstrtokenize(val.str, TOKEN_SEP); break; case MATCH_DEVICE_PATH: if (xf86getSubToken(&(ptr->comment)) != STRING) Error(QUOTE_MSG, "MatchDevicePath"); - ptr->match_device = val.str; + ptr->match_device = xstrtokenize(val.str, TOKEN_SEP); + break; + case MATCH_TAG: + if (xf86getSubToken(&(ptr->comment)) != STRING) + Error(QUOTE_MSG, "MatchTag"); + ptr->match_tag = xstrtokenize(val.str, TOKEN_SEP); break; case MATCH_IS_KEYBOARD: if (xf86getSubToken(&(ptr->comment)) != STRING) @@ -173,6 +183,8 @@ xf86parseInputClassSection(void) void xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr) { + char **list; + while (ptr) { fprintf(cf, "Section \"InputClass\"\n"); if (ptr->comment) @@ -181,12 +193,38 @@ xf86printInputClassSection (FILE * cf, XF86ConfInputClassPtr ptr) fprintf(cf, "\tIdentifier \"%s\"\n", ptr->identifier); if (ptr->driver) fprintf(cf, "\tDriver \"%s\"\n", ptr->driver); - if (ptr->match_product) - fprintf(cf, "\tMatchProduct \"%s\"\n", ptr->match_product); - if (ptr->match_vendor) - fprintf(cf, "\tMatchVendor \"%s\"\n", ptr->match_vendor); - if (ptr->match_device) - fprintf(cf, "\tMatchDevicePath \"%s\"\n", ptr->match_device); + if (ptr->match_product) { + fprintf(cf, "\tMatchProduct \""); + for (list = ptr->match_product; *list; list++) + fprintf(cf, "%s%s", + list == ptr->match_product ? "" : TOKEN_SEP, + *list); + fprintf(cf, "\"\n"); + } + if (ptr->match_vendor) { + fprintf(cf, "\tMatchVendor \""); + for (list = ptr->match_vendor; *list; list++) + fprintf(cf, "%s%s", + list == ptr->match_vendor ? "" : TOKEN_SEP, + *list); + fprintf(cf, "\"\n"); + } + if (ptr->match_device) { + fprintf(cf, "\tMatchDevicePath \""); + for (list = ptr->match_device; *list; list++) + fprintf(cf, "%s%s", + list == ptr->match_device ? "" : TOKEN_SEP, + *list); + fprintf(cf, "\"\n"); + } + if (ptr->match_tag) { + fprintf(cf, "\tMatchTag \""); + for (list = ptr->match_tag; *list; list++) + fprintf(cf, "%s%s", + list == ptr->match_tag ? "" : TOKEN_SEP, + *list); + fprintf(cf, "\"\n"); + } if (ptr->is_keyboard.set) fprintf(cf, "\tIsKeyboard \"%s\"\n", ptr->is_keyboard.val ? "yes" : "no"); @@ -215,13 +253,31 @@ void xf86freeInputClassList (XF86ConfInputClassPtr ptr) { XF86ConfInputClassPtr prev; + char **list; while (ptr) { TestFree(ptr->identifier); TestFree(ptr->driver); - TestFree(ptr->match_product); - TestFree(ptr->match_vendor); - TestFree(ptr->match_device); + if (ptr->match_product) { + for (list = ptr->match_product; *list; list++) + free(*list); + free(ptr->match_product); + } + if (ptr->match_vendor) { + for (list = ptr->match_vendor; *list; list++) + free(*list); + free(ptr->match_vendor); + } + if (ptr->match_device) { + for (list = ptr->match_device; *list; list++) + free(*list); + free(ptr->match_device); + } + if (ptr->match_tag) { + for (list = ptr->match_tag; *list; list++) + free(*list); + free(ptr->match_tag); + } TestFree(ptr->comment); xf86optionListFree(ptr->option_lst); diff --git a/hw/xfree86/parser/scan.c b/hw/xfree86/parser/scan.c index b80fbfb8f..03cbc8a44 100644 --- a/hw/xfree86/parser/scan.c +++ b/hw/xfree86/parser/scan.c @@ -227,13 +227,15 @@ xf86getNextLine(void) configFiles[curFileIndex].file); if (!ret) { - /* stop if there are no more files */ - if (++curFileIndex >= numFiles) { - curFileIndex = 0; + /* + * if the file doesn't end in a newline, add one + * and trigger another read + */ + if (pos != 0) { + strcpy(&configBuf[pos], "\n"); + ret = configBuf; + } else break; - } - configLineNo = 0; - continue; } /* search for EOL in the new block of chars */ @@ -338,7 +340,17 @@ again: } if (ret == NULL) { - return (pushToken = EOF_TOKEN); + /* + * if necessary, move to the next file and + * read the first line + */ + if (curFileIndex + 1 < numFiles) { + curFileIndex++; + configLineNo = 0; + goto again; + } + else + return (pushToken = EOF_TOKEN); } configLineNo++; configPos = 0; diff --git a/hw/xfree86/parser/xf86Parser.h b/hw/xfree86/parser/xf86Parser.h index 5e8351fc4..d79544a20 100644 --- a/hw/xfree86/parser/xf86Parser.h +++ b/hw/xfree86/parser/xf86Parser.h @@ -343,9 +343,10 @@ typedef struct GenericListRec list; char *identifier; char *driver; - char *match_product; - char *match_vendor; - char *match_device; + char **match_product; + char **match_vendor; + char **match_device; + char **match_tag; xf86TriState is_keyboard; xf86TriState is_pointer; xf86TriState is_joystick; diff --git a/hw/xfree86/parser/xf86tokens.h b/hw/xfree86/parser/xf86tokens.h index e3a9d716b..cb600704b 100644 --- a/hw/xfree86/parser/xf86tokens.h +++ b/hw/xfree86/parser/xf86tokens.h @@ -279,6 +279,7 @@ typedef enum { MATCH_PRODUCT, MATCH_VENDOR, MATCH_DEVICE_PATH, + MATCH_TAG, MATCH_IS_KEYBOARD, MATCH_IS_POINTER, MATCH_IS_JOYSTICK, diff --git a/include/input.h b/include/input.h index 7a6242d08..4a845bedf 100644 --- a/include/input.h +++ b/include/input.h @@ -215,6 +215,7 @@ typedef struct _InputAttributes { char *product; char *vendor; char *device; + char **tags; /* null-terminated */ uint32_t flags; } InputAttributes; diff --git a/include/misc.h b/include/misc.h index 877c682d4..62d813e0c 100644 --- a/include/misc.h +++ b/include/misc.h @@ -210,6 +210,9 @@ pad_to_int32(const int bytes) { return (((bytes) + 3) & ~3); } +extern char** +xstrtokenize(const char *str, const char* separators); + /* some macros to help swap requests, replies, and events */ #define LengthRestB(stuff) \ diff --git a/include/xserver-properties.h b/include/xserver-properties.h index 626d0ad27..30e8efb68 100644 --- a/include/xserver-properties.h +++ b/include/xserver-properties.h @@ -89,6 +89,17 @@ #define AXIS_LABEL_PROP_ABS_TILT_Y "Abs Tilt Y" #define AXIS_LABEL_PROP_ABS_TOOL_WIDTH "Abs Tool Width" #define AXIS_LABEL_PROP_ABS_VOLUME "Abs Volume" +#define AXIS_LABEL_PROP_ABS_MT_TOUCH_MAJOR "Abs MT Touch Major" +#define AXIS_LABEL_PROP_ABS_MT_TOUCH_MINOR "Abs MT Touch Minor" +#define AXIS_LABEL_PROP_ABS_MT_WIDTH_MAJOR "Abs MT Width Major" +#define AXIS_LABEL_PROP_ABS_MT_WIDTH_MINOR "Abs MT Width Minor" +#define AXIS_LABEL_PROP_ABS_MT_ORIENTATION "Abs MT Orientation" +#define AXIS_LABEL_PROP_ABS_MT_POSITION_X "Abs MT Position X" +#define AXIS_LABEL_PROP_ABS_MT_POSITION_Y "Abs MT Position Y" +#define AXIS_LABEL_PROP_ABS_MT_TOOL_TYPE "Abs MT Tool Type" +#define AXIS_LABEL_PROP_ABS_MT_BLOB_ID "Abs MT Blob ID" +#define AXIS_LABEL_PROP_ABS_MT_TRACKING_ID "Abs MT Tracking ID" +#define AXIS_LABEL_PROP_ABS_MT_PRESSURE "Abs MT Pressure" #define AXIS_LABEL_PROP_ABS_MISC "Abs Misc" /* Button names */ diff --git a/os/utils.c b/os/utils.c index 1edbc5b5c..79399fa53 100644 --- a/os/utils.c +++ b/os/utils.c @@ -1866,6 +1866,46 @@ CheckUserAuthorization(void) #endif } +/* + * Tokenize a string into a NULL terminated array of strings. Always returns + * an allocated array unless an error occurs. + */ +char** +xstrtokenize(const char *str, const char *separators) +{ + char **list, **nlist; + char *tok, *tmp; + unsigned num = 0, n; + + if (!str) + return NULL; + list = calloc(1, sizeof(*list)); + if (!list) + return NULL; + tmp = strdup(str); + if (!tmp) + goto error; + for (tok = strtok(tmp, separators); tok; tok = strtok(NULL, separators)) { + nlist = realloc(list, (num + 2) * sizeof(*list)); + if (!nlist) + goto error; + list = nlist; + list[num] = strdup(tok); + if (!list[num]) + goto error; + list[++num] = NULL; + } + free(tmp); + return list; + +error: + free(tmp); + for (n = 0; n < num; n++) + free(list[n]); + free(list); + return NULL; +} + #ifdef __SCO__ #include <fcntl.h> |