1: /* $Header: bits.c,v 4.3 85/05/01 11:36:15 lwall Exp $
   2:  *
   3:  * $Log:	bits.c,v $
   4:  * Revision 4.3  85/05/01  11:36:15  lwall
   5:  * Baseline for release with 4.3bsd.
   6:  *
   7:  */
   8: 
   9: #include "EXTERN.h"
  10: #include "common.h"
  11: #include "rcstuff.h"
  12: #include "head.h"
  13: #include "util.h"
  14: #include "final.h"
  15: #include "rn.h"
  16: #include "cheat.h"
  17: #include "ng.h"
  18: #include "artio.h"
  19: #include "intrp.h"
  20: #include "ngdata.h"
  21: #include "rcln.h"
  22: #include "kfile.h"
  23: #include "INTERN.h"
  24: #include "bits.h"
  25: 
  26: #ifdef DBM
  27: #    ifdef NULL
  28: #	undef NULL
  29: #    endif NULL
  30: #    include <dbm.h>
  31: #endif DBM
  32: MEM_SIZE ctlsize;           /* size of bitmap in bytes */
  33: 
  34: void
  35: bits_init()
  36: {
  37: #ifdef DELAYMARK
  38:     dmname = savestr(filexp(RNDELNAME));
  39: #else
  40:     ;
  41: #endif
  42: }
  43: 
  44: /* checkpoint the .newsrc */
  45: 
  46: void
  47: checkpoint_rc()
  48: {
  49: #ifdef DEBUGGING
  50:     if (debug & DEB_CHECKPOINTING) {
  51:     fputs("(ckpt)",stdout);
  52:     fflush(stdout);
  53:     }
  54: #endif
  55:     if (doing_ng)
  56:     restore_ng();           /* do not restore M articles */
  57:     if (rc_changed)
  58:     write_rc();
  59: #ifdef DEBUGGING
  60:     if (debug & DEB_CHECKPOINTING) {
  61:     fputs("(done)",stdout);
  62:     fflush(stdout);
  63:     }
  64: #endif
  65: }
  66: 
  67: /* reconstruct the .newsrc line in a human readable form */
  68: 
  69: void
  70: restore_ng()
  71: {
  72:     register char *s, *mybuf = buf;
  73:     register ART_NUM i;
  74:     ART_NUM count=0;
  75:     int safelen = LBUFLEN - 16;
  76: 
  77:     strcpy(buf,rcline[ng]);     /* start with the newsgroup name */
  78:     s = buf + rcnums[ng] - 1;       /* use s for buffer pointer */
  79:     *s++ = rcchar[ng];          /* put the requisite : or !*/
  80:     *s++ = ' ';             /* put the not-so-requisite space */
  81:     for (i=1; i<=lastart; i++) {    /* for each article in newsgroup */
  82:     if (s-mybuf > safelen) {    /* running out of room? */
  83:         safelen *= 2;
  84:         if (mybuf == buf) {     /* currently static? */
  85:         *s = '\0';
  86:         mybuf = safemalloc((MEM_SIZE)safelen + 16);
  87:         strcpy(mybuf,buf);  /* so we must copy it */
  88:         s = mybuf + (s-buf);
  89:                     /* fix the pointer, too */
  90:         }
  91:         else {          /* just grow in place, if possible */
  92:         char *newbuf;
  93: 
  94:         newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16);
  95:         s = newbuf + (s-mybuf);
  96:         mybuf = newbuf;
  97:         }
  98:     }
  99:     if (!was_read(i))       /* still unread? */
 100:         count++;            /* then count it */
 101:     else {              /* article was read */
 102:         ART_NUM oldi;
 103: 
 104:         sprintf(s,"%ld",(long)i);   /* put out the min of the range */
 105:         s += strlen(s);     /* keeping house */
 106:         oldi = i;           /* remember this spot */
 107:         do i++; while (i <= lastart && was_read(i));
 108:                     /* find 1st unread article or end */
 109:         i--;            /* backup to last read article */
 110:         if (i > oldi) {     /* range of more than 1? */
 111:         sprintf(s,"-%ld,",(long)i);
 112:                     /* then it out as a range */
 113:         s += strlen(s);     /* and housekeep */
 114:         }
 115:         else
 116:         *s++ = ',';     /* otherwise, just a comma will do */
 117:     }
 118:     }
 119:     if (*(s-1) == ',')          /* is there a final ','? */
 120:     s--;                /* take it back */
 121:     *s++ = '\0';            /* and terminate string */
 122: #ifdef DEBUGGING
 123:     if (debug & DEB_NEWSRC_LINE && !panic) {
 124:     printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) FLUSH;
 125:     printf("%s\n",mybuf) FLUSH;
 126:     }
 127: #endif
 128:     free(rcline[ng]);           /* return old rc line */
 129:     if (mybuf == buf) {
 130:     rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1);
 131:                     /* grab a new rc line */
 132:     strcpy(rcline[ng], buf);    /* and load it */
 133:     }
 134:     else {
 135:     mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1);
 136:                     /* be nice to the heap */
 137:     rcline[ng] = mybuf;
 138:     }
 139:     *(rcline[ng] + rcnums[ng] - 1) = '\0';
 140:     if (rcchar[ng] == NEGCHAR) {    /* did they unsubscribe? */
 141:     printf(unsubto,ngname) FLUSH;
 142:     toread[ng] = TR_UNSUB;      /* make line invisible */
 143:     }
 144:     else
 145:     /*NOSTRICT*/
 146:     toread[ng] = (ART_UNREAD)count;     /* remember how many unread there are */
 147: }
 148: 
 149: /* mark an article unread, keeping track of toread[] */
 150: 
 151: void
 152: onemore(artnum)
 153: ART_NUM artnum;
 154: {
 155: #ifdef DEBUGGING
 156:     if (debug && artnum < firstart) {
 157:     printf("onemore: %d < %d\n",artnum,firstart) FLUSH;
 158:     return;
 159:     }
 160: #endif
 161:     if (ctl_read(artnum)) {
 162:     ctl_clear(artnum);
 163:     ++toread[ng];
 164:     }
 165: }
 166: 
 167: /* mark an article read, keeping track of toread[] */
 168: 
 169: void
 170: oneless(artnum)
 171: ART_NUM artnum;
 172: {
 173: #ifdef DEBUGGING
 174:     if (debug && artnum < firstart) {
 175:     printf("oneless: %d < %d\n",artnum,firstart) FLUSH;
 176:     return;
 177:     }
 178: #endif
 179:     if (!ctl_read(artnum)) {
 180:     ctl_set(artnum);
 181:     if (toread[ng] > TR_NONE)
 182:         --toread[ng];
 183:     }
 184: }
 185: 
 186: /* mark an article as unread, making sure that firstart is properly handled */
 187: /* cross-references are left as read in the other newsgroups */
 188: 
 189: void
 190: unmark_as_read(artnum)
 191: ART_NUM artnum;
 192: {
 193:     check_first(artnum);
 194:     onemore(artnum);
 195: #ifdef MCHASE
 196:     if (!parse_maybe(artnum))
 197:     chase_xrefs(artnum,FALSE);
 198: #endif
 199: }
 200: 
 201: #ifdef DELAYMARK
 202: /* temporarily mark article as read.  When newsgroup is exited, articles */
 203: /* will be marked as unread.  Called via M command */
 204: 
 205: void
 206: delay_unmark(artnum)
 207: ART_NUM artnum;
 208: {
 209:     if (dmfp == Nullfp) {
 210:     dmfp = fopen(dmname,"w");
 211:     if (dmfp == Nullfp) {
 212:         printf(cantcreate,dmname) FLUSH;
 213:         sig_catcher(0);
 214:     }
 215:     }
 216:     oneless(artnum);            /* set the correct bit */
 217:     dmcount++;
 218:     fprintf(dmfp,"%ld\n",(long)artnum);
 219: }
 220: #endif
 221: 
 222: /* mark article as read.  If article is cross referenced to other */
 223: /* newsgroups, mark them read there also. */
 224: 
 225: void
 226: mark_as_read(artnum)
 227: ART_NUM artnum;
 228: {
 229:     oneless(artnum);            /* set the correct bit */
 230:     checkcount++;           /* get more worried about crashes */
 231:     chase_xrefs(artnum,TRUE);
 232: }
 233: 
 234: /* make sure we have bits set correctly down to firstart */
 235: 
 236: void
 237: check_first(min)
 238: ART_NUM min;
 239: {
 240:     register ART_NUM i = firstart;
 241: 
 242:     if (min < absfirst)
 243:     min = absfirst;
 244:     if (min < i) {
 245:     for (i--; i>=min; i--)
 246:         ctl_set(i);     /* mark as read */
 247:     firstart = min;
 248:     }
 249: }
 250: 
 251: /* bring back articles marked with M */
 252: 
 253: #ifdef DELAYMARK
 254: void
 255: yankback()
 256: {
 257:     register ART_NUM anum;
 258: 
 259:     if (dmfp) {         /* delayed unmarks pending? */
 260: #ifdef VERBOSE
 261:     printf("\nReturning %ld Marked article%s...\n",(long)dmcount,
 262:         dmcount == 1 ? nullstr : "s") FLUSH;
 263: #endif
 264:     fclose(dmfp);
 265:     if (dmfp = fopen(dmname,"r")) {
 266:         while (fgets(buf,sizeof buf,dmfp) != Nullch) {
 267:         anum = (ART_NUM)atol(buf);
 268:         /*NOSTRICT*/
 269:         onemore(anum);             /* then unmark them */
 270: #ifdef MCHASE
 271:         chase_xrefs(anum,FALSE);
 272: #endif
 273:         }
 274:         fclose(dmfp);
 275:         dmfp = Nullfp;
 276:         UNLINK(dmname);     /* and be tidy */
 277:     }
 278:     else {
 279:         printf(cantopen,dmname) FLUSH;
 280:         sig_catcher(0);
 281:     }
 282:     }
 283:     dmcount = 0;
 284: }
 285: #endif
 286: 
 287: /* run down xref list and mark as read or unread */
 288: 
 289: int
 290: chase_xrefs(artnum,markread)
 291: ART_NUM artnum;
 292: int markread;
 293: {
 294: #ifdef ASYNC_PARSE
 295:     if (parse_maybe(artnum))        /* make sure we have right header */
 296:     return -1;
 297: #endif
 298: #ifdef DBM
 299:     {
 300:     datum lhs, rhs;
 301:     datum fetch();
 302:     register char *idp;
 303:     char *ident_buf;
 304:     static FILE * hist_file = Nullfp;
 305: #else
 306:     if (
 307: #ifdef DEBUGGING
 308:     debug & DEB_FEED_XREF ||
 309: #endif
 310:     htype[XREF_LINE].ht_minpos >= 0) {
 311:                     /* are there article# xrefs? */
 312: #endif DBM
 313:     char *xref_buf, *curxref;
 314:     register char *xartnum;
 315:     char *rver_buf = Nullch;
 316:     static char *inews_site = Nullch;
 317:     register ART_NUM x;
 318:     char tmpbuf[128];
 319: 
 320: #ifdef DBM
 321:     rver_buf = fetchlines(artnum,NGS_LINE);
 322:                     /* get Newsgroups */
 323:     if (!index(rver_buf,','))   /* if no comma, no Xref! */
 324:         return 0;
 325:     if (hist_file == Nullfp) {  /* Init. file accesses */
 326: #ifdef DEBUGGING
 327:         if (debug)
 328:         printf ("chase_xref: opening files\n");
 329: #endif
 330:         dbminit(filexp(ARTFILE));
 331:         if ((hist_file = fopen (filexp(ARTFILE), "r")) == Nullfp)
 332:         return 0;
 333:     }
 334:     xref_buf = safemalloc((MEM_SIZE)BUFSIZ);
 335:     ident_buf = fetchlines(artnum,MESSID_LINE);
 336:                     /* get Message-ID */
 337: #ifdef DEBUGGING
 338:     if (debug)
 339:         printf ("chase_xref: Message-ID: %s\n", ident_buf);
 340: #endif
 341:     idp = ident_buf;
 342:     while (*++idp)          /* make message-id case insensitive */
 343:         if (isupper(*idp))
 344:             *idp = tolower (*idp);
 345:     lhs.dptr = ident_buf;       /* look up article by id */
 346:     lhs.dsize = strlen(lhs.dptr) + 1;
 347:     rhs = fetch(lhs);       /* fetch the record */
 348:     if (rhs.dptr == NULL)       /* if null, nothing there */
 349:         goto wild_goose;
 350:     fseek (hist_file, *((long *)rhs.dptr), 0);
 351:                     /* datum returned is position in hist file */
 352:     fgets (xref_buf, BUFSIZ, hist_file);
 353: #ifdef DEBUGGING
 354:     if (debug)
 355:         printf ("Xref from history: %s\n", xref_buf);
 356: #endif
 357:     curxref = cpytill(tmpbuf, xref_buf, '\t') + 1;
 358:     curxref = cpytill(tmpbuf, curxref, '\t') + 1;
 359: #ifdef DEBUGGING
 360:     if (debug)
 361:         printf ("chase_xref: curxref: %s\n", curxref);
 362: #endif
 363: #else !DBM
 364: #ifdef DEBUGGING
 365:     if (htype[XREF_LINE].ht_minpos >= 0)
 366: #endif
 367:         xref_buf = fetchlines(artnum,XREF_LINE);
 368:                     /* get xrefs list */
 369: #ifdef DEBUGGING
 370:     else {
 371:         xref_buf = safemalloc((MEM_SIZE)100);
 372:         printf("Give Xref: ") FLUSH;
 373:         gets(xref_buf);
 374:     }
 375: #endif
 376: #ifdef DEBUGGING
 377:     if (debug & DEB_XREF_MARKER)
 378:         printf("Xref: %s\n",xref_buf) FLUSH;
 379: #endif
 380:     curxref = cpytill(tmpbuf,xref_buf,' ') + 1;
 381: 
 382:     /* Make sure site name on Xref matches what inews thinks site is.
 383: 	 * Check first against last inews_site.  If it matches, fine.
 384: 	 * If not, fetch inews_site from current Relay-Version line and
 385: 	 * check again.  This is so that if the new administrator decides
 386: 	 * to change the system name as known to inews, rn will still do
 387: 	 * Xrefs correctly--each article need only match itself to be valid.
 388: 	 */
 389:     if (inews_site == Nullch || strNE(tmpbuf,inews_site)) {
 390:         char *t;
 391: 
 392:         if (inews_site != Nullch)
 393:         free(inews_site);
 394: #ifndef NORELAY
 395:         rver_buf = fetchlines(artnum,RVER_LINE);
 396:         if ((t = instr(rver_buf,"; site ")) == Nullch)
 397: #else NORELAY
 398: 
 399:         /* In version 2.10.3 of news or afterwards, the Relay-Version
 400: 	     * and Posting-Version header lines have been removed.  For
 401: 	     * the code below to work as intended, I have modified it to
 402: 	     * extract the first component of the Path header line.  This
 403: 	     * should give the same effect as did the old code with respect
 404: 	     * to the use of the Relay-Version site name.
 405: 	     */
 406:         rver_buf = fetchlines(artnum,PATH_LINE);
 407:         if ((t = instr(rver_buf,"!")) == Nullch)
 408: #endif NORELAY
 409:         inews_site = savestr(nullstr);
 410:         else {
 411:         char new_site[128];
 412: 
 413: #ifndef NORELAY
 414:         cpytill(new_site,t + 7,'.');
 415: #else NORELAY
 416:         cpytill(new_site,rver_buf,'!');
 417: #endif NORELAY
 418:         inews_site = savestr(new_site);
 419:         }
 420:         if (strNE(tmpbuf,inews_site)) {
 421: #ifdef DEBUGGING
 422:         if (debug)
 423:             printf("Xref not from %s--ignoring\n",inews_site) FLUSH;
 424: #endif
 425:         goto wild_goose;
 426:         }
 427:     }
 428: #endif DBM
 429:     while (*curxref) {
 430:                     /* for each newsgroup */
 431:         curxref = cpytill(tmpbuf,curxref,' ');
 432: #ifdef DBM
 433:         xartnum = index(tmpbuf,'/');
 434: #else
 435:         xartnum = index(tmpbuf,':');
 436: #endif DBM
 437:         if (!xartnum)       /* probably an old-style Xref */
 438:         break;
 439:         *xartnum++ = '\0';
 440:         if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */
 441:         x = atol(xartnum);
 442:         if (x)
 443:             if (markread) {
 444:             if (addartnum(x,tmpbuf))
 445:                 goto wild_goose;
 446:             }
 447: #ifdef MCHASE
 448:             else
 449:             subartnum(x,tmpbuf);
 450: #endif
 451:         }
 452:         while (*curxref && isspace(*curxref))
 453:         curxref++;
 454:     }
 455:       wild_goose:
 456:     free(xref_buf);
 457: #ifdef DBM
 458:     free(ident_buf);
 459: #endif DBM
 460:     if (rver_buf != Nullch)
 461:         free(rver_buf);
 462:     }
 463:     return 0;
 464: }
 465: 
 466: int
 467: initctl()
 468: {
 469:     char *mybuf = buf;          /* place to decode rc line */
 470:     register char *s, *c, *h;
 471:     register long i;
 472:     register ART_NUM unread;
 473: 
 474: #ifdef DELAYMARK
 475:     dmcount = 0;
 476: #endif
 477:     if ((lastart = getngsize(ng)) < 0)  /* this cannot happen (laugh here) */
 478:     return -1;
 479: 
 480:     absfirst = getabsfirst(ng,lastart); /* remember first existing article */
 481:     if (!absfirst)          /* no articles at all? */
 482:     absfirst = 1;           /* pretend there is one */
 483: #ifndef lint
 484:     ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20);
 485: #endif lint
 486:     ctlarea = safemalloc(ctlsize);  /* allocate control area */
 487: 
 488:     /* now modify ctlarea to reflect what has already been read */
 489: 
 490:     for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ;
 491:                     /* find numbers in rc line */
 492:     i = strlen(s);
 493: #ifndef lint
 494:     if (i >= LBUFLEN-2)         /* bigger than buf? */
 495:     mybuf = safemalloc((MEM_SIZE)(i+2));
 496: #endif lint
 497:     strcpy(mybuf,s);            /* make scratch copy of line */
 498:     mybuf[i++] = ',';           /* put extra comma on the end */
 499:     mybuf[i] = '\0';
 500:     s = mybuf;              /* initialize the for loop below */
 501:     if (strnEQ(s,"1-",2)) {     /* can we save some time here? */
 502:     firstart = atol(s+2)+1;     /* ignore first range thusly */
 503:     s=index(s,',') + 1;
 504:     }
 505:     else
 506:     firstart = 1;           /* all the bits are valid for now */
 507:     if (absfirst > firstart) {      /* do we know already? */
 508:     firstart = absfirst;        /* no point calling getngmin again */
 509:     }
 510:     else if (artopen(firstart) == Nullfp) {
 511:                     /* first unread article missing? */
 512:     i = getngmin(".",firstart); /* see if expire has been busy */
 513:     if (i) {            /* avoid a bunch of extra opens */
 514:         firstart = i;
 515:     }
 516:     }
 517: #ifdef PENDING
 518: #   ifdef CACHESUBJ
 519:     subj_to_get = firstart;
 520: #   endif
 521: #endif
 522:     unread = lastart - firstart + 1;    /* assume this range unread */
 523:     for (i=OFFSET(firstart)/BITSPERBYTE; i<ctlsize; i++)
 524:     ctlarea[i] = 0;         /* assume unread */
 525: #ifdef DEBUGGING
 526:     if (debug & DEB_CTLAREA_BITMAP) {
 527:     printf("\n%s\n",mybuf) FLUSH;
 528:     for (i=1; i <= lastart; i++)
 529:         if (! was_read(i))
 530:         printf("%ld ",(long)i) FLUSH;
 531:     }
 532: #endif
 533:     for ( ; (c = index(s,',')) != Nullch; s = ++c) {
 534:                     /* for each range */
 535:     ART_NUM min, max;
 536: 
 537:     *c = '\0';          /* do not let index see past comma */
 538:     if ((h = index(s,'-')) != Nullch) { /* is there a -? */
 539:         min = atol(s);
 540:         max = atol(h+1);
 541:         if (min < firstart)     /* make sure range is in range */
 542:         min = firstart;
 543:         if (max > lastart)
 544:         max = lastart;
 545:         if (min <= max)     /* non-null range? */
 546:         unread -= max - min + 1;/* adjust unread count */
 547:         for (i=min; i<=max; i++)    /* for all articles in range */
 548:         ctl_set(i);     /* mark them read */
 549:     }
 550:     else if ((i = atol(s)) >= firstart && i <= lastart) {
 551:                     /* is single number reasonable? */
 552:         ctl_set(i);         /* mark it read */
 553:         unread--;           /* decrement articles to read */
 554:     }
 555: #ifdef DEBUGGING
 556:     if (debug & DEB_CTLAREA_BITMAP) {
 557:         printf("\n%s\n",s) FLUSH;
 558:         for (i=1; i <= lastart; i++)
 559:         if (! was_read(i))
 560:             printf("%ld ",(long)i) FLUSH;
 561:     }
 562: #endif
 563:     }
 564: #ifdef DEBUGGING
 565:     if (debug & DEB_CTLAREA_BITMAP) {
 566:     fputs("\n(hit CR)",stdout) FLUSH;
 567:     gets(cmd_buf);
 568:     }
 569: #endif
 570:     if (mybuf != buf)
 571:     free(mybuf);
 572:     toread[ng] = unread;
 573:     return 0;
 574: }
 575: 
 576: void
 577: grow_ctl()
 578: {
 579:     ART_NUM newlast;
 580:     ART_NUM tmpfirst;
 581:     MEM_SIZE newsize;
 582:     register ART_NUM i;
 583: 
 584:     forcegrow = FALSE;
 585:     newlast = getngsize(ng);
 586:     if (newlast > lastart) {
 587:     ART_NUM tmpart = art;
 588: #ifndef lint
 589:     newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2);
 590: #else
 591:     newsize = Null(MEM_SIZE);
 592: #endif lint
 593:     if (newsize > ctlsize) {
 594:         newsize += 20;
 595:         ctlarea = saferealloc(ctlarea,newsize);
 596:         ctlsize = newsize;
 597:     }
 598:     toread[ng] += (ART_UNREAD)(newlast-lastart);
 599:     for (i=lastart+1; i<=newlast; i++)
 600:         ctl_clear(i);   /* these articles are unread */
 601: #ifdef CACHESUBJ
 602:     if (subj_list != Null(char**)) {
 603: #ifndef lint
 604:         subj_list = (char**)saferealloc((char*)subj_list,
 605:           (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) );
 606: #endif lint
 607:         for (i=lastart+1; i<=newlast; i++)
 608:         subj_list[OFFSET(i)] = Nullch;
 609:     }
 610: #endif
 611:     tmpfirst = lastart+1;
 612:     lastart = newlast;
 613: #ifdef KILLFILES
 614: #ifdef VERBOSE
 615:     IF(verbose)
 616:         sprintf(buf,
 617:         "%ld more article%s arrived--looking for more to kill...\n\n",
 618:         (long)(lastart - firstart + 1),
 619:         (lastart > firstart ? "s have" : " has" ) );
 620:     ELSE            /* my, my, how clever we are */
 621: #endif
 622: #ifdef TERSE
 623:         strcpy(buf, "More news--killing...\n\n");
 624: #endif
 625:     kill_unwanted(tmpfirst,buf,TRUE);
 626: #endif
 627:     art = tmpart;
 628:     }
 629: }

Defined functions

bits_init defined in line 34; used 2 times
chase_xrefs defined in line 289; used 4 times
checkpoint_rc defined in line 46; used 3 times
delay_unmark defined in line 205; used 3 times
grow_ctl defined in line 576; used 2 times
initctl defined in line 466; used 2 times
oneless defined in line 169; used 6 times
onemore defined in line 151; used 3 times
restore_ng defined in line 69; used 4 times
unmark_as_read defined in line 189; used 3 times
yankback defined in line 254; used 5 times

Defined variables

ctlsize defined in line 32; used 5 times
Last modified: 1986-03-29
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2430
Valid CSS Valid XHTML 1.0 Strict