[gforth] / gforth / engine / engine.c  

gforth: gforth/engine/engine.c


1 : anton 1.1 /* Gforth virtual machine (aka inner interpreter)
2 :    
3 : anton 1.22 Copyright (C) 1995,1996,1997,1998,2000 Free Software Foundation, Inc.
4 : anton 1.1
5 :     This file is part of Gforth.
6 :    
7 :     Gforth is free software; you can redistribute it and/or
8 :     modify it under the terms of the GNU General Public License
9 :     as published by the Free Software Foundation; either version 2
10 :     of the License, or (at your option) any later version.
11 :    
12 :     This program is distributed in the hope that it will be useful,
13 :     but WITHOUT ANY WARRANTY; without even the implied warranty of
14 :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 :     GNU General Public License for more details.
16 :    
17 :     You should have received a copy of the GNU General Public License
18 :     along with this program; if not, write to the Free Software
19 : anton 1.23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
20 : anton 1.1 */
21 :    
22 : pazsan 1.36 undefine(`symbols')
23 :    
24 : anton 1.1 #include "config.h"
25 : pazsan 1.31 #include "forth.h"
26 : anton 1.1 #include <ctype.h>
27 :     #include <stdio.h>
28 :     #include <string.h>
29 :     #include <math.h>
30 : pazsan 1.4 #include <assert.h>
31 :     #include <stdlib.h>
32 :     #include <errno.h>
33 :     #include "io.h"
34 :     #include "threaded.h"
35 :     #ifndef STANDALONE
36 : anton 1.1 #include <sys/types.h>
37 :     #include <sys/stat.h>
38 :     #include <fcntl.h>
39 :     #include <time.h>
40 :     #include <sys/time.h>
41 :     #include <unistd.h>
42 :     #include <pwd.h>
43 : pazsan 1.17 #include <dirent.h>
44 : anton 1.21 #include <sys/resource.h>
45 : anton 1.19 #ifdef HAVE_FNMATCH_H
46 : anton 1.18 #include <fnmatch.h>
47 : anton 1.19 #else
48 :     #include "fnmatch.h"
49 :     #endif
50 : pazsan 1.4 #else
51 :     #include "systypes.h"
52 :     #endif
53 : anton 1.1
54 :     #if defined(HAVE_LIBDL) || defined(HAVE_DLOPEN) /* what else? */
55 :     #include <dlfcn.h>
56 :     #endif
57 : pazsan 1.8 #if defined(_WIN32)
58 :     #include <windows.h>
59 :     #endif
60 : anton 1.1 #ifdef hpux
61 :     #include <dl.h>
62 :     #endif
63 :    
64 :     #ifndef SEEK_SET
65 :     /* should be defined in stdio.h, but some systems don't have it */
66 :     #define SEEK_SET 0
67 :     #endif
68 :    
69 :     #define IOR(flag) ((flag)? -512-errno : 0)
70 :    
71 : pazsan 1.4 struct F83Name {
72 :     struct F83Name *next; /* the link field for old hands */
73 :     char countetc;
74 :     char name[0];
75 :     };
76 : anton 1.1
77 :     #define F83NAME_COUNT(np) ((np)->countetc & 0x1f)
78 : anton 1.25
79 :     struct Longname {
80 :     struct Longname *next; /* the link field for old hands */
81 :     Cell countetc;
82 :     char name[0];
83 :     };
84 :    
85 :     #define LONGNAME_COUNT(np) ((np)->countetc & (((~((UCell)0))<<3)>>3))
86 : anton 1.1
87 :     Cell *SP;
88 :     Float *FP;
89 :     Address UP=NULL;
90 :    
91 :     #if 0
92 :     /* not used currently */
93 :     int emitcounter;
94 :     #endif
95 :     #define NULLC '\0'
96 :    
97 : pazsan 1.14 #ifdef MEMCMP_AS_SUBROUTINE
98 :     extern int gforth_memcmp(const char * s1, const char * s2, size_t n);
99 : anton 1.15 #define memcmp(s1,s2,n) gforth_memcmp(s1,s2,n)
100 : pazsan 1.14 #endif
101 :    
102 : pazsan 1.9 #ifdef HAS_FILE
103 : anton 1.1 char *cstr(Char *from, UCell size, int clear)
104 :     /* return a C-string corresponding to the Forth string ( FROM SIZE ).
105 :     the C-string lives until the next call of cstr with CLEAR being true */
106 :     {
107 :     static struct cstr_buffer {
108 :     char *buffer;
109 :     size_t size;
110 :     } *buffers=NULL;
111 :     static int nbuffers=0;
112 :     static int used=0;
113 :     struct cstr_buffer *b;
114 :    
115 :     if (buffers==NULL)
116 :     buffers=malloc(0);
117 :     if (clear)
118 :     used=0;
119 :     if (used>=nbuffers) {
120 :     buffers=realloc(buffers,sizeof(struct cstr_buffer)*(used+1));
121 :     buffers[used]=(struct cstr_buffer){malloc(0),0};
122 :     nbuffers=used+1;
123 :     }
124 :     b=&buffers[used];
125 :     if (size+1 > b->size) {
126 :     b->buffer = realloc(b->buffer,size+1);
127 :     b->size = size+1;
128 :     }
129 :     memcpy(b->buffer,from,size);
130 :     b->buffer[size]='\0';
131 :     used++;
132 :     return b->buffer;
133 :     }
134 :    
135 :     char *tilde_cstr(Char *from, UCell size, int clear)
136 :     /* like cstr(), but perform tilde expansion on the string */
137 :     {
138 :     char *s1,*s2;
139 :     int s1_len, s2_len;
140 :     struct passwd *getpwnam (), *user_entry;
141 :    
142 :     if (size<1 || from[0]!='~')
143 :     return cstr(from, size, clear);
144 :     if (size<2 || from[1]=='/') {
145 :     s1 = (char *)getenv ("HOME");
146 :     if(s1 == NULL)
147 :     s1 = "";
148 :     s2 = from+1;
149 :     s2_len = size-1;
150 :     } else {
151 :     UCell i;
152 :     for (i=1; i<size && from[i]!='/'; i++)
153 :     ;
154 : anton 1.13 if (i==2 && from[1]=='+') /* deal with "~+", i.e., the wd */
155 :     return cstr(from+3, size<3?0:size-3,clear);
156 : anton 1.1 {
157 :     char user[i];
158 :     memcpy(user,from+1,i-1);
159 :     user[i-1]='\0';
160 :     user_entry=getpwnam(user);
161 :     }
162 :     if (user_entry==NULL)
163 :     return cstr(from, size, clear);
164 :     s1 = user_entry->pw_dir;
165 :     s2 = from+i;
166 :     s2_len = size-i;
167 :     }
168 :     s1_len = strlen(s1);
169 :     if (s1_len>1 && s1[s1_len-1]=='/')
170 :     s1_len--;
171 :     {
172 :     char path[s1_len+s2_len];
173 :     memcpy(path,s1,s1_len);
174 :     memcpy(path+s1_len,s2,s2_len);
175 :     return cstr(path,s1_len+s2_len,clear);
176 :     }
177 :     }
178 : pazsan 1.9 #endif
179 : anton 1.21
180 :     DCell timeval2us(struct timeval *tvp)
181 :     {
182 :     #ifndef BUGGY_LONG_LONG
183 :     return (tvp->tv_sec*(DCell)1000000)+tvp->tv_usec;
184 :     #else
185 :     DCell d2;
186 :     DCell d1=mmul(tvp->tv_sec,1000000);
187 :     d2.lo = d1.lo+tvp->tv_usec;
188 :     d2.hi = d1.hi + (d2.lo<d1.lo);
189 :     return d2;
190 :     #endif
191 :     }
192 : anton 1.1
193 :     #define NEWLINE '\n'
194 :    
195 :     #ifndef HAVE_RINT
196 :     #define rint(x) floor((x)+0.5)
197 :     #endif
198 :    
199 : pazsan 1.9 #ifdef HAS_FILE
200 : anton 1.16 static char* fileattr[6]={"rb","rb","r+b","r+b","wb","wb"};
201 : pazsan 1.32 static char* pfileattr[6]={"r","r","r+","r+","w","w"};
202 : anton 1.1
203 :     #ifndef O_BINARY
204 :     #define O_BINARY 0
205 :     #endif
206 :     #ifndef O_TEXT
207 :     #define O_TEXT 0
208 :     #endif
209 :    
210 :     static int ufileattr[6]= {
211 : anton 1.16 O_RDONLY|O_BINARY, O_RDONLY|O_BINARY,
212 :     O_RDWR |O_BINARY, O_RDWR |O_BINARY,
213 :     O_WRONLY|O_BINARY, O_WRONLY|O_BINARY };
214 : pazsan 1.9 #endif
215 : anton 1.1
216 : anton 1.26 /* conversion on fetch */
217 :    
218 :     #define vm_Cell2f(x) ((Bool)(x))
219 :     #define vm_Cell2c(x) ((Char)(x))
220 :     #define vm_Cell2n(x) ((Cell)x)
221 :     #define vm_Cell2w(x) ((Cell)x)
222 :     #define vm_Cell2u(x) ((UCell)(x))
223 :     #define vm_Cell2a_(x) ((Cell *)(x))
224 :     #define vm_Cell2c_(x) ((Char *)(x))
225 :     #define vm_Cell2f_(x) ((Float *)(x))
226 :     #define vm_Cell2df_(x) ((DFloat *)(x))
227 :     #define vm_Cell2sf_(x) ((SFloat *)(x))
228 :     #define vm_Cell2xt(x) ((Xt)(x))
229 :     #define vm_Cell2f83name(x) ((struct F83Name *)(x))
230 :     #define vm_Cell2longname(x) ((struct Longname *)(x))
231 :     #define vm_Float2r(x) (x)
232 :    
233 :     /* conversion on store */
234 :    
235 :     #define vm_f2Cell(x) ((Cell)(x))
236 :     #define vm_c2Cell(x) ((Cell)(x))
237 :     #define vm_n2Cell(x) ((Cell)(x))
238 :     #define vm_w2Cell(x) ((Cell)(x))
239 :     #define vm_u2Cell(x) ((Cell)(x))
240 :     #define vm_a_2Cell(x) ((Cell)(x))
241 :     #define vm_c_2Cell(x) ((Cell)(x))
242 :     #define vm_f_2Cell(x) ((Cell)(x))
243 :     #define vm_df_2Cell(x) ((Cell)(x))
244 :     #define vm_sf_2Cell(x) ((Cell)(x))
245 :     #define vm_xt2Cell(x) ((Cell)(x))
246 :     #define vm_f83name2Cell(x) ((Cell)(x))
247 :     #define vm_longname2Cell(x) ((Cell)(x))
248 :     #define vm_r2Float(x) (x)
249 :    
250 : anton 1.29 #define vm_Cell2Cell(x) (x)
251 :    
252 : anton 1.1 /* if machine.h has not defined explicit registers, define them as implicit */
253 :     #ifndef IPREG
254 :     #define IPREG
255 :     #endif
256 :     #ifndef SPREG
257 :     #define SPREG
258 :     #endif
259 :     #ifndef RPREG
260 :     #define RPREG
261 :     #endif
262 :     #ifndef FPREG
263 :     #define FPREG
264 :     #endif
265 :     #ifndef LPREG
266 :     #define LPREG
267 :     #endif
268 :     #ifndef CFAREG
269 :     #define CFAREG
270 :     #endif
271 :     #ifndef UPREG
272 :     #define UPREG
273 :     #endif
274 :     #ifndef TOSREG
275 :     #define TOSREG
276 :     #endif
277 :     #ifndef FTOSREG
278 :     #define FTOSREG
279 :     #endif
280 :    
281 :     #ifndef CPU_DEP1
282 :     # define CPU_DEP1 0
283 :     #endif
284 :    
285 : anton 1.28 /* instructions containing these must be the last instruction of a
286 :     super-instruction (e.g., branches, EXECUTE, and other instructions
287 :     ending the basic block). Instructions containing SET_IP get this
288 :     automatically, so you usually don't have to write it. If you have
289 :     to write it, write it after IP points to the next instruction.
290 :     Used for profiling. Don't write it in a word containing SET_IP, or
291 :     the following block will be counted twice. */
292 :     #ifdef VM_PROFILING
293 :     #define SUPER_END vm_count_block(IP)
294 :     #else
295 :     #define SUPER_END
296 :     #endif
297 : anton 1.35 #define SUPER_CONTINUE
298 : anton 1.28
299 : anton 1.10 #ifdef GFORTH_DEBUGGING
300 :     /* define some VM registers as global variables, so they survive exceptions;
301 :     global register variables are not up to the task (according to the
302 :     GNU C manual) */
303 :     Xt *ip;
304 :     Cell *rp;
305 :     #endif
306 :    
307 : pazsan 1.37 #ifdef DEBUG
308 :     #define CFA_TO_NAME(__cfa) \
309 :     Cell len, i; \
310 :     char * name = __cfa; \
311 :     for(i=0; i<32; i+=sizeof(Cell)) { \
312 :     len = ((Cell*)name)[-1]; \
313 :     if(len < 0) { \
314 :     len &= 0x1F; \
315 :     if((len+sizeof(Cell)) > i) break; \
316 :     } len = 0; \
317 :     name -= sizeof(Cell); \
318 :     }
319 :     #endif
320 :    
321 : anton 1.30 Xt *primtable(Label symbols[], Cell size)
322 : anton 1.39 /* used in primitive primtable for peephole optimization */
323 : anton 1.30 {
324 :     Xt *xts = (Xt *)malloc(size*sizeof(Xt));
325 :     Cell i;
326 :    
327 :     for (i=0; i<size; i++)
328 :     xts[i] = &symbols[i];
329 :     return xts;
330 :     }
331 :    
332 : anton 1.34
333 :     define(enginerest,
334 :     `(Xt *ip0, Cell *sp0, Cell *rp0, Float *fp0, Address lp0)
335 : anton 1.1 /* executes code at ip, if ip!=NULL
336 :     returns array of machine code labels (for use in a loader), if ip==NULL
337 :     */
338 :     {
339 : anton 1.10 #ifndef GFORTH_DEBUGGING
340 :     register Xt *ip IPREG;
341 :     register Cell *rp RPREG;
342 :     #endif
343 : anton 1.1 register Cell *sp SPREG = sp0;
344 :     register Float *fp FPREG = fp0;
345 :     register Address lp LPREG = lp0;
346 :     register Xt cfa CFAREG;
347 : anton 1.11 #ifdef MORE_VARS
348 :     MORE_VARS
349 :     #endif
350 : anton 1.1 register Address up UPREG = UP;
351 : anton 1.24 IF_spTOS(register Cell spTOS TOSREG;)
352 :     IF_fpTOS(register Float fpTOS FTOSREG;)
353 : anton 1.1 #if defined(DOUBLY_INDIRECT)
354 :     static Label *symbols;
355 :     static void *routines[]= {
356 : anton 1.27 #define MAX_SYMBOLS (sizeof(routines)/sizeof(routines[0]))
357 : anton 1.1 #else /* !defined(DOUBLY_INDIRECT) */
358 :     static Label symbols[]= {
359 : anton 1.27 #define MAX_SYMBOLS (sizeof(symbols)/sizeof(symbols[0]))
360 : anton 1.1 #endif /* !defined(DOUBLY_INDIRECT) */
361 : pazsan 1.4 (Label)&&docol,
362 :     (Label)&&docon,
363 :     (Label)&&dovar,
364 :     (Label)&&douser,
365 :     (Label)&&dodefer,
366 :     (Label)&&dofield,
367 :     (Label)&&dodoes,
368 : anton 1.1 /* the following entry is normally unused;
369 : anton 1.34 it is there because its index indicates a does-handler */
370 : pazsan 1.7 CPU_DEP1,
371 : anton 1.34 #define INST_ADDR(name) (Label)&&I_##name
372 :     #include "prim_lab.i"
373 :     #undef INST_ADDR
374 :     (Label)&&after_last,
375 :     (Label)0,
376 :     #ifdef IN_ENGINE2
377 :     #define INST_ADDR(name) (Label)&&J_##name
378 : anton 1.1 #include "prim_lab.i"
379 : anton 1.34 #undef INST_ADDR
380 :     #endif
381 : anton 1.1 };
382 :     #ifdef CPU_DEP2
383 :     CPU_DEP2
384 :     #endif
385 :    
386 : anton 1.10 ip = ip0;
387 :     rp = rp0;
388 : anton 1.1 #ifdef DEBUG
389 :     fprintf(stderr,"ip=%x, sp=%x, rp=%x, fp=%x, lp=%x, up=%x\n",
390 :     (unsigned)ip,(unsigned)sp,(unsigned)rp,
391 :     (unsigned)fp,(unsigned)lp,(unsigned)up);
392 :     #endif
393 :    
394 :     if (ip == NULL) {
395 :     #if defined(DOUBLY_INDIRECT)
396 : anton 1.38 #define CODE_OFFSET (26*sizeof(Cell))
397 :     #define XT_OFFSET (22*sizeof(Cell))
398 : anton 1.1 int i;
399 : anton 1.3 Cell code_offset = offset_image? CODE_OFFSET : 0;
400 : anton 1.38 Cell xt_offset = offset_image? XT_OFFSET : 0;
401 : pazsan 1.7
402 : anton 1.3 symbols = (Label *)(malloc(MAX_SYMBOLS*sizeof(Cell)+CODE_OFFSET)+code_offset);
403 : anton 1.38 xts = (Label *)(malloc(MAX_SYMBOLS*sizeof(Cell)+XT_OFFSET)+xt_offset);
404 : anton 1.1 for (i=0; i<DOESJUMP+1; i++)
405 : anton 1.38 xts[i] = symbols[i] = (Label)routines[i];
406 : anton 1.1 for (; routines[i]!=0; i++) {
407 :     if (i>=MAX_SYMBOLS) {
408 :     fprintf(stderr,"gforth-ditc: more than %d primitives\n",MAX_SYMBOLS);
409 :     exit(1);
410 : anton 1.20 }
411 : anton 1.38 xts[i] = symbols[i] = &routines[i];
412 : anton 1.1 }
413 : anton 1.20 #endif /* defined(DOUBLY_INDIRECT) */
414 :     return symbols;
415 : pazsan 1.7 }
416 : anton 1.1
417 : anton 1.24 IF_spTOS(spTOS = sp[0]);
418 :     IF_fpTOS(fpTOS = fp[0]);
419 : pazsan 1.7 /* prep_terminal(); */
420 : anton 1.11 SET_IP(ip);
421 : anton 1.28 SUPER_END; /* count the first block, too */
422 : anton 1.1 NEXT;
423 :    
424 : anton 1.11
425 : anton 1.1 #ifdef CPU_DEP3
426 :     CPU_DEP3
427 :     #endif
428 :    
429 :     docol:
430 :     {
431 :     #ifdef DEBUG
432 : pazsan 1.37 {
433 :     CFA_TO_NAME(cfa);
434 :     fprintf(stderr,"%08lx: col: %08lx %.*s\n",(Cell)ip,(Cell)PFA1(cfa),
435 :     len,name);
436 :     }
437 : anton 1.1 #endif
438 :     #ifdef CISC_NEXT
439 :     /* this is the simple version */
440 :     *--rp = (Cell)ip;
441 : anton 1.11 SET_IP((Xt *)PFA1(cfa));
442 : anton 1.28 SUPER_END;
443 : anton 1.1 NEXT;
444 :     #else
445 : anton 1.11 /* this one is important, so we help the compiler optimizing */
446 : anton 1.1 {
447 :     DEF_CA
448 : anton 1.11 rp[-1] = (Cell)ip;
449 :     SET_IP((Xt *)PFA1(cfa));
450 : anton 1.28 SUPER_END;
451 : anton 1.11 NEXT_P1;
452 :     rp--;
453 :     NEXT_P2;
454 : anton 1.1 }
455 :     #endif
456 :     }
457 :    
458 :     docon:
459 :     {
460 :     #ifdef DEBUG
461 :     fprintf(stderr,"%08lx: con: %08lx\n",(Cell)ip,*(Cell*)PFA1(cfa));
462 :     #endif
463 :     #ifdef USE_TOS
464 : anton 1.24 *sp-- = spTOS;
465 :     spTOS = *(Cell *)PFA1(cfa);
466 : anton 1.1 #else
467 :     *--sp = *(Cell *)PFA1(cfa);
468 :     #endif
469 :     }
470 :     NEXT_P0;
471 :     NEXT;
472 :    
473 :     dovar:
474 :     {
475 :     #ifdef DEBUG
476 :     fprintf(stderr,"%08lx: var: %08lx\n",(Cell)ip,(Cell)PFA1(cfa));
477 :     #endif
478 :     #ifdef USE_TOS
479 : anton 1.24 *sp-- = spTOS;
480 :     spTOS = (Cell)PFA1(cfa);
481 : anton 1.1 #else
482 :     *--sp = (Cell)PFA1(cfa);
483 :     #endif
484 :     }
485 :     NEXT_P0;
486 :     NEXT;
487 :    
488 :     douser:
489 :     {
490 :     #ifdef DEBUG
491 :     fprintf(stderr,"%08lx: user: %08lx\n",(Cell)ip,(Cell)PFA1(cfa));
492 :     #endif
493 :     #ifdef USE_TOS
494 : anton 1.24 *sp-- = spTOS;
495 :     spTOS = (Cell)(up+*(Cell*)PFA1(cfa));
496 : anton 1.1 #else
497 :     *--sp = (Cell)(up+*(Cell*)PFA1(cfa));
498 :     #endif
499 :     }
500 :     NEXT_P0;
501 :     NEXT;
502 :    
503 :     dodefer:
504 :     {
505 :     #ifdef DEBUG
506 :     fprintf(stderr,"%08lx: defer: %08lx\n",(Cell)ip,*(Cell*)PFA1(cfa));
507 :     #endif
508 : anton 1.28 SUPER_END;
509 : anton 1.1 EXEC(*(Xt *)PFA1(cfa));
510 :     }
511 :    
512 :     dofield:
513 :     {
514 :     #ifdef DEBUG
515 :     fprintf(stderr,"%08lx: field: %08lx\n",(Cell)ip,(Cell)PFA1(cfa));
516 :     #endif
517 : anton 1.24 spTOS += *(Cell*)PFA1(cfa);
518 : anton 1.1 }
519 :     NEXT_P0;
520 :     NEXT;
521 :    
522 :     dodoes:
523 :     /* this assumes the following structure:
524 :     defining-word:
525 :    
526 :     ...
527 :     DOES>
528 :     (possible padding)
529 :     possibly handler: jmp dodoes
530 :     (possible branch delay slot(s))
531 :     Forth code after DOES>
532 :    
533 :     defined word:
534 :    
535 :     cfa: address of or jump to handler OR
536 :     address of or jump to dodoes, address of DOES-code
537 :     pfa:
538 :    
539 :     */
540 :     {
541 :     /* fprintf(stderr, "Got CFA %08lx at doescode %08lx/%08lx: does: %08lx\n",cfa,(Cell)ip,(Cell)PFA(cfa),(Cell)DOES_CODE1(cfa));*/
542 :     #ifdef DEBUG
543 :     fprintf(stderr,"%08lx/%08lx: does: %08lx\n",(Cell)ip,(Cell)PFA(cfa),(Cell)DOES_CODE1(cfa));
544 :     fflush(stderr);
545 :     #endif
546 :     *--rp = (Cell)ip;
547 :     /* PFA1 might collide with DOES_CODE1 here, so we use PFA */
548 :     #ifdef USE_TOS
549 : anton 1.24 *sp-- = spTOS;
550 :     spTOS = (Cell)PFA(cfa);
551 : anton 1.1 #else
552 :     *--sp = (Cell)PFA(cfa);
553 :     #endif
554 : anton 1.11 SET_IP(DOES_CODE1(cfa));
555 : anton 1.28 SUPER_END;
556 : anton 1.24 /* fprintf(stderr,"TOS = %08lx, IP=%08lx\n", spTOS, IP);*/
557 : anton 1.1 }
558 :     NEXT;
559 :    
560 : anton 1.34 #ifndef IN_ENGINE2
561 : anton 1.33 #define LABEL(name) I_##name
562 : anton 1.34 #else
563 :     #define LABEL(name) J_##name: asm(".skip 16"); I_##name
564 :     #endif
565 : anton 1.1 #include "prim.i"
566 : anton 1.34 #undef LABEL
567 :     after_last: return (Label *)0;
568 :     /*needed only to get the length of the last primitive */
569 :     }'
570 :     )
571 :    
572 :     Label *engine enginerest
573 :    
574 :     #define IN_ENGINE2
575 :     Label *engine2 enginerest
576 :    

CVS Admin

Powered by ViewCVS 1.0-dev
(Powered by ViewCVS)

ViewCVS and CVS Help