Merge remote-tracking branch 'origin/master' into njsf-cov
[sxemacs] / src / fns.c
1 /* Random utility Lisp functions.
2    Copyright (C) 1985, 86, 87, 93, 94, 95 Free Software Foundation, Inc.
3    Copyright (C) 1995, 1996 Ben Wing.
4
5 This file is part of SXEmacs
6
7 SXEmacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
19
20
21 /* Synched up with: Mule 2.0, FSF 19.30. */
22
23 /* This file has been Mule-ized. */
24
25 /* Note: FSF 19.30 has bool vectors.  We have bit vectors. */
26
27 /* Hacked on for Mule by Ben Wing, December 1994, January 1995. */
28
29 #include <config.h>
30
31 /* Note on some machines this defines `vector' as a typedef,
32    so make sure we don't use that name in this file.  */
33 #undef vector
34 #define vector *****
35
36 #include "lisp.h"
37
38 #include "sysfile.h"
39
40 #include "buffer.h"
41 #include "bytecode.h"
42 #include "ui/device.h"
43 #include "events/events.h"
44 #include "extents.h"
45 #include "ui/frame.h"
46 #include "systime.h"
47 #include "ui/insdel.h"
48 #include "lstream.h"
49 /* for the categorial views */
50 #include "category.h"
51 #include "seq.h"
52 /* for all the map* funs */
53 #include "map.h"
54
55 \f
56 /* NOTE: This symbol is also used in lread.c */
57 #define FEATUREP_SYNTAX
58
59 Lisp_Object Qstring_lessp, Qstring_greaterp;
60 Lisp_Object Qidentity;
61
62 static int internal_old_equal(Lisp_Object, Lisp_Object, int);
63 Lisp_Object safe_copy_tree(Lisp_Object arg, Lisp_Object vecp, int depth);
64 int internal_equalp(Lisp_Object, Lisp_Object, int);
65
66 static Lisp_Object mark_bit_vector(Lisp_Object obj)
67 {
68         return Qnil;
69 }
70
71 static void
72 print_bit_vector(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
73 {
74         size_t i;
75         Lisp_Bit_Vector *v = XBIT_VECTOR(obj);
76         size_t len = bit_vector_length(v);
77         size_t last = len;
78
79         if (INTP(Vprint_length))
80                 last = min((EMACS_INT) len, XINT(Vprint_length));
81         write_c_string("#*", printcharfun);
82         for (i = 0; i < last; i++) {
83                 if (bit_vector_bit(v, i))
84                         write_c_string("1", printcharfun);
85                 else
86                         write_c_string("0", printcharfun);
87         }
88
89         if (last != len)
90                 write_c_string("...", printcharfun);
91 }
92
93 static int bit_vector_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
94 {
95         Lisp_Bit_Vector *v1 = XBIT_VECTOR(obj1);
96         Lisp_Bit_Vector *v2 = XBIT_VECTOR(obj2);
97
98         return ((bit_vector_length(v1) == bit_vector_length(v2)) &&
99                 !memcmp(v1->bits, v2->bits,
100                         BIT_VECTOR_LONG_STORAGE(bit_vector_length(v1)) *
101                         sizeof(long)));
102 }
103
104 static unsigned long bit_vector_hash(Lisp_Object obj, int depth)
105 {
106         Lisp_Bit_Vector *v = XBIT_VECTOR(obj);
107         return HASH2(bit_vector_length(v),
108                      memory_hash(v->bits,
109                                  BIT_VECTOR_LONG_STORAGE(bit_vector_length(v)) *
110                                  sizeof(long)));
111 }
112
113 static size_t size_bit_vector(const void *lheader)
114 {
115         const Lisp_Bit_Vector *v = (const Lisp_Bit_Vector *) lheader;
116         return FLEXIBLE_ARRAY_STRUCT_SIZEOF(Lisp_Bit_Vector, unsigned long,
117                                             bits,
118                                             BIT_VECTOR_LONG_STORAGE
119                                             (bit_vector_length(v)));
120 }
121
122 static const struct lrecord_description bit_vector_description[] = {
123         {XD_LISP_OBJECT, offsetof(Lisp_Bit_Vector, next)},
124         {XD_END}
125 };
126
127 DEFINE_BASIC_LRECORD_SEQUENCE_IMPLEMENTATION("bit-vector", bit_vector,
128                                              mark_bit_vector, print_bit_vector,
129                                              0, bit_vector_equal,
130                                              bit_vector_hash,
131                                              bit_vector_description,
132                                              size_bit_vector, Lisp_Bit_Vector);
133 \f
134 DEFUN("identity", Fidentity, 1, 1, 0,   /*
135 Return the argument unchanged.
136 */
137       (arg))
138 {
139         return arg;
140 }
141
142 extern long get_random(void);
143 extern void seed_random(long arg);
144
145 DEFUN("random", Frandom, 0, 1, 0,       /*
146 Return a pseudo-random number.
147 All integers representable in Lisp are equally likely.
148 On most systems, this is 31 bits' worth.
149
150 With positive integer argument LIMIT, return random number 
151 in interval [0,LIMIT). LIMIT can be a big integer, in which
152 case the range of possible values is extended.
153
154 With argument t, set the random number seed from the 
155 current time and pid.
156 */
157       (limit))
158 {
159         EMACS_INT val;
160         unsigned long denominator;
161
162         if (EQ(limit, Qt))
163                 seed_random(getpid() + time(NULL));
164         if (NATNUMP(limit) && !ZEROP(limit)) {
165                 /* Try to take our random number from the higher bits of VAL,
166                    not the lower, since (says Gentzel) the low bits of `random'
167                    are less random than the higher ones.  We do this by using the
168                    quotient rather than the remainder.  At the high end of the RNG
169                    it's possible to get a quotient larger than limit; discarding
170                    these values eliminates the bias that would otherwise appear
171                    when using a large limit.  */
172                 denominator = ((unsigned long)1 << INT_VALBITS) / XINT(limit);
173                 do
174                         val = get_random() / denominator;
175                 while (val >= XINT(limit));
176         } else if (ZEROP(limit)) {
177                 return wrong_type_argument(Qpositivep, limit);
178 #if defined HAVE_MPZ && defined WITH_GMP
179         } else if (BIGZP(limit)) {
180                 bigz bz;
181                 Lisp_Object result;
182
183                 if (bigz_sign(XBIGZ_DATA(limit)) <= 0)
184                         return wrong_type_argument(Qpositivep, limit);
185
186                 bigz_init(bz);
187
188                 bigz_random(bz, XBIGZ_DATA(limit));
189                 result = ent_mpz_downgrade_maybe(bz);
190
191                 bigz_fini(bz);
192                 return result;
193 #endif  /* HAVE_MPZ */
194         } else
195                 val = get_random();
196
197         return make_int(val);
198 }
199
200 #if defined(WITH_GMP) && defined(HAVE_MPZ)
201 DEFUN("randomb", Frandomb, 1, 1, 0,     /*
202 Return a uniform pseudo-random number in the range [0, 2^LIMIT).
203 */
204       (limit))
205 {
206         bigz bz;
207         unsigned long limui;
208         Lisp_Object result;
209
210         CHECK_INTEGER(limit);
211
212         if (NILP(Fnonnegativep(limit)))
213                 return wrong_type_argument(Qnonnegativep, limit);
214         else if (INTP(limit))
215                 limui = XINT(limit);
216         else if (BIGZP(limit) && bigz_fits_ulong_p(XBIGZ_DATA(limit)))
217                 limui = bigz_to_ulong(XBIGZ_DATA(limit));
218         else
219                 return wrong_type_argument(Qintegerp, limit);
220
221         bigz_init(bz);
222
223         mpz_urandomb(bz, random_state, limui);
224         result = make_bigz_bz(bz);
225
226         bigz_fini(bz);
227         return result;
228 }
229 #endif  /* HAVE_MPZ */
230
231 \f
232 /* Random data-structure functions */
233
234 #ifdef LOSING_BYTECODE
235
236 /* #### Delete this shit */
237
238 /* Charcount is a misnomer here as we might be dealing with the
239    length of a vector or list, but emphasizes that we're not dealing
240    with Bytecounts in strings */
241 static Charcount length_with_bytecode_hack(Lisp_Object seq)
242 {
243         if (!COMPILED_FUNCTIONP(seq))
244                 return XINT(Flength(seq));
245         else {
246                 Lisp_Compiled_Function *f = XCOMPILED_FUNCTION(seq);
247
248                 return (f->flags.interactivep ? COMPILED_INTERACTIVE :
249                         f->flags.domainp ? COMPILED_DOMAIN :
250                         COMPILED_DOC_STRING)
251                     + 1;
252         }
253 }
254
255 #endif                          /* LOSING_BYTECODE */
256
257 void check_losing_bytecode(const char *function, Lisp_Object seq)
258 {
259         if (COMPILED_FUNCTIONP(seq))
260                 error_with_frob
261                     (seq,
262                      "As of 20.3, `%s' no longer works with compiled-function objects",
263                      function);
264 }
265
266 DEFUN("length", Flength, 1, 1, 0,       /*
267 Return the length of vector, bit vector, list or string SEQUENCE.
268 */
269       (sequence))
270 {
271 #if 1
272 /* that's whither we have to get */
273         if (LIKELY(!NILP(sequence))) {
274                 return make_int(seq_length((seq_t)sequence));
275         } else {
276                 return Qzero;
277         }
278 #elif 0
279 retry:
280         if (LIKELY(STRINGP(sequence) ||
281                    CONSP(sequence) ||
282                    VECTORP(sequence) ||
283                    DLLISTP(sequence) ||
284                    BIT_VECTORP(sequence))) {
285                 return make_int(seq_length(sequence));
286         } else if (NILP(sequence)) {
287                 return Qzero;
288         } else {
289                 check_losing_bytecode("length", sequence);
290                 sequence = wrong_type_argument(Qsequencep, sequence);
291                 goto retry;
292         }
293 #else
294 retry:
295         if (STRINGP(sequence))
296                 return make_int(XSTRING_CHAR_LENGTH(sequence));
297         else if (CONSP(sequence)) {
298                 return make_int(seq_length(sequence));
299         } else if (VECTORP(sequence))
300                 return make_int(seq_length(sequence));
301         else if (DLLISTP(sequence))
302                 return make_int(XDLLIST_SIZE(sequence));
303         else if (NILP(sequence))
304                 return Qzero;
305         else if (BIT_VECTORP(sequence))
306                 return make_int(bit_vector_length(XBIT_VECTOR(sequence)));
307         else {
308                 check_losing_bytecode("length", sequence);
309                 sequence = wrong_type_argument(Qsequencep, sequence);
310                 goto retry;
311         }
312 #endif
313 }
314
315 DEFUN("safe-length", Fsafe_length, 1, 1, 0,     /*
316 Return the length of a list, but avoid error or infinite loop.
317 This function never gets an error.  If LIST is not really a list,
318 it returns 0.  If LIST is circular, it returns a finite value
319 which is at least the number of distinct elements.
320 */
321       (list))
322 {
323         Lisp_Object hare, tortoise;
324         size_t len;
325
326         for (hare = tortoise = list, len = 0;
327              CONSP(hare) && (!EQ(hare, tortoise) || len == 0);
328              hare = XCDR(hare), len++) {
329                 if (len & 1)
330                         tortoise = XCDR(tortoise);
331         }
332
333         return make_int(len);
334 }
335
336 /*** string functions. ***/
337
338 DEFUN("string-equal", Fstring_equal, 2, 2, 0,   /*
339 Return t if two strings have identical contents.
340 Case is significant.  Text properties are ignored.
341 \(Under SXEmacs, `equal' also ignores text properties and extents in
342 strings, but this is not the case under FSF Emacs 19.  In FSF Emacs 20
343 `equal' is the same as in SXEmacs, in that respect.)
344 Symbols are also allowed; their print names are used instead.
345 */
346       (string1, string2))
347 {
348         Bytecount len;
349         Lisp_String *p1, *p2;
350
351         if (SYMBOLP(string1))
352                 p1 = XSYMBOL(string1)->name;
353         else {
354                 CHECK_STRING(string1);
355                 p1 = XSTRING(string1);
356         }
357
358         if (SYMBOLP(string2))
359                 p2 = XSYMBOL(string2)->name;
360         else {
361                 CHECK_STRING(string2);
362                 p2 = XSTRING(string2);
363         }
364
365         return (((len = string_length(p1)) == string_length(p2)) &&
366                 !memcmp(string_data(p1), string_data(p2), len)) ? Qt : Qnil;
367 }
368
369 DEFUN("string-lessp", Fstring_lessp, 2, 2, 0,   /*
370 Return t if first arg string is less than second in lexicographic order.
371 If I18N2 support (but not Mule support) was compiled in, ordering is
372 determined by the locale. (Case is significant for the default C locale.)
373 In all other cases, comparison is simply done on a character-by-
374 character basis using the numeric value of a character. (Note that
375 this may not produce particularly meaningful results under Mule if
376 characters from different charsets are being compared.)
377
378 Symbols are also allowed; their print names are used instead.
379
380 The reason that the I18N2 locale-specific collation is not used under
381 Mule is that the locale model of internationalization does not handle
382 multiple charsets and thus has no hope of working properly under Mule.
383 What we really should do is create a collation table over all built-in
384 charsets.  This is extremely difficult to do from scratch, however.
385
386 Unicode is a good first step towards solving this problem.  In fact,
387 it is quite likely that a collation table exists (or will exist) for
388 Unicode.  When Unicode support is added to SXEmacs/Mule, this problem
389 may be solved.
390 */
391       (string1, string2))
392 {
393         Lisp_String *p1, *p2;
394         Charcount end, len2;
395         int i;
396
397         if (SYMBOLP(string1))
398                 p1 = XSYMBOL(string1)->name;
399         else {
400                 CHECK_STRING(string1);
401                 p1 = XSTRING(string1);
402         }
403
404         if (SYMBOLP(string2))
405                 p2 = XSYMBOL(string2)->name;
406         else {
407                 CHECK_STRING(string2);
408                 p2 = XSTRING(string2);
409         }
410
411         end = string_char_length(p1);
412         len2 = string_char_length(p2);
413         if (end > len2)
414                 end = len2;
415
416 #if defined (I18N2) && !defined (MULE)
417         /* There is no hope of this working under Mule.  Even if we converted
418            the data into an external format so that strcoll() processed it
419            properly, it would still not work because strcoll() does not
420            handle multiple locales.  This is the fundamental flaw in the
421            locale model. */
422         {
423                 Bytecount bcend = charcount_to_bytecount(string_data(p1), end);
424                 /* Compare strings using collation order of locale. */
425                 /* Need to be tricky to handle embedded nulls. */
426
427                 for (i = 0; i < bcend;
428                      i += strlen((char *)string_data(p1) + i) + 1) {
429                         int val = strcoll((char *)string_data(p1) + i,
430                                           (char *)string_data(p2) + i);
431                         if (val < 0)
432                                 return Qt;
433                         if (val > 0)
434                                 return Qnil;
435                 }
436         }
437 #else                           /* not I18N2, or MULE */
438         {
439                 Bufbyte *ptr1 = string_data(p1);
440                 Bufbyte *ptr2 = string_data(p2);
441
442                 /* #### It is not really necessary to do this: We could compare
443                    byte-by-byte and still get a reasonable comparison, since this
444                    would compare characters with a charset in the same way.  With
445                    a little rearrangement of the leading bytes, we could make most
446                    inter-charset comparisons work out the same, too; even if some
447                    don't, this is not a big deal because inter-charset comparisons
448                    aren't really well-defined anyway. */
449                 for (i = 0; i < end; i++) {
450                         if (charptr_emchar(ptr1) != charptr_emchar(ptr2))
451                                 return charptr_emchar(ptr1) <
452                                     charptr_emchar(ptr2) ? Qt : Qnil;
453                         INC_CHARPTR(ptr1);
454                         INC_CHARPTR(ptr2);
455                 }
456         }
457 #endif                          /* not I18N2, or MULE */
458         /* Can't do i < len2 because then comparison between "foo" and "foo^@"
459            won't work right in I18N2 case */
460         return end < len2 ? Qt : Qnil;
461 }
462
463 DEFUN("string-greaterp", Fstring_greaterp, 2, 2, 0, /*
464 Return t if first arg string is greater than second in lexicographic order.
465 If I18N2 support (but not Mule support) was compiled in, ordering is
466 determined by the locale. (Case is significant for the default C locale.)
467 In all other cases, comparison is simply done on a character-by-
468 character basis using the numeric value of a character. (Note that
469 this may not produce particularly meaningful results under Mule if
470 characters from different charsets are being compared.)
471
472 Symbols are also allowed; their print names are used instead.
473
474 The reason that the I18N2 locale-specific collation is not used under
475 Mule is that the locale model of internationalization does not handle
476 multiple charsets and thus has no hope of working properly under Mule.
477 What we really should do is create a collation table over all built-in
478 charsets.  This is extremely difficult to do from scratch, however.
479
480 Unicode is a good first step towards solving this problem.  In fact,
481 it is quite likely that a collation table exists (or will exist) for
482 Unicode.  When Unicode support is added to SXEmacs/Mule, this problem
483 may be solved.
484 */
485       (string1, string2))
486 {
487         return Fstring_lessp(string2, string1);
488 }
489
490 DEFUN("string-modified-tick", Fstring_modified_tick, 1, 1, 0,   /*
491 Return STRING's tick counter, incremented for each change to the string.
492 Each string has a tick counter which is incremented each time the contents
493 of the string are changed (e.g. with `aset').  It wraps around occasionally.
494 */
495       (string))
496 {
497         Lisp_String *s;
498
499         CHECK_STRING(string);
500         s = XSTRING(string);
501         if (CONSP(s->plist) && INTP(XCAR(s->plist)))
502                 return XCAR(s->plist);
503         else
504                 return Qzero;
505 }
506
507 void bump_string_modiff(Lisp_Object str)
508 {
509         Lisp_String *s = XSTRING(str);
510         Lisp_Object *ptr = &s->plist;
511
512 #ifdef I18N3
513         /* #### remove the `string-translatable' property from the string,
514            if there is one. */
515 #endif
516         /* skip over extent info if it's there */
517         if (CONSP(*ptr) && EXTENT_INFOP(XCAR(*ptr)))
518                 ptr = &XCDR(*ptr);
519         if (CONSP(*ptr) && INTP(XCAR(*ptr)))
520                 XSETINT(XCAR(*ptr), 1 + XINT(XCAR(*ptr)));
521         else
522                 *ptr = Fcons(make_int(1), *ptr);
523 }
524 \f
525 enum concat_target_type { c_cons, c_string, c_vector, c_bit_vector, c_dllist };
526 static Lisp_Object concat(int nargs, Lisp_Object * args,
527                           enum concat_target_type target_type,
528                           int last_special);
529
530 Lisp_Object concat2(Lisp_Object string1, Lisp_Object string2)
531 {
532         Lisp_Object args[2];
533         args[0] = string1;
534         args[1] = string2;
535         return concat(2, args, c_string, 0);
536 }
537
538 Lisp_Object
539 concat3(Lisp_Object string1, Lisp_Object string2, Lisp_Object string3)
540 {
541         Lisp_Object args[3];
542         args[0] = string1;
543         args[1] = string2;
544         args[2] = string3;
545         return concat(3, args, c_string, 0);
546 }
547
548 Lisp_Object vconcat2(Lisp_Object vec1, Lisp_Object vec2)
549 {
550         Lisp_Object args[2];
551         args[0] = vec1;
552         args[1] = vec2;
553         return concat(2, args, c_vector, 0);
554 }
555
556 Lisp_Object vconcat3(Lisp_Object vec1, Lisp_Object vec2, Lisp_Object vec3)
557 {
558         Lisp_Object args[3];
559         args[0] = vec1;
560         args[1] = vec2;
561         args[2] = vec3;
562         return concat(3, args, c_vector, 0);
563 }
564
565 DEFUN("append", Fappend, 0, MANY, 0,    /*
566 Concatenate all the arguments and make the result a list.
567 The result is a list whose elements are the elements of all the arguments.
568 Each argument may be a list, vector, bit vector, or string.
569 The last argument is not copied, just used as the tail of the new list.
570 Also see: `nconc'.
571 */
572       (int nargs, Lisp_Object * args))
573 {
574         return concat(nargs, args, c_cons, 1);
575 }
576
577 DEFUN("concat", Fconcat, 0, MANY, 0,    /*
578 Concatenate all the arguments and make the result a string.
579 The result is a string whose elements are the elements of all the arguments.
580 Each argument may be a string or a list or vector of characters.
581
582 As of XEmacs 21.0, this function does NOT accept individual integers
583 as arguments.  Old code that relies on, for example, (concat "foo" 50)
584 returning "foo50" will fail.  To fix such code, either apply
585 `int-to-string' to the integer argument, or use `format'.
586 */
587       (int nargs, Lisp_Object * args))
588 {
589         return concat(nargs, args, c_string, 0);
590 }
591
592 DEFUN("vconcat", Fvconcat, 0, MANY, 0,  /*
593 Concatenate all the arguments and make the result a vector.
594 The result is a vector whose elements are the elements of all the arguments.
595 Each argument may be a list, vector, bit vector, or string.
596 */
597       (int nargs, Lisp_Object * args))
598 {
599         return concat(nargs, args, c_vector, 0);
600 }
601
602 DEFUN("bvconcat", Fbvconcat, 0, MANY, 0,        /*
603 Concatenate all the arguments and make the result a bit vector.
604 The result is a bit vector whose elements are the elements of all the
605 arguments.  Each argument may be a list, vector, bit vector, or string.
606 */
607       (int nargs, Lisp_Object * args))
608 {
609         return concat(nargs, args, c_bit_vector, 0);
610 }
611
612 /* Copy a (possibly dotted) list.  LIST must be a cons.
613    Can't use concat (1, &alist, c_cons, 0) - doesn't handle dotted lists. */
614 static Lisp_Object copy_list(Lisp_Object list)
615 {
616         Lisp_Object list_copy = Fcons(XCAR(list), XCDR(list));
617         Lisp_Object last = list_copy;
618         Lisp_Object hare, tortoise;
619         size_t len;
620
621         for (tortoise = hare = XCDR(list), len = 1;
622              CONSP(hare); hare = XCDR(hare), len++) {
623                 XCDR(last) = Fcons(XCAR(hare), XCDR(hare));
624                 last = XCDR(last);
625
626                 if (len < CIRCULAR_LIST_SUSPICION_LENGTH)
627                         continue;
628                 if (len & 1)
629                         tortoise = XCDR(tortoise);
630                 if (EQ(tortoise, hare))
631                         signal_circular_list_error(list);
632         }
633
634         return list_copy;
635 }
636
637 DEFUN("copy-list", Fcopy_list, 1, 1, 0, /*
638 Return a copy of list LIST, which may be a dotted list.
639 The elements of LIST are not copied; they are shared
640 with the original.
641 */
642       (list))
643 {
644       again:
645         if (NILP(list))
646                 return list;
647         if (CONSP(list))
648                 return copy_list(list);
649
650         list = wrong_type_argument(Qlistp, list);
651         goto again;
652 }
653
654 DEFUN("copy-sequence", Fcopy_sequence, 1, 1, 0, /*
655 Return a copy of list, dllist, vector, bit vector or string SEQUENCE.
656 The elements of a list or vector are not copied; they are shared
657 with the original. SEQUENCE may be a dotted list.
658 */
659       (sequence))
660 {
661       again:
662         if (NILP(sequence))
663                 return sequence;
664         if (CONSP(sequence))
665                 return copy_list(sequence);
666         if (DLLISTP(sequence))
667                 return Fcopy_dllist(sequence);
668         if (STRINGP(sequence))
669                 return concat(1, &sequence, c_string, 0);
670         if (VECTORP(sequence))
671                 return concat(1, &sequence, c_vector, 0);
672         if (BIT_VECTORP(sequence))
673                 return concat(1, &sequence, c_bit_vector, 0);
674
675         check_losing_bytecode("copy-sequence", sequence);
676         sequence = wrong_type_argument(Qsequencep, sequence);
677         goto again;
678 }
679
680 struct merge_string_extents_struct {
681         Lisp_Object string;
682         Bytecount entry_offset;
683         Bytecount entry_length;
684 };
685
686 static Lisp_Object
687 concat(int nargs, Lisp_Object * args,
688        enum concat_target_type target_type, int last_special)
689 {
690         Lisp_Object val;
691         Lisp_Object tail = Qnil;
692         int toindex;
693         int argnum;
694         Lisp_Object last_tail;
695         Lisp_Object prev;
696         struct merge_string_extents_struct *args_mse = 0;
697         Bufbyte *string_result = NULL;
698         Bufbyte *string_result_ptr = NULL;
699         struct gcpro gcpro1;
700         int speccount = specpdl_depth();
701         Charcount total_length;
702         
703
704         /* The modus operandi in Emacs is "caller gc-protects args".
705            However, concat is called many times in Emacs on freshly
706            created stuff.  So we help those callers out by protecting
707            the args ourselves to save them a lot of temporary-variable
708            grief. */
709
710         GCPROn(args, nargs);
711
712 #ifdef I18N3
713         /* #### if the result is a string and any of the strings have a string
714            for the `string-translatable' property, then concat should also
715            concat the args but use the `string-translatable' strings, and store
716            the result in the returned string's `string-translatable' property. */
717 #endif
718         if (target_type == c_string)
719                 XMALLOC_OR_ALLOCA(args_mse, nargs, struct merge_string_extents_struct);
720
721         /* In append, the last arg isn't treated like the others */
722         if (last_special && nargs > 0) {
723                 nargs--;
724                 last_tail = args[nargs];
725         } else
726                 last_tail = Qnil;
727
728         /* Check and coerce the arguments. */
729         for (argnum = 0; argnum < nargs; argnum++) {
730                 Lisp_Object seq = args[argnum];
731                 if (LISTP(seq) || DLLISTP(seq)) ;
732                 else if (VECTORP(seq) || STRINGP(seq) || BIT_VECTORP(seq)) ;
733 #ifdef LOSING_BYTECODE
734                 else if (COMPILED_FUNCTIONP(seq))
735                         /* Urk!  We allow this, for "compatibility"... */
736                         ;
737 #endif
738 #if 0                           /* removed for XEmacs 21 */
739                 else if (INTP(seq))
740                         /* This is too revolting to think about but maintains
741                            compatibility with FSF (and lots and lots of old code). */
742      &