Root/eeshow/file/file.c

1/*
2 * file/file.c - Open and read a file
3 *
4 * Written 2016 by Werner Almesberger
5 * Copyright 2016 by Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <stddef.h>
14#include <stdbool.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18
19#include "misc/util.h"
20#include "misc/diag.h"
21#include "file/git-file.h"
22#include "file/file.h"
23
24
25void *file_oid(const struct file *file)
26{
27    if (!file->vcs)
28        return NULL;
29    return vcs_git_get_oid(file->vcs);
30}
31
32
33bool file_oid_eq(const void *a, const void *b)
34{
35    /*
36     * If both a and b are NULL, we don't have revision data and thus
37     * can't tell if they're identical.
38     */
39    return a && b && vcs_git_oid_eq(a, b);
40}
41
42
43bool file_cat(const struct file *file, void *user, const char *line)
44{
45    printf("%s\n", line);
46    return 1;
47}
48
49
50char *file_graft_relative(const char *base, const char *name)
51{
52    const char *slash;
53    char *res;
54    unsigned len;
55
56    if (*name == '/')
57        return NULL;
58
59    slash = strrchr(base, '/');
60    if (!slash)
61        return NULL;
62
63    len = slash + 1 - base;
64    res = alloc_size(len + strlen(name) + 1);
65    memcpy(res, base, len);
66    strcpy(res + len, name);
67
68    return res;
69}
70
71
72static bool try_related(struct file *file)
73{
74    char *tmp;
75
76    if (!file->related)
77        return 0;
78
79    tmp = file_graft_relative(file->related->name, file->name);
80    if (!tmp)
81        return NULL;
82
83    if (*file->name == '/')
84        return 0;
85
86    file->file = fopen(tmp, "r");
87    if (!file->file) {
88        free(tmp);
89        return 0;
90    }
91
92    progress(1, "reading %s", tmp);
93
94    free((char *) file->name);
95    file->name = tmp;
96    return 1;
97}
98
99
100/*
101 * @@@ logic isn't quite complete yet. It should go something like this:
102 *
103 * - if there is no related item,
104 * - try file,
105 * - if there is a colon, try VCS,
106 * - give up
107 * - if there is a related item,
108 * - if it is a VCS,
109 * - try the revision matching or predating (if different repo) that of the
110 * related repo,
111 ( - fall through and try as if it was a file
112 * - try opening as file. If this fails,
113 * - if the name of the file to open is absolute, give up
114 * - try `dirname related`/file_ope_open
115 * - give up
116 *
117 * @@@ should we see if non-VCS file is in a VCS ? E.g.,
118 * /home/my-libs/foo.lib 1234:/home/my-project/foo.sch
119 * or maybe use : as indictor for VCS, i.e.,
120 * :/home/my-libs/foo.lib ...
121 *
122 * @@@ explicit revision should always win over related.
123 */
124
125static void *open_vcs(struct file *file)
126{
127    char *colon;
128
129    colon = strchr(file->name, ':');
130    if (colon) {
131        char *tmp;
132
133        tmp = stralloc(file->name);
134        tmp[colon - file->name] = 0;
135        file->vcs = vcs_git_open(tmp, colon + 1,
136            file->related ? file->related->vcs : NULL);
137        if (file->vcs) {
138            free(tmp);
139            return file->vcs;
140        }
141        progress(2, "could not open %s:%s", tmp, colon + 1);
142        return NULL;
143    } else {
144        file->vcs = vcs_git_open(NULL, file->name,
145            file->related ? file->related->vcs : NULL);
146        if (file->vcs)
147            return file->vcs;
148        progress(2, "could not open %s", file->name);
149        return NULL;
150    }
151}
152
153
154static void file_init(struct file *file, const char *name,
155    const struct file *related)
156{
157    file->name = stralloc(name);
158    file->lineno = 0;
159    file->related = related;
160    file->file = NULL;
161    file->vcs = NULL;
162}
163
164
165bool file_open(struct file *file, const char *name, const struct file *related)
166{
167    file_init(file, name, related);
168
169    if (related && related->vcs) {
170        file->vcs = open_vcs(file);
171        if (file->vcs)
172            return 1;
173    }
174
175    file->file = fopen(name, "r");
176    if (file->file) {
177        progress(1, "reading %s", name);
178        return 1;
179    }
180
181    if (try_related(file))
182        return 1;
183
184    if (verbose)
185        diag_perror(name);
186
187    if (!strchr(name, ':')) {
188        if (!verbose)
189            diag_perror(name);
190        goto fail;
191    }
192
193    file->vcs = open_vcs(file);
194    if (file->vcs)
195        return 1;
196
197    error("could not open %s", name);
198fail:
199    free((char *) file->name);
200    return 0;
201}
202
203
204bool file_open_revision(struct file *file, const char *rev, const char *name,
205    const struct file *related)
206{
207    if (!rev)
208        return file_open(file, name, related);
209
210    file_init(file, name, related);
211    file->vcs = vcs_git_open(rev, name, related ? related->vcs : NULL);
212    if (file->vcs)
213        return 1;
214    progress(2, "could not open %s at %s", name, rev);
215    return 0;
216}
217
218
219bool file_read(struct file *file,
220    bool (*parse)(const struct file *file, void *user, const char *line),
221    void *user)
222{
223    static char *buf = NULL;
224    static size_t n = 0;
225    char *nl;
226
227    if (file->vcs)
228        return vcs_read(file->vcs, file, parse, user);
229    while (getline(&buf, &n, file->file) > 0) {
230        nl = strchr(buf, '\n');
231        if (nl)
232            *nl = 0;
233        file->lineno++;
234        if (!parse(file, user, buf))
235            return 0;
236    }
237    return 1;
238}
239
240
241void file_close(struct file *file)
242{
243    if (file->file)
244        fclose(file->file);
245    if (file->vcs)
246        vcs_close(file->vcs);
247    free((char *) file->name);
248}
249

Archive Download this file

Branches:
master



interactive