NEURON
io.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include <filesystem>
3 namespace fs = std::filesystem;
4 
5 /* file.mod input routines */
6 #include <stdlib.h>
7 #include <string.h>
8 #include <limits.h>
9 #include <sys/stat.h>
10 #include <errno.h>
11 #include "modl.h"
12 #include <ctype.h>
13 #undef METHOD
14 #include "parse1.hpp"
15 #if defined(_WIN32)
16 #include <direct.h>
17 #endif
18 
19 int isend(char*, char*);
20 static void pop_file_stack();
21 static int file_stack_empty();
23 
24 char* inputline() {
25  /* and removes comment, newline, beginning and trailing blanks */
26  /* used to get the TITLE line */
27 #if SYSV || defined(MINGW)
28 #define index strchr
29 #endif
30  char* cp;
31  int i;
32 
33  buf[0] = '\0';
34  cp = Gets(buf);
35  i = strlen(buf);
36  if (i)
37  buf[i - 1] = '\0';
38  if ((cp = index(buf, '!')) != (char*) 0) {
39  *cp-- = '\0';
40  }
41  while (cp >= buf && isspace(*cp)) {
42  *cp-- = '\0';
43  }
44  /*EMPTY*/
45  for (cp = buf; *cp != '\0' && isspace(*cp); cp++) {
46  ;
47  }
48  return stralloc(cp, (char*) 0);
49 }
50 
51 static int linenum = 0;
52 
53 void inblock(char* s) { /* copy input verbatim to intoken up to END*s
54  * error if we get the whole input */
55  char* cp;
56  int l;
57  Item* q;
58 
59  l = linenum;
60  for (;;) {
61  cp = Gets(buf);
62  if (cp == (char*) 0) {
63  linenum = l;
64  diag(s, "block goes to end of file");
65  }
66  if (isend(s, buf)) {
67  break;
68  }
69  q = putintoken(buf, STRING);
70  q->itemtype = VERBATIM;
71  }
72 }
73 
74 int isend(char* s, char* buf) {
75  /* if first chars in buf form a keyword return 1 */
76  char *cp, word[256], *wp, test[256];
77  int yesno = 0;
78 
79  cp = buf;
80  Sprintf(test, "END%s", s);
81  while (*cp == ' ' || *cp == '\t')
82  cp++;
83  if (isalpha(*cp)) {
84  for (wp = word; isalpha(*cp);) {
85  *wp++ = *cp++;
86  }
87  *wp = '\0';
88  if (strcmp(test, word) == 0) {
89  yesno = 1;
90  }
91  }
92  return yesno;
93 }
94 
95 /*
96  * We painfully constuct our own input buffer so that when user errors occur
97  * we can print the whole line. Often, even this is not enough if the error
98  * is at the end of a line. We also count lines so the user can go right to
99  * the error in most cases
100  */
101 static char inlinebuf[2][NRN_BUFSIZE], *inlinep = inlinebuf[0] + 30, *ctp = inlinebuf[0] + 30;
102 static int whichbuf;
103 
104 char* Fgets(char* buf, int size, FILE* f) {
105  char* p = buf;
106  int c, i;
107  for (i = 0; i < size; ++i) {
108  c = getc(f);
109  if (c == EOF || c == 26 || c == 4) { /* ^Z and ^D are end of file */
110  /* some editors don't put a newline at last line */
111  if (p > buf) {
112  ungetc(c, f);
113  c = '\n';
114  } else {
115  break;
116  }
117  }
118  if (c == '\r') {
119  int c2 = getc(f);
120  if (c2 != '\n') {
121  ungetc(c2, f);
122  }
123  c = '\n';
124  }
125  if (c < 0 || c > 127) {
126  *p++ = '\n';
127  *p = '\0';
128  if (!in_comment_) {
129  diag("Non-Ascii character in file:", buf);
130  }
132  return buf;
133  }
134  *p++ = c;
135  if (c == '\n') {
136  *p = '\0';
138  return buf;
139  }
140  }
141  if (i >= size) {
142  buf[size - 1] = 0;
143  diag("Line too long:", buf);
144  }
145  return (char*) 0;
146 }
147 
148 int Getc() {
149  int c;
150  if (ctp == (char*) 0 || *ctp == '\0') {
151  whichbuf = (whichbuf ? 0 : 1);
152  inlinep = inlinebuf[whichbuf] + 30;
153  ctp = Fgets(inlinep, 512, fin);
154  if (ctp)
155  linenum++;
156  }
157  if (ctp == (char*) 0) {
158  ctp = inlinep;
159  *ctp = '\0';
160  if (file_stack_empty()) {
161  return EOF;
162  } else {
163  pop_file_stack();
164  return Getc();
165  }
166  }
167  c = *ctp++;
168  return c;
169 }
170 
171 int unGetc(int c) {
172  if (c == EOF)
173  return c;
174  if (ctp > inlinebuf[whichbuf]) {
175  ctp--;
176  *ctp = c;
177  } else {
178  diag("internal error in unGetc", "");
179  }
180  return c;
181 }
182 
183 char* Gets(char* buf) {
184  char* cp;
185  int c;
186 
187  cp = buf;
188  while ((c = Getc()) != EOF && c != '\n') {
189  *cp++ = c;
190  }
191  if (c == '\n') {
192  *cp++ = c;
193  *cp++ = '\0';
194  return buf;
195  } else if (c == EOF) {
196  return (char*) 0;
197  } else {
198  diag("internal error in Gets()", "");
199  }
200  return (char*) 0;
201 }
202 
203 #if 0 /* not currently used */
204 void unGets(char* buf) /* all this because we don't have an ENDBLOCK
205  * keyword */
206 {
207  if (ctp != '\0') { /* can only be called after successful Gets */
208  Strcpy(inlinep, buf);
209  ctp = inlinep;
210  } else {
211  diag("internal error in unGets()", "");
212  }
213 }
214 #endif
215 
216 char* current_line() { /* assumes we actually want the previous line */
217  static char buf[NRN_BUFSIZE];
218  char* p;
220  "at line %d in file %s:\\n%s",
221  linenum - 1,
222 #if !defined(NRN_AVOID_ABSOLUTE_PATHS)
223  finname,
224 #else
225  fs::absolute(finname).filename().c_str(),
226 #endif
227  inlinebuf[whichbuf ? 0 : 1] + 30);
228  for (p = buf; *p; ++p) {
229  if (*p == '\n') {
230  *p = '\0';
231  }
232  if (*p == '"') {
233  *p = '\047';
234  }
235  }
236  return buf;
237 }
238 
239 /* two arguments so we can pass a name to construct an error message. */
240 void diag(const char* s1, const char* s2) {
241  char* cp;
242  Fprintf(stderr, "Error: %s", s1);
243  if (s2) {
244  Fprintf(stderr, "%s", s2);
245  }
246  if (fin) {
247  Fprintf(stderr, " at line %d in file %s\n", linenum, finname);
248  Fprintf(stderr, "%s", inlinep);
249  if (ctp >= inlinep) {
250  for (cp = inlinep; cp < ctp - 1; cp++) {
251  if (*cp == '\t') {
252  Fprintf(stderr, "\t");
253  } else {
254  Fprintf(stderr, " ");
255  }
256  }
257  Fprintf(stderr, "^");
258  }
259  }
260  Fprintf(stderr, "\n");
261  exit(1);
262 }
263 
264 #if 0
265 static Symbol *symq[20], **symhead = symq, **symtail = symq;
266 
267 /*
268  * the following is a nonsensical implementation of heirarchical model
269  * building. Disregard. It assumes .mod files can be concatenated to produce
270  * meaningful models. It was this insanity which prompted us to allow use of
271  * variables before declaration
272  */
273 void enquextern(Symbol* sym)
274 {
275  *symtail++ = sym;
276 }
277 
278 FILE *dequextern()
279 {
280  char fname[256];
281  FILE *f;
282  Symbol *s;
283 
284  if (symhead >= symtail)
285  return (FILE *) 0;
286  s = *symhead++;
287  Sprintf(fname, "%s.mod", s->name);
288  f = fopen(fname, "r");
289  if (f == (FILE *) 0) {
290  diag("Can't open", fname);
291  }
292  Fclose(fin);
293  linenum = 0;
294  Strcpy(finname, fname);
295  return f;
296 }
297 #endif
298 
299 typedef struct FileStackItem {
300  char* inlinep;
301  char* ctp;
302  int linenum;
303  FILE* fp;
304  char finname[NRN_BUFSIZE];
306 
307 static List* filestack;
308 
309 static int getprefix(char* prefix, char* s) {
310  char* cp;
311  strcpy(prefix, s);
312  for (cp = prefix + strlen(prefix); cp + 1 != prefix; --cp) {
313  if (*cp == '/') {
314  break;
315  }
316  *cp = '\0';
317  }
318  return (prefix[0] != '\0');
319 }
320 
321 static FILE* include_open(char* fname, int err) {
322  FILE* f = (FILE*) 0;
323  FileStackItem* fsi;
324  char *dirs, *colon;
325  /* since dirs is a ':' separated list of paths, there is no
326  limit to the size and so allocate from size of dirs and free
327  */
328  char *buf, *buf2;
329  if (fname[0] == '/') { /* highest precedence is complete filename */
330  return fopen(fname, "r");
331  }
332 
333  fsi = (FileStackItem*) (SYM(filestack->prev));
334  buf = static_cast<char*>(emalloc(NRN_BUFSIZE));
335  if (getprefix(buf, fsi->finname)) {
336  strcat(buf, fname);
337  f = fopen(buf, "r"); /* first try in directory of last file */
338  if (f) {
339  strcpy(fname, buf);
340  free(buf);
341  return f;
342  }
343  if (err)
344  fprintf(stderr, "Couldn't open: %s\n", buf);
345  }
346  f = fopen(fname, "r"); /* next try current working directory */
347  if (f) {
348  free(buf);
349  return f;
350  }
351  std::snprintf(buf, NRN_BUFSIZE, "../%s", fname); /* Next try next dir up. */
352  if ((f = fopen(buf, "r")) != NULL) {
353  strcpy(fname, buf);
354  free(buf);
355  return f;
356  }
357 
358  if (err)
359  fprintf(stderr, "Couldn't open: %s\n", fname);
360  /* try all the directories in the environment variable */
361  /* a colon separated list of directories */
362  dirs = getenv("MODL_INCLUDE");
363  if (dirs) {
364  buf = stralloc(dirs, buf); /* frees old buf and allocates */
365  dirs = buf;
366  colon = dirs;
367  for (dirs = colon; *dirs; dirs = colon) {
368  buf2 = NULL;
369  for (; *colon; ++colon) {
370  if (*colon == ':') {
371  *colon = '\0';
372  ++colon;
373  break;
374  }
375  }
376  buf2 = static_cast<char*>(emalloc(strlen(dirs) + 2 + strlen(fname)));
377  strcpy(buf2, dirs);
378  strcat(buf2, "/");
379  strcat(buf2, fname);
380  f = fopen(buf2, "r");
381  if (f) {
382  strcpy(fname, buf2);
383  free(buf);
384  free(buf2);
385  return f;
386  }
387  if (err)
388  fprintf(stderr, "Couldn't open: %s\n", buf2);
389  free(buf2);
390  }
391  free(buf);
392  }
393  return f;
394 }
395 
397  char fname[NRN_BUFSIZE];
398  Item* qinc;
399  FileStackItem* fsi;
400  if (!filestack) {
401  filestack = newlist();
402  }
403  strcpy(fname, STR(q) + 1);
404  fname[strlen(fname) - 1] = '\0';
405  fsi = (FileStackItem*) emalloc(sizeof(FileStackItem));
406  lappendsym(filestack, (Symbol*) fsi);
407 
408  fsi->inlinep = inlinep;
409  fsi->ctp = ctp;
410  fsi->linenum = linenum;
411  fsi->fp = fin;
412  strcpy(fsi->finname, finname);
413 
414  strcpy(finname, fname);
415  if ((fin = include_open(fname, 0)) == (FILE*) 0) {
416  include_open(fname, 1);
417  diag("Couldn't open ", fname);
418  }
419  fprintf(stderr, "INCLUDEing %s\n", fname);
420  ctp = (char*) 0;
421  linenum = 0;
422 
423  qinc = filetxtlist->prev;
424  Sprintf(buf, ":::%s", STR(qinc));
425  replacstr(qinc, buf);
426  try {
427  Sprintf(buf, ":::realpath %s\n", fs::absolute(fname).c_str());
429  } catch (const std::filesystem::filesystem_error&) {
430  // If we are not able to get an absolute path from fname, simply avoid to write it.
431  }
432 }
433 
434 static void pop_file_stack() {
435  SprintfAsrt(buf, ":::end INCLUDE %s\n", finname);
437  FileStackItem* fsi;
438  fsi = (FileStackItem*) (SYM(filestack->prev));
440  linenum = fsi->linenum;
441  inlinep = fsi->inlinep;
442  fclose(fin);
443  fin = fsi->fp;
444  strcpy(finname, fsi->finname);
445  free(fsi);
446 }
447 
448 static int file_stack_empty() {
449  if (!filestack) {
450  return 1;
451  }
452  return (filestack->next == filestack);
453 }
#define STRING
Definition: bbslsrv.cpp:9
#define i
Definition: md1redef.h:19
char * Gets(char *buf)
Definition: io.cpp:91
List * filetxtlist
Definition: modl.cpp:59
char buf[512]
Definition: init.cpp:13
char finname[NRN_BUFSIZE]
Definition: model.cpp:38
FILE * fin
Definition: model.cpp:33
static int c
Definition: hoc.cpp:169
#define STR(q)
Definition: model.h:76
#define Fclose
Definition: model.h:212
#define SYM(q)
Definition: model.h:75
#define Strcpy
Definition: model.h:215
#define NRN_BUFSIZE
Definition: model.h:6
void pop_file_stack()
Definition: io.cpp:324
void diag(const char *s1, const char *s2)
Definition: io.cpp:112
struct FileStackItem FileStackItem
int unGetc(int c)
Definition: io.cpp:79
char * Fgets(char *buf, int size, FILE *f)
Definition: io.cpp:24
int Getc()
Definition: io.cpp:58
void include_file(Item *q)
Definition: io.cpp:297
char * stralloc(const char *buf, char *rel)
Definition: list.cpp:178
void replacstr(Item *q, const char *s)
Definition: list.cpp:219
Item * putintoken(const char *s, short type, short toktype)
Definition: list.cpp:224
Item * lappendsym(List *list, Symbol *sym)
Definition: list.cpp:143
Item * lappendstr(List *list, const char *str)
Definition: list.cpp:135
List * newlist()
The following routines support the concept of a list.
static void * emalloc(size_t size)
Definition: mpispike.cpp:30
void SprintfAsrt(char(&buf)[N], const char *fmt, Args &&... args)
assert if the Sprintf format data does not fit into buf
Definition: wrap_sprintf.h:27
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Redirect sprintf to snprintf if the buffer size can be deduced.
Definition: wrap_sprintf.h:14
void enquextern(Symbol *)
void unGets(char *)
static char * inlinep
Definition: io.cpp:101
static char * ctp
Definition: io.cpp:101
char * current_line()
Definition: io.cpp:216
static int getprefix(char *prefix, char *s)
Definition: io.cpp:309
static int linenum
Definition: io.cpp:51
static List * filestack
Definition: io.cpp:307
static FILE * include_open(char *fname, int err)
Definition: io.cpp:321
void inblock(char *s)
Definition: io.cpp:53
char * inputline()
Definition: io.cpp:24
static char inlinebuf[2][NRN_BUFSIZE]
Definition: io.cpp:101
static int file_stack_empty()
Definition: io.cpp:448
int in_comment_
Definition: io.cpp:22
static int whichbuf
Definition: io.cpp:102
int isend(char *, char *)
Definition: io.cpp:74
NMODL parser global flags / functions.
size_t q
size_t p
s
Definition: multisend.cpp:521
short index
Definition: cabvars.h:11
static double remove(void *v)
Definition: ocdeck.cpp:205
#define NULL
Definition: spdefs.h:105
static struct prefix prefix[]
char * inlinep
Definition: io.cpp:216
char finname[NRN_BUFSIZE]
Definition: io.cpp:220
int linenum
Definition: io.cpp:218
char * ctp
Definition: io.cpp:217
FILE * fp
Definition: io.cpp:219
Definition: model.h:8
struct Item * prev
Definition: model.h:13
struct Item * next
Definition: model.h:12
Definition: model.h:47
Definition: units.cpp:83
int Fprintf(FILE *stream, const char *fmt, Args... args)
Definition: logger.hpp:8