First import
[xorg_rtime.git] / xorg-server-1.4 / hw / xfree86 / common / xf86Option.c
blob50a7d9cb9569d8f82bc8671e75ddf3afb58dc08a
1 /*
2 * Copyright (c) 1998-2003 by The XFree86 Project, Inc.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
22 * Except as contained in this notice, the name of the copyright holder(s)
23 * and author(s) shall not be used in advertising or otherwise to promote
24 * the sale, use or other dealings in this Software without prior written
25 * authorization from the copyright holder(s) and author(s).
29 * Author: David Dawes <dawes@xfree86.org>
31 * This file includes public option handling functions.
34 #ifdef HAVE_XORG_CONFIG_H
35 #include <xorg-config.h>
36 #endif
38 #include <stdlib.h>
39 #include <ctype.h>
40 #include <X11/X.h>
41 #include "os.h"
42 #include "xf86.h"
43 #include "xf86Xinput.h"
44 #include "xf86Optrec.h"
46 static Bool ParseOptionValue(int scrnIndex, pointer options, OptionInfoPtr p,
47 Bool markUsed);
50 * xf86CollectOptions collects the options from each of the config file
51 * sections used by the screen and puts the combined list in pScrn->options.
52 * This function requires that the following have been initialised:
54 * pScrn->confScreen
55 * pScrn->Entities[i]->device
56 * pScrn->display
57 * pScrn->monitor
59 * The extraOpts parameter may optionally contain a list of additional options
60 * to include.
62 * The order of precedence for options is:
64 * extraOpts, display, confScreen, monitor, device
67 _X_EXPORT void
68 xf86CollectOptions(ScrnInfoPtr pScrn, pointer extraOpts)
70 XF86OptionPtr tmp;
71 XF86OptionPtr extras = (XF86OptionPtr)extraOpts;
72 GDevPtr device;
74 int i;
76 pScrn->options = NULL;
78 for (i=pScrn->numEntities - 1; i >= 0; i--) {
79 device = xf86GetDevFromEntity(pScrn->entityList[i],
80 pScrn->entityInstanceList[i]);
81 if (device && device->options) {
82 tmp = xf86optionListDup(device->options);
83 if (pScrn->options)
84 xf86optionListMerge(pScrn->options,tmp);
85 else
86 pScrn->options = tmp;
89 if (pScrn->monitor->options) {
90 tmp = xf86optionListDup(pScrn->monitor->options);
91 if (pScrn->options)
92 pScrn->options = xf86optionListMerge(pScrn->options, tmp);
93 else
94 pScrn->options = tmp;
96 if (pScrn->confScreen->options) {
97 tmp = xf86optionListDup(pScrn->confScreen->options);
98 if (pScrn->options)
99 pScrn->options = xf86optionListMerge(pScrn->options, tmp);
100 else
101 pScrn->options = tmp;
103 if (pScrn->display->options) {
104 tmp = xf86optionListDup(pScrn->display->options);
105 if (pScrn->options)
106 pScrn->options = xf86optionListMerge(pScrn->options, tmp);
107 else
108 pScrn->options = tmp;
110 if (extras) {
111 tmp = xf86optionListDup(extras);
112 if (pScrn->options)
113 pScrn->options = xf86optionListMerge(pScrn->options, tmp);
114 else
115 pScrn->options = tmp;
120 * xf86CollectInputOptions collects the options for an InputDevice.
121 * This function requires that the following has been initialised:
123 * pInfo->conf_idev
125 * The extraOpts parameter may optionally contain a list of additional options
126 * to include.
128 * The order of precedence for options is:
130 * extraOpts, pInfo->conf_idev->extraOptions,
131 * pInfo->conf_idev->commonOptions, defaultOpts
134 _X_EXPORT void
135 xf86CollectInputOptions(InputInfoPtr pInfo, const char **defaultOpts,
136 pointer extraOpts)
138 XF86OptionPtr tmp;
139 XF86OptionPtr extras = (XF86OptionPtr)extraOpts;
141 pInfo->options = NULL;
142 if (defaultOpts) {
143 pInfo->options = xf86OptionListCreate(defaultOpts, -1, 0);
145 if (pInfo->conf_idev && pInfo->conf_idev->commonOptions) {
146 tmp = xf86optionListDup(pInfo->conf_idev->commonOptions);
147 if (pInfo->options)
148 pInfo->options = xf86optionListMerge(pInfo->options, tmp);
149 else
150 pInfo->options = tmp;
152 if (pInfo->conf_idev && pInfo->conf_idev->extraOptions) {
153 tmp = xf86optionListDup(pInfo->conf_idev->extraOptions);
154 if (pInfo->options)
155 pInfo->options = xf86optionListMerge(pInfo->options, tmp);
156 else
157 pInfo->options = tmp;
159 if (extras) {
160 tmp = xf86optionListDup(extras);
161 if (pInfo->options)
162 pInfo->options = xf86optionListMerge(pInfo->options, tmp);
163 else
164 pInfo->options = tmp;
168 /* Created for new XInput stuff -- essentially extensions to the parser */
170 static int
171 LookupIntOption(pointer optlist, const char *name, int deflt, Bool markUsed)
173 OptionInfoRec o;
175 o.name = name;
176 o.type = OPTV_INTEGER;
177 if (ParseOptionValue(-1, optlist, &o, markUsed))
178 deflt = o.value.num;
179 return deflt;
183 static double
184 LookupRealOption(pointer optlist, const char *name, double deflt,
185 Bool markUsed)
187 OptionInfoRec o;
189 o.name = name;
190 o.type = OPTV_REAL;
191 if (ParseOptionValue(-1, optlist, &o, markUsed))
192 deflt = o.value.realnum;
193 return deflt;
197 static char *
198 LookupStrOption(pointer optlist, const char *name, char *deflt, Bool markUsed)
200 OptionInfoRec o;
202 o.name = name;
203 o.type = OPTV_STRING;
204 if (ParseOptionValue(-1, optlist, &o, markUsed))
205 deflt = o.value.str;
206 if (deflt)
207 return xstrdup(deflt);
208 else
209 return NULL;
213 static int
214 LookupBoolOption(pointer optlist, const char *name, int deflt, Bool markUsed)
216 OptionInfoRec o;
218 o.name = name;
219 o.type = OPTV_BOOLEAN;
220 if (ParseOptionValue(-1, optlist, &o, markUsed))
221 deflt = o.value.bool;
222 return deflt;
225 /* These xf86Set* functions are intended for use by non-screen specific code */
227 _X_EXPORT int
228 xf86SetIntOption(pointer optlist, const char *name, int deflt)
230 return LookupIntOption(optlist, name, deflt, TRUE);
234 _X_EXPORT double
235 xf86SetRealOption(pointer optlist, const char *name, double deflt)
237 return LookupRealOption(optlist, name, deflt, TRUE);
241 _X_EXPORT char *
242 xf86SetStrOption(pointer optlist, const char *name, char *deflt)
244 return LookupStrOption(optlist, name, deflt, TRUE);
248 _X_EXPORT int
249 xf86SetBoolOption(pointer optlist, const char *name, int deflt)
251 return LookupBoolOption(optlist, name, deflt, TRUE);
255 * These are like the Set*Option functions, but they don't mark the options
256 * as used.
258 _X_EXPORT int
259 xf86CheckIntOption(pointer optlist, const char *name, int deflt)
261 return LookupIntOption(optlist, name, deflt, FALSE);
265 _X_EXPORT double
266 xf86CheckRealOption(pointer optlist, const char *name, double deflt)
268 return LookupRealOption(optlist, name, deflt, FALSE);
272 _X_EXPORT char *
273 xf86CheckStrOption(pointer optlist, const char *name, char *deflt)
275 return LookupStrOption(optlist, name, deflt, FALSE);
279 _X_EXPORT int
280 xf86CheckBoolOption(pointer optlist, const char *name, int deflt)
282 return LookupBoolOption(optlist, name, deflt, FALSE);
286 * addNewOption() has the required property of replacing the option value
287 * if the option is already present.
289 _X_EXPORT pointer
290 xf86ReplaceIntOption(pointer optlist, const char *name, const int val)
292 char tmp[16];
293 sprintf(tmp,"%i",val);
294 return xf86AddNewOption(optlist,name,tmp);
297 _X_EXPORT pointer
298 xf86ReplaceRealOption(pointer optlist, const char *name, const double val)
300 char tmp[32];
301 snprintf(tmp,32,"%f",val);
302 return xf86AddNewOption(optlist,name,tmp);
305 _X_EXPORT pointer
306 xf86ReplaceBoolOption(pointer optlist, const char *name, const Bool val)
308 return xf86AddNewOption(optlist,name,val?"True":"False");
311 _X_EXPORT pointer
312 xf86ReplaceStrOption(pointer optlist, const char *name, const char* val)
314 return xf86AddNewOption(optlist,name,val);
317 _X_EXPORT pointer
318 xf86AddNewOption(pointer head, const char *name, const char *val)
320 /* XXX These should actually be allocated in the parser library. */
321 char *tmp = strdup(val);
322 char *tmp_name = strdup(name);
324 return xf86addNewOption(head, tmp_name, tmp);
328 _X_EXPORT pointer
329 xf86NewOption(char *name, char *value)
331 return xf86newOption(name, value);
335 _X_EXPORT pointer
336 xf86NextOption(pointer list)
338 return xf86nextOption(list);
341 _X_EXPORT pointer
342 xf86OptionListCreate(const char **options, int count, int used)
344 return xf86optionListCreate(options, count, used);
347 _X_EXPORT pointer
348 xf86OptionListMerge(pointer head, pointer tail)
350 return xf86optionListMerge(head, tail);
353 _X_EXPORT void
354 xf86OptionListFree(pointer opt)
356 xf86optionListFree(opt);
359 _X_EXPORT char *
360 xf86OptionName(pointer opt)
362 return xf86optionName(opt);
365 _X_EXPORT char *
366 xf86OptionValue(pointer opt)
368 return xf86optionValue(opt);
371 _X_EXPORT void
372 xf86OptionListReport(pointer parm)
374 XF86OptionPtr opts = parm;
376 while(opts) {
377 if (xf86optionValue(opts))
378 xf86ErrorFVerb(5, "\tOption \"%s\" \"%s\"\n",
379 xf86optionName(opts), xf86optionValue(opts));
380 else
381 xf86ErrorFVerb( 5, "\tOption \"%s\"\n", xf86optionName(opts));
382 opts = xf86nextOption(opts);
386 /* End of XInput-caused section */
388 _X_EXPORT pointer
389 xf86FindOption(pointer options, const char *name)
391 return xf86findOption(options, name);
395 _X_EXPORT char *
396 xf86FindOptionValue(pointer options, const char *name)
398 return xf86findOptionValue(options, name);
402 _X_EXPORT void
403 xf86MarkOptionUsed(pointer option)
405 if (option != NULL)
406 ((XF86OptionPtr)option)->opt_used = TRUE;
410 _X_EXPORT void
411 xf86MarkOptionUsedByName(pointer options, const char *name)
413 XF86OptionPtr opt;
415 opt = xf86findOption(options, name);
416 if (opt != NULL)
417 opt->opt_used = TRUE;
420 _X_EXPORT Bool
421 xf86CheckIfOptionUsed(pointer option)
423 if (option != NULL)
424 return ((XF86OptionPtr)option)->opt_used;
425 else
426 return FALSE;
429 _X_EXPORT Bool
430 xf86CheckIfOptionUsedByName(pointer options, const char *name)
432 XF86OptionPtr opt;
434 opt = xf86findOption(options, name);
435 if (opt != NULL)
436 return opt->opt_used;
437 else
438 return FALSE;
441 _X_EXPORT void
442 xf86ShowUnusedOptions(int scrnIndex, pointer options)
444 XF86OptionPtr opt = options;
446 while (opt) {
447 if (opt->opt_name && !opt->opt_used) {
448 xf86DrvMsg(scrnIndex, X_WARNING, "Option \"%s\" is not used\n",
449 opt->opt_name);
451 opt = opt->list.next;
456 static Bool
457 GetBoolValue(OptionInfoPtr p, const char *s)
459 if (*s == '\0') {
460 p->value.bool = TRUE;
461 } else {
462 if (xf86NameCmp(s, "1") == 0)
463 p->value.bool = TRUE;
464 else if (xf86NameCmp(s, "on") == 0)
465 p->value.bool = TRUE;
466 else if (xf86NameCmp(s, "true") == 0)
467 p->value.bool = TRUE;
468 else if (xf86NameCmp(s, "yes") == 0)
469 p->value.bool = TRUE;
470 else if (xf86NameCmp(s, "0") == 0)
471 p->value.bool = FALSE;
472 else if (xf86NameCmp(s, "off") == 0)
473 p->value.bool = FALSE;
474 else if (xf86NameCmp(s, "false") == 0)
475 p->value.bool = FALSE;
476 else if (xf86NameCmp(s, "no") == 0)
477 p->value.bool = FALSE;
478 else
479 return FALSE;
481 return TRUE;
484 static Bool
485 ParseOptionValue(int scrnIndex, pointer options, OptionInfoPtr p,
486 Bool markUsed)
488 char *s, *end;
489 Bool wasUsed = FALSE;
491 if ((s = xf86findOptionValue(options, p->name)) != NULL) {
492 if (markUsed) {
493 wasUsed = xf86CheckIfOptionUsedByName(options, p->name);
494 xf86MarkOptionUsedByName(options, p->name);
496 switch (p->type) {
497 case OPTV_INTEGER:
498 if (*s == '\0') {
499 xf86DrvMsg(scrnIndex, X_WARNING,
500 "Option \"%s\" requires an integer value\n",
501 p->name);
502 p->found = FALSE;
503 } else {
504 p->value.num = strtoul(s, &end, 0);
505 if (*end == '\0') {
506 p->found = TRUE;
507 } else {
508 xf86DrvMsg(scrnIndex, X_WARNING,
509 "Option \"%s\" requires an integer value\n",
510 p->name);
511 p->found = FALSE;
514 break;
515 case OPTV_STRING:
516 if (*s == '\0') {
517 xf86DrvMsg(scrnIndex, X_WARNING,
518 "Option \"%s\" requires an string value\n",
519 p->name);
520 p->found = FALSE;
521 } else {
522 p->value.str = s;
523 p->found = TRUE;
525 break;
526 case OPTV_ANYSTR:
527 p->value.str = s;
528 p->found = TRUE;
529 break;
530 case OPTV_REAL:
531 if (*s == '\0') {
532 xf86DrvMsg(scrnIndex, X_WARNING,
533 "Option \"%s\" requires a floating point value\n",
534 p->name);
535 p->found = FALSE;
536 } else {
537 p->value.realnum = strtod(s, &end);
538 if (*end == '\0') {
539 p->found = TRUE;
540 } else {
541 xf86DrvMsg(scrnIndex, X_WARNING,
542 "Option \"%s\" requires a floating point value\n",
543 p->name);
544 p->found = FALSE;
547 break;
548 case OPTV_BOOLEAN:
549 if (GetBoolValue(p, s)) {
550 p->found = TRUE;
551 } else {
552 xf86DrvMsg(scrnIndex, X_WARNING,
553 "Option \"%s\" requires a boolean value\n", p->name);
554 p->found = FALSE;
556 break;
557 case OPTV_FREQ:
558 if (*s == '\0') {
559 xf86DrvMsg(scrnIndex, X_WARNING,
560 "Option \"%s\" requires a frequency value\n",
561 p->name);
562 p->found = FALSE;
563 } else {
564 double freq = strtod(s, &end);
565 int units = 0;
567 if (end != s) {
568 p->found = TRUE;
569 if (!xf86NameCmp(end, "Hz"))
570 units = 1;
571 else if (!xf86NameCmp(end, "kHz") ||
572 !xf86NameCmp(end, "k"))
573 units = 1000;
574 else if (!xf86NameCmp(end, "MHz") ||
575 !xf86NameCmp(end, "M"))
576 units = 1000000;
577 else {
578 xf86DrvMsg(scrnIndex, X_WARNING,
579 "Option \"%s\" requires a frequency value\n",
580 p->name);
581 p->found = FALSE;
583 if (p->found)
584 freq *= (double)units;
585 } else {
586 xf86DrvMsg(scrnIndex, X_WARNING,
587 "Option \"%s\" requires a frequency value\n",
588 p->name);
589 p->found = FALSE;
591 if (p->found) {
592 p->value.freq.freq = freq;
593 p->value.freq.units = units;
596 break;
597 case OPTV_NONE:
598 /* Should never get here */
599 p->found = FALSE;
600 break;
602 if (p->found && markUsed) {
603 int verb = 2;
604 if (wasUsed)
605 verb = 4;
606 xf86DrvMsgVerb(scrnIndex, X_CONFIG, verb, "Option \"%s\"", p->name);
607 if (!(p->type == OPTV_BOOLEAN && *s == 0)) {
608 xf86ErrorFVerb(verb, " \"%s\"", s);
610 xf86ErrorFVerb(verb, "\n");
612 } else if (p->type == OPTV_BOOLEAN) {
613 /* Look for matches with options with or without a "No" prefix. */
614 char *n, *newn;
615 OptionInfoRec opt;
617 n = xf86NormalizeName(p->name);
618 if (!n) {
619 p->found = FALSE;
620 return FALSE;
622 if (strncmp(n, "no", 2) == 0) {
623 newn = n + 2;
624 } else {
625 xfree(n);
626 n = xalloc(strlen(p->name) + 2 + 1);
627 if (!n) {
628 p->found = FALSE;
629 return FALSE;
631 strcpy(n, "No");
632 strcat(n, p->name);
633 newn = n;
635 if ((s = xf86findOptionValue(options, newn)) != NULL) {
636 if (markUsed)
637 xf86MarkOptionUsedByName(options, newn);
638 if (GetBoolValue(&opt, s)) {
639 p->value.bool = !opt.value.bool;
640 p->found = TRUE;
641 } else {
642 xf86DrvMsg(scrnIndex, X_WARNING,
643 "Option \"%s\" requires a boolean value\n", newn);
644 p->found = FALSE;
646 } else {
647 p->found = FALSE;
649 if (p->found && markUsed) {
650 xf86DrvMsgVerb(scrnIndex, X_CONFIG, 2, "Option \"%s\"", newn);
651 if (*s != 0) {
652 xf86ErrorFVerb(2, " \"%s\"", s);
654 xf86ErrorFVerb(2, "\n");
656 xfree(n);
657 } else {
658 p->found = FALSE;
660 return p->found;
664 _X_EXPORT void
665 xf86ProcessOptions(int scrnIndex, pointer options, OptionInfoPtr optinfo)
667 OptionInfoPtr p;
669 for (p = optinfo; p->name != NULL; p++) {
670 ParseOptionValue(scrnIndex, options, p, TRUE);
675 _X_EXPORT OptionInfoPtr
676 xf86TokenToOptinfo(const OptionInfoRec *table, int token)
678 const OptionInfoRec *p, *match = NULL, *set = NULL;
680 if (!table) {
681 ErrorF("xf86TokenToOptinfo: table is NULL\n");
682 return NULL;
685 for (p = table; p->token >= 0; p++) {
686 if (p->token == token) {
687 match = p;
688 if (p->found)
689 set = p;
693 if (set)
694 return (OptionInfoPtr)set;
695 else if (match)
696 return (OptionInfoPtr)match;
697 else
698 return NULL;
702 _X_EXPORT const char *
703 xf86TokenToOptName(const OptionInfoRec *table, int token)
705 const OptionInfoRec *p;
707 p = xf86TokenToOptinfo(table, token);
708 return p->name;
712 _X_EXPORT Bool
713 xf86IsOptionSet(const OptionInfoRec *table, int token)
715 OptionInfoPtr p;
717 p = xf86TokenToOptinfo(table, token);
718 return (p && p->found);
722 _X_EXPORT char *
723 xf86GetOptValString(const OptionInfoRec *table, int token)
725 OptionInfoPtr p;
727 p = xf86TokenToOptinfo(table, token);
728 if (p && p->found)
729 return p->value.str;
730 else
731 return NULL;
735 _X_EXPORT Bool
736 xf86GetOptValInteger(const OptionInfoRec *table, int token, int *value)
738 OptionInfoPtr p;
740 p = xf86TokenToOptinfo(table, token);
741 if (p && p->found) {
742 *value = p->value.num;
743 return TRUE;
744 } else
745 return FALSE;
749 _X_EXPORT Bool
750 xf86GetOptValULong(const OptionInfoRec *table, int token, unsigned long *value)
752 OptionInfoPtr p;
754 p = xf86TokenToOptinfo(table, token);
755 if (p && p->found) {
756 *value = p->value.num;
757 return TRUE;
758 } else
759 return FALSE;
763 _X_EXPORT Bool
764 xf86GetOptValReal(const OptionInfoRec *table, int token, double *value)
766 OptionInfoPtr p;
768 p = xf86TokenToOptinfo(table, token);
769 if (p && p->found) {
770 *value = p->value.realnum;
771 return TRUE;
772 } else
773 return FALSE;
777 _X_EXPORT Bool
778 xf86GetOptValFreq(const OptionInfoRec *table, int token,
779 OptFreqUnits expectedUnits, double *value)
781 OptionInfoPtr p;
783 p = xf86TokenToOptinfo(table, token);
784 if (p && p->found) {
785 if (p->value.freq.units > 0) {
786 /* Units give, so the scaling is known. */
787 switch (expectedUnits) {
788 case OPTUNITS_HZ:
789 *value = p->value.freq.freq;
790 break;
791 case OPTUNITS_KHZ:
792 *value = p->value.freq.freq / 1000.0;
793 break;
794 case OPTUNITS_MHZ:
795 *value = p->value.freq.freq / 1000000.0;
796 break;
798 } else {
799 /* No units given, so try to guess the scaling. */
800 switch (expectedUnits) {
801 case OPTUNITS_HZ:
802 *value = p->value.freq.freq;
803 break;
804 case OPTUNITS_KHZ:
805 if (p->value.freq.freq > 1000.0)
806 *value = p->value.freq.freq / 1000.0;
807 else
808 *value = p->value.freq.freq;
809 break;
810 case OPTUNITS_MHZ:
811 if (p->value.freq.freq > 1000000.0)
812 *value = p->value.freq.freq / 1000000.0;
813 else if (p->value.freq.freq > 1000.0)
814 *value = p->value.freq.freq / 1000.0;
815 else
816 *value = p->value.freq.freq;
819 return TRUE;
820 } else
821 return FALSE;
825 _X_EXPORT Bool
826 xf86GetOptValBool(const OptionInfoRec *table, int token, Bool *value)
828 OptionInfoPtr p;
830 p = xf86TokenToOptinfo(table, token);
831 if (p && p->found) {
832 *value = p->value.bool;
833 return TRUE;
834 } else
835 return FALSE;
839 _X_EXPORT Bool
840 xf86ReturnOptValBool(const OptionInfoRec *table, int token, Bool def)
842 OptionInfoPtr p;
844 p = xf86TokenToOptinfo(table, token);
845 if (p && p->found) {
846 return p->value.bool;
847 } else
848 return def;
852 _X_EXPORT int
853 xf86NameCmp(const char *s1, const char *s2)
855 return xf86nameCompare(s1, s2);
858 char *
859 xf86NormalizeName(const char *s)
861 char *ret, *q;
862 const char *p;
864 if (s == NULL)
865 return NULL;
867 ret = xalloc(strlen(s) + 1);
868 for (p = s, q = ret; *p != 0; p++) {
869 switch (*p) {
870 case '_':
871 case ' ':
872 case '\t':
873 continue;
874 default:
875 if (isupper(*p))
876 *q++ = tolower(*p);
877 else
878 *q++ = *p;
881 *q = '\0';
882 return ret;