1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002, 2003, 2004 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru#include "pic.h" 23114402Sru#include "ptable.h" 24114402Sru#include "object.h" 25114402Sru#include "pic_tab.h" 26114402Sru 27114402Srudeclare_ptable(char) 28114402Sruimplement_ptable(char) 29114402Sru 30114402SruPTABLE(char) macro_table; 31114402Sru 32114402Sruclass macro_input : public input { 33114402Sru char *s; 34114402Sru char *p; 35114402Srupublic: 36114402Sru macro_input(const char *); 37114402Sru ~macro_input(); 38114402Sru int get(); 39114402Sru int peek(); 40114402Sru}; 41114402Sru 42114402Sruclass argument_macro_input : public input { 43114402Sru char *s; 44114402Sru char *p; 45114402Sru char *ap; 46114402Sru int argc; 47114402Sru char *argv[9]; 48114402Srupublic: 49114402Sru argument_macro_input(const char *, int, char **); 50114402Sru ~argument_macro_input(); 51114402Sru int get(); 52114402Sru int peek(); 53114402Sru}; 54114402Sru 55114402Sruinput::input() : next(0) 56114402Sru{ 57114402Sru} 58114402Sru 59114402Sruinput::~input() 60114402Sru{ 61114402Sru} 62114402Sru 63114402Sruint input::get_location(const char **, int *) 64114402Sru{ 65114402Sru return 0; 66114402Sru} 67114402Sru 68114402Srufile_input::file_input(FILE *f, const char *fn) 69114402Sru: fp(f), filename(fn), lineno(0), ptr("") 70114402Sru{ 71114402Sru} 72114402Sru 73114402Srufile_input::~file_input() 74114402Sru{ 75114402Sru fclose(fp); 76114402Sru} 77114402Sru 78114402Sruint file_input::read_line() 79114402Sru{ 80114402Sru for (;;) { 81114402Sru line.clear(); 82114402Sru lineno++; 83114402Sru for (;;) { 84114402Sru int c = getc(fp); 85114402Sru if (c == EOF) 86114402Sru break; 87114402Sru else if (invalid_input_char(c)) 88114402Sru lex_error("invalid input character code %1", c); 89114402Sru else { 90114402Sru line += char(c); 91114402Sru if (c == '\n') 92114402Sru break; 93114402Sru } 94114402Sru } 95114402Sru if (line.length() == 0) 96114402Sru return 0; 97114402Sru if (!(line.length() >= 3 && line[0] == '.' && line[1] == 'P' 98114402Sru && (line[2] == 'S' || line[2] == 'E' || line[2] == 'F') 99114402Sru && (line.length() == 3 || line[3] == ' ' || line[3] == '\n' 100114402Sru || compatible_flag))) { 101114402Sru line += '\0'; 102114402Sru ptr = line.contents(); 103114402Sru return 1; 104114402Sru } 105114402Sru } 106114402Sru} 107114402Sru 108114402Sruint file_input::get() 109114402Sru{ 110114402Sru if (*ptr != '\0' || read_line()) 111114402Sru return (unsigned char)*ptr++; 112114402Sru else 113114402Sru return EOF; 114114402Sru} 115114402Sru 116114402Sruint file_input::peek() 117114402Sru{ 118114402Sru if (*ptr != '\0' || read_line()) 119114402Sru return (unsigned char)*ptr; 120114402Sru else 121114402Sru return EOF; 122114402Sru} 123114402Sru 124114402Sruint file_input::get_location(const char **fnp, int *lnp) 125114402Sru{ 126114402Sru *fnp = filename; 127114402Sru *lnp = lineno; 128114402Sru return 1; 129114402Sru} 130114402Sru 131114402Srumacro_input::macro_input(const char *str) 132114402Sru{ 133114402Sru p = s = strsave(str); 134114402Sru} 135114402Sru 136114402Srumacro_input::~macro_input() 137114402Sru{ 138114402Sru a_delete s; 139114402Sru} 140114402Sru 141114402Sruint macro_input::get() 142114402Sru{ 143114402Sru if (p == 0 || *p == '\0') 144114402Sru return EOF; 145114402Sru else 146114402Sru return (unsigned char)*p++; 147114402Sru} 148114402Sru 149114402Sruint macro_input::peek() 150114402Sru{ 151114402Sru if (p == 0 || *p == '\0') 152114402Sru return EOF; 153114402Sru else 154114402Sru return (unsigned char)*p; 155114402Sru} 156114402Sru 157114402Sru// Character representing $1. Must be invalid input character. 158114402Sru#define ARG1 14 159114402Sru 160114402Sruchar *process_body(const char *body) 161114402Sru{ 162114402Sru char *s = strsave(body); 163114402Sru int j = 0; 164114402Sru for (int i = 0; s[i] != '\0'; i++) 165114402Sru if (s[i] == '$' && s[i+1] >= '0' && s[i+1] <= '9') { 166114402Sru if (s[i+1] != '0') 167114402Sru s[j++] = ARG1 + s[++i] - '1'; 168114402Sru } 169114402Sru else 170114402Sru s[j++] = s[i]; 171114402Sru s[j] = '\0'; 172114402Sru return s; 173114402Sru} 174114402Sru 175114402Sru 176114402Sruargument_macro_input::argument_macro_input(const char *body, int ac, char **av) 177114402Sru: ap(0), argc(ac) 178114402Sru{ 179114402Sru for (int i = 0; i < argc; i++) 180114402Sru argv[i] = av[i]; 181114402Sru p = s = process_body(body); 182114402Sru} 183114402Sru 184114402Sru 185114402Sruargument_macro_input::~argument_macro_input() 186114402Sru{ 187114402Sru for (int i = 0; i < argc; i++) 188114402Sru a_delete argv[i]; 189114402Sru a_delete s; 190114402Sru} 191114402Sru 192114402Sruint argument_macro_input::get() 193114402Sru{ 194114402Sru if (ap) { 195114402Sru if (*ap != '\0') 196114402Sru return (unsigned char)*ap++; 197114402Sru ap = 0; 198114402Sru } 199114402Sru if (p == 0) 200114402Sru return EOF; 201114402Sru while (*p >= ARG1 && *p <= ARG1 + 8) { 202114402Sru int i = *p++ - ARG1; 203114402Sru if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { 204114402Sru ap = argv[i]; 205114402Sru return (unsigned char)*ap++; 206114402Sru } 207114402Sru } 208114402Sru if (*p == '\0') 209114402Sru return EOF; 210114402Sru return (unsigned char)*p++; 211114402Sru} 212114402Sru 213114402Sruint argument_macro_input::peek() 214114402Sru{ 215114402Sru if (ap) { 216114402Sru if (*ap != '\0') 217114402Sru return (unsigned char)*ap; 218114402Sru ap = 0; 219114402Sru } 220114402Sru if (p == 0) 221114402Sru return EOF; 222114402Sru while (*p >= ARG1 && *p <= ARG1 + 8) { 223114402Sru int i = *p++ - ARG1; 224114402Sru if (i < argc && argv[i] != 0 && argv[i][0] != '\0') { 225114402Sru ap = argv[i]; 226114402Sru return (unsigned char)*ap; 227114402Sru } 228114402Sru } 229114402Sru if (*p == '\0') 230114402Sru return EOF; 231114402Sru return (unsigned char)*p; 232114402Sru} 233114402Sru 234114402Sruclass input_stack { 235114402Sru static input *current_input; 236114402Sru static int bol_flag; 237114402Srupublic: 238114402Sru static void push(input *); 239114402Sru static void clear(); 240114402Sru static int get_char(); 241114402Sru static int peek_char(); 242114402Sru static int get_location(const char **fnp, int *lnp); 243114402Sru static void push_back(unsigned char c, int was_bol = 0); 244114402Sru static int bol(); 245114402Sru}; 246114402Sru 247114402Sruinput *input_stack::current_input = 0; 248114402Sruint input_stack::bol_flag = 0; 249114402Sru 250114402Sruinline int input_stack::bol() 251114402Sru{ 252114402Sru return bol_flag; 253114402Sru} 254114402Sru 255114402Sruvoid input_stack::clear() 256114402Sru{ 257114402Sru while (current_input != 0) { 258114402Sru input *tem = current_input; 259114402Sru current_input = current_input->next; 260114402Sru delete tem; 261114402Sru } 262114402Sru bol_flag = 1; 263114402Sru} 264114402Sru 265114402Sruvoid input_stack::push(input *in) 266114402Sru{ 267114402Sru in->next = current_input; 268114402Sru current_input = in; 269114402Sru} 270114402Sru 271114402Sruvoid lex_init(input *top) 272114402Sru{ 273114402Sru input_stack::clear(); 274114402Sru input_stack::push(top); 275114402Sru} 276114402Sru 277114402Sruvoid lex_cleanup() 278114402Sru{ 279114402Sru while (input_stack::get_char() != EOF) 280114402Sru ; 281114402Sru} 282114402Sru 283114402Sruint input_stack::get_char() 284114402Sru{ 285114402Sru while (current_input != 0) { 286114402Sru int c = current_input->get(); 287114402Sru if (c != EOF) { 288114402Sru bol_flag = c == '\n'; 289114402Sru return c; 290114402Sru } 291114402Sru // don't pop the top-level input off the stack 292114402Sru if (current_input->next == 0) 293114402Sru return EOF; 294114402Sru input *tem = current_input; 295114402Sru current_input = current_input->next; 296114402Sru delete tem; 297114402Sru } 298114402Sru return EOF; 299114402Sru} 300114402Sru 301114402Sruint input_stack::peek_char() 302114402Sru{ 303114402Sru while (current_input != 0) { 304114402Sru int c = current_input->peek(); 305114402Sru if (c != EOF) 306114402Sru return c; 307114402Sru if (current_input->next == 0) 308114402Sru return EOF; 309114402Sru input *tem = current_input; 310114402Sru current_input = current_input->next; 311114402Sru delete tem; 312114402Sru } 313114402Sru return EOF; 314114402Sru} 315114402Sru 316114402Sruclass char_input : public input { 317114402Sru int c; 318114402Srupublic: 319114402Sru char_input(int); 320114402Sru int get(); 321114402Sru int peek(); 322114402Sru}; 323114402Sru 324114402Sruchar_input::char_input(int n) : c((unsigned char)n) 325114402Sru{ 326114402Sru} 327114402Sru 328114402Sruint char_input::get() 329114402Sru{ 330114402Sru int n = c; 331114402Sru c = EOF; 332114402Sru return n; 333114402Sru} 334114402Sru 335114402Sruint char_input::peek() 336114402Sru{ 337114402Sru return c; 338114402Sru} 339114402Sru 340114402Sruvoid input_stack::push_back(unsigned char c, int was_bol) 341114402Sru{ 342114402Sru push(new char_input(c)); 343114402Sru bol_flag = was_bol; 344114402Sru} 345114402Sru 346114402Sruint input_stack::get_location(const char **fnp, int *lnp) 347114402Sru{ 348114402Sru for (input *p = current_input; p; p = p->next) 349114402Sru if (p->get_location(fnp, lnp)) 350114402Sru return 1; 351114402Sru return 0; 352114402Sru} 353114402Sru 354114402Srustring context_buffer; 355114402Sru 356114402Srustring token_buffer; 357114402Srudouble token_double; 358114402Sruint token_int; 359114402Sru 360114402Sruvoid interpolate_macro_with_args(const char *body) 361114402Sru{ 362114402Sru char *argv[9]; 363114402Sru int argc = 0; 364114402Sru int i; 365114402Sru for (i = 0; i < 9; i++) 366114402Sru argv[i] = 0; 367114402Sru int level = 0; 368114402Sru int c; 369114402Sru enum { NORMAL, IN_STRING, IN_STRING_QUOTED } state = NORMAL; 370114402Sru do { 371114402Sru token_buffer.clear(); 372114402Sru for (;;) { 373114402Sru c = input_stack::get_char(); 374114402Sru if (c == EOF) { 375114402Sru lex_error("end of input while scanning macro arguments"); 376114402Sru break; 377114402Sru } 378114402Sru if (state == NORMAL && level == 0 && (c == ',' || c == ')')) { 379114402Sru if (token_buffer.length() > 0) { 380114402Sru token_buffer += '\0'; 381114402Sru argv[argc] = strsave(token_buffer.contents()); 382114402Sru } 383114402Sru // for `foo()', argc = 0 384114402Sru if (argc > 0 || c != ')' || i > 0) 385114402Sru argc++; 386114402Sru break; 387114402Sru } 388114402Sru token_buffer += char(c); 389114402Sru switch (state) { 390114402Sru case NORMAL: 391114402Sru if (c == '"') 392114402Sru state = IN_STRING; 393114402Sru else if (c == '(') 394114402Sru level++; 395114402Sru else if (c == ')') 396114402Sru level--; 397114402Sru break; 398114402Sru case IN_STRING: 399114402Sru if (c == '"') 400114402Sru state = NORMAL; 401114402Sru else if (c == '\\') 402114402Sru state = IN_STRING_QUOTED; 403114402Sru break; 404114402Sru case IN_STRING_QUOTED: 405114402Sru state = IN_STRING; 406114402Sru break; 407114402Sru } 408114402Sru } 409114402Sru } while (c != ')' && c != EOF); 410114402Sru input_stack::push(new argument_macro_input(body, argc, argv)); 411114402Sru} 412114402Sru 413114402Srustatic int docmp(const char *s1, int n1, const char *s2, int n2) 414114402Sru{ 415114402Sru if (n1 < n2) { 416114402Sru int r = memcmp(s1, s2, n1); 417114402Sru return r ? r : -1; 418114402Sru } 419114402Sru else if (n1 > n2) { 420114402Sru int r = memcmp(s1, s2, n2); 421114402Sru return r ? r : 1; 422114402Sru } 423114402Sru else 424114402Sru return memcmp(s1, s2, n1); 425114402Sru} 426114402Sru 427114402Sruint lookup_keyword(const char *str, int len) 428114402Sru{ 429114402Sru static struct keyword { 430114402Sru const char *name; 431114402Sru int token; 432114402Sru } table[] = { 433114402Sru { "Here", HERE }, 434114402Sru { "above", ABOVE }, 435114402Sru { "aligned", ALIGNED }, 436114402Sru { "and", AND }, 437114402Sru { "arc", ARC }, 438114402Sru { "arrow", ARROW }, 439114402Sru { "at", AT }, 440114402Sru { "atan2", ATAN2 }, 441114402Sru { "below", BELOW }, 442114402Sru { "between", BETWEEN }, 443114402Sru { "bottom", BOTTOM }, 444114402Sru { "box", BOX }, 445114402Sru { "by", BY }, 446114402Sru { "ccw", CCW }, 447114402Sru { "center", CENTER }, 448114402Sru { "chop", CHOP }, 449114402Sru { "circle", CIRCLE }, 450114402Sru { "color", COLORED }, 451114402Sru { "colored", COLORED }, 452114402Sru { "colour", COLORED }, 453114402Sru { "coloured", COLORED }, 454114402Sru { "command", COMMAND }, 455114402Sru { "copy", COPY }, 456114402Sru { "cos", COS }, 457114402Sru { "cw", CW }, 458114402Sru { "dashed", DASHED }, 459114402Sru { "define", DEFINE }, 460114402Sru { "diam", DIAMETER }, 461114402Sru { "diameter", DIAMETER }, 462114402Sru { "do", DO }, 463114402Sru { "dotted", DOTTED }, 464114402Sru { "down", DOWN }, 465114402Sru { "east", EAST }, 466114402Sru { "ellipse", ELLIPSE }, 467114402Sru { "else", ELSE }, 468114402Sru { "end", END }, 469114402Sru { "exp", EXP }, 470114402Sru { "figname", FIGNAME }, 471114402Sru { "fill", FILL }, 472114402Sru { "filled", FILL }, 473114402Sru { "for", FOR }, 474114402Sru { "from", FROM }, 475114402Sru { "height", HEIGHT }, 476114402Sru { "ht", HEIGHT }, 477114402Sru { "if", IF }, 478114402Sru { "int", INT }, 479114402Sru { "invis", INVISIBLE }, 480114402Sru { "invisible", INVISIBLE }, 481114402Sru { "last", LAST }, 482114402Sru { "left", LEFT }, 483114402Sru { "line", LINE }, 484114402Sru { "ljust", LJUST }, 485114402Sru { "log", LOG }, 486114402Sru { "lower", LOWER }, 487114402Sru { "max", K_MAX }, 488114402Sru { "min", K_MIN }, 489114402Sru { "move", MOVE }, 490114402Sru { "north", NORTH }, 491114402Sru { "of", OF }, 492114402Sru { "outline", OUTLINED }, 493114402Sru { "outlined", OUTLINED }, 494114402Sru { "plot", PLOT }, 495114402Sru { "print", PRINT }, 496114402Sru { "rad", RADIUS }, 497114402Sru { "radius", RADIUS }, 498114402Sru { "rand", RAND }, 499114402Sru { "reset", RESET }, 500114402Sru { "right", RIGHT }, 501114402Sru { "rjust", RJUST }, 502114402Sru { "same", SAME }, 503114402Sru { "sh", SH }, 504114402Sru { "shaded", SHADED }, 505114402Sru { "sin", SIN }, 506114402Sru { "solid", SOLID }, 507114402Sru { "south", SOUTH }, 508114402Sru { "spline", SPLINE }, 509114402Sru { "sprintf", SPRINTF }, 510114402Sru { "sqrt", SQRT }, 511114402Sru { "srand", SRAND }, 512114402Sru { "start", START }, 513114402Sru { "the", THE }, 514114402Sru { "then", THEN }, 515114402Sru { "thick", THICKNESS }, 516114402Sru { "thickness", THICKNESS }, 517114402Sru { "thru", THRU }, 518114402Sru { "to", TO }, 519114402Sru { "top", TOP }, 520114402Sru { "undef", UNDEF }, 521114402Sru { "until", UNTIL }, 522114402Sru { "up", UP }, 523114402Sru { "upper", UPPER }, 524114402Sru { "way", WAY }, 525114402Sru { "west", WEST }, 526114402Sru { "wid", WIDTH }, 527114402Sru { "width", WIDTH }, 528114402Sru { "with", WITH }, 529114402Sru }; 530114402Sru 531114402Sru const keyword *start = table; 532114402Sru const keyword *end = table + sizeof(table)/sizeof(table[0]); 533114402Sru while (start < end) { 534114402Sru // start <= target < end 535114402Sru const keyword *mid = start + (end - start)/2; 536114402Sru 537114402Sru int cmp = docmp(str, len, mid->name, strlen(mid->name)); 538114402Sru if (cmp == 0) 539114402Sru return mid->token; 540114402Sru if (cmp < 0) 541114402Sru end = mid; 542114402Sru else 543114402Sru start = mid + 1; 544114402Sru } 545114402Sru return 0; 546114402Sru} 547114402Sru 548114402Sruint get_token_after_dot(int c) 549114402Sru{ 550114402Sru // get_token deals with the case where c is a digit 551114402Sru switch (c) { 552114402Sru case 'h': 553114402Sru input_stack::get_char(); 554114402Sru c = input_stack::peek_char(); 555114402Sru if (c == 't') { 556114402Sru input_stack::get_char(); 557114402Sru context_buffer = ".ht"; 558114402Sru return DOT_HT; 559114402Sru } 560114402Sru else if (c == 'e') { 561114402Sru input_stack::get_char(); 562114402Sru c = input_stack::peek_char(); 563114402Sru if (c == 'i') { 564114402Sru input_stack::get_char(); 565114402Sru c = input_stack::peek_char(); 566114402Sru if (c == 'g') { 567114402Sru input_stack::get_char(); 568114402Sru c = input_stack::peek_char(); 569114402Sru if (c == 'h') { 570114402Sru input_stack::get_char(); 571114402Sru c = input_stack::peek_char(); 572114402Sru if (c == 't') { 573114402Sru input_stack::get_char(); 574114402Sru context_buffer = ".height"; 575114402Sru return DOT_HT; 576114402Sru } 577114402Sru input_stack::push_back('h'); 578114402Sru } 579114402Sru input_stack::push_back('g'); 580114402Sru } 581114402Sru input_stack::push_back('i'); 582114402Sru } 583114402Sru input_stack::push_back('e'); 584114402Sru } 585114402Sru input_stack::push_back('h'); 586114402Sru return '.'; 587114402Sru case 'x': 588114402Sru input_stack::get_char(); 589114402Sru context_buffer = ".x"; 590114402Sru return DOT_X; 591114402Sru case 'y': 592114402Sru input_stack::get_char(); 593114402Sru context_buffer = ".y"; 594114402Sru return DOT_Y; 595114402Sru case 'c': 596114402Sru input_stack::get_char(); 597114402Sru c = input_stack::peek_char(); 598114402Sru if (c == 'e') { 599114402Sru input_stack::get_char(); 600114402Sru c = input_stack::peek_char(); 601114402Sru if (c == 'n') { 602114402Sru input_stack::get_char(); 603114402Sru c = input_stack::peek_char(); 604114402Sru if (c == 't') { 605114402Sru input_stack::get_char(); 606114402Sru c = input_stack::peek_char(); 607114402Sru if (c == 'e') { 608114402Sru input_stack::get_char(); 609114402Sru c = input_stack::peek_char(); 610114402Sru if (c == 'r') { 611114402Sru input_stack::get_char(); 612114402Sru context_buffer = ".center"; 613114402Sru return DOT_C; 614114402Sru } 615114402Sru input_stack::push_back('e'); 616114402Sru } 617114402Sru input_stack::push_back('t'); 618114402Sru } 619114402Sru input_stack::push_back('n'); 620114402Sru } 621114402Sru input_stack::push_back('e'); 622114402Sru } 623114402Sru context_buffer = ".c"; 624114402Sru return DOT_C; 625114402Sru case 'n': 626114402Sru input_stack::get_char(); 627114402Sru c = input_stack::peek_char(); 628114402Sru if (c == 'e') { 629114402Sru input_stack::get_char(); 630114402Sru context_buffer = ".ne"; 631114402Sru return DOT_NE; 632114402Sru } 633114402Sru else if (c == 'w') { 634114402Sru input_stack::get_char(); 635114402Sru context_buffer = ".nw"; 636114402Sru return DOT_NW; 637114402Sru } 638114402Sru else { 639114402Sru context_buffer = ".n"; 640114402Sru return DOT_N; 641114402Sru } 642114402Sru break; 643114402Sru case 'e': 644114402Sru input_stack::get_char(); 645114402Sru c = input_stack::peek_char(); 646114402Sru if (c == 'n') { 647114402Sru input_stack::get_char(); 648114402Sru c = input_stack::peek_char(); 649114402Sru if (c == 'd') { 650114402Sru input_stack::get_char(); 651114402Sru context_buffer = ".end"; 652114402Sru return DOT_END; 653114402Sru } 654114402Sru input_stack::push_back('n'); 655114402Sru context_buffer = ".e"; 656114402Sru return DOT_E; 657114402Sru } 658114402Sru context_buffer = ".e"; 659114402Sru return DOT_E; 660114402Sru case 'w': 661114402Sru input_stack::get_char(); 662114402Sru c = input_stack::peek_char(); 663114402Sru if (c == 'i') { 664114402Sru input_stack::get_char(); 665114402Sru c = input_stack::peek_char(); 666114402Sru if (c == 'd') { 667114402Sru input_stack::get_char(); 668114402Sru c = input_stack::peek_char(); 669114402Sru if (c == 't') { 670114402Sru input_stack::get_char(); 671114402Sru c = input_stack::peek_char(); 672114402Sru if (c == 'h') { 673114402Sru input_stack::get_char(); 674114402Sru context_buffer = ".width"; 675114402Sru return DOT_WID; 676114402Sru } 677114402Sru input_stack::push_back('t'); 678114402Sru } 679114402Sru context_buffer = ".wid"; 680114402Sru return DOT_WID; 681114402Sru } 682114402Sru input_stack::push_back('i'); 683114402Sru } 684114402Sru context_buffer = ".w"; 685114402Sru return DOT_W; 686114402Sru case 's': 687114402Sru input_stack::get_char(); 688114402Sru c = input_stack::peek_char(); 689114402Sru if (c == 'e') { 690114402Sru input_stack::get_char(); 691114402Sru context_buffer = ".se"; 692114402Sru return DOT_SE; 693114402Sru } 694114402Sru else if (c == 'w') { 695114402Sru input_stack::get_char(); 696114402Sru context_buffer = ".sw"; 697114402Sru return DOT_SW; 698114402Sru } 699114402Sru else { 700114402Sru if (c == 't') { 701114402Sru input_stack::get_char(); 702114402Sru c = input_stack::peek_char(); 703114402Sru if (c == 'a') { 704114402Sru input_stack::get_char(); 705114402Sru c = input_stack::peek_char(); 706114402Sru if (c == 'r') { 707114402Sru input_stack::get_char(); 708114402Sru c = input_stack::peek_char(); 709114402Sru if (c == 't') { 710114402Sru input_stack::get_char(); 711114402Sru context_buffer = ".start"; 712114402Sru return DOT_START; 713114402Sru } 714114402Sru input_stack::push_back('r'); 715114402Sru } 716114402Sru input_stack::push_back('a'); 717114402Sru } 718114402Sru input_stack::push_back('t'); 719114402Sru } 720114402Sru context_buffer = ".s"; 721114402Sru return DOT_S; 722114402Sru } 723114402Sru break; 724114402Sru case 't': 725114402Sru input_stack::get_char(); 726114402Sru c = input_stack::peek_char(); 727114402Sru if (c == 'o') { 728114402Sru input_stack::get_char(); 729114402Sru c = input_stack::peek_char(); 730114402Sru if (c == 'p') { 731114402Sru input_stack::get_char(); 732114402Sru context_buffer = ".top"; 733114402Sru return DOT_N; 734114402Sru } 735114402Sru input_stack::push_back('o'); 736114402Sru } 737114402Sru context_buffer = ".t"; 738114402Sru return DOT_N; 739114402Sru case 'l': 740114402Sru input_stack::get_char(); 741114402Sru c = input_stack::peek_char(); 742114402Sru if (c == 'e') { 743114402Sru input_stack::get_char(); 744114402Sru c = input_stack::peek_char(); 745114402Sru if (c == 'f') { 746114402Sru input_stack::get_char(); 747114402Sru c = input_stack::peek_char(); 748114402Sru if (c == 't') { 749114402Sru input_stack::get_char(); 750114402Sru context_buffer = ".left"; 751114402Sru return DOT_W; 752114402Sru } 753114402Sru input_stack::push_back('f'); 754114402Sru } 755114402Sru input_stack::push_back('e'); 756114402Sru } 757114402Sru context_buffer = ".l"; 758114402Sru return DOT_W; 759114402Sru case 'r': 760114402Sru input_stack::get_char(); 761114402Sru c = input_stack::peek_char(); 762114402Sru if (c == 'a') { 763114402Sru input_stack::get_char(); 764114402Sru c = input_stack::peek_char(); 765114402Sru if (c == 'd') { 766114402Sru input_stack::get_char(); 767114402Sru context_buffer = ".rad"; 768114402Sru return DOT_RAD; 769114402Sru } 770114402Sru input_stack::push_back('a'); 771114402Sru } 772114402Sru else if (c == 'i') { 773114402Sru input_stack::get_char(); 774114402Sru c = input_stack::peek_char(); 775114402Sru if (c == 'g') { 776114402Sru input_stack::get_char(); 777114402Sru c = input_stack::peek_char(); 778114402Sru if (c == 'h') { 779114402Sru input_stack::get_char(); 780114402Sru c = input_stack::peek_char(); 781114402Sru if (c == 't') { 782114402Sru input_stack::get_char(); 783114402Sru context_buffer = ".right"; 784114402Sru return DOT_E; 785114402Sru } 786114402Sru input_stack::push_back('h'); 787114402Sru } 788114402Sru input_stack::push_back('g'); 789114402Sru } 790114402Sru input_stack::push_back('i'); 791114402Sru } 792114402Sru context_buffer = ".r"; 793114402Sru return DOT_E; 794114402Sru case 'b': 795114402Sru input_stack::get_char(); 796114402Sru c = input_stack::peek_char(); 797114402Sru if (c == 'o') { 798114402Sru input_stack::get_char(); 799114402Sru c = input_stack::peek_char(); 800114402Sru if (c == 't') { 801114402Sru input_stack::get_char(); 802114402Sru c = input_stack::peek_char(); 803114402Sru if (c == 't') { 804114402Sru input_stack::get_char(); 805114402Sru c = input_stack::peek_char(); 806114402Sru if (c == 'o') { 807114402Sru input_stack::get_char(); 808114402Sru c = input_stack::peek_char(); 809114402Sru if (c == 'm') { 810114402Sru input_stack::get_char(); 811114402Sru context_buffer = ".bottom"; 812114402Sru return DOT_S; 813114402Sru } 814114402Sru input_stack::push_back('o'); 815114402Sru } 816114402Sru input_stack::push_back('t'); 817114402Sru } 818114402Sru context_buffer = ".bot"; 819114402Sru return DOT_S; 820114402Sru } 821114402Sru input_stack::push_back('o'); 822114402Sru } 823114402Sru context_buffer = ".b"; 824114402Sru return DOT_S; 825114402Sru default: 826114402Sru context_buffer = '.'; 827114402Sru return '.'; 828114402Sru } 829114402Sru} 830114402Sru 831114402Sruint get_token(int lookup_flag) 832114402Sru{ 833114402Sru context_buffer.clear(); 834114402Sru for (;;) { 835114402Sru int n = 0; 836114402Sru int bol = input_stack::bol(); 837114402Sru int c = input_stack::get_char(); 838114402Sru if (bol && c == command_char) { 839114402Sru token_buffer.clear(); 840114402Sru token_buffer += c; 841114402Sru // the newline is not part of the token 842114402Sru for (;;) { 843114402Sru c = input_stack::peek_char(); 844114402Sru if (c == EOF || c == '\n') 845114402Sru break; 846114402Sru input_stack::get_char(); 847114402Sru token_buffer += char(c); 848114402Sru } 849114402Sru context_buffer = token_buffer; 850114402Sru return COMMAND_LINE; 851114402Sru } 852114402Sru switch (c) { 853114402Sru case EOF: 854114402Sru return EOF; 855114402Sru case ' ': 856114402Sru case '\t': 857114402Sru break; 858114402Sru case '\\': 859114402Sru { 860114402Sru int d = input_stack::peek_char(); 861114402Sru if (d != '\n') { 862114402Sru context_buffer = '\\'; 863114402Sru return '\\'; 864114402Sru } 865114402Sru input_stack::get_char(); 866114402Sru break; 867114402Sru } 868114402Sru case '#': 869114402Sru do { 870114402Sru c = input_stack::get_char(); 871114402Sru } while (c != '\n' && c != EOF); 872114402Sru if (c == '\n') 873114402Sru context_buffer = '\n'; 874114402Sru return c; 875114402Sru case '"': 876114402Sru context_buffer = '"'; 877114402Sru token_buffer.clear(); 878114402Sru for (;;) { 879114402Sru c = input_stack::get_char(); 880114402Sru if (c == '\\') { 881114402Sru context_buffer += '\\'; 882114402Sru c = input_stack::peek_char(); 883114402Sru if (c == '"') { 884114402Sru input_stack::get_char(); 885114402Sru token_buffer += '"'; 886114402Sru context_buffer += '"'; 887114402Sru } 888114402Sru else 889114402Sru token_buffer += '\\'; 890114402Sru } 891114402Sru else if (c == '\n') { 892114402Sru error("newline in string"); 893114402Sru break; 894114402Sru } 895114402Sru else if (c == EOF) { 896114402Sru error("missing `\"'"); 897114402Sru break; 898114402Sru } 899114402Sru else if (c == '"') { 900114402Sru context_buffer += '"'; 901114402Sru break; 902114402Sru } 903114402Sru else { 904114402Sru context_buffer += char(c); 905114402Sru token_buffer += char(c); 906114402Sru } 907114402Sru } 908114402Sru return TEXT; 909114402Sru case '0': 910114402Sru case '1': 911114402Sru case '2': 912114402Sru case '3': 913114402Sru case '4': 914114402Sru case '5': 915114402Sru case '6': 916114402Sru case '7': 917114402Sru case '8': 918114402Sru case '9': 919114402Sru { 920114402Sru int overflow = 0; 921114402Sru n = 0; 922114402Sru for (;;) { 923114402Sru if (n > (INT_MAX - 9)/10) { 924114402Sru overflow = 1; 925114402Sru break; 926114402Sru } 927114402Sru n *= 10; 928114402Sru n += c - '0'; 929114402Sru context_buffer += char(c); 930114402Sru c = input_stack::peek_char(); 931114402Sru if (c == EOF || !csdigit(c)) 932114402Sru break; 933114402Sru c = input_stack::get_char(); 934114402Sru } 935114402Sru token_double = n; 936114402Sru if (overflow) { 937114402Sru for (;;) { 938114402Sru token_double *= 10.0; 939114402Sru token_double += c - '0'; 940114402Sru context_buffer += char(c); 941114402Sru c = input_stack::peek_char(); 942114402Sru if (c == EOF || !csdigit(c)) 943114402Sru break; 944114402Sru c = input_stack::get_char(); 945114402Sru } 946114402Sru // if somebody asks for 1000000000000th, we will silently 947114402Sru // give them INT_MAXth 948114402Sru double temp = token_double; // work around gas 1.34/sparc bug 949114402Sru if (token_double > INT_MAX) 950114402Sru n = INT_MAX; 951114402Sru else 952114402Sru n = int(temp); 953114402Sru } 954114402Sru } 955114402Sru switch (c) { 956114402Sru case 'i': 957114402Sru case 'I': 958114402Sru context_buffer += char(c); 959114402Sru input_stack::get_char(); 960114402Sru return NUMBER; 961114402Sru case '.': 962114402Sru { 963114402Sru context_buffer += '.'; 964114402Sru input_stack::get_char(); 965114402Sru got_dot: 966114402Sru double factor = 1.0; 967114402Sru for (;;) { 968114402Sru c = input_stack::peek_char(); 969114402Sru if (c == EOF || !csdigit(c)) 970114402Sru break; 971114402Sru input_stack::get_char(); 972114402Sru context_buffer += char(c); 973114402Sru factor /= 10.0; 974114402Sru if (c != '0') 975114402Sru token_double += factor*(c - '0'); 976114402Sru } 977114402Sru if (c != 'e' && c != 'E') { 978114402Sru if (c == 'i' || c == 'I') { 979114402Sru context_buffer += char(c); 980114402Sru input_stack::get_char(); 981114402Sru } 982114402Sru return NUMBER; 983114402Sru } 984114402Sru } 985114402Sru // fall through 986114402Sru case 'e': 987114402Sru case 'E': 988114402Sru { 989114402Sru int echar = c; 990114402Sru input_stack::get_char(); 991114402Sru c = input_stack::peek_char(); 992114402Sru int sign = '+'; 993114402Sru if (c == '+' || c == '-') { 994114402Sru sign = c; 995114402Sru input_stack::get_char(); 996114402Sru c = input_stack::peek_char(); 997114402Sru if (c == EOF || !csdigit(c)) { 998114402Sru input_stack::push_back(sign); 999114402Sru input_stack::push_back(echar); 1000114402Sru return NUMBER; 1001114402Sru } 1002114402Sru context_buffer += char(echar); 1003114402Sru context_buffer += char(sign); 1004114402Sru } 1005114402Sru else { 1006114402Sru if (c == EOF || !csdigit(c)) { 1007114402Sru input_stack::push_back(echar); 1008114402Sru return NUMBER; 1009114402Sru } 1010114402Sru context_buffer += char(echar); 1011114402Sru } 1012114402Sru input_stack::get_char(); 1013114402Sru context_buffer += char(c); 1014114402Sru n = c - '0'; 1015114402Sru for (;;) { 1016114402Sru c = input_stack::peek_char(); 1017114402Sru if (c == EOF || !csdigit(c)) 1018114402Sru break; 1019114402Sru input_stack::get_char(); 1020114402Sru context_buffer += char(c); 1021114402Sru n = n*10 + (c - '0'); 1022114402Sru } 1023114402Sru if (sign == '-') 1024114402Sru n = -n; 1025114402Sru if (c == 'i' || c == 'I') { 1026114402Sru context_buffer += char(c); 1027114402Sru input_stack::get_char(); 1028114402Sru } 1029114402Sru token_double *= pow(10.0, n); 1030114402Sru return NUMBER; 1031114402Sru } 1032114402Sru case 'n': 1033114402Sru input_stack::get_char(); 1034114402Sru c = input_stack::peek_char(); 1035114402Sru if (c == 'd') { 1036114402Sru input_stack::get_char(); 1037114402Sru token_int = n; 1038114402Sru context_buffer += "nd"; 1039114402Sru return ORDINAL; 1040114402Sru } 1041114402Sru input_stack::push_back('n'); 1042114402Sru return NUMBER; 1043114402Sru case 'r': 1044114402Sru input_stack::get_char(); 1045114402Sru c = input_stack::peek_char(); 1046114402Sru if (c == 'd') { 1047114402Sru input_stack::get_char(); 1048114402Sru token_int = n; 1049114402Sru context_buffer += "rd"; 1050114402Sru return ORDINAL; 1051114402Sru } 1052114402Sru input_stack::push_back('r'); 1053114402Sru return NUMBER; 1054114402Sru case 't': 1055114402Sru input_stack::get_char(); 1056114402Sru c = input_stack::peek_char(); 1057114402Sru if (c == 'h') { 1058114402Sru input_stack::get_char(); 1059114402Sru token_int = n; 1060114402Sru context_buffer += "th"; 1061114402Sru return ORDINAL; 1062114402Sru } 1063114402Sru input_stack::push_back('t'); 1064114402Sru return NUMBER; 1065114402Sru case 's': 1066114402Sru input_stack::get_char(); 1067114402Sru c = input_stack::peek_char(); 1068114402Sru if (c == 't') { 1069114402Sru input_stack::get_char(); 1070114402Sru token_int = n; 1071114402Sru context_buffer += "st"; 1072114402Sru return ORDINAL; 1073114402Sru } 1074114402Sru input_stack::push_back('s'); 1075114402Sru return NUMBER; 1076114402Sru default: 1077114402Sru return NUMBER; 1078114402Sru } 1079114402Sru break; 1080114402Sru case '\'': 1081114402Sru { 1082114402Sru c = input_stack::peek_char(); 1083114402Sru if (c == 't') { 1084114402Sru input_stack::get_char(); 1085114402Sru c = input_stack::peek_char(); 1086114402Sru if (c == 'h') { 1087114402Sru input_stack::get_char(); 1088114402Sru context_buffer = "'th"; 1089114402Sru return TH; 1090114402Sru } 1091114402Sru else 1092114402Sru input_stack::push_back('t'); 1093114402Sru } 1094114402Sru context_buffer = "'"; 1095114402Sru return '\''; 1096114402Sru } 1097114402Sru case '.': 1098114402Sru { 1099114402Sru c = input_stack::peek_char(); 1100114402Sru if (c != EOF && csdigit(c)) { 1101114402Sru n = 0; 1102114402Sru token_double = 0.0; 1103114402Sru context_buffer = '.'; 1104114402Sru goto got_dot; 1105114402Sru } 1106114402Sru return get_token_after_dot(c); 1107114402Sru } 1108114402Sru case '<': 1109114402Sru c = input_stack::peek_char(); 1110114402Sru if (c == '-') { 1111114402Sru input_stack::get_char(); 1112114402Sru c = input_stack::peek_char(); 1113114402Sru if (c == '>') { 1114114402Sru input_stack::get_char(); 1115114402Sru context_buffer = "<->"; 1116114402Sru return DOUBLE_ARROW_HEAD; 1117114402Sru } 1118114402Sru context_buffer = "<-"; 1119114402Sru return LEFT_ARROW_HEAD; 1120114402Sru } 1121114402Sru else if (c == '=') { 1122114402Sru input_stack::get_char(); 1123114402Sru context_buffer = "<="; 1124114402Sru return LESSEQUAL; 1125114402Sru } 1126114402Sru context_buffer = "<"; 1127114402Sru return '<'; 1128114402Sru case '-': 1129114402Sru c = input_stack::peek_char(); 1130114402Sru if (c == '>') { 1131114402Sru input_stack::get_char(); 1132114402Sru context_buffer = "->"; 1133114402Sru return RIGHT_ARROW_HEAD; 1134114402Sru } 1135114402Sru context_buffer = "-"; 1136114402Sru return '-'; 1137114402Sru case '!': 1138114402Sru c = input_stack::peek_char(); 1139114402Sru if (c == '=') { 1140114402Sru input_stack::get_char(); 1141114402Sru context_buffer = "!="; 1142114402Sru return NOTEQUAL; 1143114402Sru } 1144114402Sru context_buffer = "!"; 1145114402Sru return '!'; 1146114402Sru case '>': 1147114402Sru c = input_stack::peek_char(); 1148114402Sru if (c == '=') { 1149114402Sru input_stack::get_char(); 1150114402Sru context_buffer = ">="; 1151114402Sru return GREATEREQUAL; 1152114402Sru } 1153114402Sru context_buffer = ">"; 1154114402Sru return '>'; 1155114402Sru case '=': 1156114402Sru c = input_stack::peek_char(); 1157114402Sru if (c == '=') { 1158114402Sru input_stack::get_char(); 1159114402Sru context_buffer = "=="; 1160114402Sru return EQUALEQUAL; 1161114402Sru } 1162114402Sru context_buffer = "="; 1163114402Sru return '='; 1164114402Sru case '&': 1165114402Sru c = input_stack::peek_char(); 1166114402Sru if (c == '&') { 1167114402Sru input_stack::get_char(); 1168114402Sru context_buffer = "&&"; 1169114402Sru return ANDAND; 1170114402Sru } 1171114402Sru context_buffer = "&"; 1172114402Sru return '&'; 1173114402Sru case '|': 1174114402Sru c = input_stack::peek_char(); 1175114402Sru if (c == '|') { 1176114402Sru input_stack::get_char(); 1177114402Sru context_buffer = "||"; 1178114402Sru return OROR; 1179114402Sru } 1180114402Sru context_buffer = "|"; 1181114402Sru return '|'; 1182114402Sru default: 1183114402Sru if (c != EOF && csalpha(c)) { 1184114402Sru token_buffer.clear(); 1185114402Sru token_buffer = c; 1186114402Sru for (;;) { 1187114402Sru c = input_stack::peek_char(); 1188114402Sru if (c == EOF || (!csalnum(c) && c != '_')) 1189114402Sru break; 1190114402Sru input_stack::get_char(); 1191114402Sru token_buffer += char(c); 1192114402Sru } 1193114402Sru int tok = lookup_keyword(token_buffer.contents(), 1194114402Sru token_buffer.length()); 1195114402Sru if (tok != 0) { 1196114402Sru context_buffer = token_buffer; 1197114402Sru return tok; 1198114402Sru } 1199114402Sru char *def = 0; 1200114402Sru if (lookup_flag) { 1201114402Sru token_buffer += '\0'; 1202114402Sru def = macro_table.lookup(token_buffer.contents()); 1203114402Sru token_buffer.set_length(token_buffer.length() - 1); 1204114402Sru if (def) { 1205114402Sru if (c == '(') { 1206114402Sru input_stack::get_char(); 1207114402Sru interpolate_macro_with_args(def); 1208114402Sru } 1209114402Sru else 1210114402Sru input_stack::push(new macro_input(def)); 1211114402Sru } 1212114402Sru } 1213114402Sru if (!def) { 1214114402Sru context_buffer = token_buffer; 1215114402Sru if (csupper(token_buffer[0])) 1216114402Sru return LABEL; 1217114402Sru else 1218114402Sru return VARIABLE; 1219114402Sru } 1220114402Sru } 1221114402Sru else { 1222114402Sru context_buffer = char(c); 1223114402Sru return (unsigned char)c; 1224114402Sru } 1225114402Sru break; 1226114402Sru } 1227114402Sru } 1228114402Sru} 1229114402Sru 1230114402Sruint get_delimited() 1231114402Sru{ 1232114402Sru token_buffer.clear(); 1233114402Sru int c = input_stack::get_char(); 1234114402Sru while (c == ' ' || c == '\t' || c == '\n') 1235114402Sru c = input_stack::get_char(); 1236114402Sru if (c == EOF) { 1237114402Sru lex_error("missing delimiter"); 1238114402Sru return 0; 1239114402Sru } 1240114402Sru context_buffer = char(c); 1241114402Sru int had_newline = 0; 1242114402Sru int start = c; 1243114402Sru int level = 0; 1244114402Sru enum { NORMAL, IN_STRING, IN_STRING_QUOTED, DELIM_END } state = NORMAL; 1245114402Sru for (;;) { 1246114402Sru c = input_stack::get_char(); 1247114402Sru if (c == EOF) { 1248114402Sru lex_error("missing closing delimiter"); 1249114402Sru return 0; 1250114402Sru } 1251114402Sru if (c == '\n') 1252114402Sru had_newline = 1; 1253114402Sru else if (!had_newline) 1254114402Sru context_buffer += char(c); 1255114402Sru switch (state) { 1256114402Sru case NORMAL: 1257114402Sru if (start == '{') { 1258114402Sru if (c == '{') { 1259114402Sru level++; 1260114402Sru break; 1261114402Sru } 1262114402Sru if (c == '}') { 1263114402Sru if (--level < 0) 1264114402Sru state = DELIM_END; 1265114402Sru break; 1266114402Sru } 1267114402Sru } 1268114402Sru else { 1269114402Sru if (c == start) { 1270114402Sru state = DELIM_END; 1271114402Sru break; 1272114402Sru } 1273114402Sru } 1274114402Sru if (c == '"') 1275114402Sru state = IN_STRING; 1276114402Sru break; 1277114402Sru case IN_STRING_QUOTED: 1278114402Sru if (c == '\n') 1279114402Sru state = NORMAL; 1280114402Sru else 1281114402Sru state = IN_STRING; 1282114402Sru break; 1283114402Sru case IN_STRING: 1284114402Sru if (c == '"' || c == '\n') 1285114402Sru state = NORMAL; 1286114402Sru else if (c == '\\') 1287114402Sru state = IN_STRING_QUOTED; 1288114402Sru break; 1289114402Sru case DELIM_END: 1290114402Sru // This case it just to shut cfront 2.0 up. 1291114402Sru default: 1292114402Sru assert(0); 1293114402Sru } 1294114402Sru if (state == DELIM_END) 1295114402Sru break; 1296114402Sru token_buffer += c; 1297114402Sru } 1298114402Sru return 1; 1299114402Sru} 1300114402Sru 1301114402Sruvoid do_define() 1302114402Sru{ 1303114402Sru int t = get_token(0); // do not expand what we are defining 1304114402Sru if (t != VARIABLE && t != LABEL) { 1305114402Sru lex_error("can only define variable or placename"); 1306114402Sru return; 1307114402Sru } 1308114402Sru token_buffer += '\0'; 1309114402Sru string nm = token_buffer; 1310114402Sru const char *name = nm.contents(); 1311114402Sru if (!get_delimited()) 1312114402Sru return; 1313114402Sru token_buffer += '\0'; 1314114402Sru macro_table.define(name, strsave(token_buffer.contents())); 1315114402Sru} 1316114402Sru 1317114402Sruvoid do_undef() 1318114402Sru{ 1319114402Sru int t = get_token(0); // do not expand what we are undefining 1320114402Sru if (t != VARIABLE && t != LABEL) { 1321114402Sru lex_error("can only define variable or placename"); 1322114402Sru return; 1323114402Sru } 1324114402Sru token_buffer += '\0'; 1325114402Sru macro_table.define(token_buffer.contents(), 0); 1326114402Sru} 1327114402Sru 1328114402Sru 1329114402Sruclass for_input : public input { 1330114402Sru char *var; 1331114402Sru char *body; 1332151497Sru double from; 1333114402Sru double to; 1334114402Sru int by_is_multiplicative; 1335114402Sru double by; 1336114402Sru const char *p; 1337114402Sru int done_newline; 1338114402Srupublic: 1339151497Sru for_input(char *, double, double, int, double, char *); 1340114402Sru ~for_input(); 1341114402Sru int get(); 1342114402Sru int peek(); 1343114402Sru}; 1344114402Sru 1345151497Srufor_input::for_input(char *vr, double f, double t, 1346151497Sru int bim, double b, char *bd) 1347151497Sru: var(vr), body(bd), from(f), to(t), by_is_multiplicative(bim), by(b), 1348151497Sru p(body), done_newline(0) 1349114402Sru{ 1350114402Sru} 1351114402Sru 1352114402Srufor_input::~for_input() 1353114402Sru{ 1354114402Sru a_delete var; 1355114402Sru a_delete body; 1356114402Sru} 1357114402Sru 1358114402Sruint for_input::get() 1359114402Sru{ 1360114402Sru if (p == 0) 1361114402Sru return EOF; 1362114402Sru for (;;) { 1363114402Sru if (*p != '\0') 1364114402Sru return (unsigned char)*p++; 1365114402Sru if (!done_newline) { 1366114402Sru done_newline = 1; 1367114402Sru return '\n'; 1368114402Sru } 1369114402Sru double val; 1370114402Sru if (!lookup_variable(var, &val)) { 1371114402Sru lex_error("body of `for' terminated enclosing block"); 1372114402Sru return EOF; 1373114402Sru } 1374114402Sru if (by_is_multiplicative) 1375114402Sru val *= by; 1376114402Sru else 1377114402Sru val += by; 1378114402Sru define_variable(var, val); 1379151497Sru if ((from <= to && val > to) 1380151497Sru || (from >= to && val < to)) { 1381114402Sru p = 0; 1382114402Sru return EOF; 1383114402Sru } 1384114402Sru p = body; 1385114402Sru done_newline = 0; 1386114402Sru } 1387114402Sru} 1388114402Sru 1389114402Sruint for_input::peek() 1390114402Sru{ 1391114402Sru if (p == 0) 1392114402Sru return EOF; 1393114402Sru if (*p != '\0') 1394114402Sru return (unsigned char)*p; 1395114402Sru if (!done_newline) 1396114402Sru return '\n'; 1397114402Sru double val; 1398114402Sru if (!lookup_variable(var, &val)) 1399114402Sru return EOF; 1400114402Sru if (by_is_multiplicative) { 1401114402Sru if (val * by > to) 1402114402Sru return EOF; 1403114402Sru } 1404114402Sru else { 1405151497Sru if ((from <= to && val + by > to) 1406151497Sru || (from >= to && val + by < to)) 1407114402Sru return EOF; 1408114402Sru } 1409114402Sru if (*body == '\0') 1410114402Sru return EOF; 1411114402Sru return (unsigned char)*body; 1412114402Sru} 1413114402Sru 1414114402Sruvoid do_for(char *var, double from, double to, int by_is_multiplicative, 1415114402Sru double by, char *body) 1416114402Sru{ 1417114402Sru define_variable(var, from); 1418151497Sru if ((by_is_multiplicative && by <= 0) 1419151497Sru || (by > 0 && from > to) 1420151497Sru || (by < 0 && from < to)) 1421151497Sru return; 1422151497Sru input_stack::push(new for_input(var, from, to, 1423151497Sru by_is_multiplicative, by, body)); 1424114402Sru} 1425114402Sru 1426114402Sru 1427114402Sruvoid do_copy(const char *filename) 1428114402Sru{ 1429114402Sru errno = 0; 1430114402Sru FILE *fp = fopen(filename, "r"); 1431114402Sru if (fp == 0) { 1432114402Sru lex_error("can't open `%1': %2", filename, strerror(errno)); 1433114402Sru return; 1434114402Sru } 1435114402Sru input_stack::push(new file_input(fp, filename)); 1436114402Sru} 1437114402Sru 1438114402Sruclass copy_thru_input : public input { 1439114402Sru int done; 1440114402Sru char *body; 1441114402Sru char *until; 1442114402Sru const char *p; 1443114402Sru const char *ap; 1444114402Sru int argv[9]; 1445114402Sru int argc; 1446114402Sru string line; 1447114402Sru int get_line(); 1448114402Sru virtual int inget() = 0; 1449114402Srupublic: 1450114402Sru copy_thru_input(const char *b, const char *u); 1451114402Sru ~copy_thru_input(); 1452114402Sru int get(); 1453114402Sru int peek(); 1454114402Sru}; 1455114402Sru 1456114402Sruclass copy_file_thru_input : public copy_thru_input { 1457114402Sru input *in; 1458114402Srupublic: 1459114402Sru copy_file_thru_input(input *, const char *b, const char *u); 1460114402Sru ~copy_file_thru_input(); 1461114402Sru int inget(); 1462114402Sru}; 1463114402Sru 1464114402Srucopy_file_thru_input::copy_file_thru_input(input *i, const char *b, 1465114402Sru const char *u) 1466114402Sru: copy_thru_input(b, u), in(i) 1467114402Sru{ 1468114402Sru} 1469114402Sru 1470114402Srucopy_file_thru_input::~copy_file_thru_input() 1471114402Sru{ 1472114402Sru delete in; 1473114402Sru} 1474114402Sru 1475114402Sruint copy_file_thru_input::inget() 1476114402Sru{ 1477114402Sru if (!in) 1478114402Sru return EOF; 1479114402Sru else 1480114402Sru return in->get(); 1481114402Sru} 1482114402Sru 1483114402Sruclass copy_rest_thru_input : public copy_thru_input { 1484114402Srupublic: 1485114402Sru copy_rest_thru_input(const char *, const char *u); 1486114402Sru int inget(); 1487114402Sru}; 1488114402Sru 1489114402Srucopy_rest_thru_input::copy_rest_thru_input(const char *b, const char *u) 1490114402Sru: copy_thru_input(b, u) 1491114402Sru{ 1492114402Sru} 1493114402Sru 1494114402Sruint copy_rest_thru_input::inget() 1495114402Sru{ 1496114402Sru while (next != 0) { 1497114402Sru int c = next->get(); 1498114402Sru if (c != EOF) 1499114402Sru return c; 1500114402Sru if (next->next == 0) 1501114402Sru return EOF; 1502114402Sru input *tem = next; 1503114402Sru next = next->next; 1504114402Sru delete tem; 1505114402Sru } 1506114402Sru return EOF; 1507114402Sru 1508114402Sru} 1509114402Sru 1510114402Srucopy_thru_input::copy_thru_input(const char *b, const char *u) 1511114402Sru: done(0) 1512114402Sru{ 1513114402Sru ap = 0; 1514114402Sru body = process_body(b); 1515114402Sru p = 0; 1516114402Sru until = strsave(u); 1517114402Sru} 1518114402Sru 1519114402Sru 1520114402Srucopy_thru_input::~copy_thru_input() 1521114402Sru{ 1522114402Sru a_delete body; 1523114402Sru a_delete until; 1524114402Sru} 1525114402Sru 1526114402Sruint copy_thru_input::get() 1527114402Sru{ 1528114402Sru if (ap) { 1529114402Sru if (*ap != '\0') 1530114402Sru return (unsigned char)*ap++; 1531114402Sru ap = 0; 1532114402Sru } 1533114402Sru for (;;) { 1534114402Sru if (p == 0) { 1535114402Sru if (!get_line()) 1536114402Sru break; 1537114402Sru p = body; 1538114402Sru } 1539114402Sru if (*p == '\0') { 1540114402Sru p = 0; 1541114402Sru return '\n'; 1542114402Sru } 1543114402Sru while (*p >= ARG1 && *p <= ARG1 + 8) { 1544114402Sru int i = *p++ - ARG1; 1545114402Sru if (i < argc && line[argv[i]] != '\0') { 1546114402Sru ap = line.contents() + argv[i]; 1547114402Sru return (unsigned char)*ap++; 1548114402Sru } 1549114402Sru } 1550114402Sru if (*p != '\0') 1551114402Sru return (unsigned char)*p++; 1552114402Sru } 1553114402Sru return EOF; 1554114402Sru} 1555114402Sru 1556114402Sruint copy_thru_input::peek() 1557114402Sru{ 1558114402Sru if (ap) { 1559114402Sru if (*ap != '\0') 1560114402Sru return (unsigned char)*ap; 1561114402Sru ap = 0; 1562114402Sru } 1563114402Sru for (;;) { 1564114402Sru if (p == 0) { 1565114402Sru if (!get_line()) 1566114402Sru break; 1567114402Sru p = body; 1568114402Sru } 1569114402Sru if (*p == '\0') 1570114402Sru return '\n'; 1571114402Sru while (*p >= ARG1 && *p <= ARG1 + 8) { 1572114402Sru int i = *p++ - ARG1; 1573114402Sru if (i < argc && line[argv[i]] != '\0') { 1574114402Sru ap = line.contents() + argv[i]; 1575114402Sru return (unsigned char)*ap; 1576114402Sru } 1577114402Sru } 1578114402Sru if (*p != '\0') 1579114402Sru return (unsigned char)*p; 1580114402Sru } 1581114402Sru return EOF; 1582114402Sru} 1583114402Sru 1584114402Sruint copy_thru_input::get_line() 1585114402Sru{ 1586114402Sru if (done) 1587114402Sru return 0; 1588114402Sru line.clear(); 1589114402Sru argc = 0; 1590114402Sru int c = inget(); 1591114402Sru for (;;) { 1592114402Sru while (c == ' ') 1593114402Sru c = inget(); 1594114402Sru if (c == EOF || c == '\n') 1595114402Sru break; 1596114402Sru if (argc == 9) { 1597114402Sru do { 1598114402Sru c = inget(); 1599114402Sru } while (c != '\n' && c != EOF); 1600114402Sru break; 1601114402Sru } 1602114402Sru argv[argc++] = line.length(); 1603114402Sru do { 1604114402Sru line += char(c); 1605114402Sru c = inget(); 1606114402Sru } while (c != ' ' && c != '\n'); 1607114402Sru line += '\0'; 1608114402Sru } 1609114402Sru if (until != 0 && argc > 0 && strcmp(&line[argv[0]], until) == 0) { 1610114402Sru done = 1; 1611114402Sru return 0; 1612114402Sru } 1613114402Sru return argc > 0 || c == '\n'; 1614114402Sru} 1615114402Sru 1616114402Sruclass simple_file_input : public input { 1617114402Sru const char *filename; 1618114402Sru int lineno; 1619114402Sru FILE *fp; 1620114402Srupublic: 1621114402Sru simple_file_input(FILE *, const char *); 1622114402Sru ~simple_file_input(); 1623114402Sru int get(); 1624114402Sru int peek(); 1625114402Sru int get_location(const char **, int *); 1626114402Sru}; 1627114402Sru 1628114402Srusimple_file_input::simple_file_input(FILE *p, const char *s) 1629114402Sru: filename(s), lineno(1), fp(p) 1630114402Sru{ 1631114402Sru} 1632114402Sru 1633114402Srusimple_file_input::~simple_file_input() 1634114402Sru{ 1635114402Sru // don't delete the filename 1636114402Sru fclose(fp); 1637114402Sru} 1638114402Sru 1639114402Sruint simple_file_input::get() 1640114402Sru{ 1641114402Sru int c = getc(fp); 1642114402Sru while (invalid_input_char(c)) { 1643114402Sru error("invalid input character code %1", c); 1644114402Sru c = getc(fp); 1645114402Sru } 1646114402Sru if (c == '\n') 1647114402Sru lineno++; 1648114402Sru return c; 1649114402Sru} 1650114402Sru 1651114402Sruint simple_file_input::peek() 1652114402Sru{ 1653114402Sru int c = getc(fp); 1654114402Sru while (invalid_input_char(c)) { 1655114402Sru error("invalid input character code %1", c); 1656114402Sru c = getc(fp); 1657114402Sru } 1658114402Sru if (c != EOF) 1659114402Sru ungetc(c, fp); 1660114402Sru return c; 1661114402Sru} 1662114402Sru 1663114402Sruint simple_file_input::get_location(const char **fnp, int *lnp) 1664114402Sru{ 1665114402Sru *fnp = filename; 1666114402Sru *lnp = lineno; 1667114402Sru return 1; 1668114402Sru} 1669114402Sru 1670114402Sru 1671114402Sruvoid copy_file_thru(const char *filename, const char *body, const char *until) 1672114402Sru{ 1673114402Sru errno = 0; 1674114402Sru FILE *fp = fopen(filename, "r"); 1675114402Sru if (fp == 0) { 1676114402Sru lex_error("can't open `%1': %2", filename, strerror(errno)); 1677114402Sru return; 1678114402Sru } 1679114402Sru input *in = new copy_file_thru_input(new simple_file_input(fp, filename), 1680114402Sru body, until); 1681114402Sru input_stack::push(in); 1682114402Sru} 1683114402Sru 1684114402Sruvoid copy_rest_thru(const char *body, const char *until) 1685114402Sru{ 1686114402Sru input_stack::push(new copy_rest_thru_input(body, until)); 1687114402Sru} 1688114402Sru 1689114402Sruvoid push_body(const char *s) 1690114402Sru{ 1691114402Sru input_stack::push(new char_input('\n')); 1692114402Sru input_stack::push(new macro_input(s)); 1693114402Sru} 1694114402Sru 1695114402Sruint delim_flag = 0; 1696114402Sru 1697114402Sruchar *get_thru_arg() 1698114402Sru{ 1699114402Sru int c = input_stack::peek_char(); 1700114402Sru while (c == ' ') { 1701114402Sru input_stack::get_char(); 1702114402Sru c = input_stack::peek_char(); 1703114402Sru } 1704114402Sru if (c != EOF && csalpha(c)) { 1705114402Sru // looks like a macro 1706114402Sru input_stack::get_char(); 1707114402Sru token_buffer = c; 1708114402Sru for (;;) { 1709114402Sru c = input_stack::peek_char(); 1710114402Sru if (c == EOF || (!csalnum(c) && c != '_')) 1711114402Sru break; 1712114402Sru input_stack::get_char(); 1713114402Sru token_buffer += char(c); 1714114402Sru } 1715114402Sru context_buffer = token_buffer; 1716114402Sru token_buffer += '\0'; 1717114402Sru char *def = macro_table.lookup(token_buffer.contents()); 1718114402Sru if (def) 1719114402Sru return strsave(def); 1720114402Sru // I guess it wasn't a macro after all; so push the macro name back. 1721114402Sru // -2 because we added a '\0' 1722114402Sru for (int i = token_buffer.length() - 2; i >= 0; i--) 1723114402Sru input_stack::push_back(token_buffer[i]); 1724114402Sru } 1725114402Sru if (get_delimited()) { 1726114402Sru token_buffer += '\0'; 1727114402Sru return strsave(token_buffer.contents()); 1728114402Sru } 1729114402Sru else 1730114402Sru return 0; 1731114402Sru} 1732114402Sru 1733114402Sruint lookahead_token = -1; 1734114402Srustring old_context_buffer; 1735114402Sru 1736114402Sruvoid do_lookahead() 1737114402Sru{ 1738114402Sru if (lookahead_token == -1) { 1739114402Sru old_context_buffer = context_buffer; 1740114402Sru lookahead_token = get_token(1); 1741114402Sru } 1742114402Sru} 1743114402Sru 1744114402Sruint yylex() 1745114402Sru{ 1746114402Sru if (delim_flag) { 1747114402Sru assert(lookahead_token == -1); 1748114402Sru if (delim_flag == 2) { 1749114402Sru if ((yylval.str = get_thru_arg()) != 0) 1750114402Sru return DELIMITED; 1751114402Sru else 1752114402Sru return 0; 1753114402Sru } 1754114402Sru else { 1755114402Sru if (get_delimited()) { 1756114402Sru token_buffer += '\0'; 1757114402Sru yylval.str = strsave(token_buffer.contents()); 1758114402Sru return DELIMITED; 1759114402Sru } 1760114402Sru else 1761114402Sru return 0; 1762114402Sru } 1763114402Sru } 1764114402Sru for (;;) { 1765114402Sru int t; 1766114402Sru if (lookahead_token >= 0) { 1767114402Sru t = lookahead_token; 1768114402Sru lookahead_token = -1; 1769114402Sru } 1770114402Sru else 1771114402Sru t = get_token(1); 1772114402Sru switch (t) { 1773114402Sru case '\n': 1774114402Sru return ';'; 1775114402Sru case EOF: 1776114402Sru return 0; 1777114402Sru case DEFINE: 1778114402Sru do_define(); 1779114402Sru break; 1780114402Sru case UNDEF: 1781114402Sru do_undef(); 1782114402Sru break; 1783114402Sru case ORDINAL: 1784114402Sru yylval.n = token_int; 1785114402Sru return t; 1786114402Sru case NUMBER: 1787114402Sru yylval.x = token_double; 1788114402Sru return t; 1789114402Sru case COMMAND_LINE: 1790114402Sru case TEXT: 1791114402Sru token_buffer += '\0'; 1792114402Sru if (!input_stack::get_location(&yylval.lstr.filename, 1793114402Sru &yylval.lstr.lineno)) { 1794114402Sru yylval.lstr.filename = 0; 1795114402Sru yylval.lstr.lineno = -1; 1796114402Sru } 1797114402Sru yylval.lstr.str = strsave(token_buffer.contents()); 1798114402Sru return t; 1799114402Sru case LABEL: 1800114402Sru case VARIABLE: 1801114402Sru token_buffer += '\0'; 1802114402Sru yylval.str = strsave(token_buffer.contents()); 1803114402Sru return t; 1804114402Sru case LEFT: 1805114402Sru // change LEFT to LEFT_CORNER when followed by OF 1806114402Sru old_context_buffer = context_buffer; 1807114402Sru lookahead_token = get_token(1); 1808114402Sru if (lookahead_token == OF) 1809114402Sru return LEFT_CORNER; 1810114402Sru else 1811114402Sru return t; 1812114402Sru case RIGHT: 1813114402Sru // change RIGHT to RIGHT_CORNER when followed by OF 1814114402Sru old_context_buffer = context_buffer; 1815114402Sru lookahead_token = get_token(1); 1816114402Sru if (lookahead_token == OF) 1817114402Sru return RIGHT_CORNER; 1818114402Sru else 1819114402Sru return t; 1820114402Sru case UPPER: 1821114402Sru // recognise UPPER only before LEFT or RIGHT 1822114402Sru old_context_buffer = context_buffer; 1823114402Sru lookahead_token = get_token(1); 1824114402Sru if (lookahead_token != LEFT && lookahead_token != RIGHT) { 1825114402Sru yylval.str = strsave("upper"); 1826114402Sru return VARIABLE; 1827114402Sru } 1828114402Sru else 1829114402Sru return t; 1830114402Sru case LOWER: 1831114402Sru // recognise LOWER only before LEFT or RIGHT 1832114402Sru old_context_buffer = context_buffer; 1833114402Sru lookahead_token = get_token(1); 1834114402Sru if (lookahead_token != LEFT && lookahead_token != RIGHT) { 1835114402Sru yylval.str = strsave("lower"); 1836114402Sru return VARIABLE; 1837114402Sru } 1838114402Sru else 1839114402Sru return t; 1840114402Sru case NORTH: 1841114402Sru // recognise NORTH only before OF 1842114402Sru old_context_buffer = context_buffer; 1843114402Sru lookahead_token = get_token(1); 1844114402Sru if (lookahead_token != OF) { 1845114402Sru yylval.str = strsave("north"); 1846114402Sru return VARIABLE; 1847114402Sru } 1848114402Sru else 1849114402Sru return t; 1850114402Sru case SOUTH: 1851114402Sru // recognise SOUTH only before OF 1852114402Sru old_context_buffer = context_buffer; 1853114402Sru lookahead_token = get_token(1); 1854114402Sru if (lookahead_token != OF) { 1855114402Sru yylval.str = strsave("south"); 1856114402Sru return VARIABLE; 1857114402Sru } 1858114402Sru else 1859114402Sru return t; 1860114402Sru case EAST: 1861114402Sru // recognise EAST only before OF 1862114402Sru old_context_buffer = context_buffer; 1863114402Sru lookahead_token = get_token(1); 1864114402Sru if (lookahead_token != OF) { 1865114402Sru yylval.str = strsave("east"); 1866114402Sru return VARIABLE; 1867114402Sru } 1868114402Sru else 1869114402Sru return t; 1870114402Sru case WEST: 1871114402Sru // recognise WEST only before OF 1872114402Sru old_context_buffer = context_buffer; 1873114402Sru lookahead_token = get_token(1); 1874114402Sru if (lookahead_token != OF) { 1875114402Sru yylval.str = strsave("west"); 1876114402Sru return VARIABLE; 1877114402Sru } 1878114402Sru else 1879114402Sru return t; 1880114402Sru case TOP: 1881114402Sru // recognise TOP only before OF 1882114402Sru old_context_buffer = context_buffer; 1883114402Sru lookahead_token = get_token(1); 1884114402Sru if (lookahead_token != OF) { 1885114402Sru yylval.str = strsave("top"); 1886114402Sru return VARIABLE; 1887114402Sru } 1888114402Sru else 1889114402Sru return t; 1890114402Sru case BOTTOM: 1891114402Sru // recognise BOTTOM only before OF 1892114402Sru old_context_buffer = context_buffer; 1893114402Sru lookahead_token = get_token(1); 1894114402Sru if (lookahead_token != OF) { 1895114402Sru yylval.str = strsave("bottom"); 1896114402Sru return VARIABLE; 1897114402Sru } 1898114402Sru else 1899114402Sru return t; 1900114402Sru case CENTER: 1901114402Sru // recognise CENTER only before OF 1902114402Sru old_context_buffer = context_buffer; 1903114402Sru lookahead_token = get_token(1); 1904114402Sru if (lookahead_token != OF) { 1905114402Sru yylval.str = strsave("center"); 1906114402Sru return VARIABLE; 1907114402Sru } 1908114402Sru else 1909114402Sru return t; 1910114402Sru case START: 1911114402Sru // recognise START only before OF 1912114402Sru old_context_buffer = context_buffer; 1913114402Sru lookahead_token = get_token(1); 1914114402Sru if (lookahead_token != OF) { 1915114402Sru yylval.str = strsave("start"); 1916114402Sru return VARIABLE; 1917114402Sru } 1918114402Sru else 1919114402Sru return t; 1920114402Sru case END: 1921114402Sru // recognise END only before OF 1922114402Sru old_context_buffer = context_buffer; 1923114402Sru lookahead_token = get_token(1); 1924114402Sru if (lookahead_token != OF) { 1925114402Sru yylval.str = strsave("end"); 1926114402Sru return VARIABLE; 1927114402Sru } 1928114402Sru else 1929114402Sru return t; 1930114402Sru default: 1931114402Sru return t; 1932114402Sru } 1933114402Sru } 1934114402Sru} 1935114402Sru 1936114402Sruvoid lex_error(const char *message, 1937114402Sru const errarg &arg1, 1938114402Sru const errarg &arg2, 1939114402Sru const errarg &arg3) 1940114402Sru{ 1941114402Sru const char *filename; 1942114402Sru int lineno; 1943114402Sru if (!input_stack::get_location(&filename, &lineno)) 1944114402Sru error(message, arg1, arg2, arg3); 1945114402Sru else 1946114402Sru error_with_file_and_line(filename, lineno, message, arg1, arg2, arg3); 1947114402Sru} 1948114402Sru 1949114402Sruvoid lex_warning(const char *message, 1950114402Sru const errarg &arg1, 1951114402Sru const errarg &arg2, 1952114402Sru const errarg &arg3) 1953114402Sru{ 1954114402Sru const char *filename; 1955114402Sru int lineno; 1956114402Sru if (!input_stack::get_location(&filename, &lineno)) 1957114402Sru warning(message, arg1, arg2, arg3); 1958114402Sru else 1959114402Sru warning_with_file_and_line(filename, lineno, message, arg1, arg2, arg3); 1960114402Sru} 1961114402Sru 1962114402Sruvoid yyerror(const char *s) 1963114402Sru{ 1964114402Sru const char *filename; 1965114402Sru int lineno; 1966114402Sru const char *context = 0; 1967114402Sru if (lookahead_token == -1) { 1968114402Sru if (context_buffer.length() > 0) { 1969114402Sru context_buffer += '\0'; 1970114402Sru context = context_buffer.contents(); 1971114402Sru } 1972114402Sru } 1973114402Sru else { 1974114402Sru if (old_context_buffer.length() > 0) { 1975114402Sru old_context_buffer += '\0'; 1976114402Sru context = old_context_buffer.contents(); 1977114402Sru } 1978114402Sru } 1979114402Sru if (!input_stack::get_location(&filename, &lineno)) { 1980114402Sru if (context) { 1981114402Sru if (context[0] == '\n' && context[1] == '\0') 1982114402Sru error("%1 before newline", s); 1983114402Sru else 1984114402Sru error("%1 before `%2'", s, context); 1985114402Sru } 1986114402Sru else 1987114402Sru error("%1 at end of picture", s); 1988114402Sru } 1989114402Sru else { 1990114402Sru if (context) { 1991114402Sru if (context[0] == '\n' && context[1] == '\0') 1992114402Sru error_with_file_and_line(filename, lineno, "%1 before newline", s); 1993114402Sru else 1994114402Sru error_with_file_and_line(filename, lineno, "%1 before `%2'", 1995114402Sru s, context); 1996114402Sru } 1997114402Sru else 1998114402Sru error_with_file_and_line(filename, lineno, "%1 at end of picture", s); 1999114402Sru } 2000114402Sru} 2001114402Sru 2002