Coverage Report

Created: 2026-10-22 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/io/dl.c
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
}