1 /*********************************************************************
2 Arithmetic operations on data structures.
3 This is part of GNU Astronomy Utilities (Gnuastro) package.
4 
5 Original author:
6      Mohammad Akhlaghi <mohammad@akhlaghi.org>
7 Contributing author(s):
8 Copyright (C) 2021, Free Software Foundation, Inc.
9 
10 Gnuastro is free software: you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation, either version 3 of the License, or (at your
13 option) any later version.
14 
15 Gnuastro is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with Gnuastro. If not, see <http://www.gnu.org/licenses/>.
22 **********************************************************************/
23 #include <config.h>
24 
25 #include <stdio.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include <gnuastro/list.h>
32 
33 #include <gnuastro-internal/checkset.h>
34 #include <gnuastro-internal/arithmetic-set.h>
35 
36 
37 
38 
39 
40 /* Remove a name from the list of names and return the dataset it points
41    to. */
42 static gal_data_t *
arithmetic_set_remove_name(struct gal_arithmetic_set_params * p,char * name)43 arithmetic_set_remove_name(struct gal_arithmetic_set_params *p,
44                            char *name)
45 {
46   gal_data_t *tmp, *removed=NULL, *prev=NULL;
47 
48   /* Go over all the given names. */
49   for(tmp=p->named;tmp!=NULL;tmp=tmp->next)
50     {
51       if( !strcmp(tmp->name, name) )
52         {
53           removed=tmp;
54           if(prev) prev->next = tmp->next;
55           else     p->named   = tmp->next;
56         }
57 
58       /* Set this node as the 'prev' pointer. */
59       prev=tmp;
60     }
61 
62   /* A small sanity check. */
63   if(removed==NULL)
64     error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
65           "fix the problem. 'removed' must not be NULL at this point",
66           __func__, PACKAGE_BUGREPORT);
67 
68   /* Nothing in the list points to it now. So we can safely modify and
69      return it. */
70   free(removed->name);
71   removed->next=NULL;
72   removed->name=NULL;
73   return removed;
74 }
75 
76 
77 
78 
79 
80 /* Pop a dataset and keep it in the 'named' list for later use. */
81 void
gal_arithmetic_set_name(struct gal_arithmetic_set_params * p,char * token)82 gal_arithmetic_set_name(struct gal_arithmetic_set_params *p, char *token)
83 {
84   gal_data_t *tmp, *tofree;
85   char *varname=&token[ GAL_ARITHMETIC_SET_PREFIX_LENGTH ];
86 
87   /* If a dataset with this name already exists, it will be removed/deleted
88      so we can use the name for the newly designated dataset. */
89   for(tmp=p->named; tmp!=NULL; tmp=tmp->next)
90     if( !strcmp(varname, tmp->name) )
91       {
92         tofree=arithmetic_set_remove_name(p, varname);
93         gal_data_free(tofree);
94 
95         /* IMPORTANT: we MUST break here! 'tmp' does't point to the right
96            place any more. We can define a 'prev' node and modify it on
97            every attempt, but since there is only one dataset with a given
98            name, that is redundant and will just make the program slow. */
99         break;
100       }
101 
102   /* Pop the top operand, then add it to the list of named datasets, but
103      only if it is used in later tokens. If it isn't, free the popped
104      dataset. The latter case (to define a name, but not use it), is
105      obviously a redundant operation, but that is upto the user, we
106      shouldn't worry about it here. We should just have everything in
107      place, so no crashes occur or no extra memory is consumed. */
108   if( p->used_later(p, varname) )
109     {
110       /* Add the top popped operand to the list of names. */
111       gal_list_data_add(&p->named, p->pop(p));
112 
113       /* Write the requested name into this dataset. But note that 'name'
114          MUST be already empty. So to be safe, we'll do a sanity check. */
115       if(p->named->name )
116         error(EXIT_FAILURE, 0, "%s: a bug! Please contact us at %s to "
117               "fix the problem. The 'name' element should be NULL at "
118               "this point, but it isn't", __func__, PACKAGE_BUGREPORT);
119       if(p->named->unit)
120         { free(p->named->unit); p->named->unit=NULL; }
121       if(p->named->comment)
122         { free(p->named->comment); p->named->comment=NULL; }
123       gal_checkset_allocate_copy(varname, &p->named->name);
124     }
125   else
126     {
127       /* Pop the top operand, then free it: for example the user has ran
128          'set-i', but forgot to actually use it (happens a lot due to human
129          error!). */
130       tmp=p->pop(p);
131       gal_data_free(tmp);
132     }
133 }
134 
135 
136 
137 
138 
139 /* See if a given token is the name of a variable. */
140 int
gal_arithmetic_set_is_name(gal_data_t * named,char * token)141 gal_arithmetic_set_is_name(gal_data_t *named, char *token)
142 {
143   gal_data_t *tmp;
144 
145   /* Make sure the variable name hasn't been set before. */
146   for(tmp=named; tmp!=NULL; tmp=tmp->next)
147     if( !strcmp(token, tmp->name) )
148       return 1;
149 
150   /* If control reaches here, then there was no match*/
151   return 0;
152 }
153 
154 
155 
156 
157 
158 /* Return a copy of the named dataset. */
159 gal_data_t *
gal_arithmetic_set_copy_named(struct gal_arithmetic_set_params * p,char * name)160 gal_arithmetic_set_copy_named(struct gal_arithmetic_set_params *p,
161                               char *name)
162 {
163   gal_data_t *out=NULL, *tmp;
164 
165   /* Find the proper named element to use. */
166   for(tmp=p->named;tmp!=NULL;tmp=tmp->next)
167     {
168     if( !strcmp(tmp->name, name) )
169       {
170         /* If the named operand is used later, then copy it into the
171            output. */
172         if( p->used_later(p, name) )
173           {
174             out=gal_data_copy(tmp);
175             out->next=NULL;
176             if(out->name)    { free(out->name);    out->name=NULL;    }
177             if(out->unit)    { free(out->unit);    out->unit=NULL;    }
178             if(out->comment) { free(out->comment); out->comment=NULL; }
179           }
180 
181         /* The named operand is not used any more. Remove it from the list
182            of named datasets and continue. */
183         else out=arithmetic_set_remove_name(p, name);
184       }
185     }
186 
187   /* A small sanity check. */
188   if(out==NULL)
189     error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s to fix the "
190           "problem. The requested name '%s' couldn't be found in the list",
191           __func__, PACKAGE_BUGREPORT, name);
192 
193   /* Return. */
194   return out;
195 }
196