Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2005-2020 The igraph development team |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation; either version 2 of the License, or |
8 | | (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program; if not, write to the Free Software |
17 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | | 02110-1301 USA |
19 | | |
20 | | */ |
21 | | |
22 | | #include "igraph_foreign.h" |
23 | | |
24 | | #include "igraph_attributes.h" |
25 | | #include "igraph_interface.h" |
26 | | |
27 | | #include "io/dl-header.h" |
28 | | #include "io/parsers/dl-parser.h" |
29 | | |
30 | | int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t *user_defined, |
31 | | void *scanner); |
32 | | int igraph_dl_yylex_destroy(void *scanner); |
33 | | int igraph_dl_yyparse(igraph_i_dl_parsedata_t *context); |
34 | | void igraph_dl_yyset_in(FILE *in_str, void *yyscanner); |
35 | | |
36 | | /* for IGRAPH_FINALLY, which assumes that destructor functions return void */ |
37 | 1.82k | void igraph_dl_yylex_destroy_wrapper (void *scanner ) { |
38 | 1.82k | (void) igraph_dl_yylex_destroy(scanner); |
39 | 1.82k | } |
40 | | |
41 | | /** |
42 | | * \function igraph_read_graph_dl |
43 | | * \brief Reads a file in the DL format of UCINET. |
44 | | * |
45 | | * This is a simple textual file format used by UCINET. See |
46 | | * http://www.analytictech.com/networks/dataentry.htm for |
47 | | * examples. All the forms described here are supported by |
48 | | * igraph. Vertex names and edge weights are also supported and they |
49 | | * are added as attributes. (If an attribute handler is attached.) |
50 | | * |
51 | | * </para><para> Note the specification does not mention whether the |
52 | | * format is case-sensitive or not. For igraph DL files are case |
53 | | * sensitive, i.e. \c Larry and \c larry are not the same. |
54 | | * |
55 | | * \param graph Pointer to an uninitialized graph object. |
56 | | * \param instream The stream to read the DL file from. |
57 | | * \param directed Boolean, whether to create a directed graph. |
58 | | * \return Error code. |
59 | | * |
60 | | * Time complexity: linear in terms of the number of edges and |
61 | | * vertices, except for the matrix format, which is quadratic in the |
62 | | * number of vertices. |
63 | | * |
64 | | * \example examples/simple/igraph_read_graph_dl.c |
65 | | */ |
66 | | |
67 | | igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, |
68 | 2.32k | igraph_bool_t directed) { |
69 | | |
70 | 2.32k | igraph_int_t n, n2; |
71 | 2.32k | const igraph_strvector_t *namevec = 0; |
72 | 2.32k | igraph_attribute_record_list_t name, weight; |
73 | 2.32k | igraph_attribute_record_list_t *pname = NULL; |
74 | 2.32k | igraph_attribute_record_list_t *pweight = NULL; |
75 | 2.32k | igraph_attribute_record_t *namerec, *weightrec; |
76 | 2.32k | const char *namestr = "name", *weightstr = "weight"; |
77 | 2.32k | igraph_i_dl_parsedata_t context; |
78 | | |
79 | 2.32k | context.eof = 0; |
80 | 2.32k | context.mode = 0; |
81 | 2.32k | context.n = -1; |
82 | 2.32k | context.from = 0; |
83 | 2.32k | context.to = 0; |
84 | 2.32k | context.errmsg[0] = '\0'; |
85 | 2.32k | context.igraph_errno = IGRAPH_SUCCESS; |
86 | | |
87 | 2.32k | IGRAPH_VECTOR_INT_INIT_FINALLY(&context.edges, 0); |
88 | 2.32k | IGRAPH_VECTOR_INIT_FINALLY(&context.weights, 0); |
89 | 2.32k | IGRAPH_CHECK(igraph_strvector_init(&context.labels, 0)); |
90 | 2.32k | IGRAPH_FINALLY(igraph_strvector_destroy, &context.labels); |
91 | 2.32k | IGRAPH_TRIE_INIT_FINALLY(&context.trie, /*names=*/ 1); |
92 | | |
93 | 2.32k | igraph_dl_yylex_init_extra(&context, &context.scanner); |
94 | 2.32k | IGRAPH_FINALLY(igraph_dl_yylex_destroy_wrapper, context.scanner); |
95 | | |
96 | 2.32k | igraph_dl_yyset_in(instream, context.scanner); |
97 | | |
98 | | /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ |
99 | 2.32k | IGRAPH_FINALLY_ENTER(); |
100 | 2.32k | int err = igraph_dl_yyparse(&context); |
101 | 2.32k | IGRAPH_FINALLY_EXIT(); |
102 | 2.32k | switch (err) { |
103 | 827 | case 0: /* success */ |
104 | 827 | break; |
105 | 1.49k | case 1: /* parse error */ |
106 | 1.49k | if (context.errmsg[0] != 0) { |
107 | 1.44k | IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); |
108 | 1.44k | } else if (context.igraph_errno != IGRAPH_SUCCESS) { |
109 | 53 | IGRAPH_ERROR("", context.igraph_errno); |
110 | 53 | } else { |
111 | 0 | IGRAPH_ERROR("Cannot read DL file.", IGRAPH_PARSEERROR); |
112 | 0 | } |
113 | 0 | break; |
114 | 0 | case 2: /* out of memory */ |
115 | 0 | IGRAPH_ERROR("Cannot read DL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ |
116 | 0 | break; |
117 | 0 | default: /* must never reach here */ |
118 | | /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison |
119 | | * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being |
120 | | * returned in place of a Bison error code. |
121 | | * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? |
122 | | */ |
123 | 0 | IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading DL file.", err); |
124 | 2.32k | } |
125 | | |
126 | | /* Extend the weight vector, if needed */ |
127 | 827 | n = igraph_vector_size(&context.weights); |
128 | 827 | n2 = igraph_vector_int_size(&context.edges) / 2; |
129 | 827 | if (n != 0) { |
130 | 262 | IGRAPH_CHECK(igraph_vector_resize(&context.weights, n2)); |
131 | 9.74k | for (; n < n2; n++) { |
132 | 9.47k | VECTOR(context.weights)[n] = IGRAPH_NAN; |
133 | 9.47k | } |
134 | 262 | } |
135 | | |
136 | | /* Check number of vertices */ |
137 | 827 | if (n2 > 0) { |
138 | 599 | n = igraph_vector_int_max(&context.edges); |
139 | 599 | } else { |
140 | 228 | n = 0; |
141 | 228 | } |
142 | 827 | if (n >= context.n) { |
143 | 304 | IGRAPH_WARNING("More vertices than specified in `DL' file"); |
144 | 304 | context.n = n; |
145 | 304 | } |
146 | | |
147 | | /* Prepare attributes */ |
148 | | |
149 | | /* Labels */ |
150 | 827 | if (igraph_strvector_size(&context.labels) != 0) { |
151 | 96 | namevec = (const igraph_strvector_t*) &context.labels; |
152 | 731 | } else if (igraph_trie_size(&context.trie) != 0) { |
153 | 76 | namevec = igraph_i_trie_borrow_keys(&context.trie); |
154 | 76 | } |
155 | 827 | if (namevec) { |
156 | 172 | IGRAPH_CHECK(igraph_attribute_record_list_init(&name, 1)); |
157 | 172 | IGRAPH_FINALLY(igraph_attribute_record_list_destroy, &name); |
158 | | |
159 | 172 | namerec = igraph_attribute_record_list_get_ptr(&name, 0); |
160 | 172 | IGRAPH_CHECK(igraph_attribute_record_set_name(namerec, namestr)); |
161 | 172 | IGRAPH_CHECK(igraph_attribute_record_set_type(namerec, IGRAPH_ATTRIBUTE_STRING)); |
162 | 172 | IGRAPH_CHECK(igraph_strvector_update(namerec->value.as_strvector, namevec)); |
163 | | |
164 | 172 | pname = &name; |
165 | 172 | } |
166 | | |
167 | | /* Weights */ |
168 | 827 | if (igraph_vector_size(&context.weights) != 0) { |
169 | 262 | IGRAPH_CHECK(igraph_attribute_record_list_init(&weight, 1)); |
170 | 262 | IGRAPH_FINALLY(igraph_attribute_record_list_destroy, &weight); |
171 | | |
172 | 262 | weightrec = igraph_attribute_record_list_get_ptr(&weight, 0); |
173 | 262 | IGRAPH_CHECK(igraph_attribute_record_set_name(weightrec, weightstr)); |
174 | 262 | IGRAPH_CHECK(igraph_attribute_record_set_type(weightrec, IGRAPH_ATTRIBUTE_NUMERIC)); |
175 | 262 | igraph_vector_swap(weightrec->value.as_vector, &context.weights); |
176 | | |
177 | 262 | pweight = &weight; |
178 | 262 | } |
179 | | |
180 | | /* Create graph */ |
181 | 827 | IGRAPH_CHECK(igraph_empty(graph, 0, directed)); |
182 | 827 | IGRAPH_FINALLY(igraph_destroy, graph); |
183 | 827 | IGRAPH_CHECK(igraph_add_vertices(graph, context.n, pname)); |
184 | 690 | IGRAPH_CHECK(igraph_add_edges(graph, &context.edges, pweight)); |
185 | | |
186 | 498 | if (pweight) { |
187 | 148 | igraph_attribute_record_list_destroy(pweight); |
188 | 148 | IGRAPH_FINALLY_CLEAN(1); |
189 | 148 | } |
190 | | |
191 | 498 | if (pname) { |
192 | 34 | igraph_attribute_record_list_destroy(pname); |
193 | 34 | IGRAPH_FINALLY_CLEAN(1); |
194 | 34 | } |
195 | | |
196 | | /* don't destroy the graph itself but pop it from the finally stack */ |
197 | 498 | IGRAPH_FINALLY_CLEAN(1); |
198 | | |
199 | 498 | igraph_trie_destroy(&context.trie); |
200 | 498 | igraph_strvector_destroy(&context.labels); |
201 | 498 | igraph_vector_int_destroy(&context.edges); |
202 | 498 | igraph_vector_destroy(&context.weights); |
203 | 498 | igraph_dl_yylex_destroy(context.scanner); |
204 | 498 | IGRAPH_FINALLY_CLEAN(5); |
205 | | |
206 | 498 | return IGRAPH_SUCCESS; |
207 | 690 | } |