1 /* @source embgroup ***********************************************************
2 **
3 ** Group Routines.
4 **
5 ** @author Copyright (c) 1999 Alan Bleasby
6 ** @version $Revision: 1.60 $
7 ** @modified $Date: 2012/12/07 10:23:51 $ by $Author: rice $
8 ** @@
9 **
10 ** This library is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU Lesser General Public
12 ** License as published by the Free Software Foundation; either
13 ** version 2.1 of the License, or (at your option) any later version.
14 **
15 ** This library is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ** Lesser General Public License for more details.
19 **
20 ** You should have received a copy of the GNU Lesser General Public
21 ** License along with this library; if not, write to the Free Software
22 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23 ** MA  02110-1301,  USA.
24 **
25 ******************************************************************************/
26 
27 
28 #include "ajlib.h"
29 
30 #include "embgroup.h"
31 #include "ajlist.h"
32 #include "ajfile.h"
33 #include "ajtable.h"
34 #include "ajobo.h"
35 #include "ajoboread.h"
36 #include "ajsys.h"
37 #include "ajnam.h"
38 #include "ajfileio.h"
39 #include "ajacd.h"
40 
41 #include <sys/types.h>  /* for opendir etc. */
42 #ifndef WIN32
43 #include <dirent.h>     /* for readdir */
44 #else
45 #include "win32.h"
46 #include "dirent_w32.h"
47 #endif
48 #include <string.h>
49 #include <sys/stat.h>   /* for stat */
50 
51 
52 
53 static void grpProgClear(EmbPGroupProg gl);
54 static void grpGroupsListClear(AjPList groupslist);
55 static void grpRelationsListClear(AjPList relslist);
56 static EmbPGroupProg grpCopyPnode(const EmbPGroupProg pnode);
57 static EmbPGroupRelation grpRelationsCopy(const EmbPGroupRelation gr);
58 static void grpGetAcdFiles(AjPList glist, AjPList alpha, char * const env[],
59 			   const AjPStr acddir, AjBool explode, AjBool colon,
60 			   AjBool gui, AjBool embassy,
61 			   const AjPStr embassyname);
62 static void grpGetAcdDirs(AjPList glist, AjPList alpha, char * const env[],
63 			  const AjPStr acddir, AjBool explode, AjBool colon,
64 			  AjBool gui, AjBool embassy,
65 			  const AjPStr embassyname);
66 static void grpParse(AjPFile file, AjPList groups,
67                      AjBool explode, AjBool colon,
68 		     AjBool *gui, AjBool* embassy,
69                      EmbPGroupProg *Pprognode);
70 static void grpParseEmbassy(AjPFile file, AjPStr* embassyname);
71 static void grpNoComment(AjPStr* text);
72 static AjPStr grpParseValueRB(AjPStrTok tokenhandle, const char* delim);
73 static void grpSplitList(AjPList groups, const AjPStr value, AjBool explode,
74 			 AjBool colon, AjPStr* keywords);
75 static void grpSubSplitList(AjPList groups, AjPList sublist);
76 static void grpAddGroupsToList(const AjPList alpha, AjPList glist,
77 			       const AjPList groups,
78 			       EmbPGroupProg *Pprognode);
79 static AjBool grpGetAcdByname(const AjPStr appname, const AjPStr acddir,
80 			      AjPStr* embassyname);
81 
82 static AjPStr grpStr1 = NULL;
83 static AjPStr grpStr2 = NULL;
84 
85 
86 
87 
88 /* @func embGrpGetProgGroups **************************************************
89 **
90 ** Optionally constructs a path to the directory of normal EMBOSS or
91 ** embassy ACD files. Calls grpGetAcdFiles to construct lists of the
92 ** group, doc and program name information.
93 **
94 ** @param [w] glist [AjPList] List of groups of programs
95 ** @param [w] alpha [AjPList] Alphabetic list of programs
96 ** @param [r] env [char* const[]] Environment passed in from C main()
97 **                                 parameters
98 ** @param [r] emboss [AjBool] Read in EMBOSS ACD data
99 ** @param [r] embassy [AjBool] Read in EMBASSY ACD data
100 ** @param [r] embassyname [const AjPStr] Name of embassy package.
101 **                                       default is to search for all
102 ** @param [r] explode [AjBool] Expand group names around ':'
103 ** @param [r] colon [AjBool] Retain ':' in group names
104 ** @param [r] gui [AjBool] Only report programs that are OK in GUIs
105 ** @return [void]
106 **
107 ** @release 1.0.0
108 ** @@
109 ******************************************************************************/
110 
embGrpGetProgGroups(AjPList glist,AjPList alpha,char * const env[],AjBool emboss,AjBool embassy,const AjPStr embassyname,AjBool explode,AjBool colon,AjBool gui)111 void embGrpGetProgGroups(AjPList glist, AjPList alpha, char * const env[],
112 			 AjBool emboss, AjBool embassy,
113 			 const AjPStr embassyname,
114 			 AjBool explode, AjBool colon, AjBool gui)
115 {
116 
117     AjPStr acdroot     = NULL;
118     AjPStr acdrootdir  = NULL;
119     AjPStr acdrootinst = NULL;
120     AjPStr acdpack     = NULL;
121     AjPStr alphaname   = NULL;
122     EmbPGroupTop gpnode; /* new member (first & only) of alpha being added */
123     AjBool doneinstall = ajFalse;
124 
125     /* set up alpha programs group list */
126     ajStrAssignC(&alphaname, "Alphabetic list of programs");
127     gpnode = embGrpMakeNewGnode(alphaname);
128     ajListPushAppend(alpha, gpnode);
129     ajStrDel(&alphaname);
130 
131 
132     /* look at all EMBOSS ACD files */
133     acdpack     = ajStrNew();
134     acdroot     = ajStrNew();
135     acdrootdir  = ajStrNew();
136     acdrootinst = ajStrNew();
137     alphaname   = ajStrNew();
138 
139     ajStrAssignS(&acdpack, ajNamValuePackage());
140     ajStrAssignS(&acdrootinst, ajNamValueInstalldir());
141 
142     if(emboss)
143     {
144 	if(ajNamGetValueC("acdroot", &acdroot))
145 	{
146 	    ajDirnameFix(&acdroot);
147 	    /*ajStrAppendC(&acdroot, "acd/");*/
148 	}
149 	else
150 	{
151 	    ajDirnameFix(&acdrootinst);
152 	    ajFmtPrintS(&acdroot, "%Sshare/%S/acd/", acdrootinst, acdpack);
153 
154 	    if(ajDirnameFixExists(&acdroot))
155 		doneinstall = ajTrue;
156 	    else
157 	    {
158 		ajStrAssignS(&acdrootdir, ajNamValueRootdir());
159 		ajDirnameFix(&acdrootdir);
160 		ajFmtPrintS(&acdroot, "%Sacd/", acdrootdir);
161 	    }
162 	}
163 
164 	/* normal EMBOSS ACD */
165 	grpGetAcdFiles(glist, alpha, env, acdroot, explode, colon,
166 		       gui, embassy, embassyname);
167     }
168 
169     if(embassy && !doneinstall)
170     {
171 	ajDirnameFix(&acdroot);
172 
173 	/* EMBOSS install directory */
174 	ajFmtPrintS(&acdroot, "%Sshare/%S/acd/",
175 		    acdrootinst, acdpack);
176 
177 	if(ajDirnameFixExists(&acdroot))
178 	    /* embassadir ACD files */
179 	    grpGetAcdFiles(glist, alpha, env, acdroot, explode, colon,
180 			   gui, embassy, embassyname);
181 	else
182 	{
183 	    /* look for all source directories */
184 	    ajStrAssignS(&acdrootdir, ajNamValueRootdir());
185 	    ajDirnameUp(&acdrootdir);
186 	    ajFmtPrintS(&acdroot, "%Sembassy/", acdrootdir);
187 	    /* embassadir ACD files */
188 	    grpGetAcdDirs(glist, alpha, env, acdroot, explode, colon,
189 			  gui, embassy, embassyname);
190 	}
191 
192     }
193 
194     /* sort the groups and alpha lists */
195     embGrpSortGroupsList(glist);
196     embGrpSortGroupsList(alpha);
197 
198     ajStrDel(&acdroot);
199     ajStrDel(&acdrootdir);
200     ajStrDel(&acdrootinst);
201     ajStrDel(&alphaname);
202     ajStrDel(&acdpack);
203 
204     return;
205 }
206 
207 
208 
209 
210 /* @func embGrpGetEmbassy *****************************************************
211 **
212 ** Optionally constructs a path to the directory of normal EMBOSS or
213 ** embassy ACD files. Calls grpGetAcdFiles to construct lists of the
214 ** group, doc and program name information.
215 **
216 ** @param [r] appname [const AjPStr] Application name
217 ** @param [w] embassyname [AjPStr*] Embassy package attribute value,
218 **                                  or an empty string if in the main package
219 ** @return [AjBool] ajTrue if an ACD file was found
220 **
221 ** @release 4.1.0
222 ** @@
223 ******************************************************************************/
224 
embGrpGetEmbassy(const AjPStr appname,AjPStr * embassyname)225 AjBool embGrpGetEmbassy(const AjPStr appname, AjPStr* embassyname)
226 {
227 
228     AjPStr acdroot     = NULL;
229     AjPStr acdrootdir  = NULL;
230     AjPStr acdrootinst = NULL;
231     AjPStr acdpack     = NULL;
232     AjPFile acdfile     = NULL;
233     AjPStr filename    = NULL;
234     AjBool ok = ajFalse;
235 
236     /* look at all EMBOSS ACD files */
237     acdpack     = ajStrNew();
238     acdroot     = ajStrNew();
239     acdrootdir  = ajStrNew();
240     acdrootinst = ajStrNew();
241 
242     ajStrAssignS(&acdpack, ajNamValuePackage());
243     ajStrAssignS(&acdrootinst, ajNamValueInstalldir());
244 
245     ajStrAssignC(embassyname, "");
246 
247     if(ajNamGetValueC("acdroot", &acdroot))
248     {
249 	ajDirnameFix(&acdroot);
250 	/*ajStrAppendC(&acdroot, "acd/");*/
251     }
252     else
253     {
254 	ajDirnameFix(&acdrootinst);
255 	ajFmtPrintS(&acdroot, "%Sshare/%S/acd/", acdrootinst, acdpack);
256 
257 	if(!ajDirnameFixExists(&acdroot))
258 	{
259 	    ajStrAssignS(&acdrootdir, ajNamValueRootdir());
260 	    ajDirnameFix(&acdrootdir);
261 	    ajFmtPrintS(&acdroot, "%Sacd/", acdrootdir);
262 	}
263     }
264 
265     /* normal EMBOSS ACD */
266     ajFmtPrintS(&filename, "%S%S.acd", acdroot, appname);
267     acdfile = ajFileNewInNameS(filename);
268 
269     if(acdfile)
270     {
271 	grpParseEmbassy(acdfile, embassyname);
272 	ajFileClose(&acdfile);
273 	ok = ajTrue;
274     }
275 
276     if(!ok)
277     {
278       /* look for all source directories */
279       ajStrAssignS(&acdrootdir, ajNamValueRootdir());
280       ajDirnameUp(&acdrootdir);
281       ajFmtPrintS(&acdroot, "%Sembassy/", acdrootdir);
282       /* embassadir ACD files */
283       ok = grpGetAcdByname(appname, acdroot, embassyname);
284     }
285 
286     ajStrDel(&acdroot);
287     ajStrDel(&acdrootdir);
288     ajStrDel(&acdrootinst);
289     ajStrDel(&acdpack);
290     ajStrDel(&filename);
291 
292     ajDebug("embGrpGetEmbassy ok:%B embassy '%S'\n",
293 	    ok, *embassyname);
294     return ok;
295 }
296 
297 
298 
299 
300 /* @funcstatic grpGetAcdByname ************************************************
301 **
302 ** Given a directory from main package or EMBASSY sources, it searches
303 ** for directories of ACD files and passes processing on to
304 ** grpGetAcdFiles
305 **
306 ** @param [r] appname [const AjPStr] Application name
307 ** @param [r] acddir [const AjPStr] Path of directory holding ACD files
308 **                                  to read in
309 ** @param [w] embassyname [AjPStr*] Embassy package name
310 **                                  or empty string for main package
311 ** @return [AjBool] ajTrue if an ACD file was found
312 **
313 ** @release 4.1.0
314 ** @@
315 ******************************************************************************/
316 
grpGetAcdByname(const AjPStr appname,const AjPStr acddir,AjPStr * embassyname)317 static AjBool grpGetAcdByname(const AjPStr appname, const AjPStr acddir,
318 			      AjPStr* embassyname)
319 {
320     DIR *dirp;
321     DIR *dirpa;
322     struct dirent *dp;
323     AjPStr dirname = NULL;
324     AjPStr filename = NULL;
325     AjPFile acdfile = NULL;
326     AjBool ok = ajFalse;
327 
328     /* go through all the directories in this directory */
329     dirp = opendir(ajStrGetPtr(acddir));
330 
331     if(!dirp)
332       return ajFalse;		/* could be no embassy installed */
333 
334     ajDebug("grpGetAcdbyName '%S' '%S'\n", acddir, appname);
335 
336     for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
337     {
338 	if(dp->d_name[0] == '.')
339 	    continue;			/* don't want hidden files */
340 
341 	ajFmtPrintS(&dirname, "%S%s/emboss_acd/", acddir, dp->d_name);
342 	dirpa = opendir(ajStrGetPtr(dirname));
343 
344 	if(dirpa)
345 	{
346 	    closedir(dirpa);
347 	    ajFmtPrintS(&filename, "%S%S.acd", dirname, appname);
348 	    acdfile = ajFileNewInNameS(filename);
349 
350 	    if(acdfile)
351             {
352 	      grpParseEmbassy(acdfile, embassyname);
353 	      ajFileClose(&acdfile);
354 	      ok = ajTrue;
355 	    }
356 	}
357 
358         if(ok)
359 	  break;
360     }
361 
362     ajStrDel(&dirname);
363     ajStrDel(&filename);
364     closedir(dirp);
365 
366     return ok;
367 }
368 
369 
370 
371 
372 /* @funcstatic grpGetAcdDirs **************************************************
373 **
374 ** Given a directory from EMBASSY sources, it searches for directories
375 ** of ACD files and passes processing on to grpGetAcdFiles
376 **
377 ** @param [w] glist [AjPList] List of groups of programs
378 ** @param [w] alpha [AjPList] Alphabetic list of programs
379 ** @param [r] env [char* const[]] Environment passed in from C main()
380 **                                 parameters
381 ** @param [r] acddir [const AjPStr] path of directory holding ACD files
382 **                                  to read in
383 ** @param [r] explode [AjBool] Expand group names around ':'
384 ** @param [r] colon [AjBool] Retain ':' in group names
385 ** @param [r] gui [AjBool] Report only those applications OK in GUIs
386 ** @param [r] embassy [AjBool] Report only those applications not in
387 **                             an EMBASSY package (embassy attribute in ACD)
388 ** @param [r] embassyname [const AjPStr] Name of embassy package.
389 **                                       default is to search for all
390 ** @return [void]
391 **
392 ** @release 2.5.0
393 ** @@
394 ******************************************************************************/
395 
grpGetAcdDirs(AjPList glist,AjPList alpha,char * const env[],const AjPStr acddir,AjBool explode,AjBool colon,AjBool gui,AjBool embassy,const AjPStr embassyname)396 static void grpGetAcdDirs(AjPList glist, AjPList alpha, char * const env[],
397 			  const AjPStr acddir, AjBool explode, AjBool colon,
398 			  AjBool gui, AjBool embassy,
399 			  const AjPStr embassyname)
400 {
401     DIR *dirp;
402     DIR *dirpa;
403     struct dirent *dp;
404     static AjPStr dirname = NULL;
405 
406 
407     /* go through all the directories in this directory */
408     if((dirp = opendir(ajStrGetPtr(acddir))) == NULL)
409 	return;			   /* could be no embassy installed */
410 
411 
412     for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
413     {
414 	if(dp->d_name[0] == '.')
415 	    continue;			/* don't want hidden files */
416 
417 	ajFmtPrintS(&dirname, "%S%s/emboss_acd/", acddir, dp->d_name);
418 
419 	if((dirpa = opendir(ajStrGetPtr(dirname))))
420 	{
421 	    grpGetAcdFiles(glist, alpha, env, dirname, explode, colon,
422 			   gui, embassy, embassyname);
423 	    closedir(dirpa);
424 	}
425     }
426 
427     closedir(dirp);
428 
429     return;
430 }
431 
432 
433 
434 
435 /* @funcstatic grpGetAcdFiles *************************************************
436 **
437 ** Given a directory, it searches for ACD files which describe an
438 ** existing program on the path,
439 ** parses out the documentation and groups from these ACD files,
440 ** returns a list of program names and documentation grouped by group names,
441 ** and returns an alphabetic list of program names and documentation.
442 **
443 ** @param [w] glist [AjPList] List of groups of programs
444 ** @param [w] alpha [AjPList] Alphabetic list of programs
445 ** @param [r] env [char* const[]] Environment passed in from C main()
446 **                             parameters
447 ** @param [r] acddir [const AjPStr] path of directory holding ACD files
448 **                           to read in
449 ** @param [r] explode [AjBool] Expand group names around ':'
450 ** @param [r] colon [AjBool] Retain ':' in group names
451 ** @param [r] gui [AjBool] Report only those applications OK in GUIs
452 ** @param [r] embassy [AjBool] Report only those applications not in
453 **                             an EMBASSY package (embassy attribute in ACD)
454 ** @param [r] embassyname [const AjPStr] Name of embassy package.
455 **                                       default is to search for all
456 ** @return [void]
457 **
458 ** @release 2.5.0
459 ** @@
460 ******************************************************************************/
461 
grpGetAcdFiles(AjPList glist,AjPList alpha,char * const env[],const AjPStr acddir,AjBool explode,AjBool colon,AjBool gui,AjBool embassy,const AjPStr embassyname)462 static void grpGetAcdFiles(AjPList glist, AjPList alpha, char * const env[],
463 			   const AjPStr acddir, AjBool explode, AjBool colon,
464 			   AjBool gui, AjBool embassy,
465 			   const AjPStr embassyname)
466 {
467     DIR *dirp;
468     struct dirent *dp;
469     AjPStr progpath = NULL;
470     AjPFile file    = NULL;
471     AjPStr appl     = NULL;
472     AjPStr applpath = NULL;		/* path of application */
473     AjPStr doc      = NULL;
474     AjPList groups  = NULL;
475     AjPStr keywords = NULL;
476     AjBool guiresult;
477     AjBool isembassy;
478     EmbPGroupProg prognode = NULL;
479 
480     prognode = embGrpMakeNewPnode(NULL, NULL, NULL, NULL);
481 
482     /* go through all the files in this directory */
483     if((dirp = opendir(ajStrGetPtr(acddir))) == NULL)
484 	ajFatal("You do not have read permission on the directory '%S'",
485 		acddir);
486 
487     for(dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
488     {
489 	if(dp->d_name[0] != '.')
490 	{
491 	    ajStrAssignResS(&progpath,
492 			    ajStrGetLen(acddir)+strlen(dp->d_name)+3,
493 			    acddir);
494 	    ajStrAppendC(&progpath, dp->d_name);
495 
496 	    /* does it end with ".acd" ? */
497 	    if(ajStrSuffixC(progpath, ".acd"))
498 	    {
499 		/* see if it is a normal file */
500 		if(ajFilenameExistsRead(progpath))
501 		{
502 		    /* open the file and parse it */
503 		    if((file = ajFileNewInNameS(progpath)) != NULL)
504 		    {
505 			groups = ajListstrNew();
506 			grpParse(file,  groups,
507 				 explode, colon, &guiresult,
508 				 &isembassy, &prognode);
509 
510 			/* see if the appl is the name of a real program */
511 			ajStrAssignS(&appl,prognode->name);
512 			ajStrAssignS(&applpath, appl);
513 
514 			if(ajSysFileWhichEnv(&applpath, env))
515 			{
516 			    /*
517 			    ** see if the appl is OK in GUIs or we don't
518 			    ** want just GUI apps
519 			    */
520 			    if(gui && !guiresult)
521 				ajDebug("%S is not a OK in GUIs\n", appl);
522 			    else if(!embassy && isembassy)
523 				ajDebug("%S is in EMBASSY\n", appl);
524 			    else if (ajStrGetLen(embassyname) &&
525 				     prognode &&
526                                      !ajStrMatchCaseS(embassyname,
527 						      prognode->package))
528 				ajDebug("%S is in not in EMBASSY %S\n",
529 					appl, embassyname);
530 			    else
531 				grpAddGroupsToList(alpha, glist, groups,
532 						   &prognode);
533 			}
534 
535 			ajFileClose(&file);
536 			ajListstrFreeData(&groups);
537 			ajStrDel(&appl);
538 			ajStrDel(&doc);
539 		    }
540 		}
541 	    }
542 
543 	    ajStrDel(&progpath);
544 	}
545     }
546 
547     closedir(dirp);
548     ajStrDel(&applpath);
549     ajStrDel(&keywords);
550 
551     embGrpProgDel(&prognode);
552 
553     return;
554 }
555 
556 
557 
558 
559 /* @funcstatic grpParse *******************************************************
560 **
561 ** parse the acd file.
562 **
563 ** @param [u] file [AjPFile]  ACD file
564 ** @param [w] groups [AjPList] Program groups list
565 ** @param [r] explode [AjBool] Expand group names around ':'
566 ** @param [r] colon [AjBool] Retain ':' in group names
567 ** @param [w] gui [AjBool*] returns ajTrue if application is OK in GUIs
568 ** @param [w] embassy [AjBool*] returns ajTrue if application has
569 **                              an EMBASSY package definition
570 **
571 ** @param [w] Pprognode [EmbPGroupProg*] Program node
572 ** @return [void]
573 **
574 ** @release 2.5.0
575 ** @@
576 ******************************************************************************/
577 
grpParse(AjPFile file,AjPList groups,AjBool explode,AjBool colon,AjBool * gui,AjBool * embassy,EmbPGroupProg * Pprognode)578 static void grpParse(AjPFile file, AjPList groups,
579 		     AjBool explode, AjBool colon,
580 		     AjBool *gui, AjBool* embassy,
581                      EmbPGroupProg *Pprognode)
582 {
583 
584     AjPStr line = NULL;
585     AjPStr text = NULL;
586 
587     AjPStrTok tokenhandle;
588     char white[]     = " \t\n\r";
589     char whiteplus[] = " \t\n\r:=";
590     AjPStr tmpstr = NULL;
591     AjPStr token  = NULL;
592     AjPStr value  = NULL;
593     AjBool done = 0;
594     AjPStr nullgroup = NULL;
595     AjPStr newstr    = NULL;
596     AjPStr tmpvalue  = NULL;
597     AjPStr type  = NULL;
598     AjPStr qual  = NULL;
599     AjPStr edamid  = NULL;
600     AjPStr edamspace  = NULL;
601     AjPStr edamname  = NULL;
602     EmbPGroupRelation gprel = NULL;
603     EmbPGroupProg ppnode = NULL;
604 
605     if(!*Pprognode)
606         *Pprognode = embGrpMakeNewPnode(NULL, NULL, NULL, NULL);
607     else
608         grpProgClear(*Pprognode);
609 
610     ppnode = *Pprognode;
611 
612     /* initialise a name for programs with no assigned group */
613     ajStrAppendC(&nullgroup, "ASSORTED");
614 
615     ajStrAssignC(&ppnode->keywords, "");
616 
617     /* if 'gui' not defined in ACD, default is 'gui: Y' */
618     *gui = ajTrue;
619     *embassy = ajFalse;
620 
621     /* read file into one line, stripping out comment lines and blanks */
622     while(ajReadlineTrim(file, &line))
623     {
624 	grpNoComment(&line);
625 	if(ajStrGetLen(line))
626 	{
627 	    ajStrAppendS(&text, line);
628 	    ajStrAppendC(&text, " ");
629 	}
630     }
631 
632     tokenhandle = ajStrTokenNewC(text, white);
633 
634     /* find application token */
635     while(ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr))
636 	if(ajStrPrefixCaseC(tmpstr, "application"))
637 	    break;
638 
639     /* next token is the application name */
640     ajStrTokenNextParseC(tokenhandle, white, &ppnode->name);
641 
642     /* if next token is '[' */
643     ajStrTokenNextParseC(tokenhandle, white, &tmpstr);
644 
645     if(ajStrCmpC(tmpstr, "[") == 0)
646     {
647 	token=ajStrNew();
648 
649 	/* is the next token 'doc' or 'groups' or 'gui' */
650 	while(ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr))
651 	{
652 	    while(!ajStrMatchC(tmpstr, "]"))
653 	    {
654 		ajStrAssignS(&token, tmpstr);
655 		value = grpParseValueRB(tokenhandle, white);
656 		done = ajStrMatchC(value, "]");
657 
658 		if(!done)
659 		{
660 		    ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr);
661 		    ajStrFmtLower(&tmpstr);
662 		    done = ajStrMatchC(tmpstr, "]");
663 		}
664 
665 		if(ajStrPrefixCaseC(token, "documentation"))
666 		{
667 		    ajStrAssignS(&ppnode->doc, value);
668 		    ajStrTrimWhite(&ppnode->doc);
669 		    ajStrTrimC(&ppnode->doc, ".,");
670 
671 		}
672 		else if(ajStrPrefixCaseC(token, "gui"))
673 		{
674 		    ajStrAssignS(&tmpvalue, value);
675 		    ajStrTrimWhite(&tmpvalue);
676 		    ajDebug("gui value '%S'\n", tmpvalue);
677 
678 		    /* test for '[Nn]*' */
679 		    if(tolower((int)(ajStrGetPtr(tmpvalue))[0]) == 'n')
680 			*gui = ajFalse;
681 
682 		    ajStrDel(&tmpvalue);
683 		}
684 		else if(ajStrPrefixCaseC(token, "groups"))
685 		{
686 		    grpSplitList(groups, value, explode, colon,
687                                  &ppnode->keywords);
688 		}
689 		else if(ajStrPrefixCaseC(token, "keywords"))
690 		{
691 		    ajStrExchangeKK(&value, ' ', '_');
692 
693 		    if(ajStrGetLen(ppnode->keywords))
694 			ajStrAppendK(&ppnode->keywords, ' ');
695 
696 		    ajStrAppendS(&ppnode->keywords, value);
697 		}
698 		else if(ajStrPrefixCaseC(token, "embassy"))
699 		{
700 		    *embassy = ajTrue;
701 		    ajStrAssignS(&ppnode->package, value);
702 		}
703 		if(ajStrPrefixCaseC(token, "relation"))
704                 {
705 		  /*ajDebug("+++ done:%B '%S' '%S'\n", done, token, value);*/
706                     if(ajAcdedamParse(value, &edamid, &edamspace, &edamname))
707                     {
708                         AJNEW0(gprel);
709                         ajStrAssignC(&gprel->type, "application");
710                         ajStrAssignS(&gprel->qual, ppnode->name);
711                         ajStrAssignC(&gprel->acdgroup, "application");
712                         ajStrAssignS(&gprel->id, edamid);
713                         ajStrAssignS(&gprel->namespace, edamspace);
714                         ajStrAssignS(&gprel->name, edamname);
715                         if(ajStrMatchC(edamspace, "topic"))
716                             ajListPushAppend(ppnode->acdtopics, gprel);
717                         else if(ajStrMatchC(edamspace, "operation"))
718                             ajListPushAppend(ppnode->acdoperations, gprel);
719                         else
720                             ajListPushAppend(ppnode->acdparams, gprel);
721                     }
722                     else
723                     {
724                         ajErr("%F: bad ACD relation '%S'", file, value);
725                     }
726                 }
727 	    }
728 
729 	    if(done)
730 		break;
731 	}
732     }
733 
734     /* check that we got the doc, keywords and groups descriptions */
735     if(!ajStrGetLen(ppnode->doc))
736 	ajStrAssignC(&ppnode->doc, "");
737 
738     if(!ajStrGetLen(ppnode->keywords))
739 	ajStrAssignC(&ppnode->keywords, "");
740 
741     if(!ajListGetLength(groups))
742     {
743 	newstr = ajStrNewRef(nullgroup);
744 	ajListstrPushAppend(groups, newstr);
745     }
746 
747     ajStrAssignEmptyC(&ppnode->package, "");
748 
749 
750 /* now process the qualifiers */
751 
752     ajDebug("appl: '%S'\n", ppnode->name);
753 
754     while(ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr))
755     {
756         ajStrAssignS(&type, tmpstr);
757         ajStrTokenNextParseC(tokenhandle, white, &tmpstr);
758         ajStrAssignS(&qual, tmpstr);
759 
760         /*ajDebug("qual: '%S' '%S'\n", type, qual);*/
761 
762         if(ajStrMatchC(type, "endsection"))
763             continue;
764 
765         while(!ajStrMatchC(tmpstr, "["))
766             ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr);
767 
768 	while(ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr))
769 	{
770 	    while(!ajStrMatchC(tmpstr, "]"))
771 	    {
772 		ajStrAssignS(&token, tmpstr);
773 		value = grpParseValueRB(tokenhandle, white);
774 		done = ajStrMatchC(value, "]");
775 
776 		if(!done)
777 		{
778 		    ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr);
779 		    ajStrFmtLower(&tmpstr);
780 		    done = ajStrMatchC(tmpstr, "]");
781 		}
782 		if(ajStrPrefixCaseC(token, "relation"))
783                 {
784                     if(ajAcdedamParse(value, &edamid, &edamspace, &edamname))
785                     {
786                         AJNEW0(gprel);
787                         ajStrAssignS(&gprel->type, type);
788                         ajStrAssignS(&gprel->qual, qual);
789                         ajStrAssignC(&gprel->acdgroup, ajAcdtypeGetGroup(type));
790                         ajStrAssignS(&gprel->id, edamid);
791                         ajStrAssignS(&gprel->namespace, edamspace);
792                         ajStrAssignS(&gprel->name, edamname);
793                         if(ajStrMatchC(gprel->acdgroup, "input"))
794                             ajListPushAppend(ppnode->acdinputs, gprel);
795                         else if(ajStrMatchC(gprel->acdgroup, "output"))
796                             ajListPushAppend(ppnode->acdoutputs, gprel);
797                         else
798                             ajListPushAppend(ppnode->acdparams, gprel);
799                         /*ajDebug("+++ done:%B %S: %S (%S) edam:%S '%S'\n",
800                                 done, type, qual, gprel->acdgroup,
801                                 edamid, edamname);*/
802                     }
803                     else
804                     {
805                         ajErr("%F: bad ACD relation '%S'", file, value);
806                     }
807                 }
808 		/*
809                 else
810                     ajDebug("    done:%B '%S' '%S'\n", done, token, value);
811 		*/
812             }
813             if(done)
814                 break;
815         }
816     }
817 
818 
819     ajStrDel(&nullgroup);
820     ajStrDel(&tmpstr);
821     ajStrDel(&line);
822     ajStrDel(&text);
823     ajStrTokenDel(&tokenhandle);
824     ajStrDel(&type);
825     ajStrDel(&edamid);
826     ajStrDel(&edamspace);
827     ajStrDel(&edamname);
828     ajStrDel(&qual);
829     ajStrDel(&token);
830     ajStrDel(&nullgroup);
831 
832     return;
833 }
834 
835 
836 
837 
838 /* @funcstatic grpParseEmbassy ************************************************
839 **
840 ** parse the acd file to get the EMBASSY application attribute
841 **
842 ** @param [u] file [AjPFile]  ACD file
843 ** @param [w] embassyname [AjPStr*] EMBASSY package name from
844 **                                     embassy attribute
845 ** @return [void]
846 **
847 ** @release 4.1.0
848 ** @@
849 ******************************************************************************/
850 
grpParseEmbassy(AjPFile file,AjPStr * embassyname)851 static void grpParseEmbassy(AjPFile file, AjPStr* embassyname)
852 {
853 
854     AjPStr line = NULL;
855     AjPStr text = NULL;
856 
857     AjPStrTok tokenhandle;
858     char white[]     = " \t\n\r";
859     char whiteplus[] = " \t\n\r:=";
860     AjPStr tmpstr = NULL;
861     AjPStr token  = NULL;
862     AjPStr value  = NULL;
863     ajint done = 0;
864 
865     ajStrAssignC(embassyname, "");
866 
867     /* read file into one line, stripping out comment lines and blanks */
868     while(ajReadlineTrim(file, &line))
869     {
870 	grpNoComment(&line);
871 
872 	if(ajStrGetLen(line))
873 	{
874 	    ajStrAppendS(&text, line);
875 	    ajStrAppendC(&text, " ");
876 	}
877     }
878 
879     tokenhandle = ajStrTokenNewC(text, white);
880 
881     /* find appl token */
882     while(ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr))
883 	if(ajStrPrefixCaseC(tmpstr, "application"))
884 	    break;
885 
886     /* next token is the application name */
887     ajStrTokenNextParseC(tokenhandle, white, &tmpstr);
888 
889     /* if next token is '[' */
890     ajStrTokenNextParseC(tokenhandle, white, &tmpstr);
891     if(ajStrCmpC(tmpstr, "[") == 0)
892     {
893 	token=ajStrNew();
894 
895 	/* is the next token 'doc' or 'groups' or 'gui' */
896 	while(ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr))
897 	{
898 	    while(!ajStrMatchC(tmpstr, "]"))
899 	    {
900 		ajStrAssignS(&token, tmpstr);
901 		value = grpParseValueRB(tokenhandle, white);
902 		done = ajStrMatchC(value, "]");
903 
904 		if(!done)
905 		{
906 		    ajStrTokenNextParseC(tokenhandle, whiteplus, &tmpstr);
907 		    ajStrFmtLower(&tmpstr);
908 		    done = ajStrMatchC(tmpstr, "]");
909 		}
910 
911 		if(ajStrPrefixCaseC(token, "embassy"))
912 		{
913 		    ajStrAssignS(embassyname, value);
914 		}
915 	    }
916 
917 	    if(done)
918 		break;
919 	}
920     }
921 
922     ajStrDel(&tmpstr);
923     ajStrDel(&line);
924     ajStrDel(&text);
925     ajStrTokenDel(&tokenhandle);
926 
927     return;
928 }
929 
930 
931 
932 
933 /* @funcstatic grpNoComment ***************************************************
934 **
935 ** Strips comments from a character string (a line from an trn file).
936 ** Comments are blank lines or any text following a "#" character.
937 ** Whitespace characters can be included in a blank line.
938 **
939 ** @param [u] text [AjPStr*] Line of text from input file
940 ** @return [void]
941 **
942 ** @release 2.5.0
943 ** @@
944 ******************************************************************************/
945 
grpNoComment(AjPStr * text)946 static void grpNoComment(AjPStr* text)
947 {
948     ajint i;
949     char *cp;
950 
951     ajStrTrimWhite(text);
952     i = ajStrGetLen(*text);
953 
954     if(!i)				/* empty string */
955 	return;
956 
957     cp = strchr(ajStrGetPtr(*text), '#');
958 
959     if(cp)
960     {					/* comment found */
961 	*cp = '\0';
962 	ajStrSetValid(text);
963     }
964 
965     return;
966 }
967 
968 
969 
970 
971 /* @funcstatic grpParseValueRB ************************************************
972 **
973 ** Copied from ajacd.c
974 **
975 ** Uses ajStrTok to complete a (possibly) quoted value.
976 ** Note that the AjPStrTok object has a stored internal copy of the text string
977 ** which is set up in the calling function and is being used here.
978 **
979 ** Quotes can be single or double, or any kind of parentheses,
980 ** depending on the first character of the next token examined.
981 **
982 ** @param [u] tokenhandle [AjPStrTok] Current parsing handle for input text
983 ** @param [r] delim [const char*] Delimiter string
984 ** @return [AjPStr] String containing next value using acdStrTok
985 **
986 ** @release 1.0.0
987 ** @@
988 ******************************************************************************/
989 
grpParseValueRB(AjPStrTok tokenhandle,const char * delim)990 static AjPStr grpParseValueRB(AjPStrTok tokenhandle, const char* delim)
991 {
992     char  endq[]   = " ";
993     char  endqbr[] = " ]";
994     ajint iquote;
995     char *cq;
996     AjBool done   = ajFalse;
997     AjBool rightb = ajFalse;
998 
999     const char *quotes    = "\"";
1000     const char *endquotes = "\"";
1001 
1002     if(!ajStrTokenNextParseC(tokenhandle, delim, &grpStr1))
1003 	return NULL;
1004 
1005     cq = strchr(quotes, ajStrGetCharFirst(grpStr1));
1006 
1007     if(!cq)
1008 	return grpStr1;
1009 
1010 
1011     /* quote found: parse up to closing quote then strip white space */
1012 
1013     ajStrDelStatic(&grpStr2);
1014 
1015     iquote = (ajint) (cq - quotes);
1016     endq[0] = endqbr[0] = endquotes[iquote];
1017     ajStrCutStart(&grpStr1, 1);
1018 
1019     while(!done)
1020     {
1021 	if(ajStrSuffixC(grpStr1, endq))
1022 	{
1023 	    ajStrCutEnd(&grpStr1, 1);
1024 	    done = ajTrue;
1025 	}
1026 
1027 	if(ajStrSuffixC(grpStr1, endqbr))
1028 	{
1029 	    ajStrCutEnd(&grpStr1, 2);
1030 	    rightb = ajTrue;
1031 	    done = ajTrue;
1032 	}
1033 
1034 	if(ajStrGetLen(grpStr1))
1035 	{
1036 	    if(ajStrGetLen(grpStr2))
1037 		ajStrAppendC(&grpStr2, " ");
1038 
1039 	    ajStrAppendS(&grpStr2, grpStr1);
1040 	}
1041 
1042 	if(!done)
1043 	    if(!ajStrTokenNextParseC(tokenhandle, delim, &grpStr1))
1044 		return NULL;
1045     }
1046 
1047     if(rightb)
1048 	ajStrAppendC(&grpStr2, "]");
1049 
1050     return grpStr2;
1051 }
1052 
1053 
1054 
1055 
1056 /* @funcstatic grpSplitList ***************************************************
1057 **
1058 ** Split a string containing group names into a list on the delimiters
1059 ** ',' or ';' or '|' to form the primary names of the groups.
1060 ** Any names containing a colon ':' are optionally expanded in a call to
1061 ** grpSubSplitList() to form many combinations of group names.
1062 **
1063 ** The group names are returned as a list.
1064 **
1065 ** @param [u] groups [AjPList] List of groups
1066 ** @param [r]  value  [const AjPStr] Groups string from ACD file
1067 ** @param [r]  explode [AjBool] Expand group names around ':'
1068 ** @param [r]  colon [AjBool] Retain ':' in group names
1069 ** @param [u]  keywords [AjPStr*] List of keywords
1070 **
1071 ** @return [void]
1072 **
1073 ** @release 2.5.0
1074 ** @@
1075 ******************************************************************************/
1076 
grpSplitList(AjPList groups,const AjPStr value,AjBool explode,AjBool colon,AjPStr * keywords)1077 static void grpSplitList(AjPList groups, const AjPStr value, AjBool explode,
1078 			 AjBool colon, AjPStr *keywords)
1079 {
1080     AjPStrTok colontokenhandle;
1081     AjPStrTok tokenhandle;
1082     char delim[]       = ",;|";
1083     char colonstring[] = ":";
1084     AjPList subnames;
1085     AjPStr tmpstr  = NULL;
1086     AjPStr substr  = NULL;
1087     AjPStr copystr = NULL;
1088     AjPStr keystr  = NULL;
1089 
1090     tokenhandle = ajStrTokenNewC(value, delim);
1091 
1092     while(ajStrTokenNextParse(tokenhandle, &tmpstr))
1093     {
1094 	ajStrTrimWhite(&tmpstr);
1095 	ajStrTrimC(&tmpstr, ".");
1096 
1097 	ajStrAssignS(&keystr, tmpstr);
1098 	ajStrExchangeKK(&keystr, ':', '_');
1099 	ajStrExchangeKK(&keystr, ' ', '_');
1100 
1101 	if(ajStrGetLen(*keywords))
1102 	    ajStrAppendK(keywords, ' ');
1103 
1104 	ajStrAppendS(keywords, keystr);
1105 
1106 	/*
1107 	** split the group name on colons and expand the sub-names into several
1108 	** combinations of name
1109 	*/
1110 	if(explode)
1111 	{
1112 	    subnames = ajListstrNew();
1113 	    colontokenhandle  = ajStrTokenNewC(tmpstr, colonstring);
1114 
1115 	    while(ajStrTokenNextParse(colontokenhandle, &substr))
1116 	    {
1117 		copystr = ajStrNewS(substr); /* make new copy of the string
1118 					       for the list to hold */
1119 		ajStrTrimWhite(&copystr);
1120 		ajListstrPushAppend(subnames, copystr);
1121 	    }
1122 
1123 	    /*
1124 	    ** make the combinations of sub-names and add them to the list of
1125 	    ** group names
1126 	    */
1127 	    grpSubSplitList(groups, subnames);
1128 
1129 	    ajStrTokenDel(&colontokenhandle);
1130 	    ajStrDel(&substr);
1131 	    ajListstrFreeData(&subnames);
1132 	    /*
1133 	     ** don't free up copystr - because ajListstrFreeData()
1134 	     ** then tries to free
1135 	     ** it as well ajStrDel(&copystr);
1136 	     */
1137 
1138 	}
1139 	else
1140 	{
1141 	    /*
1142 	    ** don't explode, just remove ':'s and excess spaces and add to
1143 	    ** 'groups' list
1144 	    */
1145 	    copystr = ajStrNewRef(tmpstr);	/* make new copy of the string
1146 					   for the list to hold */
1147 	    /*
1148 	     ** we might want to retain the ':' in the output
1149 	     ** if it is being parsed by
1150 	     ** other programs that create 2-level menus for an interface etc.
1151 	     */
1152 	    if(!colon)
1153 	    {
1154 		ajStrExchangeSetCC(&copystr, ":", " ");
1155 		ajStrRemoveWhiteExcess(&copystr);
1156 	    }
1157 	    else
1158 	    {
1159 		/* tidy up spurious spaces around the colon */
1160 		ajStrRemoveWhiteExcess(&copystr);
1161 		ajStrExchangeCC(&copystr, " :", ":");
1162 		ajStrExchangeCC(&copystr, ": ", ":");
1163 	    }
1164 
1165 	    ajListstrPushAppend(groups, copystr);
1166 	}
1167     }
1168 
1169     ajStrTokenDel(&tokenhandle);
1170     ajStrDel(&tmpstr);
1171     ajStrDel(&substr);
1172     ajStrDel(&keystr);
1173 
1174     return;
1175 }
1176 
1177 
1178 
1179 
1180 /* @funcstatic grpSubSplitList ************************************************
1181 **
1182 ** Takes a list of words and makes several combinations of them to
1183 ** construct the expanded group constructs made from the ':' operator in
1184 ** the group names.
1185 **
1186 ** For example, the group name 'aaa:bbb:ccc' will be passed over to this
1187 ** routine as the list 'aaa', 'bbb', 'ccc' and the group names:
1188 ** 'aaa bbb ccc'
1189 ** 'ccc bbb aaa'
1190 ** 'aaa bbb'
1191 ** 'bbb aaa'
1192 ** 'bbb ccc'
1193 ** 'ccc bbb'
1194 ** 'ccc'
1195 ** 'bbb'
1196 ** 'aaa'
1197 ** will be constructed and added to the list of group names in 'groups'
1198 **
1199 ** @param [u] groups [AjPList] List of groups
1200 ** @param [u] sublist [AjPList] (Sub)-names of groups string from ACD file
1201 **
1202 ** @return [void]
1203 **
1204 ** @release 2.5.0
1205 ** @@
1206 ******************************************************************************/
1207 
grpSubSplitList(AjPList groups,AjPList sublist)1208 static void grpSubSplitList(AjPList groups, AjPList sublist)
1209 {
1210     AjPStr *sub;			/* array of sub-names */
1211     ajint len;			    /* length of array of sub-names */
1212     ajint i;
1213     ajint j;
1214     AjPStr head;			/* constructed group names */
1215     AjPStr tail;
1216     AjPStr revhead;
1217     AjPStr revtail;
1218     AjPStr dummy = NULL;        /* dummy string for ajListstrPop() */
1219 
1220 
1221     len = (ajuint) ajListstrToarray(sublist, &sub);
1222 
1223     for(i=0; i<len; i++)
1224     {
1225 	/* do head of list */
1226 	head = ajStrNew();
1227 
1228 	for(j=0; j<=i; j++)
1229 	{
1230 	    if(ajStrGetLen(head) > 0)
1231 		ajStrAppendC(&head, " ");
1232 
1233 	    ajStrAppendS(&head, sub[j]);
1234 	}
1235 
1236 	ajListstrPushAppend(groups, head);
1237 
1238 	/*
1239 	** do reverse head of list if there is more than
1240 	** one name in the head
1241 	*/
1242 	if(i)
1243 	{
1244 	    revhead = ajStrNew();
1245 
1246 	    for(j=i; j>=0; j--)
1247 	    {
1248 		if(ajStrGetLen(revhead) > 0)
1249 		    ajStrAppendC(&revhead, " ");
1250 
1251 		ajStrAppendS(&revhead, sub[j]);
1252 	    }
1253 
1254 	    ajListstrPushAppend(groups, revhead);
1255 	}
1256 
1257 	/* do tail of list, if there is any tail left */
1258 	if(i < len-1)
1259 	{
1260 	    tail = ajStrNew();
1261 
1262 	    for(j=i+1; j<len; j++)
1263 	    {
1264 		if(ajStrGetLen(tail) > 0)
1265 		    ajStrAppendC(&tail, " ");
1266 
1267 		ajStrAppendS(&tail, sub[j]);
1268 	    }
1269 
1270 	    ajListstrPushAppend(groups, tail);
1271 	}
1272 
1273 	/*
1274 	** do reverse tail of list if there is more than
1275 	** one name in the tail
1276 	*/
1277 	if(i < len-2)
1278 	{
1279 	    revtail = ajStrNew();
1280 
1281 	    for(j=len-1; j>i; j--)
1282 	    {
1283 		if(ajStrGetLen(revtail) > 0)
1284                     ajStrAppendC(&revtail, " ");
1285 
1286 		ajStrAppendS(&revtail, sub[j]);
1287 	    }
1288 
1289 	    ajListstrPushAppend(groups, revtail);
1290 	}
1291 
1292     }
1293 
1294     AJFREE(sub);
1295 
1296     /* if list length is greater than 2, pop off head and tail and recurse */
1297     if(len > 2)
1298     {
1299 	ajListstrPop(sublist, &dummy);	/* remove first node */
1300 	ajStrDel(&dummy);
1301 
1302 	/* remove last node of list. */
1303 
1304 	ajListstrReverse(sublist);
1305 	ajListstrPop(sublist, &dummy);	/* remove first node */
1306 	ajStrDel(&dummy);
1307 	ajListstrReverse(sublist);
1308 
1309 	/* recurse */
1310 	grpSubSplitList(groups, sublist);
1311     }
1312 
1313     return;
1314 }
1315 
1316 
1317 
1318 
1319 /* @funcstatic grpAddGroupsToList *********************************************
1320 **
1321 ** Add application to applications list and its groups to the full groups list
1322 ** Results are alpha list and glist.
1323 **
1324 ** alpha is a list of application with sub-lists of their groups
1325 ** glist is a list of groups with sub-lists of their applications
1326 **
1327 ** @param [r] alpha [const AjPList] Alphabetic list of programs
1328 ** @param [u] glist [AjPList] List of all known groups
1329 ** @param [r] groups [const AjPList] List of groups for this application
1330 ** @param [u] Pprognode  [EmbPGroupProg*] Program node by reference
1331 **
1332 ** @return [void]
1333 **
1334 ** @release 2.5.0
1335 ** @@
1336 ******************************************************************************/
1337 
grpAddGroupsToList(const AjPList alpha,AjPList glist,const AjPList groups,EmbPGroupProg * Pprognode)1338 static void grpAddGroupsToList(const AjPList alpha, AjPList glist,
1339 			       const AjPList groups,
1340 			       EmbPGroupProg *Pprognode)
1341 {
1342     AjPStr g = NULL;	/* temporary value of member of groups list */
1343     AjIList aiter;	/* 'alpha' iterator */
1344     AjIList iter;	/* 'groups' iterator */
1345     AjIList giter;	/* 'glist' iterator */
1346     AjIList niter;	/* 'nlist' iterator */
1347     EmbPGroupTop al;	/* next (first) member of alpha */
1348     EmbPGroupTop gl;	/* next member of glist */
1349     EmbPGroupTop nl;	/* next member of nlist */
1350     EmbPGroupTop gpnode;	/* new member of glist being added */
1351     EmbPGroupProg ppnode;	/* new member of plist list being added */
1352     EmbPGroupProg apnode;	/* new member of alpha list being added */
1353     AjPList nlist=NULL;	/* list of programs in a group - used to check
1354 			   name is unique */
1355     AjBool foundit;	/* flag for found the program name */
1356 
1357     ppnode = *Pprognode;
1358 
1359     /* add this program to the alphabetic list of programs */
1360     apnode = embGrpMakeNewPnode(ppnode->name, ppnode->doc,
1361                                 ppnode->keywords, ppnode->package);
1362     aiter = ajListIterNewread(alpha);
1363     al = ajListIterGet(aiter);
1364     ajListPushAppend(al->progs, apnode);
1365     ajListIterDel(&aiter);
1366 
1367     /*
1368     ** Now step through all groups that this program belongs to and add this
1369     ** program to the groups
1370     */
1371     iter = ajListIterNewread(groups);
1372 
1373     while((g = ajListIterGet(iter)) != NULL)
1374     {
1375 	/* add the group name to the program node in alpha list */
1376 	gpnode = embGrpMakeNewGnode(g);
1377 	ajListPushAppend(apnode->groups, gpnode);
1378 
1379 	/* add the application to the appropriate groups list in glist */
1380 	giter = ajListIterNewread(glist);
1381 
1382 	while((gl = ajListIterGet(giter)) != NULL)
1383 	{
1384 	    /* is this our group ? */
1385 	    if(!ajStrCmpCaseS(gl->name, g))
1386 	    {
1387 		/*
1388 		** found the group.
1389 		** look through the program names in this group
1390 		** and only add if name is
1391 		** not already there
1392 		*/
1393 		foundit = ajFalse;
1394 		nlist   = gl->progs;
1395 		niter   = ajListIterNewread(nlist);
1396 
1397 		while((nl = ajListIterGet(niter)) != NULL)
1398 		    if(ajStrMatchCaseS(nl->name, ppnode->name))
1399 		    {
1400 			/* found the program name */
1401 			foundit = ajTrue;
1402 			break;
1403 		    }
1404 
1405 		ajListIterDel(&niter);
1406 
1407 		if(!foundit)
1408 		{
1409 		    ajListPushAppend(gl->progs, grpCopyPnode(ppnode));
1410 		}
1411 
1412 		break;
1413 	    }
1414 	}
1415 
1416 	if(gl == NULL)
1417 	{
1418 	    /* went past the end of the group list - new group */
1419 	    gpnode = embGrpMakeNewGnode(g);
1420 	    ajListPushAppend(glist, gpnode);
1421             ajListPushAppend(gpnode->progs, grpCopyPnode(ppnode));
1422         }
1423 	ajListIterDel(&giter);
1424     }
1425 
1426     embGrpProgDel(Pprognode);
1427 
1428     ajListIterDel(&iter);
1429     ajStrDel(&g);
1430 
1431     /* sort the groups for this application in alpha list */
1432     embGrpSortGroupsList(apnode->groups);
1433 
1434     return;
1435 }
1436 
1437 
1438 
1439 
1440 /* @func embGrpMakeNewGnode ***************************************************
1441 **
1442 ** Creates a new pointer to a Gnode struct for holding a group's
1443 ** name and pointer to a list of programs (also held in Gnodes).
1444 **
1445 ** @param [r] name [const AjPStr] Name of the group
1446 ** @return [EmbPGroupTop] pointer to a new GPnode struct
1447 **
1448 ** @release 2.0.0
1449 ** @@
1450 ******************************************************************************/
1451 
embGrpMakeNewGnode(const AjPStr name)1452 EmbPGroupTop embGrpMakeNewGnode(const AjPStr name)
1453 {
1454     EmbPGroupTop gpnode;
1455     AjPStr newstr = NULL;
1456     AjPStr dummy  = NULL;
1457 
1458 
1459     /*  ajDebug("New groups gnode name=%S\n", name); */
1460     AJNEW0(gpnode);
1461 
1462     newstr = ajStrNewS(name);
1463     ajStrFmtUpper(&newstr);
1464 
1465     gpnode->name = newstr;
1466     ajStrAssignC(&dummy, "");
1467     gpnode->doc = dummy;
1468     gpnode->progs = ajListNew();
1469 
1470     return gpnode;
1471 }
1472 
1473 
1474 
1475 
1476 /* @func embGrpMakeNewPnode ***************************************************
1477 **
1478 ** Creates a new pointer to a Gnode struct for holding a program's
1479 ** name and documentation.
1480 **
1481 ** @param [r] name [const AjPStr] Name of the program
1482 ** @param [r] doc [const AjPStr] Description of the program
1483 ** @param [r] keywords [const AjPStr] Keywords for this program
1484 **                                    with underscores for spaces and
1485 **                                    with spaces as separators
1486 ** @param [r] package [const AjPStr] Name of the package
1487 ** @return [EmbPGroupProg] pointer to a new gnode struct
1488 **
1489 ** @release 2.0.0
1490 ** @@
1491 ******************************************************************************/
1492 
embGrpMakeNewPnode(const AjPStr name,const AjPStr doc,const AjPStr keywords,const AjPStr package)1493 EmbPGroupProg embGrpMakeNewPnode(const AjPStr name, const AjPStr doc,
1494 				 const AjPStr keywords, const AjPStr package)
1495 {
1496     EmbPGroupProg gpnode;
1497 
1498     AJNEW0(gpnode);
1499     gpnode->name    = ajStrNewS(name);
1500     gpnode->doc     = ajStrNewS(doc);
1501     gpnode->keywords= ajStrNewS(keywords);
1502     gpnode->package = ajStrNewS(package);
1503     gpnode->groups  = ajListNew();
1504     gpnode->acdtopics      = ajListNew();
1505     gpnode->acdoperations  = ajListNew();
1506     gpnode->acdinputs      = ajListNew();
1507     gpnode->acdoutputs     = ajListNew();
1508     gpnode->acdparams      = ajListNew();
1509 
1510     return gpnode;
1511 }
1512 
1513 
1514 
1515 
1516 /* @funcstatic grpCopyPnode ***************************************************
1517 **
1518 ** Creates a new pointer to a Pnode struct for holding a program's
1519 ** name and documentation.
1520 **
1521 ** @param [r] pnode [const EmbPGroupProg] Source program node
1522 ** @return [EmbPGroupProg] pointer to a new pnode struct
1523 **
1524 ** @release 6.4.0
1525 ** @@
1526 ******************************************************************************/
1527 
grpCopyPnode(const EmbPGroupProg pnode)1528 static EmbPGroupProg grpCopyPnode(const EmbPGroupProg pnode)
1529 {
1530     EmbPGroupProg gpnode;
1531     EmbPGroupTop gl;
1532     EmbPGroupTop newgl;
1533     EmbPGroupRelation gr;
1534     AjIList iter = NULL;
1535 
1536     AJNEW0(gpnode);
1537     gpnode->name    = ajStrNewS(pnode->name);
1538     gpnode->doc     = ajStrNewS(pnode->doc);
1539     gpnode->keywords= ajStrNewS(pnode->keywords);
1540     gpnode->package = ajStrNewS(pnode->package);
1541 
1542     if(pnode->groups)
1543     {
1544         gpnode->groups  = ajListNew();
1545         iter = ajListIterNewread(pnode->groups);
1546         while(!ajListIterDone(iter))
1547         {
1548             gl = ajListIterGet(iter);
1549             AJNEW0(newgl);
1550             newgl->name = ajStrNewS(gl->name);
1551             newgl->doc = ajStrNewS(gl->doc);
1552             ajListPushAppend(gpnode->groups, newgl);
1553         }
1554         ajListIterDel(&iter);
1555     }
1556 
1557     if(pnode->acdtopics)
1558     {
1559         gpnode->acdtopics  = ajListNew();
1560         iter = ajListIterNewread(pnode->acdtopics);
1561         while(!ajListIterDone(iter))
1562         {
1563             gr = ajListIterGet(iter);
1564             ajListPushAppend(gpnode->acdtopics, grpRelationsCopy(gr));
1565         }
1566         ajListIterDel(&iter);
1567     }
1568 
1569     if(pnode->acdoperations)
1570     {
1571         gpnode->acdoperations  = ajListNew();
1572         iter = ajListIterNewread(pnode->acdoperations);
1573         while(!ajListIterDone(iter))
1574         {
1575             gr = ajListIterGet(iter);
1576             ajListPushAppend(gpnode->acdoperations, grpRelationsCopy(gr));
1577         }
1578         ajListIterDel(&iter);
1579     }
1580 
1581     if(pnode->acdinputs)
1582     {
1583         gpnode->acdinputs  = ajListNew();
1584         iter = ajListIterNewread(pnode->acdinputs);
1585         while(!ajListIterDone(iter))
1586         {
1587             gr = ajListIterGet(iter);
1588             ajListPushAppend(gpnode->acdinputs, grpRelationsCopy(gr));
1589         }
1590         ajListIterDel(&iter);
1591     }
1592 
1593     if(pnode->acdoutputs)
1594     {
1595         gpnode->acdoutputs = ajListNew();
1596         iter = ajListIterNewread(pnode->acdoutputs);
1597         while(!ajListIterDone(iter))
1598         {
1599             gr = ajListIterGet(iter);
1600             ajListPushAppend(gpnode->acdoutputs, grpRelationsCopy(gr));
1601         }
1602         ajListIterDel(&iter);
1603     }
1604 
1605     if(pnode->acdparams)
1606     {
1607         gpnode->acdparams  = ajListNew();
1608         iter = ajListIterNewread(pnode->acdparams);
1609         while(!ajListIterDone(iter))
1610         {
1611             gr = ajListIterGet(iter);
1612             ajListPushAppend(gpnode->acdparams, grpRelationsCopy(gr));
1613         }
1614         ajListIterDel(&iter);
1615     }
1616 
1617     return gpnode;
1618 }
1619 
1620 
1621 
1622 
1623 /* @func embGrpSortGroupsList *************************************************
1624 **
1625 ** Sort a list of GPnodes by their name.
1626 **
1627 ** @param [u] groupslist [AjPList] List to sort
1628 ** @return [void]
1629 **
1630 ** @release 2.0.0
1631 ** @@
1632 ******************************************************************************/
1633 
embGrpSortGroupsList(AjPList groupslist)1634 void embGrpSortGroupsList(AjPList groupslist)
1635 {
1636     EmbPGroupTop gl;
1637     AjIList giter;
1638 
1639     /* sort the programs for each group */
1640     giter = ajListIterNewread(groupslist);
1641 
1642     while((gl = ajListIterGet(giter)) != NULL)
1643 	ajListSort(gl->progs, &embGrpCompareTwoPnodes);
1644 
1645 
1646     ajListIterDel(&giter);
1647 
1648     /* sort the groups themselves */
1649     ajListSort(groupslist, &embGrpCompareTwoGnodes);
1650 
1651     return;
1652 }
1653 
1654 
1655 
1656 
1657 /* @func embGrpSortProgsList **************************************************
1658 **
1659 ** Sort a list of Pnodes by their name.
1660 **
1661 ** @param [u] progslist [AjPList] List to sort
1662 ** @return [void]
1663 **
1664 ** @release 4.0.0
1665 ** @@
1666 ******************************************************************************/
1667 
embGrpSortProgsList(AjPList progslist)1668 void embGrpSortProgsList(AjPList progslist)
1669 {
1670     EmbPGroupProg pl;
1671     AjIList piter;
1672 
1673     /* sort the groups for each program */
1674     piter = ajListIterNewread(progslist);
1675 
1676     while((pl = ajListIterGet(piter)) != NULL)
1677 	ajListSort(pl->groups, &embGrpCompareTwoGnodes);
1678 
1679 
1680     ajListIterDel(&piter);
1681 
1682     /* sort the groups themselves */
1683     ajListSort(progslist, &embGrpCompareTwoPnodes);
1684 
1685     return;
1686 }
1687 
1688 
1689 
1690 
1691 /* @func embGrpCompareTwoGnodes ***********************************************
1692 **
1693 ** Compare two Gnodes as case-insensitive strings.
1694 **
1695 ** @param [r] a [const void *] First node
1696 ** @param [r] b [const void *] Second node
1697 **
1698 ** @return [ajint] Compare value (-1, 0, +1)
1699 **
1700 ** @release 2.0.0
1701 ** @@
1702 ******************************************************************************/
1703 
embGrpCompareTwoGnodes(const void * a,const void * b)1704 ajint embGrpCompareTwoGnodes(const void * a, const void * b)
1705 {
1706     return ajStrCmpCaseS((*(EmbPGroupTop const *)a)->name,
1707 			 (*(EmbPGroupTop const *)b)->name);
1708 }
1709 
1710 
1711 
1712 
1713 /* @func embGrpCompareTwoPnodes ***********************************************
1714 **
1715 ** Compare two Pnodes as case-insensitive strings.
1716 **
1717 ** @param [r] a [const void *] First node
1718 ** @param [r] b [const void *] Second node
1719 **
1720 ** @return [ajint] Compare value (-1, 0, +1)
1721 **
1722 ** @release 4.0.0
1723 ** @@
1724 ******************************************************************************/
1725 
embGrpCompareTwoPnodes(const void * a,const void * b)1726 ajint embGrpCompareTwoPnodes(const void * a, const void * b)
1727 {
1728     return ajStrCmpCaseS((*(EmbPGroupProg const *)a)->name,
1729 			 (*(EmbPGroupProg const *)b)->name);
1730 }
1731 
1732 
1733 
1734 
1735 /* @func embGrpOutputGroupsList ***********************************************
1736 **
1737 ** Displays a list of groups to an output file handle.
1738 **
1739 ** @param [u] outfile [AjPFile] Output file handle
1740 ** @param [r] groupslist [const AjPList] List of groups to be displayed
1741 ** @param [r] showprogs [AjBool] If True, display the programs in each group
1742 ** @param [r] html [AjBool] If True, format for HTML, else make a simple list
1743 ** @param [r] showkey [AjBool] If True, show keywords
1744 ** @param [r] package [const AjPStr] Name of current package
1745 ** @return [void]
1746 **
1747 ** @release 2.0.0
1748 ** @@
1749 ******************************************************************************/
1750 
embGrpOutputGroupsList(AjPFile outfile,const AjPList groupslist,AjBool showprogs,AjBool html,AjBool showkey,const AjPStr package)1751 void embGrpOutputGroupsList(AjPFile outfile, const AjPList groupslist,
1752 			    AjBool showprogs, AjBool html,
1753 			    AjBool showkey, const AjPStr package)
1754 {
1755     EmbPGroupTop gl;
1756     AjIList giter;			/* 'groupslist' iterator */
1757 
1758     /* output the programs for each group */
1759     if(!showprogs && html)
1760 	ajFmtPrintF(outfile,"<ul>\n");
1761 
1762     giter = ajListIterNewread(groupslist);
1763     while((gl = ajListIterGet(giter)) != NULL)
1764     {
1765 	if(html)
1766 	{
1767 	    if(showprogs)
1768 		ajFmtPrintF(outfile,"<h2><a name=\"%S\">%S</a></h2>\n",
1769 			    gl->name, gl->name);
1770 	    else
1771 	    {
1772 		ajFmtPrintF(outfile,"<li><a href=\"%S.html\">%S</a></li>\n",
1773 				gl->name,gl->name);
1774 	    }
1775 	}
1776 	else
1777 	    ajFmtPrintF(outfile,"%S\n", gl->name);
1778 
1779 	if(showprogs)
1780 	{
1781 	    if(html) ajFmtPrintF(outfile,"<table border cellpadding=4 "
1782 				 "bgcolor=\"#FFFFF0\">\n");
1783 
1784 	    embGrpOutputProgsList(outfile, gl->progs, html, showkey, package);
1785 
1786 	    if(html)
1787 		ajFmtPrintF(outfile,"</table>\n");
1788 	    else
1789 		ajFmtPrintF(outfile,"\n");
1790 	}
1791     }
1792 
1793     if(!showprogs && html)
1794 	ajFmtPrintF(outfile,"</ul>\n");
1795 
1796     ajListIterDel(&giter);
1797 
1798     return;
1799 }
1800 
1801 
1802 
1803 
1804 /* @func embGrpOutputProgsList ************************************************
1805 **
1806 ** Displays a list of programs and their descriptions to an output file handle.
1807 **
1808 ** @param [u] outfile [AjPFile] Output file handle
1809 ** @param [r] progslist [const AjPList] List of programs to be displayed
1810 ** @param [r] html [AjBool] If True, format for HTML, else make a simple list
1811 ** @param [r] showkey [AjBool] Show keywords in output
1812 ** @param [r] package [const AjPStr] Name of current package
1813 ** @return [void]
1814 **
1815 ** @release 2.0.0
1816 ** @@
1817 ******************************************************************************/
1818 
embGrpOutputProgsList(AjPFile outfile,const AjPList progslist,AjBool html,AjBool showkey,const AjPStr package)1819 void embGrpOutputProgsList(AjPFile outfile, const AjPList progslist,
1820 			   AjBool html, AjBool showkey, const AjPStr package)
1821 {
1822     EmbPGroupProg pl;
1823     AjIList piter;			/* 'progslist' iterator */
1824     AjPStr keystr = NULL;
1825     ajint maxwidth = 6;
1826     AjBool isembassy = ajFalse;
1827 
1828     if(ajStrGetLen(package))
1829         isembassy = ajTrue;
1830 
1831     /* output the programs for each group */
1832     if(!html)
1833     {
1834         piter = ajListIterNewread(progslist);
1835 
1836         while((pl = ajListIterGet(piter)) != NULL)
1837         {
1838             if(ajStrGetLen(pl->name) > (ajuint) maxwidth)
1839                 maxwidth = ajStrGetLen(pl->name);
1840         }
1841 
1842         ajListIterDel(&piter);
1843     }
1844 
1845     piter = ajListIterNewread(progslist);
1846 
1847     if(html) ajFmtPrintF(outfile,
1848 			 "<tr><th>Program name</th>\n"
1849                          "<th>Description</th></tr>\n");
1850 
1851     while((pl = ajListIterGet(piter)) != NULL)
1852     {
1853 	if(showkey && ajStrGetLen(pl->keywords)) {
1854 	    ajFmtPrintS(&keystr, "(%S)", pl->keywords);
1855 	    ajStrExchangeKK(&keystr, ' ', ',');
1856 	    ajStrExchangeKK(&keystr, '_', ' ');
1857 	    ajStrInsertK(&keystr, 0, ' ');
1858 	}
1859 	else
1860 	{
1861 	    ajStrAssignC(&keystr, "");
1862 	}
1863 
1864 	if(html)
1865 	{
1866 	    ajFmtPrintF(outfile, "<tr>\n");
1867 
1868 	    if(ajStrMatchCaseS(package, pl->package))
1869 		ajFmtPrintF(outfile,
1870 			    "<td><a href=\"%S.html\">%S</a></td>\n",
1871 			    pl->name, pl->name);
1872 	    else if(isembassy && ajStrGetLen(pl->package))
1873 		ajFmtPrintF(outfile,
1874 			    "<td><a href=\"../%S/%S.html\">%S</a></td>\n",
1875 			    pl->package, pl->name, pl->name);
1876 	    else if(ajStrGetLen(pl->package))
1877 		ajFmtPrintF(outfile,
1878 			    "<td><a href=\"/embassy/%S/%S.html\">%S</a></td>\n",
1879 			    pl->package, pl->name, pl->name);
1880 	    else
1881 		ajFmtPrintF(outfile,
1882 			    "<td><a href=\"../../emboss/apps/%S.html\">%S"
1883                             "</a></td>\n",
1884 			    pl->name, pl->name);
1885 	    ajFmtPrintF(outfile,
1886 			"<td>%S%S</td>\n</tr>\n\n",
1887 			pl->doc, keystr);
1888 	}
1889 	else
1890 	    ajFmtPrintF(outfile, "%-*S %S%S\n",
1891                         maxwidth, pl->name, pl->doc, keystr);
1892     }
1893 
1894     ajListIterDel(&piter);
1895 
1896     ajStrDel(&keystr);
1897 
1898     return;
1899 }
1900 
1901 
1902 
1903 
1904 /* @funcstatic grpGroupsListClear *********************************************
1905 **
1906 ** Clear a groups list
1907 **
1908 ** @param [d] groupslist [AjPList] List of groups to be cleared
1909 ** @return [void]
1910 **
1911 ** @release 6.4.0
1912 ** @@
1913 ******************************************************************************/
1914 
grpGroupsListClear(AjPList groupslist)1915 static void grpGroupsListClear(AjPList groupslist)
1916 {
1917     EmbPGroupTop gl;
1918     AjIList giter;
1919 
1920     giter = ajListIterNew(groupslist);
1921 
1922     while((gl = ajListIterGet(giter)) != NULL)
1923     {
1924 	ajStrDel(&(gl->doc));
1925 	ajStrDel(&(gl->name));
1926 	embGrpProgsListDel(&(gl->progs));
1927 	AJFREE(gl);
1928     }
1929 
1930     ajListIterDel(&giter);
1931 
1932     return;
1933 }
1934 
1935 
1936 
1937 
1938 /* @func embGrpGroupsListDel **************************************************
1939 **
1940 ** Destructor for a groups list
1941 **
1942 ** @param [d] groupslist [AjPList*] List of groups to be destroyed
1943 ** @return [void]
1944 **
1945 ** @release 2.0.0
1946 ** @@
1947 ******************************************************************************/
1948 
embGrpGroupsListDel(AjPList * groupslist)1949 void embGrpGroupsListDel(AjPList *groupslist)
1950 {
1951     grpGroupsListClear(*groupslist);
1952     ajListFree(groupslist);
1953 
1954     return;
1955 }
1956 
1957 
1958 
1959 
1960 /* @func embGrpProgsListDel ***************************************************
1961 **
1962 ** Destructor for a groups list
1963 **
1964 ** @param [d] progslist [AjPList*] List of programs to be destroyed
1965 ** @return [void]
1966 **
1967 ** @release 4.0.0
1968 ** @@
1969 ******************************************************************************/
1970 
embGrpProgsListDel(AjPList * progslist)1971 void embGrpProgsListDel(AjPList *progslist)
1972 {
1973     EmbPGroupProg gl;
1974     AjIList piter;
1975 
1976     if(!*progslist)
1977         return;
1978 
1979     piter = ajListIterNew(*progslist);
1980 
1981     while((gl = ajListIterGet(piter)) != NULL)
1982     {
1983         embGrpProgDel(&gl);
1984     }
1985 
1986     ajListIterDel(&piter);
1987     ajListFree(progslist);
1988 
1989     return;
1990 }
1991 
1992 
1993 
1994 
1995 /* @funcstatic grpProgClear ***************************************************
1996 **
1997 ** Reset a program node
1998 **
1999 ** @param [d] gl [EmbPGroupProg] Program node
2000 ** @return [void]
2001 **
2002 ** @release 6.4.0
2003 ** @@
2004 ******************************************************************************/
2005 
grpProgClear(EmbPGroupProg gl)2006 static void grpProgClear(EmbPGroupProg gl)
2007 {
2008     ajStrSetClear(&(gl->name));
2009     ajStrSetClear(&(gl->doc));
2010     ajStrSetClear(&(gl->package));
2011     ajStrSetClear(&(gl->keywords));
2012     grpGroupsListClear((gl->groups));
2013     grpRelationsListClear((gl->acdtopics));
2014     grpRelationsListClear((gl->acdoperations));
2015     grpRelationsListClear((gl->acdinputs));
2016     grpRelationsListClear((gl->acdoutputs));
2017     grpRelationsListClear((gl->acdparams));
2018 
2019     return;
2020 }
2021 
2022 
2023 
2024 
2025 /* @func embGrpProgDel ********************************************************
2026 **
2027 ** Destructor for a program node
2028 **
2029 ** @param [d] Pgl [EmbPGroupProg*] Program node
2030 ** @return [void]
2031 **
2032 ** @release 6.4.0
2033 ** @@
2034 ******************************************************************************/
2035 
embGrpProgDel(EmbPGroupProg * Pgl)2036 void embGrpProgDel(EmbPGroupProg *Pgl)
2037 {
2038     EmbPGroupProg gl = *Pgl;
2039 
2040     if(!*Pgl)
2041         return;
2042 
2043     ajStrDel(&(gl->name));
2044     ajStrDel(&(gl->doc));
2045     ajStrDel(&(gl->package));
2046     ajStrDel(&(gl->keywords));
2047     embGrpGroupsListDel(&(gl->groups));
2048     embGrpRelationsListDel(&(gl->acdtopics));
2049     embGrpRelationsListDel(&(gl->acdoperations));
2050     embGrpRelationsListDel(&(gl->acdinputs));
2051     embGrpRelationsListDel(&(gl->acdoutputs));
2052     embGrpRelationsListDel(&(gl->acdparams));
2053 
2054     AJFREE(*Pgl);
2055 
2056     return;
2057 }
2058 
2059 
2060 
2061 
2062 /* @funcstatic grpRelationsListClear ******************************************
2063 **
2064 ** Clear a relations list
2065 **
2066 ** @param [d] relslist [AjPList] List of relations to be cleared
2067 ** @return [void]
2068 **
2069 ** @release 6.4.0
2070 ** @@
2071 ******************************************************************************/
2072 
grpRelationsListClear(AjPList relslist)2073 static void grpRelationsListClear(AjPList relslist)
2074 {
2075     EmbPGroupRelation gl;
2076 
2077     while(ajListGetLength(relslist))
2078     {
2079 	ajListPop(relslist, (void**) &gl);
2080         ajStrDel(&(gl->type));
2081 	ajStrDel(&(gl->qual));
2082 	ajStrDel(&(gl->acdgroup));
2083 	ajStrDel(&(gl->id));
2084 	ajStrDel(&(gl->namespace));
2085 	ajStrDel(&(gl->name));
2086 	AJFREE(gl);
2087     }
2088 
2089     return;
2090 }
2091 
2092 
2093 
2094 
2095 /* @func embGrpRelationsListDel ***********************************************
2096 **
2097 ** Destructor for a relations list
2098 **
2099 ** @param [d] relslist [AjPList*] List of relations to be destroyed
2100 ** @return [void]
2101 **
2102 ** @release 6.4.0
2103 ** @@
2104 ******************************************************************************/
2105 
embGrpRelationsListDel(AjPList * relslist)2106 void embGrpRelationsListDel(AjPList *relslist)
2107 {
2108     EmbPGroupRelation gl;
2109     AjIList piter;
2110 
2111     piter = ajListIterNew(*relslist);
2112 
2113     while((gl = ajListIterGet(piter)) != NULL)
2114     {
2115 	ajStrDel(&(gl->type));
2116 	ajStrDel(&(gl->qual));
2117 	ajStrDel(&(gl->acdgroup));
2118 	ajStrDel(&(gl->id));
2119 	ajStrDel(&(gl->namespace));
2120 	ajStrDel(&(gl->name));
2121 	AJFREE(gl);
2122     }
2123 
2124     ajListIterDel(&piter);
2125     ajListFree(relslist);
2126 
2127     return;
2128 }
2129 
2130 
2131 
2132 
2133 /* @funcstatic grpRelationsCopy ***********************************************
2134 **
2135 ** Copy constructor for a relation
2136 **
2137 ** @param [r] gr [const EmbPGroupRelation] Source relation
2138 ** @return [EmbPGroupRelation] Relation object
2139 **
2140 ** @release 6.4.0
2141 ** @@
2142 ******************************************************************************/
2143 
grpRelationsCopy(const EmbPGroupRelation gr)2144 static EmbPGroupRelation grpRelationsCopy(const EmbPGroupRelation gr)
2145 {
2146     EmbPGroupRelation ret;
2147 
2148     AJNEW0(ret);
2149     ret->type = ajStrNewS(gr->type);
2150     ret->qual = ajStrNewS(gr->qual);
2151     ret->acdgroup = ajStrNewS(gr->acdgroup);
2152     ret->id = ajStrNewS(gr->id);
2153     ret->namespace = ajStrNewS(gr->namespace);
2154     ret->name = ajStrNewS(gr->name);
2155 
2156     return ret;
2157 }
2158 
2159 
2160 
2161 
2162 /* @func embGrpKeySearchProgs *************************************************
2163 **
2164 ** Searches a list of groups and programs for (partial) matches to a keyword
2165 **
2166 ** @param [w] newlist [AjPList] List of matching EmbPGroupProg struct returned
2167 ** @param [r] glist [const AjPList] List of EmbPGroupProg struct to
2168 **                                  search through
2169 ** @param [r] key [const AjPStr] String to search for
2170 ** @param [r] all [AjBool] Match all words in key search string
2171 ** @return [void]
2172 **
2173 ** @release 2.0.0
2174 ** @@
2175 ******************************************************************************/
2176 
embGrpKeySearchProgs(AjPList newlist,const AjPList glist,const AjPStr key,AjBool all)2177 void embGrpKeySearchProgs(AjPList newlist,
2178 			  const AjPList glist, const AjPStr key, AjBool all)
2179 {
2180     AjIList giter;		/* 'glist' iterator */
2181     AjIList piter;		/* 'plist' iterator */
2182     EmbPGroupTop gl;			/* next member of glist */
2183     EmbPGroupTop gpnode;		/* new member of glist being added */
2184     EmbPGroupProg pl;			/* next member of plist */
2185     EmbPGroupProg ppnode;		/* new member of plist being added */
2186     AjPStr gname = NULL;
2187     AjPStr name  = NULL;
2188     AjPStr doc   = NULL;
2189     AjPStr keywords= NULL;
2190     AjPStr keystr = NULL;
2191 
2192     /*
2193     ** compare case independently - so use upper case of both key
2194     ** and name/doc
2195     */
2196     keystr = ajStrNewS(key);
2197     ajStrFmtUpper(&keystr);
2198 
2199     /* make new group */
2200     ajStrAssignC(&gname, "Search for '");
2201     ajStrAppendS(&gname, keystr);
2202     ajStrAppendC(&gname, "'");
2203     gpnode = embGrpMakeNewGnode(gname);
2204     ajListPushAppend(newlist, gpnode);
2205 
2206     giter = ajListIterNewread(glist); /* iterate through existing groups list */
2207 
2208     while((gl = ajListIterGet(giter)) != NULL)
2209     {
2210 	piter = ajListIterNewread(gl->progs);
2211 
2212 	while((pl = ajListIterGet(piter)) != NULL)
2213 	{
2214 	    ajStrAssignS(&name, pl->name);
2215 	    ajStrAssignS(&doc, pl->doc);
2216 	    ajStrAssignS(&keywords, pl->keywords);
2217 	    ajStrFmtUpper(&name);
2218 	    ajStrFmtUpper(&doc);
2219 	    ajStrFmtUpper(&keywords);
2220 
2221 	    if (all)
2222 	    {
2223 		if(ajStrMatchWordAllS(doc,keystr) ||
2224 		   ajStrMatchWordAllS(keywords, keystr) ||
2225 		   ajStrMatchWordAllS(name, keystr))
2226 		{
2227 		    ajDebug("Search '%S' in name:'%S' doc:'%S' key:'%S'\n",
2228 			    keystr, pl->name, pl->doc, pl->keywords);
2229 		    ppnode = grpCopyPnode(pl);
2230 		    ajListPushAppend(gpnode->progs, ppnode);
2231 		}
2232 	    }
2233 	    else
2234 	    {
2235 		if(ajStrMatchWordOneS(doc,keystr) ||
2236 		   ajStrMatchWordOneS(keywords, keystr) ||
2237 		   ajStrMatchWordOneS(name, keystr))
2238 		{
2239 		    ajDebug("Search '%S' in name:'%S' doc:'%S' key:'%S'\n",
2240 			    keystr, pl->name, pl->doc, pl->keywords);
2241 		    ppnode = grpCopyPnode(pl);
2242 		    ajListPushAppend(gpnode->progs, ppnode);
2243 		}
2244 	    }
2245 
2246 	    ajStrDel(&name);
2247 	    ajStrDel(&doc);
2248 	}
2249 
2250 	ajListIterDel(&piter);
2251     }
2252 
2253     ajListIterDel(&giter);
2254 
2255     /* sort the results */
2256     embGrpSortGroupsList(newlist);
2257 
2258     ajStrDel(&gname);
2259     ajStrDel(&name);
2260     ajStrDel(&doc);
2261     ajStrDel(&keystr);
2262     ajStrDel(&keywords);
2263 
2264     return;
2265 }
2266 
2267 
2268 
2269 
2270 /* @func embGrpSearchProgsEdam ************************************************
2271 **
2272 ** Searches a list of groups and programs for (partial) matches to
2273 ** EDAM topic terms
2274 **
2275 ** @param [w] newlist [AjPList] List of matching EmbPGroupProg struct returned
2276 ** @param [r] glist [const AjPList] List of EmbPGroupProg struct to
2277 **                                  search through
2278 ** @param [r] query [const AjPStr] String(s) to search for
2279 ** @param [r] namespace [const char*] EDAM namespace to search
2280 ** @param [r] sensitive [AjBool] Match EDAM definitions
2281 ** @param [r] subclasses [AjBool] Match EDAM subclasses
2282 ** @param [r] obsolete [AjBool] Match EDAM obsolete terms
2283 ** @return [void]
2284 **
2285 ** @release 6.4.0
2286 ** @@
2287 ******************************************************************************/
2288 
embGrpSearchProgsEdam(AjPList newlist,const AjPList glist,const AjPStr query,const char * namespace,AjBool sensitive,AjBool subclasses,AjBool obsolete)2289 void embGrpSearchProgsEdam(AjPList newlist, const AjPList glist,
2290                            const AjPStr query, const char* namespace,
2291                            AjBool sensitive, AjBool subclasses,
2292                            AjBool obsolete)
2293 {
2294     AjIList giter;		/* 'glist' iterator */
2295     AjIList piter;		/* 'plist' iterator */
2296     AjIList eiter;		/* 'relations' iterator */
2297     EmbPGroupTop gl;			/* next member of glist */
2298     EmbPGroupTop gpnode;		/* new member of glist being added */
2299     EmbPGroupProg pl;			/* next member of plist */
2300     EmbPGroupProg ppnode;		/* new member of plist being added */
2301     EmbPGroupRelation rl;		/* next relation for program */
2302     AjPStr gname = NULL;
2303 
2304     AjPStr qrystr = NULL;
2305 
2306     AjPOboin oboin = NULL;
2307     AjPObo obo = NULL;
2308     AjPObo obotest = NULL;
2309 
2310     AjPStr oboqry = NULL;
2311     AjPTable obotable = NULL;
2312     AjPTable apptable = NULL;
2313 
2314     AjPStrTok handle = NULL;
2315     AjPList obolist = NULL;
2316     AjBool dotopics = ajFalse;
2317     AjBool dooperations = ajFalse;
2318     AjBool doinputs = ajFalse;
2319     AjBool dooutputs = ajFalse;
2320     AjBool doparams = ajFalse;
2321 
2322     AjBool matched = ajFalse;
2323 
2324     AjPStr edamNamespace = NULL;
2325 
2326     ajuint i;
2327     ajuint imax = 3;
2328 
2329     const char* fields[] = {"id", "acc", "nam", "des"};
2330 
2331     ajDebug("embGrpSearchProgsEdam '%S' namespace '%s' sens %B sub %B obs %B\n",
2332             query, namespace, sensitive, subclasses, obsolete);
2333     oboin = ajOboinNew();
2334     obo = ajOboNew();
2335 
2336     obolist = ajListNew();
2337     obotable = ajTablestrNew(600);
2338     apptable = ajTablestrNew(600);
2339 
2340     if(sensitive)
2341         imax++;
2342 
2343     if(ajCharMatchC(namespace, "topic"))
2344     {
2345         dotopics = ajTrue;
2346         edamNamespace = ajStrNewC("topic");
2347     }
2348     else if (ajCharMatchC(namespace, "operation"))
2349     {
2350         dooperations = ajTrue;
2351         edamNamespace = ajStrNewC("operation");
2352     }
2353     else if (ajCharMatchC(namespace, "input"))
2354     {
2355         doinputs = ajTrue;
2356         edamNamespace = ajStrNewC("data");
2357     }
2358     else if (ajCharMatchC(namespace, "output"))
2359     {
2360         dooutputs = ajTrue;
2361         edamNamespace = ajStrNewC("data");
2362     }
2363     else if (ajCharMatchC(namespace, "param"))
2364     {
2365         doparams = ajTrue;
2366         edamNamespace = ajStrNewC("data");
2367     }
2368     else if (ajCharMatchC(namespace, "data"))
2369     {
2370         doinputs = ajTrue;
2371         doparams = ajTrue;
2372         dooutputs = ajTrue;
2373         edamNamespace = ajStrNewC("data");
2374     }
2375     else
2376     {
2377         ajErr("Unknown namespace '%s' for embGrpSearchProgsEdam", namespace);
2378         return;
2379     }
2380 
2381     handle = ajStrTokenNewC(query, ",");
2382     while(ajStrTokenNextParse(handle, &qrystr))
2383     {
2384         for(i=0;i<imax;i++)
2385         {
2386             ajFmtPrintS(&oboqry, "edam-%s:%S", fields[i], qrystr);
2387 
2388             ajOboinQryS(oboin, oboqry);
2389 
2390             while(ajOboinRead(oboin, obo))
2391             {
2392                 if(!ajStrMatchS(ajOboGetNamespace(obo), edamNamespace))
2393                     continue;
2394 
2395                 if(!obsolete && ajOboIsObsolete(obo))
2396                     continue;
2397 
2398                 ajListPushAppend(obolist, ajOboNewObo(obo));
2399                 if(subclasses)
2400                     ajOboGetTree(obo, obolist);
2401 
2402                 ajDebug("%S '%S' %Lu\n",
2403                        qrystr, obo->Id, ajListGetLength(obolist));
2404 
2405                 while(ajListGetLength(obolist))
2406                 {
2407                     ajListPop(obolist, (void**) &obotest);
2408 
2409                     if(!ajStrMatchS(ajOboGetNamespace(obotest), edamNamespace))
2410                     {
2411                         ajOboDel(&obotest);
2412                         continue;
2413                     }
2414 
2415                     if(!obsolete && ajOboIsObsolete(obotest))
2416                     {
2417                         ajOboDel(&obotest);
2418                         continue;
2419                     }
2420 
2421                     if(!ajTableMatchS(obotable, obotest->Id))
2422                     {
2423                         ajDebug("edam id '%S' namespace '%S' %d '%S'\n",
2424                                 obotest->Id, obotest->Namespace,
2425                                 fields[i], obotest->Name);
2426                         ajTablePut(obotable, ajStrNewS(obotest->Id),
2427                                    (void *) 1);
2428                     }
2429 
2430                     ajOboDel(&obotest);
2431                 }
2432             }
2433         }
2434     }
2435 
2436     /*
2437     ** compare case independently - so use upper case of both key
2438     ** and name/doc
2439     */
2440 
2441     /* make new group */
2442     ajFmtPrintS(&gname, "Search for %s '%S'", namespace, query);
2443     gpnode = embGrpMakeNewGnode(gname);
2444     ajListPushAppend(newlist, gpnode);
2445 
2446     giter = ajListIterNewread(glist); /* iterate through existing groups list */
2447 
2448     while((gl = ajListIterGet(giter)) != NULL)
2449     {
2450 	piter = ajListIterNewread(gl->progs);
2451 
2452 	while((pl = ajListIterGet(piter)) != NULL)
2453 	{
2454             if(!ajTableMatchS(apptable, pl->name))
2455             {
2456                 matched = ajFalse;
2457                 rl = NULL;
2458 
2459                 if(dotopics)
2460                 {
2461                     eiter = ajListIterNewread(pl->acdtopics);
2462 
2463                     while(!matched && (rl = ajListIterGet(eiter)) != NULL)
2464                     {
2465                         if(ajTableMatchS(obotable, rl->id))
2466                             matched = ajTrue;
2467                     }
2468                     ajListIterDel(&eiter);
2469                 }
2470 
2471                 if(!matched && dooperations)
2472                 {
2473                     eiter = ajListIterNewread(pl->acdoperations);
2474 
2475                     while(!matched && (rl = ajListIterGet(eiter)) != NULL)
2476                     {
2477                         if(ajTableMatchS(obotable, rl->id))
2478                             matched = ajTrue;
2479                     }
2480                     ajListIterDel(&eiter);
2481                 }
2482 
2483                 if(!matched && doinputs)
2484                 {
2485                     eiter = ajListIterNewread(pl->acdinputs);
2486 
2487                     while(!matched && (rl = ajListIterGet(eiter)) != NULL)
2488                     {
2489                         if(ajTableMatchS(obotable, rl->id))
2490                             matched = ajTrue;
2491                     }
2492                     ajListIterDel(&eiter);
2493                 }
2494 
2495                 if(!matched && dooutputs)
2496                 {
2497                     eiter = ajListIterNewread(pl->acdoutputs);
2498 
2499                     while(!matched && (rl = ajListIterGet(eiter)) != NULL)
2500                     {
2501                         if(ajTableMatchS(obotable, rl->id))
2502                             matched = ajTrue;
2503                     }
2504                     ajListIterDel(&eiter);
2505                 }
2506 
2507                 if(!matched && doparams)
2508                 {
2509                     eiter = ajListIterNewread(pl->acdparams);
2510 
2511                     while(!matched && (rl = ajListIterGet(eiter)) != NULL)
2512                     {
2513                         if(ajTableMatchS(obotable, rl->id))
2514                             matched = ajTrue;
2515                     }
2516                     ajListIterDel(&eiter);
2517                 }
2518                 if(matched && rl)
2519                 {
2520                     ajDebug("program %S edam:%S '%S' namespace '%S'\n",
2521                             pl->name, rl->id, rl->name, rl->namespace);
2522                     ppnode = grpCopyPnode(pl);
2523                     ajListPushAppend(gpnode->progs, ppnode);
2524                     ajTablePut(apptable, ajStrNewS(pl->name),
2525                                (void *) 1);
2526                 }
2527             }
2528         }
2529 
2530 	ajListIterDel(&piter);
2531     }
2532 
2533     ajListIterDel(&giter);
2534 
2535     /* sort the results */
2536     embGrpSortGroupsList(newlist);
2537 
2538     ajListFree(&obolist);
2539     ajTablestrFreeKey(&obotable);
2540     ajTablestrFreeKey(&apptable);
2541 
2542     ajOboinDel(&oboin);
2543     ajOboDel(&obo);
2544 
2545     ajStrDel(&edamNamespace);
2546     ajStrDel(&qrystr);
2547     ajStrDel(&oboqry);
2548     ajStrDel(&gname);
2549 
2550     ajStrTokenDel(&handle);
2551 
2552     return;
2553 }
2554 
2555 
2556 
2557 
2558 /* @func embGrpKeySearchSeeAlso ***********************************************
2559 **
2560 ** Takes an application name and returns a list of the groups that the
2561 ** application belongs to and a list of the applications that are in
2562 ** those groups.
2563 **
2564 ** If the program we are searching for is not found, it returns *appgroups
2565 ** as NULL.
2566 **
2567 ** @param [u] newlist [AjPList] List of application groups EmbPGroupTop
2568 **                              returned
2569 ** @param [w] appgroups [AjPList *] List of EmbPGroupTop groups of programs
2570 **                                  returned
2571 ** @param [w] package [AjPStr *] List of EmbPGroupTop groups of programs
2572 ** @param [r] alpha [const AjPList] List of EmbPGroupProg struct to
2573 **                                  search through
2574 ** @param [r] glist [const AjPList] List of EmbPGroupTop struct to
2575 **                                  search through
2576 ** @param [r] key [const AjPStr] program name to search for
2577 ** @return [void]
2578 **
2579 ** @release 2.0.0
2580 ** @@
2581 ******************************************************************************/
2582 
embGrpKeySearchSeeAlso(AjPList newlist,AjPList * appgroups,AjPStr * package,const AjPList alpha,const AjPList glist,const AjPStr key)2583 void embGrpKeySearchSeeAlso(AjPList newlist, AjPList *appgroups,
2584 			    AjPStr* package,
2585 			    const AjPList alpha, const AjPList glist,
2586 			    const AjPStr key)
2587 {
2588 
2589     AjIList giter;	 /* 'glist' iterator */
2590     AjIList piter;	 /* 'plist' iterator */
2591     AjIList griter;      /* iterator through list of groups we have found */
2592     EmbPGroupTop gl;	 /* next member of glist */
2593     EmbPGroupTop gpnode; /* new member of newlist being added */
2594     EmbPGroupProg ppnode; /* new member of glist being added */
2595     EmbPGroupProg pl;	 /* next member of plist */
2596     EmbPGroupTop gr;     /* next member of list of groups we have found */
2597     AjPStr tmp = NULL;
2598     AjPList base;
2599 
2600     /* make initial group node and push on newlist */
2601     tmp = ajStrNewC("See also");
2602     gpnode = embGrpMakeNewGnode(tmp);
2603     base = gpnode->progs;
2604     ajListPushAppend(newlist, gpnode);
2605 
2606 
2607     /*
2608     **  set *appgroups to NULL initially - test to see if still NULL after
2609     **  we have searched for the application name
2610     **/
2611     *appgroups = NULL;
2612 
2613     /*
2614     **  initially look for our application in list 'alpha' to get its list of
2615     **  groups
2616     **/
2617 
2618     /* iterate through existing applications list */
2619     giter = ajListIterNewread(alpha);
2620 
2621     while((gl = ajListIterGet(giter)) != NULL)
2622     {
2623 	piter = ajListIterNewread(gl->progs);
2624 
2625 	while((pl = ajListIterGet(piter)) != NULL)
2626 	    if(ajStrMatchCaseS(pl->name, key))
2627 	    {
2628 		*appgroups = pl->groups;
2629 		ajStrAssignS(package, pl->package);
2630 	    }
2631 
2632 	ajListIterDel(&piter);
2633     }
2634 
2635     ajListIterDel(&giter);
2636 
2637     /* If application not found */
2638     if(*appgroups == NULL)
2639 	return;
2640 
2641     /*
2642     ** go through each group in glist finding those that are
2643     ** used by the application
2644     */
2645 
2646     /* iterate through existing applications list */
2647     giter = ajListIterNewread(glist);
2648 
2649     while((gl = ajListIterGet(giter)) != NULL)
2650     {
2651         /* iterate through groups found */
2652 	griter = ajListIterNewread(*appgroups);
2653 
2654 	while((gr = ajListIterGet(griter)) != NULL)
2655 	{
2656 	    if(!ajStrCmpCaseS(gr->name, gl->name))
2657 	    {
2658 		/*
2659 		**  found one of the groups - pull out
2660 		**  the applications
2661 		**/
2662 		piter = ajListIterNewread(gl->progs);
2663 
2664 		while((pl = ajListIterGet(piter)) != NULL)
2665 		{
2666 		    /* don't want to include our key program */
2667 		    if(!ajStrCmpS(pl->name, key))
2668 			continue;
2669 
2670 		    /* make new application node and push on base */
2671 		    ppnode = grpCopyPnode(pl);
2672 		    ajListPushAppend(base, ppnode);
2673 
2674 		}
2675 
2676 		ajListIterDel(&piter);
2677 	    }
2678 	}
2679 
2680 	ajListIterDel(&griter);
2681     }
2682 
2683     ajListIterDel(&giter);
2684 
2685     /* sort the results and remove duplicates */
2686     embGrpSortProgsList(base);
2687     embGrpProgsMakeUnique(base);
2688 
2689     ajStrDel(&tmp);
2690 
2691     return;
2692 }
2693 
2694 
2695 
2696 
2697 /* @func embGrpProgsMakeUnique ************************************************
2698 **
2699 ** Takes a sorted EmbPGroupProg list and ensures that there are no duplicate
2700 ** group or application names in that list.
2701 **
2702 ** @param [u] list [AjPList] List of application GPnode returned
2703 ** @return [void]
2704 **
2705 ** @release 4.0.0
2706 ** @@
2707 ******************************************************************************/
2708 
embGrpProgsMakeUnique(AjPList list)2709 void embGrpProgsMakeUnique(AjPList list)
2710 {
2711     AjIList iter;
2712     EmbPGroupProg l;			/* next member of list */
2713     AjPStr old = NULL;			/* previous name */
2714 
2715     old = ajStrNewC("");
2716 
2717     iter = ajListIterNew(list);
2718 
2719     while((l = ajListIterGet(iter)) != NULL)
2720     {
2721 	if(!ajStrCmpCaseS(l->name, old))
2722 	{
2723 
2724 	    /* delete this GPnode's lists and data */
2725 	    embGrpGroupsListDel(&l->groups);
2726 	    ajStrDel(&(l->name));
2727 	    ajStrDel(&(l->doc));
2728 	    ajStrDel(&(l->package));
2729 	    AJFREE(l);
2730 
2731 	    /* delete this element of the list */
2732 	    ajListIterRemove(iter);
2733 
2734 	}
2735 	else
2736 	{
2737 	    ajStrDel(&old);
2738 	    old = ajStrNewRef(l->name);
2739 	    embGrpGroupMakeUnique(l->groups);
2740 	}
2741 
2742     }
2743 
2744     ajListIterDel(&iter);
2745     ajStrDel(&old);
2746 
2747     return;
2748 }
2749 
2750 
2751 
2752 
2753 /* @func embGrpGroupMakeUnique ************************************************
2754 **
2755 ** Takes a sorted EmbPGroupTop list and ensures that there are no duplicate
2756 ** group or application names in that list.
2757 **
2758 ** @param [u] list [AjPList] List of application GPnode returned
2759 ** @return [void]
2760 **
2761 ** @release 4.0.0
2762 ** @@
2763 ******************************************************************************/
2764 
embGrpGroupMakeUnique(AjPList list)2765 void embGrpGroupMakeUnique(AjPList list)
2766 {
2767     AjIList iter;
2768     EmbPGroupTop l;			/* next member of list */
2769     AjPStr old = NULL;			/* previous name */
2770 
2771     old = ajStrNewC("");
2772 
2773     iter = ajListIterNew(list);
2774 
2775     while((l = ajListIterGet(iter)) != NULL)
2776     {
2777 
2778 	if(!ajStrCmpCaseS(l->name, old))
2779 	{
2780 
2781 	    /* delete this GPnode's lists and data */
2782 	    embGrpProgsListDel(&l->progs);
2783 	    ajStrDel(&(l->doc));
2784 	    ajStrDel(&(l->name));
2785 	    AJFREE(l);
2786 
2787 	    /* delete this element of the list */
2788 	    ajListIterRemove(iter);
2789 
2790 	}
2791 	else
2792 	{
2793 	    ajStrDel(&old);
2794 	    old = ajStrNewRef(l->name);
2795 	    embGrpProgsMakeUnique(l->progs);
2796 	}
2797 
2798     }
2799 
2800     ajListIterDel(&iter);
2801     ajStrDel(&old);
2802 
2803     return;
2804 }
2805 
2806 
2807 
2808 
2809 /* @func embGrpExit ***********************************************************
2810 **
2811 ** Cleanup program group internals on exit
2812 **
2813 ** @return [void]
2814 **
2815 ** @release 4.0.0
2816 ******************************************************************************/
2817 
embGrpExit(void)2818 void embGrpExit(void)
2819 {
2820     ajStrDel(&grpStr1);
2821     ajStrDel(&grpStr2);
2822 
2823     return;
2824 }
2825