1 1.1 christos BEGIN { 2 1.1 christos macros = "/usr/bwk/chem/chem.macros" # CHANGE ME!!!!! 3 1.1 christos macros = "/dev/null" # since originals are lost 4 1.1 christos 5 1.1 christos pi = 3.141592654 6 1.1 christos deg = 57.29578 7 1.1 christos setparams(1.0) 8 1.1 christos set(dc, "up 0 right 90 down 180 left 270 ne 45 se 135 sw 225 nw 315") 9 1.1 christos set(dc, "0 n 30 ne 45 ne 60 ne 90 e 120 se 135 se 150 se 180 s") 10 1.1 christos set(dc, "300 nw 315 nw 330 nw 270 w 210 sw 225 sw 240 sw") 11 1.1 christos } 12 1.1 christos function init() { 13 1.1 christos printf ".PS\n" 14 1.1 christos if (firsttime++ == 0) { 15 1.1 christos printf "copy \"%s\"\n", macros 16 1.1 christos printf "\ttextht = %g; textwid = .1; cwid = %g\n", textht, cwid 17 1.1 christos printf "\tlineht = %g; linewid = %g\n", lineht, linewid 18 1.1 christos } 19 1.1 christos printf "Last: 0,0\n" 20 1.1 christos RING = "R"; MOL = "M"; BOND = "B"; OTHER = "O" # manifests 21 1.1 christos last = OTHER 22 1.1 christos dir = 90 23 1.1 christos } 24 1.1 christos function setparams(scale) { 25 1.1 christos lineht = scale * 0.2 26 1.1 christos linewid = scale * 0.2 27 1.1 christos textht = scale * 0.16 28 1.1 christos db = scale * 0.2 # bond length 29 1.1 christos cwid = scale * 0.12 # character width 30 1.1 christos cr = scale * 0.08 # rad of invis circles at ring vertices 31 1.1 christos crh = scale * 0.16 # ht of invis ellipse at ring vertices 32 1.1 christos crw = scale * 0.12 # wid 33 1.1 christos dav = scale * 0.015 # vertical shift up for atoms in atom macro 34 1.1 christos dew = scale * 0.02 # east-west shift for left of/right of 35 1.1 christos ringside = scale * 0.3 # side of all rings 36 1.1 christos dbrack = scale * 0.1 # length of bottom of bracket 37 1.1 christos } 38 1.1 christos 39 1.1 christos { lineno++ } 40 1.1 christos 41 1.1 christos /^(\.cstart)|(begin chem)/ { init(); inchem = 1; next } 42 1.1 christos /^(\.cend)|(end)/ { inchem = 0; print ".PE"; next } 43 1.1 christos 44 1.1 christos /^\./ { print; next } # troff 45 1.1 christos 46 1.1 christos inchem == 0 { print; next } # everything else 47 1.1 christos 48 1.1 christos $1 == "pic" { shiftfields(1); print; next } # pic pass-thru 49 1.1 christos $1 ~ /^#/ { next } # comment 50 1.1 christos 51 1.1 christos $1 == "textht" { textht = $NF; next } 52 1.1 christos $1 == "cwid" { cwid = $NF; next } 53 1.1 christos $1 == "db" { db = $NF; next } 54 1.1 christos $1 == "size" { if ($NF <= 4) size = $NF; else size = $NF/10 55 1.1 christos setparams(size); next } 56 1.1 christos 57 1.1 christos { print "\n#", $0 } # debugging, etc. 58 1.1 christos { lastname = "" } 59 1.1 christos 60 1.1 christos $1 ~ /^[A-Z].*:$/ { # label; falls thru after shifting left 61 1.1 christos lastname = substr($1, 1, length($1)-1) 62 1.1 christos print $1 63 1.1 christos shiftfields(1) 64 1.1 christos } 65 1.1 christos 66 1.1 christos $1 ~ /^\"/ { print "Last: ", $0; last = OTHER; next } 67 1.1 christos 68 1.1 christos $1 ~ /bond/ { bond($1); next } 69 1.1 christos $1 ~ /^(double|triple|front|back)$/ && $2 == "bond" { 70 1.1 christos $1 = $1 $2; shiftfields(2); bond($1); next } 71 1.1 christos 72 1.1 christos $1 == "aromatic" { temp = $1; $1 = $2; $2 = temp } 73 1.1 christos $1 ~ /ring|benz/ { ring($1); next } 74 1.1 christos 75 1.1 christos $1 == "methyl" { $1 = "CH3" } # left here as an example 76 1.1 christos 77 1.1 christos $1 ~ /^[A-Z]/ { molecule(); next } 78 1.1 christos 79 1.1 christos $1 == "left" { left[++stack] = fields(2, NF); printf("Last: [\n"); next } 80 1.1 christos 81 1.1 christos $1 == "right" { bracket(); stack--; next } 82 1.1 christos 83 1.1 christos $1 == "label" { label(); next } 84 1.1 christos 85 1.1 christos /./ { print "Last: ", $0; last = OTHER } 86 1.1 christos 87 1.1 christos END { if (firsttime == 0) error("did you forget .cstart and .cend?") 88 1.1 christos if (inchem) printf ".PE\n" 89 1.1 christos } 90 1.1 christos 91 1.1 christos function bond(type, i, goes, from) { 92 1.1 christos goes = "" 93 1.1 christos for (i = 2; i <= NF; i++) 94 1.1 christos if ($i == ";") { 95 1.1 christos goes = $(i+1) 96 1.1 christos NF = i - 1 97 1.1 christos break 98 1.1 christos } 99 1.1 christos leng = db 100 1.1 christos from = "" 101 1.1 christos for (cf = 2; cf <= NF; ) { 102 1.1 christos if ($cf ~ /(\+|-)?[0-9]+|up|down|right|left|ne|se|nw|sw/) 103 1.1 christos dir = cvtdir(dir) 104 1.1 christos else if ($cf ~ /^leng/) { 105 1.1 christos leng = $(cf+1) 106 1.1 christos cf += 2 107 1.1 christos } else if ($cf == "to") { 108 1.1 christos leng = 0 109 1.1 christos from = fields(cf, NF) 110 1.1 christos break 111 1.1 christos } else if ($cf == "from") { 112 1.1 christos from = dofrom() 113 1.1 christos break 114 1.1 christos } else if ($cf ~ /^#/) { 115 1.1 christos cf = NF+1 116 1.1 christos break; 117 1.1 christos } else { 118 1.1 christos from = fields(cf, NF) 119 1.1 christos break 120 1.1 christos } 121 1.1 christos } 122 1.1 christos if (from ~ /( to )|^to/) # said "from ... to ...", so zap length 123 1.1 christos leng = 0 124 1.1 christos else if (from == "") # no from given at all 125 1.1 christos from = "from Last." leave(last, dir) " " fields(cf, NF) 126 1.1 christos printf "Last: %s(%g, %g, %s)\n", type, leng, dir, from 127 1.1 christos last = BOND 128 1.1 christos if (lastname != "") 129 1.1 christos labsave(lastname, last, dir) 130 1.1 christos if (goes) { 131 1.1 christos $0 = goes 132 1.1 christos molecule() 133 1.1 christos } 134 1.1 christos } 135 1.1 christos 136 1.1 christos function dofrom( n, s) { 137 1.1 christos cf++ # skip "from" 138 1.1 christos n = $cf 139 1.1 christos if (n in labtype) # "from Thing" => "from Thing.V.s" 140 1.1 christos return "from " n "." leave(labtype[n], dir) 141 1.1 christos if (n ~ /^\.[A-Z]/) # "from .V" => "from Last.V.s" 142 1.1 christos return "from Last" n "." corner(dir) 143 1.1 christos if (n ~ /^[A-Z][^.]*\.[A-Z][^.]*$/) # "from X.V" => "from X.V.s" 144 1.1 christos return "from " n "." corner(dir) 145 1.1 christos return fields(cf-1, NF) 146 1.1 christos } 147 1.1 christos 148 1.1 christos function bracket( t) { 149 1.1 christos printf("]\n") 150 1.1 christos if ($2 == ")") 151 1.1 christos t = "spline" 152 1.1 christos else 153 1.1 christos t = "line" 154 1.1 christos printf("%s from last [].sw+(%g,0) to last [].sw to last [].nw to last [].nw+(%g,0)\n", 155 1.1 christos t, dbrack, dbrack) 156 1.1 christos printf("%s from last [].se-(%g,0) to last [].se to last [].ne to last [].ne-(%g,0)\n", 157 1.1 christos t, dbrack, dbrack) 158 1.1 christos if ($3 == "sub") 159 1.1 christos printf("\" %s\" ljust at last [].se\n", fields(4,NF)) 160 1.1 christos } 161 1.1 christos 162 1.1 christos function molecule( n, type) { 163 1.1 christos n = $1 164 1.1 christos if (n == "BP") { 165 1.1 christos $1 = "\"\" ht 0 wid 0" 166 1.1 christos type = OTHER 167 1.1 christos } else { 168 1.1 christos $1 = atom(n) 169 1.1 christos type = MOL 170 1.1 christos } 171 1.1 christos gsub(/[^A-Za-z0-9]/, "", n) # for stuff like C(OH3): zap non-alnum 172 1.1 christos if ($2 == "") 173 1.1 christos printf "Last: %s: %s with .%s at Last.%s\n", \ 174 1.1 christos n, $0, leave(type,dir+180), leave(last,dir) 175 1.1 christos else if ($2 == "below") 176 1.1 christos printf("Last: %s: %s with .n at %s.s\n", n, $1, $3) 177 1.1 christos else if ($2 == "above") 178 1.1 christos printf("Last: %s: %s with .s at %s.n\n", n, $1, $3) 179 1.1 christos else if ($2 == "left" && $3 == "of") 180 1.1 christos printf("Last: %s: %s with .e at %s.w+(%g,0)\n", n, $1, $4, dew) 181 1.1 christos else if ($2 == "right" && $3 == "of") 182 1.1 christos printf("Last: %s: %s with .w at %s.e-(%g,0)\n", n, $1, $4, dew) 183 1.1 christos else 184 1.1 christos printf "Last: %s: %s\n", n, $0 185 1.1 christos last = type 186 1.1 christos if (lastname != "") 187 1.1 christos labsave(lastname, last, dir) 188 1.1 christos labsave(n, last, dir) 189 1.1 christos } 190 1.1 christos 191 1.1 christos function label( i, v) { 192 1.1 christos if (substr(labtype[$2], 1, 1) != RING) 193 1.1 christos error(sprintf("%s is not a ring", $2)) 194 1.1 christos else { 195 1.1 christos v = substr(labtype[$2], 2, 1) 196 1.1 christos for (i = 1; i <= v; i++) 197 1.1 christos printf("\"\\s-3%d\\s0\" at 0.%d<%s.C,%s.V%d>\n", i, v+2, $2, $2, i) 198 1.1 christos } 199 1.1 christos } 200 1.1 christos 201 1.1 christos function ring(type, typeint, pt, verts, i) { 202 1.1 christos pt = 0 # points up by default 203 1.1 christos if (type ~ /[1-8]$/) 204 1.1 christos verts = substr(type, length(type), 1) 205 1.1 christos else if (type ~ /flat/) 206 1.1 christos verts = 5 207 1.1 christos else 208 1.1 christos verts = 6 209 1.1 christos fused = other = "" 210 1.1 christos for (i = 1; i <= verts; i++) 211 1.1 christos put[i] = dbl[i] = "" 212 1.1 christos nput = aromatic = withat = 0 213 1.1 christos for (cf = 2; cf <= NF; ) { 214 1.1 christos if ($cf == "pointing") 215 1.1 christos pt = cvtdir(0) 216 1.1 christos else if ($cf == "double" || $cf == "triple") 217 1.1 christos dblring(verts) 218 1.1 christos else if ($cf ~ /arom/) { 219 1.1 christos aromatic++ 220 1.1 christos cf++ # handled later 221 1.1 christos } else if ($cf == "put") { 222 1.1 christos putring(verts) 223 1.1 christos nput++ 224 1.1 christos } else if ($cf ~ /^#/) { 225 1.1 christos cf = NF+1 226 1.1 christos break; 227 1.1 christos } else { 228 1.1 christos if ($cf == "with" || $cf == "at") 229 1.1 christos withat = 1 230 1.1 christos other = other " " $cf 231 1.1 christos cf++ 232 1.1 christos } 233 1.1 christos } 234 1.1 christos typeint = RING verts pt # RING | verts | dir 235 1.1 christos if (withat == 0) 236 1.1 christos fused = joinring(typeint, dir, last) 237 1.1 christos printf "Last: [\n" 238 1.1 christos makering(type, pt, verts) 239 1.1 christos printf "] %s %s\n", fused, other 240 1.1 christos last = typeint 241 1.1 christos if (lastname != "") 242 1.1 christos labsave(lastname, last, dir) 243 1.1 christos } 244 1.1 christos 245 1.1 christos function makering(type, pt, v, i, a, r) { 246 1.1 christos if (type ~ /flat/) 247 1.1 christos v = 6 248 1.1 christos # vertices 249 1.1 christos r = ringside / (2 * sin(pi/v)) 250 1.1 christos printf "\tC: 0,0\n" 251 1.1 christos for (i = 0; i <= v+1; i++) { 252 1.1 christos a = ((i-1) / v * 360 + pt) / deg 253 1.1 christos printf "\tV%d: (%g,%g)\n", i, r * sin(a), r * cos(a) 254 1.1 christos } 255 1.1 christos if (type ~ /flat/) { 256 1.1 christos printf "\tV4: V5; V5: V6\n" 257 1.1 christos v = 5 258 1.1 christos } 259 1.1 christos # sides 260 1.1 christos if (nput > 0) { # hetero ... 261 1.1 christos for (i = 1; i <= v; i++) { 262 1.1 christos c1 = c2 = 0 263 1.1 christos if (put[i] != "") { 264 1.1 christos printf("\tV%d: ellipse invis ht %g wid %g at V%d\n", 265 1.1 christos i, crh, crw, i) 266 1.1 christos printf("\t%s at V%d\n", put[i], i) 267 1.1 christos c1 = cr 268 1.1 christos } 269 1.1 christos j = i+1 270 1.1 christos if (j > v) 271 1.1 christos j = 1 272 1.1 christos if (put[j] != "") 273 1.1 christos c2 = cr 274 1.1 christos printf "\tline from V%d to V%d chop %g chop %g\n", i, j, c1, c2 275 1.1 christos if (dbl[i] != "") { # should check i<j 276 1.1 christos if (type ~ /flat/ && i == 3) { 277 1.1 christos rat = 0.75; fix = 5 278 1.1 christos } else { 279 1.1 christos rat = 0.85; fix = 1.5 280 1.1 christos } 281 1.1 christos if (put[i] == "") 282 1.1 christos c1 = 0 283 1.1 christos else 284 1.1 christos c1 = cr/fix 285 1.1 christos if (put[j] == "") 286 1.1 christos c2 = 0 287 1.1 christos else 288 1.1 christos c2 = cr/fix 289 1.1 christos printf "\tline from %g<C,V%d> to %g<C,V%d> chop %g chop %g\n", 290 1.1 christos rat, i, rat, j, c1, c2 291 1.1 christos if (dbl[i] == "triple") 292 1.1 christos printf "\tline from %g<C,V%d> to %g<C,V%d> chop %g chop %g\n", 293 1.1 christos 2-rat, i, 2-rat, j, c1, c2 294 1.1 christos } 295 1.1 christos } 296 1.1 christos } else { # regular 297 1.1 christos for (i = 1; i <= v; i++) { 298 1.1 christos j = i+1 299 1.1 christos if (j > v) 300 1.1 christos j = 1 301 1.1 christos printf "\tline from V%d to V%d\n", i, j 302 1.1 christos if (dbl[i] != "") { # should check i<j 303 1.1 christos if (type ~ /flat/ && i == 3) { 304 1.1 christos rat = 0.75 305 1.1 christos } else 306 1.1 christos rat = 0.85 307 1.1 christos printf "\tline from %g<C,V%d> to %g<C,V%d>\n", 308 1.1 christos rat, i, rat, j 309 1.1 christos if (dbl[i] == "triple") 310 1.1 christos printf "\tline from %g<C,V%d> to %g<C,V%d>\n", 311 1.1 christos 2-rat, i, 2-rat, j 312 1.1 christos } 313 1.1 christos } 314 1.1 christos } 315 1.1 christos # punt on triple temporarily 316 1.1 christos # circle 317 1.1 christos if (type ~ /benz/ || aromatic > 0) { 318 1.1 christos if (type ~ /flat/) 319 1.1 christos r *= .4 320 1.1 christos else 321 1.1 christos r *= .5 322 1.1 christos printf "\tcircle rad %g at 0,0\n", r 323 1.1 christos } 324 1.1 christos } 325 1.1 christos 326 1.1 christos function putring(v) { # collect "put Mol at n" 327 1.1 christos cf++ 328 1.1 christos mol = $(cf++) 329 1.1 christos if ($cf == "at") 330 1.1 christos cf++ 331 1.1 christos if ($cf >= 1 && $cf <= v) { 332 1.1 christos m = mol 333 1.1 christos gsub(/[^A-Za-z0-9]/, "", m) 334 1.1 christos put[$cf] = m ":" atom(mol) 335 1.1 christos } 336 1.1 christos cf++ 337 1.1 christos } 338 1.1 christos 339 1.1 christos function joinring(type, dir, last) { # join a ring to something 340 1.1 christos if (substr(last, 1, 1) == RING) { # ring to ring 341 1.1 christos if (substr(type, 3) == substr(last, 3)) # fails if not 6-sided 342 1.1 christos return "with .V6 at Last.V2" 343 1.1 christos } 344 1.1 christos # if all else fails 345 1.1 christos return sprintf("with .%s at Last.%s", \ 346 1.1 christos leave(type,dir+180), leave(last,dir)) 347 1.1 christos } 348 1.1 christos 349 1.1 christos function leave(last, d, c, c1) { # return vertex of last in dir d 350 1.1 christos if (last == BOND) 351 1.1 christos return "end" 352 1.1 christos d = reduce(d) 353 1.1 christos if (substr(last, 1, 1) == RING) 354 1.1 christos return ringleave(last, d) 355 1.1 christos if (last == MOL) { 356 1.1 christos if (d == 0 || d == 180) 357 1.1 christos c = "C" 358 1.1 christos else if (d > 0 && d < 180) 359 1.1 christos c = "R" 360 1.1 christos else 361 1.1 christos c = "L" 362 1.1 christos if (d in dc) 363 1.1 christos c1 = dc[d] 364 1.1 christos else 365 1.1 christos c1 = corner(d) 366 1.1 christos return sprintf("%s.%s", c, c1) 367 1.1 christos } 368 1.1 christos if (last == OTHER) 369 1.1 christos return corner(d) 370 1.1 christos return "c" 371 1.1 christos } 372 1.1 christos 373 1.1 christos function ringleave(last, d, rd, verts) { # return vertex of ring in dir d 374 1.1 christos verts = substr(last, 2, 1) 375 1.1 christos rd = substr(last, 3) 376 1.1 christos return sprintf("V%d.%s", int(reduce(d-rd)/(360/verts)) + 1, corner(d)) 377 1.1 christos } 378 1.1 christos 379 1.1 christos function corner(dir) { 380 1.1 christos return dc[reduce(45 * int((dir+22.5)/45))] 381 1.1 christos } 382 1.1 christos 383 1.1 christos function labsave(name, type, dir) { 384 1.1 christos labtype[name] = type 385 1.1 christos labdir[name] = dir 386 1.1 christos } 387 1.1 christos 388 1.1 christos function dblring(v, d, v1, v2) { # should canonicalize to i,i+1 mod v 389 1.1 christos d = $cf 390 1.1 christos for (cf++; $cf ~ /^[1-9]/; cf++) { 391 1.1 christos v1 = substr($cf,1,1) 392 1.1 christos v2 = substr($cf,3,1) 393 1.1 christos if (v2 == v1+1 || v1 == v && v2 == 1) # e.g., 2,3 or 5,1 394 1.1 christos dbl[v1] = d 395 1.1 christos else if (v1 == v2+1 || v2 == v && v1 == 1) # e.g., 3,2 or 1,5 396 1.1 christos dbl[v2] = d 397 1.1 christos else 398 1.1 christos error(sprintf("weird %s bond in\n\t%s", d, $0)) 399 1.1 christos } 400 1.1 christos } 401 1.1 christos 402 1.1 christos function cvtdir(d) { # maps "[pointing] somewhere" to degrees 403 1.1 christos if ($cf == "pointing") 404 1.1 christos cf++ 405 1.1 christos if ($cf ~ /^[+\-]?[0-9]+/) 406 1.1 christos return reduce($(cf++)) 407 1.1 christos else if ($cf ~ /left|right|up|down|ne|nw|se|sw/) 408 1.1 christos return reduce(dc[$(cf++)]) 409 1.1 christos else { 410 1.1 christos cf++ 411 1.1 christos return d 412 1.1 christos } 413 1.1 christos } 414 1.1 christos 415 1.1 christos function reduce(d) { # reduces d to 0 <= d < 360 416 1.1 christos while (d >= 360) 417 1.1 christos d -= 360 418 1.1 christos while (d < 0) 419 1.1 christos d += 360 420 1.1 christos return d 421 1.1 christos } 422 1.1 christos 423 1.1 christos function atom(s, c, i, n, nsub, cloc, nsubc) { # convert CH3 to atom(...) 424 1.1 christos if (s == "\"\"") 425 1.1 christos return s 426 1.1 christos n = length(s) 427 1.1 christos nsub = nsubc = 0 428 1.1 christos cloc = index(s, "C") 429 1.1 christos if (cloc == 0) 430 1.1 christos cloc = 1 431 1.1 christos for (i = 1; i <= n; i++) 432 1.1 christos if (substr(s, i, 1) !~ /[A-Z]/) { 433 1.1 christos nsub++ 434 1.1 christos if (i < cloc) 435 1.1 christos nsubc++ 436 1.1 christos } 437 1.1 christos gsub(/([0-9]+\.[0-9]+)|([0-9]+)/, "\\s-3\\d&\\u\\s+3", s) 438 1.1 christos if (s ~ /([^0-9]\.)|(\.[^0-9])/) # centered dot 439 1.1 christos gsub(/\./, "\\v#-.3m#.\\v#.3m#", s) 440 1.1 christos return sprintf("atom(\"%s\", %g, %g, %g, %g, %g, %g)", 441 1.1 christos s, (n-nsub/2)*cwid, textht, (cloc-nsubc/2-0.5)*cwid, crh, crw, dav) 442 1.1 christos } 443 1.1 christos 444 1.1 christos function in_line( i, n, s, s1, os) { 445 1.1 christos s = $0 446 1.1 christos os = "" 447 1.1 christos while ((n = match(s, /!?[A-Z][A-Za-z]*(([0-9]+\.[0-9]+)|([0-9]+))/)) > 0) { 448 1.1 christos os = os substr(s, 1, n-1) # prefix 449 1.1 christos s1 = substr(s, n, RLENGTH) # molecule 450 1.1 christos if (substr(s1, 1, 1) == "!") { # !mol => leave alone 451 1.1 christos s1 = substr(s1, 2) 452 1.1 christos } else { 453 1.1 christos gsub(/([0-9]+\.[0-9]+)|([0-9]+)/, "\\s-3\\d&\\u\\s+3", s1) 454 1.1 christos if (s1 ~ /([^0-9]\.)|(\.[^0-9])/) # centered dot 455 1.1 christos gsub(/\./, "\\v#-.3m#.\\v#.3m#", s1) 456 1.1 christos } 457 1.1 christos os = os s1 458 1.1 christos s = substr(s, n + RLENGTH) # tail 459 1.1 christos } 460 1.1 christos os = os s 461 1.1 christos print os 462 1.1 christos return 463 1.1 christos } 464 1.1 christos 465 1.1 christos function shiftfields(n, i) { # move $n+1..$NF to $n..$NF-1, zap $NF 466 1.1 christos for (i = n; i < NF; i++) 467 1.1 christos $i = $(i+1) 468 1.1 christos $NF = "" 469 1.1 christos NF-- 470 1.1 christos } 471 1.1 christos 472 1.1 christos function fields(n1, n2, i, s) { 473 1.1 christos if (n1 > n2) 474 1.1 christos return "" 475 1.1 christos s = "" 476 1.1 christos for (i = n1; i <= n2; i++) { 477 1.1 christos if ($i ~ /^#/) 478 1.1 christos break; 479 1.1 christos s = s $i " " 480 1.1 christos } 481 1.1 christos return s 482 1.1 christos } 483 1.1 christos 484 1.1 christos function set(a, s, i, n, q) { 485 1.1 christos n = split(s, q) 486 1.1 christos for (i = 1; i <= n; i += 2) 487 1.1 christos a[q[i]] = q[i+1] 488 1.1 christos } 489 1.1 christos 490 1.1 christos function error(s) { 491 1.1 christos printf "chem\007: error on line %d: %s\n", lineno, s | "cat 1>&2" 492 1.1 christos } 493