2 * hre_api.c: Implementation of HRE API
4 * Created On: Wed Dec 9 13:49:14 1992
5 * Last Modified By: James Kempf
6 * Last Modified On: Fri Sep 23 13:49:04 1994
8 * Copyright (c) 1994 by Sun Microsystems Computer Company
11 * Use and copying of this software and preparation of
12 * derivative works based upon this software are permitted.
13 * Any distribution of this software or derivative works
14 * must comply with all applicable United States export control
17 * This software is made available as is, and Sun Microsystems
18 * Computer Company makes no warranty about the software, its
19 * performance, or its conformity to any specification
27 #include "scribbleimpl.h"
28 #include "hre_internal.h"
30 /* ari -- prototype for rii function */
31 recognizer __recognizer_internal_initialize(rec_info* ri);
33 /*Version number of API.*/
35 char* REC_VERSION = "2.0";
37 /*Domain name for internationalized text.*/
39 #define INTL_DOMAIN "recognition_manager"
41 /* XXX -- Intl Hack -- Jay & Ari */
42 #define dgettext(domain, msg) (msg)
43 #define bindtextdomain(dirname, domain)
46 * These magic numbers are used to ensure the integrity of the
47 * recognizer structure.
51 #define REC_MAGIC 0xfeed
52 #define REC_END_MAGIC 0xbeef
54 /*Check the recognizer for validity*/
56 #define RI_CHECK_MAGIC(rec) \
58 (((recognizer)rec)->recognizer_magic == REC_MAGIC) && \
59 (((recognizer)rec)->recognizer_end_magic == REC_END_MAGIC) &&\
60 (((recognizer)rec)->recognizer_version == REC_VERSION) )
62 /*The name of the initialization & finalization functions.*/
64 /* static char rii_name[] = "__recognizer_internal_initialize";
65 static char rif_name[] = "__recognizer_internal_finalize"; */
67 /*User home directory for recognizer info.*/
68 /* ari -- changed USERRECHOME from ".recognizers" */
70 #define USERRECHOME ".classifiers"
74 static char* shared_library_name(char* directory,char* locale,char* name);
75 static rec_info* make_rec_info(char* directory,char* name,char** subset);
76 static void delete_rec_info(rec_info* ri);
77 static int check_for_user_home(void);
78 static void intl_initialize(void);
80 static void cleanup_rec_element(rec_element* re,bool delete_points_p);
84 static char* the_last_error = nil;
86 static char *safe_malloc (int nbytes)
88 char *res = malloc(nbytes);
90 sysfatal("malloc failure");
97 * Implementation of API functions
101 * recognizer_load - Load the recognizer matching the rec_info struct.
102 * If name is not null, then load the recognizer having that name. Returns
103 * the recognizer object, or null if it can't load the recognizer, and
104 * sets errno to indicate why.
108 recognizer_load(char* directory, char* name, char** subset)
110 recognizer rec; /*the recognizer*/
111 rec_info* rinf; /*rec_info for recognizer information*/
112 static bool intl_init = false; /*true if recog. manager initted.*/
114 if( intl_init == false ) {
119 /*The name takes precedence.*/
120 rinf = make_rec_info(directory, name, subset);
123 dgettext(INTL_DOMAIN,
124 "Ran out of memory during prelinking initialization.");
125 return((recognizer)nil);
127 /* fprint(2, "Got past make_rec_info.\n"); */
129 /*Let recognition code create recognizer and initialize*/
130 rec = __recognizer_internal_initialize(rinf);
132 return((recognizer)nil);
134 /* fprint(2, "Did rii.\n"); */
135 /*Check whether it's been correctly initialized*/
137 if( rec->recognizer_load_state == nil ||
138 rec->recognizer_save_state == nil ||
139 rec->recognizer_load_dictionary == nil ||
140 rec->recognizer_save_dictionary == nil ||
141 rec->recognizer_free_dictionary == nil ||
142 rec->recognizer_add_to_dictionary == nil ||
143 rec->recognizer_delete_from_dictionary == nil ||
144 rec->recognizer_error == nil ||
145 rec->recognizer_set_context == nil ||
146 rec->recognizer_get_context == nil ||
147 rec->recognizer_clear == nil ||
148 rec->recognizer_get_buffer == nil ||
149 rec->recognizer_set_buffer == nil ||
150 rec->recognizer_translate == nil ||
151 rec->recognizer_get_extension_functions == nil ||
152 rec->recognizer_get_gesture_names == nil ||
153 rec->recognizer_set_gesture_action == nil
156 recognizer_unload(rec);
157 /* fprint(2, "Unloading b/c null function pointer.\n"); */
159 dgettext(INTL_DOMAIN,
160 "One or more recognizer function pointers is nil.");
161 return((recognizer)nil);
165 /*Set the rec_info structure.*/
167 rec->recognizer_info = rinf;
169 /*Check whether home directory is there for recognizer info.*/
172 * ari -- don't bother. We're not going to load from each user's
173 * home directory at this point. Instead, we'll use a stupid
174 * little a-b-c file because it loads FAST.
176 * if( check_for_user_home() < 0 ) {
177 * recognizer_unload(rec);
178 * return((recognizer)nil);
182 /* fprint(2, "Done.\n"); */
188 * recognizer_unload - Unload the recognizer.
192 recognizer_unload(recognizer rec)
194 /*Make sure magic numbers right.*/
196 if( !RI_CHECK_MAGIC(rec) ) {
197 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
201 return __recognizer_internal_finalize(rec);
205 * recognizer_load_state-Get any recognizer state associated with name
206 * in dir. Note that name may not be simple file name, since
207 * there may be more than one file involved. Return 0 if successful,
211 int recognizer_load_state(recognizer rec, char* dir, char* name)
213 /*Make sure magic numbers right.*/
215 if( !RI_CHECK_MAGIC(rec) ) {
216 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
222 return(rec->recognizer_load_state(rec, dir, name));
226 * recognizer_save_state-Save any recognizer state to name
227 * in dir. Note that name may not be a simple file name, since
228 * there may be more than one file involved. Return 0 if successful,
232 int recognizer_save_state(recognizer rec,char* dir,char* name)
234 /*Make sure magic numbers right.*/
236 if( !RI_CHECK_MAGIC(rec) ) {
237 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
243 return(rec->recognizer_save_state(rec,dir,name));
247 * recognizer_load_dictionary-Load dictionary, return pointer
248 * to it, or nil if error.
251 wordset recognizer_load_dictionary(recognizer rec,char* dir,char* name)
253 /*Make sure magic numbers right.*/
255 if( !RI_CHECK_MAGIC(rec) ) {
256 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
262 return(rec->recognizer_load_dictionary(rec,dir,name));
266 * recognizer_save_dictionary-Save the dictionary to the file, return 0 if
270 int recognizer_save_dictionary(recognizer rec,char* dir,char* name,wordset dict)
272 /*Make sure magic numbers right.*/
274 if( !RI_CHECK_MAGIC(rec) ) {
275 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
281 return(rec->recognizer_save_dictionary(rec,dir,name,dict));
285 * recognizer_free_dictionary-Free the dictionary, return 0 if
289 int recognizer_free_dictionary(recognizer rec,wordset dict)
291 /*Make sure magic numbers right.*/
293 if( !RI_CHECK_MAGIC(rec) ) {
294 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
300 return(rec->recognizer_free_dictionary(rec,dict));
304 * recognizer_add_to_dictionary-Add word to the dictionary,
305 * return 0 if OK, -1 if error.
309 int recognizer_add_to_dictionary(recognizer rec,letterset* word,wordset dict)
311 /*Make sure magic numbers right.*/
313 if( !RI_CHECK_MAGIC(rec) ) {
314 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
320 return(rec->recognizer_add_to_dictionary(rec,word,dict));
324 * recognizer_delete_from_dictionary-Delete word from the dictionary,
325 * return 0 if OK, -1 if error.
329 recognizer_delete_from_dictionary(recognizer rec,letterset* word,wordset dict)
331 /*Make sure magic numbers right.*/
333 if( !RI_CHECK_MAGIC(rec) ) {
334 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
340 return(rec->recognizer_delete_from_dictionary(rec,word,dict));
344 * recognizer_get_info-Get a pointers to the rec_info
345 * giving the locales and subsets supported by the recognizer
346 * and the shared library pathname.
350 recognizer_get_info(recognizer rec)
352 /*Make sure magic numbers right.*/
354 if( !RI_CHECK_MAGIC(rec) ) {
355 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
356 return((rec_info*)nil);
359 /*Return the rec_info object.*/
361 return(rec->recognizer_info);
365 * recognizer_manager_version-Return the version number string of the
366 * recognition manager.
369 const char* recognizer_manager_version(recognizer rec)
371 /*Make sure magic numbers right.*/
373 if( !RI_CHECK_MAGIC(rec) ) {
374 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
378 return(rec->recognizer_version);
382 * recognizer_error-Return the last error message, or nil if none.
385 char* recognizer_error(recognizer rec)
388 /*Make sure magic numbers right and function there.*/
390 if( !RI_CHECK_MAGIC(rec) && the_last_error == nil ) {
391 return(dgettext(INTL_DOMAIN,"Bad recognizer object."));
393 } else if( the_last_error != nil ) {
394 char* error = the_last_error;
396 the_last_error = nil;
402 return(rec->recognizer_error(rec));
406 * recognizer_set_context-Set the recognition context for translation.
407 * Return 0 if successful, -1 if error.
410 int recognizer_set_context(recognizer rec,rc* rec_xt)
413 /*Make sure magic numbers right.*/
415 if( !RI_CHECK_MAGIC(rec) ) {
416 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
422 return(rec->recognizer_set_context(rec,rec_xt));
426 * recognzier_get_context-Get the recognition context for translation.
427 * If none or error, return nil.
430 rc* recognizer_get_context(recognizer rec)
433 /*Make sure magic numbers right.*/
435 if( !RI_CHECK_MAGIC(rec) ) {
436 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
442 return(rec->recognizer_get_context(rec));
446 * recognizer_clear-Clear buffer and recognition context.
447 * Return 0 if success, else -1.
450 int recognizer_clear(recognizer rec,bool delete_points_p)
453 /*Make sure magic numbers right.*/
455 if( !RI_CHECK_MAGIC(rec) ) {
456 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
462 return(rec->recognizer_clear(rec,delete_points_p));
465 /*recognizer_get_buffer-Get stroke buffer. Return 0 if success, else -1.*/
468 int recognizer_get_buffer(recognizer rec, uint* nstrokes,Stroke** strokes)
471 /*Make sure magic numbers right.*/
473 if( !RI_CHECK_MAGIC(rec) ) {
474 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
480 return(rec->recognizer_get_buffer(rec,nstrokes,strokes));
485 * recognizer_set_buffer-Set stroke buffer to arg. Return 0 if success, else
489 int recognizer_set_buffer(recognizer rec,uint nstrokes,Stroke* strokes)
492 /*Make sure magic numbers right.*/
494 if( !RI_CHECK_MAGIC(rec) ) {
495 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
501 return(rec->recognizer_set_buffer(rec,nstrokes,strokes));
506 * recognizer_translate-Translate the strokes in the current context, including
507 * buffered strokes. If nstrokes == 0 or strokes == nil, return
508 * translation of stroke buffer.
511 int recognizer_translate(recognizer rec,
516 rec_alternative** ret)
520 /*Make sure magic numbers right.*/
522 if( !RI_CHECK_MAGIC(rec) ) {
523 the_last_error = dgettext(INTL_DOMAIN, msg);
531 * pen_point* ari_pts;
533 * for (i = 0; i < nstrokes; i++) {
534 * ari_pstr = strokes[i];
535 * ari_pts = ari_pstr.ps_pts;
536 * fprint(2, "\nrecognizer_translate: ari_pts = %ld, sizeof(Time) = %d, sizeof(ari_pts[0] = %d, %d points are...\n", ari_pts, sizeof(Time), sizeof(ari_pts[0]), ari_pstr.ps_npts);
537 * for (ari = 0; ari < ari_pstr.ps_npts; ari++)
538 * fprint(2, "%ld -- (%d, %d) ", ari_pts[ari], ari_pts[ari].x, ari_pts[ari].y);
543 /* ari -- this is calling cmu_recognizer_translate */
544 retval = rec->recognizer_translate(rec,
555 * recognizer_get_extension_functions-Return a null terminated array
556 * of functions providing extended functionality. Their interfaces
557 * will change depending on the recognizer.
560 rec_fn* recognizer_get_extension_functions(recognizer rec)
562 /*Make sure magic numbers right.*/
564 if( !RI_CHECK_MAGIC(rec) ) {
565 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
566 return((rec_fn*)nil);
571 return(rec->recognizer_get_extension_functions(rec));
576 * recognizer_get_gesture_names - Return a null terminated array of
577 * gesture name strings.
581 recognizer_get_gesture_names(recognizer rec)
583 /*Make sure magic numbers right.*/
585 if( !RI_CHECK_MAGIC(rec) ) {
586 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
592 return(rec->recognizer_get_gesture_names(rec));
596 * recognizer_set_gesture_action-Set the action function for the gesture.
600 recognizer_train_gestures(recognizer rec,char* name,xgesture fn,void* wsinfo)
602 /*Make sure magic numbers right.*/
604 if( !RI_CHECK_MAGIC(rec) ) {
605 the_last_error = dgettext(INTL_DOMAIN,"Bad recognizer object.");
606 return((xgesture)-1);
611 return(rec->recognizer_set_gesture_action(rec,name,fn,wsinfo));
619 * shared_library_name-Get the full pathname to the shared library,
620 * based on the recognizer name and the environment.
624 static char* shared_library_name(char* directory,char* locale,char* name)
627 int len = strlen(name);
629 /*If directory is there, it takes precedence.*/
631 if( directory != nil ) {
632 ret = (char*)safe_malloc(strlen(directory) + len + 2);
633 strcpy(ret,directory);
639 /*First try the environment variable.*/
641 if( (dir = getenv(RECHOME)) == nil ) {
642 dir = "REC_DEFAULT_HOME_DIR";
646 ret = (char*)safe_malloc(strlen(dir) + strlen(locale) + len + 3);
647 /*Form the pathname.*/
659 * intl_initialize-Initialize the internationaliztion of messages for
660 * the recognition manager.
663 static void intl_initialize(void)
667 /*Get recognizer home directory name from environment.*/
669 if( (dirname = getenv(RECHOME)) == nil ) {
670 dirname = "REC_DEFAULT_HOME_DIR";
673 /*Bind the text domain.*/
675 bindtextdomain(dirname, INTL_DOMAIN);
679 /*make_rec_info-Create a rec_info structure*/
681 static rec_info* make_rec_info(char*, char*, char** subset)
687 ri = (rec_info*)safe_malloc(sizeof(rec_info));
694 if( (locale = getenv(LANG)) == nil ) {
695 locale = strdup(REC_DEFAULT_LOCALE);
698 if( (ri->ri_locale = strdup(locale)) == nil ) {
703 /*Get shared library pathname.*/
705 /*Initialize the subset information.*/
707 if( subset != nil ) {
709 /*Count the subset strings.*/
711 for( len = 1; subset[len] != nil; len++ ) ;
713 /*Copy the subset strings.*/
715 ri->ri_subset = (char**)safe_malloc((len +1)*sizeof(char*));
717 for( i = 0; i < len; i++ ) {
718 if( subset[i] != nil ) {
719 if( (ri->ri_subset[i] = strdup(subset[i])) == nil ) {
724 ri->ri_subset[i] = subset[i];
728 ri->ri_subset[i] = nil;
738 static void delete_rec_info(rec_info* ri)
741 if( ri->ri_locale != nil ) {
745 * if( ri->ri_name != nil ) {
749 if( ri->ri_subset != nil ) {
751 for( i = 0; ri->ri_subset[i] != nil; i++) {
752 free(ri->ri_subset[i]);
760 /*check_for_user_home-Check whether USERRECHOME has been created.*/
762 static int check_for_user_home()
764 char* homedir = getenv(HOME);
768 if( homedir == nil ) {
769 the_last_error = "Home environment variable HOME not set.";
773 rechome = (char*)safe_malloc(strlen(homedir) + strlen(USERRECHOME) + 2);
777 strcpy(rechome,homedir);
779 strcat(rechome,USERRECHOME);
781 /*Create directory.*/
783 dir = dirstat(rechome);
785 if (dir->mode & DMDIR) {
793 if ((fd = create(rechome, OREAD, DMDIR|0755)) >= 0) {
804 * Constructor functions for making structures.
806 * The general philosophy here is that we control all memory
807 * in connected data structures, *except* for pen_point arrays.
808 * There are likely to be lots and lots of points, they are likely
809 * to come from the window system; so if we wanted to control them,
810 * we would have to copy which would be slow. We require the client
811 * to deal with them directly, or the client can give us permission
820 recognizer make_recognizer(rec_info* rif)
826 rec = (recognizer)safe_malloc(sizeof(*rec));
827 rec->recognizer_magic = REC_MAGIC;
828 rec->recognizer_version = REC_VERSION;
829 rec->recognizer_info = rif;
830 rec->recognizer_specific = nil;
831 rec->recognizer_end_magic = REC_END_MAGIC;
832 rec->recognizer_load_state = nil;
833 rec->recognizer_save_state = nil;
834 rec->recognizer_load_dictionary = nil;
835 rec->recognizer_save_dictionary = nil;
836 rec->recognizer_free_dictionary = nil;
837 rec->recognizer_add_to_dictionary = nil;
838 rec->recognizer_delete_from_dictionary = nil;
839 rec->recognizer_error = nil;
840 rec->recognizer_set_context = nil;
841 rec->recognizer_get_context = nil;
842 rec->recognizer_clear = nil;
843 rec->recognizer_get_buffer = nil;
844 rec->recognizer_set_buffer = nil;
845 rec->recognizer_translate = nil;
846 rec->recognizer_get_extension_functions = nil;
847 rec->recognizer_get_gesture_names = nil;
848 rec->recognizer_set_gesture_action = nil;
852 void delete_recognizer(recognizer rec)
856 if( rec->recognizer_info != nil ) {
857 delete_rec_info(rec->recognizer_info);
867 rec_alternative* make_rec_alternative_array(uint size)
872 ri = (rec_alternative*) safe_malloc(size * sizeof(rec_alternative));
874 for( i = 0; i < size; i++ ) {
875 ri[i].ra_elem.re_type = REC_NONE;
876 ri[i].ra_elem.re_result.aval = nil;
877 ri[i].ra_elem.re_conf = 0;
886 initialize_rec_alternative(rec_alternative* ra, uint nelm)
889 if( (ra->ra_next = make_rec_alternative_array(nelm)) == nil ) {
893 ra->ra_nalter = nelm;
899 void delete_rec_alternative_array(uint nalter,
901 bool delete_points_p)
907 for( i = 0; i < nalter; i++ ) {
908 cleanup_rec_element(&ra[i].ra_elem,delete_points_p);
910 /*Now do the next one down the line.*/
912 if( ra[i].ra_nalter > 0 ) {
913 delete_rec_alternative_array(ra[i].ra_nalter,
924 /*initialize_rec_element-Initialize a recognition element.*/
927 initialize_rec_element(rec_element* re,
937 re->re_result.aval = nil;
942 if( size > 0 && trans != nil ) {
944 (gesture*)safe_malloc(sizeof(gesture));
945 memcpy((void*)re->re_result.gval,trans,sizeof(gesture));
952 if( size > 0 && trans != nil ) {
954 (char*)safe_malloc((size+1)*sizeof(char));
955 memcpy((void*)re->re_result.aval,trans,size*sizeof(char));
956 re->re_result.aval[size] = '\000';
961 if( size > 0 && trans != nil ) {
963 (wchar_t*)safe_malloc((size+1)*sizeof(wchar_t));
964 memcpy((void*)re->re_result.wval,trans,size*sizeof(wchar_t));
965 re->re_result.wval[size] = '\000';
970 if( size > 0 && trans != nil ) {
971 re->re_result.rcval =
972 (rec_correlation*)safe_malloc(sizeof(rec_correlation));
973 memcpy((void*)re->re_result.rcval,
975 sizeof(rec_correlation));
988 static void cleanup_rec_element(rec_element* re,bool delete_points_p)
990 switch(re->re_type) {
999 free(re->re_result.aval);
1003 delete_gesture_array(1,re->re_result.gval,true);
1007 delete_rec_correlation(re->re_result.rcval,
1021 make_rec_correlation(char type,
1024 rec_confidence conf,
1027 rec_correlation* rc;
1029 rc = (rec_correlation*)safe_malloc(sizeof(rec_correlation));
1031 rc->ro_nstrokes = ps_size;
1033 /*First initialize element.*/
1035 if( initialize_rec_element(&(rc->ro_elem),
1043 if( (rc->ro_strokes = make_Stroke_array(ps_size)) == nil ) {
1047 rc->ro_start = (uint*)safe_malloc(ps_size * sizeof(int));
1048 rc->ro_stop = (uint*)safe_malloc(ps_size * sizeof(int));
1052 void delete_rec_correlation(rec_correlation* rc,bool delete_points_p)
1056 cleanup_rec_element(&rc->ro_elem,delete_points_p);
1058 delete_Stroke_array(rc->ro_nstrokes,rc->ro_strokes,delete_points_p);
1060 if( rc->ro_start != nil ) {
1064 if( rc->ro_stop != nil ) {
1079 rec_fn* make_rec_fn_array(uint size)
1081 rec_fn* ri = (rec_fn*)safe_malloc((size + 1) * sizeof(rec_fn));
1084 for( i = 0; i < size; i++ ) {
1093 void delete_rec_fn_array(rec_fn* rf)
1105 Stroke* make_Stroke_array(uint size)
1110 ri = (Stroke*) safe_malloc(size * sizeof(Stroke));
1111 for( i = 0; i < size; i++ ) {
1119 Stroke* initialize_Stroke(Stroke* ps,
1130 void delete_Stroke_array(uint size,Stroke* ps,bool delete_points_p)
1136 for( i = 0; i < size; i++ ) {
1137 if( delete_points_p ) {
1138 delete_pen_point_array(ps[i].pts);
1150 void delete_pen_point_array(pen_point* pp)
1162 make_gesture_array(uint size)
1164 return((gesture*)safe_malloc(size * sizeof(gesture)));
1167 gesture* initialize_gesture(gesture* g,
1177 /*We don't do points, 'cause they come from the window system.*/
1180 g->g_hspots = hspots;
1182 g->g_name = strdup(name);
1186 g->g_wsinfo = wsinfo;
1192 delete_gesture_array(uint size,gesture* ga,bool delete_points_p)
1198 for( i = 0; i < size; i++ ) {
1202 if( delete_points_p ) {
1203 delete_pen_point_array(ga[i].g_hspots);
1212 * copy fns for stroke buffer management.
1216 copy_Stroke(Stroke* ps1,Stroke* ps2)
1218 initialize_Stroke(ps1,
1226 copy_Stroke_array(uint nstrokes,
1230 Stroke* ps = make_Stroke_array(nstrokes);
1234 for( i = 0; i < nstrokes; i++ ) {
1236 copy_Stroke(&ps[i],&strokes[i]);
1246 copy_state_trans_array(uint ntrans,uint* trans)
1248 uint* pt = (uint*)safe_malloc(ntrans*sizeof(uint));
1251 for( i = 0; i < ntrans; i++ ) {
1259 concatenate_Strokes(int nstrokes1,
1270 /*Measure new strokes*/
1272 ns = nstrokes1 + nstrokes2;
1276 if( (ps = make_Stroke_array(ns)) == nil ) {
1280 /*Copy old ones into new.*/
1282 for( i = 0; i < nstrokes1; i++ ) {
1283 if( copy_Stroke(&ps[i],&strokes1[i]) == nil ) {
1284 delete_Stroke_array(ns,ps,false);
1289 for( ; i < ns; i++ ) {
1290 if( copy_Stroke(&ps[i],&strokes2[i - nstrokes1]) == nil ) {
1291 delete_Stroke_array(ns,ps,false);