home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume19 / cnews2 / part10 < prev    next >
Text File  |  1989-06-29  |  54KB  |  2,424 lines

  1. Subject:  v19i087:  Cnews production release, Part10/19
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: utzoo!henry
  7. Posting-number: Volume 19, Issue 87
  8. Archive-name: cnews2/part10
  9.  
  10. : ---CUT HERE---
  11. echo 'libc/getdate.y':
  12. sed 's/^X//' >'libc/getdate.y' <<'!'
  13. X%token ID MONTH DAY MERIDIAN NUMBER UNIT MUNIT SUNIT ZONE DAYZONE AGO
  14. X%{
  15. X    /*     Steven M. Bellovin (unc!smb)            */
  16. X    /*    Dept. of Computer Science            */
  17. X    /*    University of North Carolina at Chapel Hill    */
  18. X    /*    @(#)getdate.y    2.13    9/16/86 */
  19. X
  20. X#include <sys/types.h>
  21. X#include <sys/timeb.h>
  22. X#include <ctype.h>
  23. X#include <time.h>
  24. X
  25. X#define    NULL    0
  26. X
  27. X#define daysec (24L*60L*60L)
  28. X
  29. X    static int timeflag, zoneflag, dateflag, dayflag, relflag;
  30. X    static time_t relsec, relmonth;
  31. X    static int hh, mm, ss, merid, daylight;
  32. X    static int dayord, dayreq;
  33. X    static int month, day, year;
  34. X    static int ourzone;
  35. X
  36. X#define AM 1
  37. X#define PM 2
  38. X#define DAYLIGHT 1
  39. X#define STANDARD 2
  40. X#define MAYBE    3
  41. X%}
  42. X
  43. X%%
  44. Xtimedate:         /* empty */
  45. X    | timedate item;
  46. X
  47. Xitem:    tspec =
  48. X        {timeflag++;}
  49. X    | zone =
  50. X        {zoneflag++;}
  51. X    | dtspec =
  52. X        {dateflag++;}
  53. X    | dyspec =
  54. X        {dayflag++;}
  55. X    | rspec =
  56. X        {relflag++;}
  57. X    | nspec;
  58. X
  59. Xnspec:    NUMBER =
  60. X        {if (timeflag && dateflag && !relflag) year = $1;
  61. X        else {timeflag++;hh = $1/100;mm = $1%100;ss = 0;merid = 24;}};
  62. X
  63. Xtspec:    NUMBER MERIDIAN =
  64. X        {hh = $1; mm = 0; ss = 0; merid = $2;}
  65. X    | NUMBER ':' NUMBER =
  66. X        {hh = $1; mm = $3; merid = 24;}
  67. X    | NUMBER ':' NUMBER MERIDIAN =
  68. X        {hh = $1; mm = $3; merid = $4;}
  69. X    | NUMBER ':' NUMBER NUMBER =
  70. X        {hh = $1; mm = $3; merid = 24;
  71. X        daylight = STANDARD; ourzone = $4%100 + 60*$4/100;}
  72. X    | NUMBER ':' NUMBER ':' NUMBER =
  73. X        {hh = $1; mm = $3; ss = $5; merid = 24;}
  74. X    | NUMBER ':' NUMBER ':' NUMBER MERIDIAN =
  75. X        {hh = $1; mm = $3; ss = $5; merid = $6;}
  76. X    | NUMBER ':' NUMBER ':' NUMBER NUMBER =
  77. X        {hh = $1; mm = $3; ss = $5; merid = 24;
  78. X        daylight = STANDARD; ourzone = $6%100 + 60*$6/100;};
  79. X
  80. Xzone:    ZONE =
  81. X        {ourzone = $1; daylight = STANDARD;}
  82. X    | DAYZONE =
  83. X        {ourzone = $1; daylight = DAYLIGHT;};
  84. X
  85. Xdyspec:    DAY =
  86. X        {dayord = 1; dayreq = $1;}
  87. X    | DAY ',' =
  88. X        {dayord = 1; dayreq = $1;}
  89. X    | NUMBER DAY =
  90. X        {dayord = $1; dayreq = $2;};
  91. X
  92. Xdtspec:    NUMBER '/' NUMBER =
  93. X        {month = $1; day = $3;}
  94. X    | NUMBER '/' NUMBER '/' NUMBER =
  95. X        {month = $1; day = $3; year = $5;}
  96. X    | MONTH NUMBER =
  97. X        {month = $1; day = $2;}
  98. X    | MONTH NUMBER ',' NUMBER =
  99. X        {month = $1; day = $2; year = $4;}
  100. X    | NUMBER MONTH =
  101. X        {month = $2; day = $1;}
  102. X    | NUMBER MONTH NUMBER =
  103. X        {month = $2; day = $1; year = $3;};
  104. X
  105. X
  106. Xrspec:    NUMBER UNIT =
  107. X        {relsec +=  60L * $1 * $2;}
  108. X    | NUMBER MUNIT =
  109. X        {relmonth += $1 * $2;}
  110. X    | NUMBER SUNIT =
  111. X        {relsec += $1;}
  112. X    | UNIT =
  113. X        {relsec +=  60L * $1;}
  114. X    | MUNIT =
  115. X        {relmonth += $1;}
  116. X    | SUNIT =
  117. X        {relsec++;}
  118. X    | rspec AGO =
  119. X        {relsec = -relsec; relmonth = -relmonth;};
  120. X%%
  121. X
  122. Xstatic int mdays[12] =
  123. X    {31, 0, 31,  30, 31, 30,  31, 31, 30,  31, 30, 31};
  124. X#define epoch 1970
  125. X
  126. Xextern struct tm *localtime();
  127. Xtime_t dateconv(mm, dd, yy, h, m, s, mer, zone, dayflag)
  128. Xint mm, dd, yy, h, m, s, mer, zone, dayflag;
  129. X{
  130. X    time_t tod, jdate;
  131. X    register int i;
  132. X    time_t timeconv();
  133. X
  134. X    if (yy < 0) yy = -yy;
  135. X    if (yy < 100) yy += 1900;
  136. X    mdays[1] = 28 + (yy%4 == 0 && (yy%100 != 0 || yy%400 == 0));
  137. X    if (yy < epoch || yy > 1999 || mm < 1 || mm > 12 ||
  138. X        dd < 1 || dd > mdays[--mm]) return (-1);
  139. X    jdate = dd-1;
  140. X        for (i=0; i<mm; i++) jdate += mdays[i];
  141. X    for (i = epoch; i < yy; i++) jdate += 365 + (i%4 == 0);
  142. X    jdate *= daysec;
  143. X    jdate += zone * 60L;
  144. X    if ((tod = timeconv(h, m, s, mer)) < 0) return (-1);
  145. X    jdate += tod;
  146. X    if (dayflag==DAYLIGHT || (dayflag==MAYBE&&localtime(&jdate)->tm_isdst))
  147. X        jdate += -1*60*60;
  148. X    return (jdate);
  149. X}
  150. X
  151. Xtime_t dayconv(ord, day, now) int ord, day; time_t now;
  152. X{
  153. X    register struct tm *loctime;
  154. X    time_t tod;
  155. X    time_t daylcorr();
  156. X
  157. X    tod = now;
  158. X    loctime = localtime(&tod);
  159. X    tod += daysec * ((day - loctime->tm_wday + 7) % 7);
  160. X    tod += 7*daysec*(ord<=0?ord:ord-1);
  161. X    return daylcorr(tod, now);
  162. X}
  163. X
  164. Xtime_t timeconv(hh, mm, ss, mer) register int hh, mm, ss, mer;
  165. X{
  166. X    if (mm < 0 || mm > 59 || ss < 0 || ss > 59) return (-1);
  167. X    switch (mer) {
  168. X        case AM: if (hh < 1 || hh > 12) return(-1);
  169. X             return (60L * ((hh%12)*60L + mm)+ss);
  170. X        case PM: if (hh < 1 || hh > 12) return(-1);
  171. X             return (60L * ((hh%12 +12)*60L + mm)+ss);
  172. X        case 24: if (hh < 0 || hh > 23) return (-1);
  173. X             return (60L * (hh*60L + mm)+ss);
  174. X        default: return (-1);
  175. X    }
  176. X}
  177. Xtime_t monthadd(sdate, relmonth) time_t sdate, relmonth;
  178. X{
  179. X    struct tm *ltime;
  180. X    time_t dateconv();
  181. X    time_t daylcorr();
  182. X    int mm, yy;
  183. X
  184. X    if (relmonth == 0) return 0;
  185. X    ltime = localtime(&sdate);
  186. X    mm = 12*ltime->tm_year + ltime->tm_mon + relmonth;
  187. X    yy = mm/12;
  188. X    mm = mm%12 + 1;
  189. X    return daylcorr(dateconv(mm, ltime->tm_mday, yy, ltime->tm_hour,
  190. X        ltime->tm_min, ltime->tm_sec, 24, ourzone, MAYBE), sdate);
  191. X}
  192. X
  193. Xtime_t daylcorr(future, now) time_t future, now;
  194. X{
  195. X    int fdayl, nowdayl;
  196. X
  197. X    nowdayl = (localtime(&now)->tm_hour+1) % 24;
  198. X    fdayl = (localtime(&future)->tm_hour+1) % 24;
  199. X    return (future-now) + 60L*60L*(nowdayl-fdayl);
  200. X}
  201. X
  202. Xstatic char *lptr;
  203. X
  204. Xyylex()
  205. X{
  206. X    extern int yylval;
  207. X    int sign;
  208. X    register char c;
  209. X    register char *p;
  210. X    char idbuf[20];
  211. X    int pcnt;
  212. X
  213. X    for (;;) {
  214. X        while (isspace(*lptr)) lptr++;
  215. X
  216. X        if (isdigit(c = *lptr) || c == '-' || c == '+') {
  217. X            if (c== '-' || c == '+') {
  218. X                if (c=='-') sign = -1;
  219. X                else sign = 1;
  220. X                if (!isdigit(*++lptr)) {
  221. X                    /* yylval = sign; return (NUMBER); */
  222. X                    return yylex();    /* skip the '-' sign */
  223. X                }
  224. X            } else sign = 1;
  225. X            yylval = 0;
  226. X            while (isdigit(c = *lptr++)) yylval = 10*yylval + c - '0';
  227. X            yylval *= sign;
  228. X            lptr--;
  229. X            return (NUMBER);
  230. X
  231. X        } else if (isalpha(c)) {
  232. X            p = idbuf;
  233. X            while (isalpha(c = *lptr++) || c=='.')
  234. X                if (p < &idbuf[sizeof(idbuf)-1])
  235. X                    *p++ = c;
  236. X            *p = '\0';
  237. X            lptr--;
  238. X            return (lookup(idbuf));
  239. X        }
  240. X
  241. X        else if (c == '(') {
  242. X            pcnt = 0;
  243. X            do {
  244. X                c = *lptr++;
  245. X                if (c == '\0') return(c);
  246. X                else if (c == '(') pcnt++;
  247. X                else if (c == ')') pcnt--;
  248. X            } while (pcnt > 0);
  249. X        }
  250. X
  251. X        else return (*lptr++);
  252. X    }
  253. X}
  254. X
  255. Xstruct table {
  256. X    char *name;
  257. X    int type, value;
  258. X};
  259. X
  260. Xstruct table mdtab[] = {
  261. X    {"January", MONTH, 1},
  262. X    {"February", MONTH, 2},
  263. X    {"March", MONTH, 3},
  264. X    {"April", MONTH, 4},
  265. X    {"May", MONTH, 5},
  266. X    {"June", MONTH, 6},
  267. X    {"July", MONTH, 7},
  268. X    {"August", MONTH, 8},
  269. X    {"September", MONTH, 9},
  270. X    {"Sept", MONTH, 9},
  271. X    {"October", MONTH, 10},
  272. X    {"November", MONTH, 11},
  273. X    {"December", MONTH, 12},
  274. X
  275. X    {"Sunday", DAY, 0},
  276. X    {"Monday", DAY, 1},
  277. X    {"Tuesday", DAY, 2},
  278. X    {"Tues", DAY, 2},
  279. X    {"Wednesday", DAY, 3},
  280. X    {"Wednes", DAY, 3},
  281. X    {"Thursday", DAY, 4},
  282. X    {"Thur", DAY, 4},
  283. X    {"Thurs", DAY, 4},
  284. X    {"Friday", DAY, 5},
  285. X    {"Saturday", DAY, 6},
  286. X    {0, 0, 0}};
  287. X
  288. X#define HRS *60
  289. X#define HALFHR 30
  290. Xstruct table mztab[] = {
  291. X    {"a.m.", MERIDIAN, AM},
  292. X    {"am", MERIDIAN, AM},
  293. X    {"p.m.", MERIDIAN, PM},
  294. X    {"pm", MERIDIAN, PM},
  295. X    {"nst", ZONE, 3 HRS + HALFHR},        /* Newfoundland */
  296. X    {"n.s.t.", ZONE, 3 HRS + HALFHR},
  297. X    {"ast", ZONE, 4 HRS},        /* Atlantic */
  298. X    {"a.s.t.", ZONE, 4 HRS},
  299. X    {"adt", DAYZONE, 4 HRS},
  300. X    {"a.d.t.", DAYZONE, 4 HRS},
  301. X    {"est", ZONE, 5 HRS},        /* Eastern */
  302. X    {"e.s.t.", ZONE, 5 HRS},
  303. X    {"edt", DAYZONE, 5 HRS},
  304. X    {"e.d.t.", DAYZONE, 5 HRS},
  305. X    {"cst", ZONE, 6 HRS},        /* Central */
  306. X    {"c.s.t.", ZONE, 6 HRS},
  307. X    {"cdt", DAYZONE, 6 HRS},
  308. X    {"c.d.t.", DAYZONE, 6 HRS},
  309. X    {"mst", ZONE, 7 HRS},        /* Mountain */
  310. X    {"m.s.t.", ZONE, 7 HRS},
  311. X    {"mdt", DAYZONE, 7 HRS},
  312. X    {"m.d.t.", DAYZONE, 7 HRS},
  313. X    {"pst", ZONE, 8 HRS},        /* Pacific */
  314. X    {"p.s.t.", ZONE, 8 HRS},
  315. X    {"pdt", DAYZONE, 8 HRS},
  316. X    {"p.d.t.", DAYZONE, 8 HRS},
  317. X    {"yst", ZONE, 9 HRS},        /* Yukon */
  318. X    {"y.s.t.", ZONE, 9 HRS},
  319. X    {"ydt", DAYZONE, 9 HRS},
  320. X    {"y.d.t.", DAYZONE, 9 HRS},
  321. X    {"hst", ZONE, 10 HRS},        /* Hawaii */
  322. X    {"h.s.t.", ZONE, 10 HRS},
  323. X    {"hdt", DAYZONE, 10 HRS},
  324. X    {"h.d.t.", DAYZONE, 10 HRS},
  325. X
  326. X    {"gmt", ZONE, 0 HRS},
  327. X    {"g.m.t.", ZONE, 0 HRS},
  328. X    {"bst", DAYZONE, 0 HRS},        /* British Summer Time */
  329. X    {"b.s.t.", DAYZONE, 0 HRS},
  330. X    {"eet", ZONE, 0 HRS},        /* European Eastern Time */
  331. X    {"e.e.t.", ZONE, 0 HRS},
  332. X    {"eest", DAYZONE, 0 HRS},    /* European Eastern Summer Time */
  333. X    {"e.e.s.t.", DAYZONE, 0 HRS},
  334. X    {"met", ZONE, -1 HRS},        /* Middle European Time */
  335. X    {"m.e.t.", ZONE, -1 HRS},
  336. X    {"mest", DAYZONE, -1 HRS},    /* Middle European Summer Time */
  337. X    {"m.e.s.t.", DAYZONE, -1 HRS},
  338. X    {"wet", ZONE, -2 HRS },        /* Western European Time */
  339. X    {"w.e.t.", ZONE, -2 HRS },
  340. X    {"west", DAYZONE, -2 HRS},    /* Western European Summer Time */
  341. X    {"w.e.s.t.", DAYZONE, -2 HRS},
  342. X
  343. X    {"jst", ZONE, -9 HRS},        /* Japan Standard Time */
  344. X    {"j.s.t.", ZONE, -9 HRS},    /* Japan Standard Time */
  345. X                    /* No daylight savings time */
  346. X
  347. X    {"aest", ZONE, -10 HRS},    /* Australian Eastern Time */
  348. X    {"a.e.s.t.", ZONE, -10 HRS},
  349. X    {"aesst", DAYZONE, -10 HRS},    /* Australian Eastern Summer Time */
  350. X    {"a.e.s.s.t.", DAYZONE, -10 HRS},
  351. X    {"acst", ZONE, -(9 HRS + HALFHR)},    /* Australian Central Time */
  352. X    {"a.c.s.t.", ZONE, -(9 HRS + HALFHR)},
  353. X    {"acsst", DAYZONE, -(9 HRS + HALFHR)},    /* Australian Central Summer */
  354. X    {"a.c.s.s.t.", DAYZONE, -(9 HRS + HALFHR)},
  355. X    {"awst", ZONE, -8 HRS},        /* Australian Western Time */
  356. X    {"a.w.s.t.", ZONE, -8 HRS},    /* (no daylight time there, I'm told */
  357. X    {0, 0, 0}};
  358. X
  359. Xstruct table unittb[] = {
  360. X    {"year", MUNIT, 12},
  361. X    {"month", MUNIT, 1},
  362. X    {"fortnight", UNIT, 14*24*60},
  363. X    {"week", UNIT, 7*24*60},
  364. X    {"day", UNIT, 1*24*60},
  365. X    {"hour", UNIT, 60},
  366. X    {"minute", UNIT, 1},
  367. X    {"min", UNIT, 1},
  368. X    {"second", SUNIT, 1},
  369. X    {"sec", SUNIT, 1},
  370. X    {0, 0, 0}};
  371. X
  372. Xstruct table othertb[] = {
  373. X    {"tomorrow", UNIT, 1*24*60},
  374. X    {"yesterday", UNIT, -1*24*60},
  375. X    {"today", UNIT, 0},
  376. X    {"now", UNIT, 0},
  377. X    {"last", NUMBER, -1},
  378. X    {"this", UNIT, 0},
  379. X    {"next", NUMBER, 2},
  380. X    {"first", NUMBER, 1},
  381. X    /* {"second", NUMBER, 2}, */
  382. X    {"third", NUMBER, 3},
  383. X    {"fourth", NUMBER, 4},
  384. X    {"fifth", NUMBER, 5},
  385. X    {"sixth", NUMBER, 6},
  386. X    {"seventh", NUMBER, 7},
  387. X    {"eigth", NUMBER, 8},
  388. X    {"ninth", NUMBER, 9},
  389. X    {"tenth", NUMBER, 10},
  390. X    {"eleventh", NUMBER, 11},
  391. X    {"twelfth", NUMBER, 12},
  392. X    {"ago", AGO, 1},
  393. X    {0, 0, 0}};
  394. X
  395. Xstruct table milzone[] = {
  396. X    {"a", ZONE, 1 HRS},
  397. X    {"b", ZONE, 2 HRS},
  398. X    {"c", ZONE, 3 HRS},
  399. X    {"d", ZONE, 4 HRS},
  400. X    {"e", ZONE, 5 HRS},
  401. X    {"f", ZONE, 6 HRS},
  402. X    {"g", ZONE, 7 HRS},
  403. X    {"h", ZONE, 8 HRS},
  404. X    {"i", ZONE, 9 HRS},
  405. X    {"k", ZONE, 10 HRS},
  406. X    {"l", ZONE, 11 HRS},
  407. X    {"m", ZONE, 12 HRS},
  408. X    {"n", ZONE, -1 HRS},
  409. X    {"o", ZONE, -2 HRS},
  410. X    {"p", ZONE, -3 HRS},
  411. X    {"q", ZONE, -4 HRS},
  412. X    {"r", ZONE, -5 HRS},
  413. X    {"s", ZONE, -6 HRS},
  414. X    {"t", ZONE, -7 HRS},
  415. X    {"u", ZONE, -8 HRS},
  416. X    {"v", ZONE, -9 HRS},
  417. X    {"w", ZONE, -10 HRS},
  418. X    {"x", ZONE, -11 HRS},
  419. X    {"y", ZONE, -12 HRS},
  420. X    {"z", ZONE, 0 HRS},
  421. X    {0, 0, 0}};
  422. X
  423. Xlookup(id) char *id;
  424. X{
  425. X#define gotit (yylval=i->value,  i->type)
  426. X#define getid for(j=idvar, k=id; *j++ = *k++; )
  427. X
  428. X    char idvar[20];
  429. X    register char *j, *k;
  430. X    register struct table *i;
  431. X    int abbrev;
  432. X
  433. X    getid;
  434. X    if (strlen(idvar) == 3) abbrev = 1;
  435. X    else if (strlen(idvar) == 4 && idvar[3] == '.') {
  436. X        abbrev = 1;
  437. X        idvar[3] = '\0';
  438. X    }
  439. X    else abbrev = 0;
  440. X
  441. X    if (islower(*idvar)) *idvar = toupper(*idvar);
  442. X
  443. X    for (i = mdtab; i->name; i++) {
  444. X        k = idvar;
  445. X        for (j = i->name; *j++ == *k++;) {
  446. X            if (abbrev && j==i->name+3) return gotit;
  447. X            if (j[-1] == 0) return gotit;
  448. X        }
  449. X    }
  450. X
  451. X    getid;
  452. X    for (i = mztab; i->name; i++)
  453. X        if (strcmp(i->name, idvar) == 0) return gotit;
  454. X
  455. X    for (j = idvar; *j; j++)
  456. X        if (isupper(*j)) *j = tolower(*j);
  457. X    for (i=mztab; i->name; i++)
  458. X        if (strcmp(i->name, idvar) == 0) return gotit;
  459. X
  460. X    getid;
  461. X    for (i=unittb; i->name; i++)
  462. X        if (strcmp(i->name, idvar) == 0) return gotit;
  463. X
  464. X    if (idvar[strlen(idvar)-1] == 's')
  465. X        idvar[strlen(idvar)-1] = '\0';
  466. X    for (i=unittb; i->name; i++)
  467. X        if (strcmp(i->name, idvar) == 0) return gotit;
  468. X
  469. X    getid;
  470. X    for (i = othertb; i->name; i++)
  471. X        if (strcmp(i->name, idvar) == 0) return gotit;
  472. X
  473. X    getid;
  474. X    if (strlen(idvar) == 1 && isalpha(*idvar)) {
  475. X        if (isupper(*idvar)) *idvar = tolower(*idvar);
  476. X        for (i = milzone; i->name; i++)
  477. X            if (strcmp(i->name, idvar) == 0) return gotit;
  478. X    }
  479. X
  480. X    return(ID);
  481. X}
  482. X
  483. Xtime_t getdate(p, now) char *p; struct timeb *now;
  484. X{
  485. X#define mcheck(f)    if (f>1) err++
  486. X    time_t monthadd();
  487. X    int err;
  488. X    struct tm *lt;
  489. X    struct timeb ftz;
  490. X
  491. X    time_t sdate, tod;
  492. X
  493. X    lptr = p;
  494. X    if (now == ((struct timeb *) NULL)) {
  495. X        now = &ftz;
  496. X        ftime(&ftz);
  497. X    }
  498. X    lt = localtime(&now->time);
  499. X    year = lt->tm_year;
  500. X    month = lt->tm_mon+1;
  501. X    day = lt->tm_mday;
  502. X    relsec = 0; relmonth = 0;
  503. X    timeflag=zoneflag=dateflag=dayflag=relflag=0;
  504. X    ourzone = now->timezone;
  505. X    daylight = MAYBE;
  506. X    hh = mm = ss = 0;
  507. X    merid = 24;
  508. X
  509. X    if (err = yyparse()) return (-1);
  510. X
  511. X    mcheck(timeflag);
  512. X    mcheck(zoneflag);
  513. X    mcheck(dateflag);
  514. X    mcheck(dayflag);
  515. X
  516. X    if (err) return (-1);
  517. X    if (dateflag || timeflag || dayflag) {
  518. X        sdate = dateconv(month,day,year,hh,mm,ss,merid,ourzone,daylight);
  519. X        if (sdate < 0) return -1;
  520. X    }
  521. X    else {
  522. X        sdate = now->time;
  523. X        if (relflag == 0)
  524. X            sdate -= (lt->tm_sec + lt->tm_min*60 +
  525. X                lt->tm_hour*(60L*60L));
  526. X    }
  527. X
  528. X    sdate += relsec;
  529. X    sdate += monthadd(sdate, relmonth);
  530. X
  531. X    if (dayflag && !dateflag) {
  532. X        tod = dayconv(dayord, dayreq, sdate);
  533. X        sdate += tod;
  534. X    }
  535. X
  536. X    return sdate;
  537. X}
  538. X
  539. Xyyerror(s) char *s;
  540. X{}
  541. !
  542. echo 'libc/nfclose.c':
  543. sed 's/^X//' >'libc/nfclose.c' <<'!'
  544. X/*
  545. X * nfclose(stream) - flush the stream, fsync its file descriptor and
  546. X * fclose the stream, checking for errors at all stages.  This dance
  547. X * is needed to work around the lack of Unix file system semantics
  548. X * in Sun's NFS.  Returns EOF on error.
  549. X */
  550. X
  551. X#include <stdio.h>
  552. X
  553. Xint
  554. Xnfclose(stream)
  555. Xregister FILE *stream;
  556. X{
  557. X    register int ret = 0;
  558. X
  559. X    if (fflush(stream) == EOF)
  560. X        ret = EOF;
  561. X    if (fsync(fileno(stream)) < 0)        /* may get delayed error here */
  562. X        ret = EOF;
  563. X    if (fclose(stream) == EOF)
  564. X        ret = EOF;
  565. X    return ret;
  566. X}
  567. !
  568. echo 'libcnews/config.c':
  569. sed 's/^X//' >'libcnews/config.c' <<'!'
  570. X/*
  571. X * news configuration inquiry
  572. X */
  573. X
  574. X#include <stdio.h>
  575. X#include <sys/types.h>
  576. X#include "libc.h"
  577. X#include "news.h"
  578. X#include "config.h"
  579. X
  580. X#ifndef NULL
  581. X#define    NULL    0
  582. X#endif
  583. X
  584. X#ifndef NEWSCTL
  585. X/* =()<#define    NEWSCTL    "@<NEWSCTL>@">()= */
  586. X#define    NEWSCTL    "/usr/lib/news"
  587. X#endif
  588. X#ifndef NEWSPATH
  589. X/* =()<#define    NEWSPATH    "@<NEWSPATH>@">()= */
  590. X#define    NEWSPATH    "/bin:/usr/bin"
  591. X#endif
  592. X#ifndef NEWSARTS
  593. X/* =()<#define    NEWSARTS    "@<NEWSARTS>@">()= */
  594. X#define    NEWSARTS    "/usr/spool/news"
  595. X#endif
  596. X#ifndef NEWSBIN
  597. X/* =()<#define    NEWSBIN    "@<NEWSBIN>@">()= */
  598. X#define    NEWSBIN    "/usr/lib/newsbin"
  599. X#endif
  600. X#ifndef NEWSUMASK
  601. X/* =()<#define    NEWSUMASK    @<NEWSUMASK>@>()= */
  602. X#define    NEWSUMASK    002
  603. X#endif
  604. X#ifndef NEWSMASTER
  605. X/* =()<#define    NEWSMASTER    "@<NEWSMASTER>@">()= */
  606. X#define    NEWSMASTER    "usenet"
  607. X#endif
  608. X
  609. Xstatic char *pwd = NULL;    /* Current directory, NULL means unknown. */
  610. Xstatic int dirsset = NO;    /* Have the following been set up? */
  611. Xstatic char *arts = NEWSARTS;
  612. Xstatic char *bin = NEWSBIN;
  613. Xstatic char *ctl = NEWSCTL;
  614. Xstatic char *path = NEWSPATH;
  615. Xstatic int numask = NEWSUMASK;
  616. Xstatic char *nmaster = NEWSMASTER;
  617. X#define    DIRS()    if (!dirsset) setdirs()
  618. X
  619. Xextern char *strcpy();
  620. Xextern char *strcat();
  621. Xextern char *getenv();
  622. X
  623. X/*
  624. X - setdirs - set up stuff from environment, for use by other functions
  625. X *
  626. X * Invokes user-supplied function unprivileged() if non-standard values used.
  627. X */
  628. Xstatic void
  629. Xsetdirs()
  630. X{
  631. X    register char *p;
  632. X    register int nonstd = NO;
  633. X    register int mask;
  634. X    register char *scan;
  635. X
  636. X    if (dirsset)
  637. X        return;
  638. X
  639. X    p = getenv("NEWSARTS");
  640. X    if (p != NULL && !STREQ(p, arts)) {
  641. X        nonstd = YES;
  642. X        arts = p;
  643. X    }
  644. X
  645. X    p = getenv("NEWSCTL");
  646. X    if (p != NULL && !STREQ(p, ctl)) {
  647. X        ctl = p;
  648. X        nonstd = YES;
  649. X    }
  650. X
  651. X    p = getenv("NEWSPATH");
  652. X    if (p != NULL && !STREQ(p, path)) {
  653. X        path = p;
  654. X        nonstd = YES;
  655. X    }
  656. X
  657. X    p = getenv("NEWSBIN");
  658. X    if (p != NULL && !STREQ(p, bin)) {
  659. X        bin = p;
  660. X        nonstd = YES;
  661. X    }
  662. X
  663. X    p = getenv("NEWSUMASK");
  664. X    if (p != NULL) {
  665. X        mask = 0;
  666. X        for (scan = p; *scan != '\0'; scan++)
  667. X            if ('0' <= *scan && *scan <= '7' && mask <= 077)
  668. X                mask = (mask << 3) | (*scan - '0');
  669. X            else {    /* Garbage, ignore it. */
  670. X                mask = numask;
  671. X                break;            /* NOTE BREAK OUT */
  672. X            }
  673. X        if (mask != numask) {
  674. X            numask = mask;
  675. X            nonstd = YES;
  676. X        }
  677. X    }
  678. X
  679. X    p = getenv("NEWSMASTER");
  680. X    if (p != NULL && !STREQ(p, nmaster)) {
  681. X        nmaster = p;
  682. X        nonstd = YES;
  683. X    }
  684. X
  685. X    dirsset = YES;
  686. X    if (nonstd)
  687. X        unprivileged();
  688. X}
  689. X
  690. X/*
  691. X - artfile - best pathname for a file in NEWSARTS
  692. X */
  693. Xchar *
  694. Xartfile(base)
  695. Xchar *base;
  696. X{
  697. X    static char *artf = NULL;
  698. X
  699. X    DIRS();
  700. X
  701. X    if (base == NULL)    /* he just wants the directory */
  702. X        return (arts);
  703. X
  704. X    if (artf != NULL)
  705. X        free(artf);    /* toss old returned value */
  706. X    if (pwd != NULL && STREQ(pwd, arts))
  707. X        artf = strsave(base);
  708. X    else
  709. X        artf = str3save(arts, SFNDELIM, base);
  710. X
  711. X    return (artf);
  712. X}
  713. X
  714. X/*
  715. X - fullartfile - full pathname for a file in NEWSARTS
  716. X */
  717. Xchar *
  718. Xfullartfile(base)
  719. Xchar *base;
  720. X{
  721. X    register char *p;
  722. X    register char *pwdsave;
  723. X
  724. X    pwdsave = pwd;
  725. X    pwd = NULL;        /* fool artfile() into giving full path */
  726. X    p = artfile(base);
  727. X    pwd = pwdsave;
  728. X    return (p);
  729. X}
  730. X
  731. X/*
  732. X - ctlfile - full pathname for a file in NEWSCTL
  733. X */
  734. Xchar *
  735. Xctlfile(base)
  736. Xchar *base;
  737. X{
  738. X    static char *ctlf = NULL;
  739. X
  740. X    DIRS();
  741. X
  742. X    if (ctlf != NULL)
  743. X        free(ctlf);        /* toss old returned value */
  744. X
  745. X    if (base == NULL) {
  746. X        ctlf = NULL;
  747. X        return(ctl);
  748. X    } else {
  749. X        ctlf = str3save(ctl, SFNDELIM, base);
  750. X        return(ctlf);
  751. X    }
  752. X}
  753. X
  754. X/*
  755. X - binfile - full pathname for a file in NEWSBIN
  756. X */
  757. Xchar *
  758. Xbinfile(base)
  759. Xchar *base;
  760. X{
  761. X    static char *binf = NULL;
  762. X
  763. X    DIRS();
  764. X
  765. X    if (binf != NULL)
  766. X        free(binf);        /* toss old returned value */
  767. X
  768. X    if (base == NULL) {
  769. X        binf = NULL;
  770. X        return(bin);
  771. X    } else {
  772. X        binf = str3save(bin, SFNDELIM, base);
  773. X        return (binf);
  774. X    }
  775. X}
  776. X
  777. X/*
  778. X - cd - change to a directory, with checking
  779. X */
  780. Xvoid
  781. Xcd(dir)
  782. Xchar *dir;
  783. X{
  784. X    if (pwd != NULL)
  785. X        free(pwd);
  786. X    if (chdir(dir) < 0)
  787. X        errunlock("cannot chdir(%s)", dir);
  788. X    pwd = strsave(dir);
  789. X}
  790. X
  791. X/*
  792. X - newspath - search path for normal system commands
  793. X */
  794. Xchar *
  795. Xnewspath()
  796. X{
  797. X    DIRS();
  798. X    return(path);
  799. X}
  800. X
  801. X/*
  802. X - newsumask - suitable value of umask for news stuff
  803. X */
  804. Xint
  805. Xnewsumask()
  806. X{
  807. X    DIRS();
  808. X    return(numask);
  809. X}
  810. X
  811. X/*
  812. X - newsmaster - mail address to complain to
  813. X */
  814. Xchar *
  815. Xnewsmaster()
  816. X{
  817. X    DIRS();
  818. X    return(nmaster);
  819. X}
  820. !
  821. echo 'libcnews/gethdr.c':
  822. sed 's/^X//' >'libcnews/gethdr.c' <<'!'
  823. X/*
  824. X * gethdr - read an entire RFC 822 header "line", including continuations
  825. X */
  826. X
  827. X#include <stdio.h>
  828. X#include <ctype.h>
  829. X#include <fgetmfs.h>
  830. X#include <sys/types.h>
  831. X#include "news.h"
  832. X#include "libc.h"
  833. X
  834. X/*
  835. X * Read the first line; if it's a header, repeatedly read lines until a
  836. X * non-continuation line is found.  For each continuation line, grow
  837. X * hdr to accomodate it and append it to hdr.
  838. X * *limitp is updated by subtracting the number of bytes read.
  839. X * 
  840. X */
  841. Xchar *                        /* malloced; caller must not free */
  842. Xgethdr(in, limitp, ishdrp)
  843. XFILE *in;
  844. Xregister long *limitp;
  845. Xint *ishdrp;
  846. X{
  847. X    register int c, hdrlen, contlen, limitset = *limitp >= 0;
  848. X    register char *contin = NULL;
  849. X    static char *hdr = NULL;
  850. X
  851. X    nnfree(&hdr);
  852. X    *ishdrp = NO;
  853. X    hdr = fgetmfs(in, (int)*limitp, CONT_NO);
  854. X    if (hdr == NULL)
  855. X        return hdr;
  856. X    hdrlen = strlen(hdr);
  857. X    *limitp -= hdrlen;
  858. X    *ishdrp = ishdr(hdr);
  859. X    if (!*ishdrp)
  860. X        return hdr;
  861. X    while (hdr != NULL && (!limitset || *limitp > 1) && (c = getc(in)) != EOF) {
  862. X        (void) ungetc(c, in);
  863. X
  864. X        if (!iswhite(c))
  865. X            break;
  866. X        contin = fgetmfs(in, (int)*limitp, CONT_NO);
  867. X        if (contin == NULL)
  868. X            break;
  869. X
  870. X        contlen = strlen(contin);
  871. X        *limitp -= contlen;
  872. X        hdr = realloc(hdr, (unsigned)(hdrlen + contlen + 1)); /* 1 for NUL */
  873. X        if (hdr != NULL) {
  874. X            (void) strcpy(hdr + hdrlen, contin);
  875. X            hdrlen += contlen;
  876. X        }
  877. X        free(contin);
  878. X        contin = NULL;
  879. X    }
  880. X    return hdr;
  881. X}
  882. X
  883. X
  884. X/*
  885. X * Is s an RFC 822 header line?
  886. X * If a colon is seen before whitespace, it is.
  887. X */
  888. Xint
  889. Xishdr(s)
  890. Xregister char *s;
  891. X{
  892. X    register char *cp = s;
  893. X    register int c;
  894. X
  895. X    while ((c = *cp) != '\0' && !(isascii(c) && isspace(c)) && c != ':')
  896. X        ++cp;
  897. X    return c == ':' && cp > s;
  898. X}
  899. !
  900. echo 'libcnews/strlower.c':
  901. sed 's/^X//' >'libcnews/strlower.c' <<'!'
  902. X/*
  903. X * make a string all lower-case.
  904. X */
  905. X
  906. X#include <ctype.h>
  907. X
  908. Xstrlower(s)
  909. Xregister char *s;
  910. X{
  911. X    for (; *s != '\0'; ++s)
  912. X        if (isascii(*s) && isupper(*s))
  913. X            *s = tolower(*s);
  914. X}
  915. !
  916. echo 'libcnews/complain.c':
  917. sed 's/^X//' >'libcnews/complain.c' <<'!'
  918. X#include <stdio.h>
  919. X
  920. X/*
  921. X - complain - lodge a complaint
  922. X */
  923. Xvoid
  924. Xcomplain(s1, s2)
  925. Xchar *s1;
  926. Xchar *s2;
  927. X{
  928. X    extern char *progname;
  929. X
  930. X    (void) fprintf(stderr, "%s: ", progname);
  931. X    (void) fprintf(stderr, s1, s2);
  932. X    (void) putc('\n', stderr);
  933. X}
  934. !
  935. echo 'libcnews/ngmatch.c':
  936. sed 's/^X//' >'libcnews/ngmatch.c' <<'!'
  937. X/*
  938. X * ngmatch - newsgroup name matching
  939. X *
  940. X * ngmatch returns true iff the newsgroup(s) in ngs match
  941. X * the pattern(s) in ngpat, where
  942. X *
  943. X *     ngpats: { ngpat { "," ngpat }* }?
  944. X *    ngpat: "!"? word { "." word }*
  945. X *    word: { alphanum }+ | "all"
  946. X *
  947. X * Only one group need match for success.  (Redundant?)
  948. X *
  949. X * For each group, note the depth of each match against the patterns,
  950. X * negated or not.  Ignore mismatches.  The deepest match wins at the end;
  951. X * if it's a tie, negated matches are rejections.
  952. X *
  953. X * A match of any group against the patterns is a success.
  954. X * Failure to match any pattern with a group is a mismatch of that group.
  955. X * Failure to match any group against any pattern is a total failure.
  956. X *
  957. X * "all" in a pattern is a wildcard that matches exactly one word;
  958. X * it does not cross "." (NGDELIM) delimiters.
  959. X */
  960. X
  961. X#include <stdio.h>
  962. X#include <string.h>
  963. X#include <sys/types.h>
  964. X#include "news.h"
  965. X
  966. X#define truth(bool) ((bool)? "yes": "no")
  967. X
  968. X#ifndef STATIC
  969. X#define STATIC /* static */
  970. X#endif
  971. X
  972. X#define ALL "all"            /* word wildcard */
  973. X
  974. X/* private */
  975. Xstatic boolean debug = NO;
  976. X
  977. X/* forwards */
  978. Xextern boolean mpatsmatch();
  979. X
  980. Xvoid
  981. Xmatchdebug(state)
  982. Xboolean state;
  983. X{
  984. X    debug = state;
  985. X}
  986. X
  987. Xboolean
  988. Xngmatch(ngpat, ngs)
  989. Xchar *ngpat, *ngs;
  990. X{
  991. X    register char *ngp;            /* point at current group */
  992. X    register char *ngcomma;
  993. X    register char *rngpat = ngpat;
  994. X
  995. X    if (debug)
  996. X        (void) fprintf(stderr, "ngmatch(`%s', `%s')\n", rngpat, ngs);
  997. X    for (ngp = ngs; ngp != NULL; ngp = ngcomma) {
  998. X        register boolean match;
  999. X
  1000. X        INDEX(ngp, NGSEP, ngcomma);
  1001. X        if (ngcomma != NULL)
  1002. X            *ngcomma = '\0';    /* will be restored below */
  1003. X        match = mpatsmatch(rngpat, ngp); /* try 1 group, n-patterns */
  1004. X        if (ngcomma != NULL)
  1005. X            *ngcomma++ = NGSEP;    /* point after the comma */
  1006. X        if (match)
  1007. X            return YES;
  1008. X    }
  1009. X    return NO;            /* no pattern matched any group */
  1010. X}
  1011. X
  1012. X/*
  1013. X * Match one group against multiple patterns, as above.
  1014. X * The key is to keep track of how deeply plain and negated patterns matched.
  1015. X */
  1016. XSTATIC boolean
  1017. Xmpatsmatch(ngpat, grp)
  1018. Xchar *ngpat, *grp;
  1019. X{
  1020. X    register char *patp;        /* point at current pattern */
  1021. X    register char *patcomma;
  1022. X    register int depth;
  1023. X    register int faildeepest = 0, hitdeepest = 0;    /* in case no match */
  1024. X    register boolean negation;
  1025. X
  1026. X    if (debug)
  1027. X        (void) fprintf(stderr, "mpatsmatch(`%s', `%s')\n", ngpat, grp);
  1028. X    for (patp = ngpat; patp != NULL; patp = patcomma) {
  1029. X        negation = NO;
  1030. X        INDEX(patp, NGSEP, patcomma);
  1031. X        if (patcomma != NULL)
  1032. X            *patcomma = '\0';    /* will be restored below */
  1033. X        if (*patp == NGNEG) {
  1034. X            ++patp;
  1035. X            negation = YES;
  1036. X        }
  1037. X        depth = onepatmatch(patp, grp);    /* try 1 pattern, 1 group */
  1038. X        if (patcomma != NULL)
  1039. X            *patcomma++ = NGSEP;    /* point after the comma */
  1040. X        if (depth == 0)            /* mis-match */
  1041. X            ;            /* ignore it */
  1042. X        else if (negation) {
  1043. X            /* record ordinal # of deepest negated matched word */
  1044. X            if (depth > faildeepest)
  1045. X                faildeepest = depth;
  1046. X        } else {
  1047. X            /* record ordinal # of deepest plain matched word */
  1048. X            if (depth > hitdeepest)
  1049. X                hitdeepest = depth;
  1050. X        }
  1051. X    }
  1052. X    if (debug)
  1053. X        (void) fprintf(stderr, "mpatsmatch(`%s', `%s') returns %s\n",
  1054. X            ngpat, grp, truth(hitdeepest > faildeepest));
  1055. X    return hitdeepest > faildeepest;
  1056. X}
  1057. X
  1058. X/*
  1059. X * Match a pattern against a group by looking at each word of pattern in turn.
  1060. X *
  1061. X * On a match, return the ordinal number of the rightmost word that matches.
  1062. X * If group runs out first, the match fails; else it succeeds.
  1063. X * On a failure, return zero.
  1064. X */
  1065. XSTATIC int
  1066. Xonepatmatch(patp, grp)
  1067. Xchar *patp, *grp;
  1068. X{
  1069. X    register char *rpatwd;        /* used by word match (inner loop) */
  1070. X    register char *patdot, *grdot;    /* point at dots after words */
  1071. X    register char *patwd, *grwd;    /* point at current words */
  1072. X    register int depth = 0;
  1073. X
  1074. X    for (patwd = patp, grwd = grp; patwd != NULL && grwd != NULL;
  1075. X        patwd = patdot, grwd = grdot, depth++) {
  1076. X        register boolean match;
  1077. X
  1078. X            /* null-terminate words */
  1079. X            INDEX(patwd, NGDELIM, patdot);
  1080. X        if (patdot != NULL)
  1081. X            *patdot = '\0';        /* will be restored below */
  1082. X            INDEX(grwd, NGDELIM, grdot);
  1083. X        if (grdot != NULL)
  1084. X            *grdot = '\0';        /* will be restored below */
  1085. X
  1086. X        /*
  1087. X         * Match one word of pattern with one word of group.
  1088. X         * A pattern word of "all" matches any group word.
  1089. X         */
  1090. X#ifdef FAST_STRCMP
  1091. X        match = STREQ(patwd, grwd) || STREQ(patwd, ALL);
  1092. X#else
  1093. X        match = NO;
  1094. X        for (rpatwd = patwd; *rpatwd == *grwd++; )
  1095. X            if (*rpatwd++ == '\0') {
  1096. X                match = YES;        /* literal match */
  1097. X                break;
  1098. X            }
  1099. X        if (!match) {
  1100. X            /* ugly special case match for "all" */
  1101. X            rpatwd = patwd;
  1102. X            match = *rpatwd++ == 'a' && *rpatwd++ == 'l' &&
  1103. X                    *rpatwd++ == 'l' && *rpatwd   == '\0';
  1104. X        }
  1105. X#endif                /* FAST_STRCMP */
  1106. X
  1107. X        if (patdot != NULL)
  1108. X            *patdot++ = NGDELIM;    /* point after the dot */
  1109. X        if (grdot != NULL)
  1110. X            *grdot++ = NGDELIM;
  1111. X        if (!match) {
  1112. X            depth = 0;        /* words differed - mismatch */
  1113. X            break;
  1114. X        }
  1115. X    }
  1116. X    /* if group name ran out before pattern, then match fails */
  1117. X    if (grwd == NULL && patwd != NULL)
  1118. X        depth = 0;
  1119. X    if (debug)
  1120. X        (void) fprintf(stderr, "onepatmatch(`%s', `%s') returns %d\n",
  1121. X            patp, grp, depth);
  1122. X    return depth;
  1123. X}
  1124. X
  1125. X#ifdef CROSS_POSTINGS_RESTRICTED
  1126. X/*
  1127. X * ngtopsame(ngs) - true iff ngs are all in the same top-level distribution
  1128. X */
  1129. Xboolean
  1130. Xngtopsame(ngs)
  1131. Xregister char *ngs;
  1132. X{
  1133. X    register char *nextng;
  1134. X
  1135. X    INDEX(ngs, NGSEP, nextng);
  1136. X    if (nextng == NULL)        /* no groups left */
  1137. X        return YES;
  1138. X    ++nextng;            /* skip NGSEP */
  1139. X    return firstsame(ngs, nextng) && ngtopsame(nextng);
  1140. X}
  1141. X
  1142. X/*
  1143. X * firstsame(ng1, ng2) - true iff first characters (up to the first
  1144. X * NGDELIM or NGSEP) are the same in each string.  Neither string
  1145. X * is guaranteed to be null-terminated (a small lie; one *is*).
  1146. X */
  1147. XSTATIC boolean
  1148. Xfirstsame(ng1, ng2)
  1149. Xregister char *ng1, *ng2;
  1150. X{
  1151. X    register int ng1brk;
  1152. X    static char delimstr[] = { NGSEP, NGDELIM, '\0' };
  1153. X    extern int strcspn();
  1154. X
  1155. X    ng1brk = strcspn(ng1, delimstr);
  1156. X    return ng1brk == strcspn(ng2, delimstr) && STREQN(ng1, ng2, ng1brk);
  1157. X}
  1158. X#endif                /* CROSS_POSTINGS_RESTRICTED */
  1159. !
  1160. echo 'libcnews/time.c':
  1161. sed 's/^X//' >'libcnews/time.c' <<'!'
  1162. X/*
  1163. X * time utilities
  1164. X */
  1165. X
  1166. X#include <stdio.h>
  1167. X#include <sys/types.h>
  1168. X#include <sys/timeb.h>
  1169. X#include "libc.h"
  1170. X#include "news.h"
  1171. X
  1172. X/*
  1173. X * Write a timestamp of the form "Jun 12 12:34:56.789" on fp.
  1174. X * N.B.: no trailing newline is written.
  1175. X */
  1176. Xvoid
  1177. Xtimestamp(fp, timep)
  1178. XFILE *fp;
  1179. Xtime_t *timep;    /* if non-null, return time() here for later use */
  1180. X{
  1181. X    struct timeb ftnow;
  1182. X    char ms[4];                /* room for "123" and a NUL */
  1183. X
  1184. X    ftime(&ftnow);
  1185. X    if (timep != NULL)
  1186. X        *timep = ftnow.time;
  1187. X    /* .15 excludes yyyy\n\0; + 4 omits day-of-week */
  1188. X    (void) fprintf(fp, "%.15s.", ctime(&ftnow.time) + 4);
  1189. X    (void) ltoza(ms, (long)ftnow.millitm, 3);    /* 3 digits of output */
  1190. X    (void) fputs(ms, fp);
  1191. X}
  1192. !
  1193. echo 'libcnews/nemalloc.c':
  1194. sed 's/^X//' >'libcnews/nemalloc.c' <<'!'
  1195. X#include <stdio.h>
  1196. X#include <sys/types.h>
  1197. X#include "libc.h"
  1198. X
  1199. Xchar *
  1200. Xnemalloc(size)            /* news emalloc - calls errunlock on error */
  1201. Xunsigned size;
  1202. X{
  1203. X    register char *result = malloc(size);
  1204. X
  1205. X    if (result == NULL)
  1206. X        errunlock("out of memory", "");
  1207. X    return result;
  1208. X}
  1209. !
  1210. echo 'libcnews/ltoza.c':
  1211. sed 's/^X//' >'libcnews/ltoza.c' <<'!'
  1212. X/*
  1213. X * ltoza, ltozan - long to zero-padded ascii conversions
  1214. X *
  1215. X * These functions exist only because there is no portable way
  1216. X * to do this with printf and there may be no way do it at all
  1217. X * with printf on V7, due to a bug in V7's printf.
  1218. X */
  1219. X#include <stdlib.h>
  1220. X
  1221. X#define RADIX 10
  1222. X
  1223. X/*
  1224. X * convert value to at most width characters in outstr, padding with
  1225. X * zeros on the left (after any sign); do not terminate with a NUL.
  1226. X * returns true iff the value fits in width characters.
  1227. X */
  1228. Xint                    /* boolean */
  1229. Xltozan(outstr, value, width)
  1230. Xchar *outstr;
  1231. Xlong value;
  1232. Xint width;
  1233. X{
  1234. X    register char *op = outstr;
  1235. X    register long wval = value;
  1236. X    register int wwid = width;
  1237. X
  1238. X    if (wval < 0 && wwid > 0) {
  1239. X        *op++ = '-';
  1240. X        --wwid;
  1241. X        wval = -wval;        /* fails on smallest int; tough */
  1242. X    }
  1243. X    op += wwid - 1;            /* find right end */
  1244. X    while (wwid-- > 0) {        /* generate "wwid" digits */
  1245. X        register ldiv_t result;
  1246. X
  1247. X        result = ldiv(wval, (long)RADIX);    /* shades of V6! */
  1248. X        wval = result.quot;
  1249. X        *op-- = result.rem + '0';
  1250. X    }
  1251. X    return wval == 0;
  1252. X}
  1253. X
  1254. X/*
  1255. X * convert value to at most width characters in outstr, padding with
  1256. X * zeros on the left (after any sign); terminate with a NUL.
  1257. X */
  1258. Xint                    /* boolean */
  1259. Xltoza(outstr, value, width)
  1260. Xregister char *outstr;            /* char outstr[width+1]; */
  1261. Xlong value;
  1262. Xregister int width;
  1263. X{
  1264. X    register int fits = ltozan(outstr, value, width);
  1265. X
  1266. X    outstr[width] = '\0';
  1267. X    return fits;
  1268. X}
  1269. !
  1270. echo 'libcnews/str3save.c':
  1271. sed 's/^X//' >'libcnews/str3save.c' <<'!'
  1272. X#include <stdio.h>
  1273. X#include <sys/types.h>
  1274. X#include "libc.h"
  1275. X#include "news.h"
  1276. X
  1277. X/*
  1278. X - str3save - malloc space for 3 strings, concatenated, and concatenate them
  1279. X * This may seem kind of ad-hoc, but it's just right for filename work.
  1280. X */
  1281. Xchar *
  1282. Xstr3save(s1, s2, s3)
  1283. Xchar *s1;
  1284. Xchar *s2;
  1285. Xchar *s3;
  1286. X{
  1287. X    register char *p;
  1288. X    static char *empty = "";
  1289. X
  1290. X    if (s1 == NULL)
  1291. X        s1 = empty;
  1292. X    if (s2 == NULL)
  1293. X        s2 = empty;
  1294. X    if (s3 == NULL)
  1295. X        s3 = empty;
  1296. X
  1297. X    p = nemalloc((unsigned)(strlen(s1) + strlen(s2) + strlen(s3) + 1));
  1298. X    (void) strcpy(p, s1);
  1299. X    (void) strcat(p, s2);
  1300. X    (void) strcat(p, s3);
  1301. X    return(p);
  1302. X}
  1303. !
  1304. echo 'libcnews/lock.c':
  1305. sed 's/^X//' >'libcnews/lock.c' <<'!'
  1306. X/*
  1307. X * C news system locking.
  1308. X * It's compatible with B 2.10.1 news, except that locks are never
  1309. X * declared stale (blow 'em away in /etc/rc).
  1310. X * Only permit relaynews to run on a file server to make this sane.
  1311. X */
  1312. X
  1313. X#include <stdio.h>
  1314. X#include <errno.h>
  1315. X#include <sys/types.h>
  1316. X#include "libc.h"
  1317. X#include "news.h"
  1318. X#include "config.h"
  1319. X
  1320. X#define LOCKNAME "LOCK"
  1321. X#define LOCKTEMP "LOCKTMXXXXXX"
  1322. X#define INTERVAL 25        /* seconds to sleep on a busy lock */
  1323. X
  1324. Xstatic boolean debug = NO;
  1325. Xstatic boolean mylock = NO;
  1326. X
  1327. Xvoid
  1328. Xlockdebug(state)
  1329. Xboolean state;
  1330. X{
  1331. X    debug = state;
  1332. X}
  1333. X
  1334. X/*
  1335. X * lock the news system.
  1336. X * create a temporary name in $NEWSCTL for linking, store my pid in it.
  1337. X * repeatedly try to link the temporary name to LOCKNAME.
  1338. X */
  1339. Xvoid
  1340. Xnewslock()
  1341. X{
  1342. X    register char *tempnm, *lockfile;
  1343. X    register FILE *tempfp;
  1344. X    int locktries = 0;
  1345. X
  1346. X    tempnm = strsave(ctlfile(LOCKTEMP));
  1347. X    (void) mktemp(tempnm);
  1348. X    tempfp = fopen(tempnm, "w");
  1349. X    if (tempfp == NULL)
  1350. X        error("can't create lock temporary `%s'", tempnm);
  1351. X    (void) fprintf(tempfp, "%d\n", getpid());
  1352. X    (void) fclose(tempfp);
  1353. X
  1354. X    lockfile = strsave(ctlfile(LOCKNAME));
  1355. X    while (link(tempnm, lockfile) < 0) {
  1356. X        if (errno != EEXIST)
  1357. X            error("can't link `%s' to LOCK", tempnm);
  1358. X        /*
  1359. X         * Could decide here if the lock is stale.
  1360. X         * If so, remove it and try again to lock.
  1361. X         */
  1362. X        if (debug && ++locktries == 1)
  1363. X            (void) printf("%s: sleeping on LOCK\n", progname);
  1364. X        sleep(INTERVAL);
  1365. X    }
  1366. X    free(lockfile);
  1367. X    (void) unlink(tempnm);
  1368. X    free(tempnm);
  1369. X    mylock = YES;
  1370. X}
  1371. X
  1372. Xvoid
  1373. Xnewsunlock()
  1374. X{
  1375. X    if (mylock) {
  1376. X        (void) unlink(ctlfile(LOCKNAME));
  1377. X        mylock = NO;
  1378. X    }
  1379. X}
  1380. X
  1381. Xvoid
  1382. Xerrunlock(fmt, s)        /* like error(3), but unlock before exit */
  1383. Xchar *fmt, *s;
  1384. X{
  1385. X    warning(fmt, s);
  1386. X    newsunlock();
  1387. X    exit(1);
  1388. X    /* NOTREACHED */
  1389. X}
  1390. !
  1391. echo 'libcnews/fopenclex.c':
  1392. sed 's/^X//' >'libcnews/fopenclex.c' <<'!'
  1393. X/*
  1394. X * fopen and set close-on-exec (to avoid leaking descriptors into children)
  1395. X */
  1396. X
  1397. X#include <stdio.h>
  1398. X#include <sys/types.h>
  1399. X#include "news.h"
  1400. X
  1401. XFILE *
  1402. Xfopenwclex(name, mode)    /* open name; close-on-exec if OK, else warning */
  1403. Xchar *name, *mode;
  1404. X{
  1405. X    register FILE *fp;
  1406. X
  1407. X    if ((fp = fopenclex(name, mode)) == NULL)
  1408. X        warning("can't open `%s'", name);
  1409. X    return fp;
  1410. X}
  1411. X
  1412. XFILE *
  1413. Xfopenclex(file, mode)        /* open file and if OK, close-on-exec */
  1414. Xchar *file, *mode;
  1415. X{
  1416. X    register FILE *fp;
  1417. X
  1418. X    if ((fp = fopen(file, mode)) != NULL)
  1419. X        fclsexec(fp);
  1420. X    return fp;
  1421. X}
  1422. !
  1423. echo 'libcnews/string.c':
  1424. sed 's/^X//' >'libcnews/string.c' <<'!'
  1425. X/*
  1426. X * string operations
  1427. X */
  1428. X
  1429. X#include <stdio.h>
  1430. X#include <ctype.h>
  1431. X#include <sys/types.h>
  1432. X#include "libc.h"
  1433. X#include "news.h"
  1434. X
  1435. X/* forwards */
  1436. Xchar *findhost();
  1437. X
  1438. X/*
  1439. X * Return strsave() of the first word in "tokens".
  1440. X * Words are delimited by spaces.
  1441. X */
  1442. Xchar *
  1443. Xfirst(tokens)
  1444. Xchar *tokens;
  1445. X{
  1446. X    return strsvto(tokens, ' ');
  1447. X}
  1448. X
  1449. X/*
  1450. X * Turn a newsgroup name into a file name, in place.
  1451. X */
  1452. Xvoid
  1453. Xmkfilenm(ng)
  1454. Xregister char *ng;
  1455. X{
  1456. X    for (; *ng != '\0'; ng++)
  1457. X        if (*ng == NGDELIM)
  1458. X            *ng = FNDELIM;
  1459. X}
  1460. X
  1461. Xvoid
  1462. Xtrim(s)                    /* trim trailing newline */
  1463. Xchar *s;
  1464. X{
  1465. X    register char *nl;
  1466. X
  1467. X    INDEX(s, '\n', nl);
  1468. X    if (nl != NULL)
  1469. X        *nl = '\0';
  1470. X}
  1471. X
  1472. Xchar *
  1473. Xskipsp(s)                /* skip any whitespace at *s */
  1474. Xregister char *s;
  1475. X{
  1476. X    while (iswhite(*s))
  1477. X        s++;
  1478. X    return s;
  1479. X}
  1480. X
  1481. Xchar *
  1482. Xstrsvto(s, c)                /* save s up to (but excluding) c */
  1483. Xchar *s;
  1484. Xint c;
  1485. X{
  1486. X    register char *endp, *copy;
  1487. X
  1488. X    endp = index(s, c);        /* find interesting part's end of s */
  1489. X    if (endp != NULL)
  1490. X        *endp = '\0';        /* restored below */
  1491. X    copy = strsave(s);        /* copy interesting substring of s */
  1492. X        if (endp != NULL)
  1493. X            *endp = c;
  1494. X    return copy;
  1495. X}
  1496. X
  1497. Xint
  1498. Xcharcount(s, c)            /* how many c's in s? */
  1499. Xregister char *s;
  1500. Xregister int c;
  1501. X{
  1502. X    register int count = 0;
  1503. X
  1504. X#ifdef CLASSY
  1505. X    for (; (s = index(s, c)) != NULL; s = (s == NULL? NULL: s+1))
  1506. X        ++count;
  1507. X#else
  1508. X    while (*s != '\0')
  1509. X        if (*s++ == c)
  1510. X            ++count;
  1511. X#endif                /* CLASSY */
  1512. X    return count;
  1513. X}
  1514. X
  1515. Xchar *
  1516. Xnullify(s)                /* return s or "" if NULL */
  1517. Xregister char *s;
  1518. X{
  1519. X    if (s == NULL)
  1520. X        return "";
  1521. X    else
  1522. X        return s;
  1523. X}
  1524. X
  1525. X/*
  1526. X * If c is NUL, hostchar will be false, so don't test (optimisation: ==).
  1527. X */
  1528. X#define nothostchar(c, ch) (!hostchar(c, ch) /* || (c) == '\0' */ )
  1529. X/*
  1530. X * True if c can be part of a hostname. RFC 850 allows letters, digits, periods,
  1531. X * and hyphens and specifically disallows blanks. False may mean c is NUL.
  1532. X */
  1533. X#define hostchar(c, ch) ((ch) = (c), \
  1534. X    (isascii(ch) && isalnum(ch) || (ch) == '.' || (ch) == '-'))
  1535. X
  1536. X/*
  1537. X * Return true iff any host in hosts appears in s, as per hostin().
  1538. X * hosts are separated by non-hostname characters.
  1539. X */
  1540. Xboolean
  1541. Xanyhostin(hosts, s)
  1542. Xchar *hosts, *s;
  1543. X{
  1544. X    register char *host = hosts;
  1545. X
  1546. X    while (*host != '\0') {
  1547. X        register char *delimp;
  1548. X        register int ch;
  1549. X        register int delim;
  1550. X        register boolean hostisin;
  1551. X
  1552. X        while (nothostchar(*host, ch) && *host != '\0')
  1553. X            ++host;            /* skip leading delims */
  1554. X        if (*host == '\0')        /* no more hosts */
  1555. X            break;
  1556. X        for (delimp = host; hostchar(*delimp, ch); delimp++)
  1557. X            ;            /* skip to next delim */
  1558. X        delim = *delimp;        /* may be NUL */
  1559. X        *delimp = '\0';            /* terminate host */
  1560. X        hostisin = hostin(host, s);
  1561. X        *delimp = delim;        /* restore hosts delimiter */
  1562. X        if (hostisin)
  1563. X            return YES;
  1564. X        host = delimp;            /* advance to next host */
  1565. X    }
  1566. X    return NO;
  1567. X}
  1568. X
  1569. X/*
  1570. X * Return true iff host appears in s, with no characters from the alphabet
  1571. X * of legal hostname characters immediately adjacent.
  1572. X */
  1573. Xboolean
  1574. Xhostin(host, s)
  1575. Xregister char *host, *s;
  1576. X{
  1577. X    return findhost(host, s) != NULL;
  1578. X}
  1579. X
  1580. X/*
  1581. X * Return the number of machines appearing in path,
  1582. X * by counting transitions from delimiters.
  1583. X * See hostin() for the rules, and the macros.
  1584. X */
  1585. Xint
  1586. Xhopcount(path)
  1587. Xregister char *path;
  1588. X{
  1589. X    register int count = 0;
  1590. X    register int ch;
  1591. X
  1592. X    for (; *path != '\0'; path++)
  1593. X        if (nothostchar(path[0], ch) &&
  1594. X            (hostchar(path[1], ch) || path[1] == '\0'))
  1595. X            ++count;    /* trailing edge of delimiters */
  1596. X    return count;
  1597. X}
  1598. X
  1599. Xchar *
  1600. Xsendersite(path)
  1601. Xregister char *path;
  1602. X{
  1603. X    register char *p;
  1604. X    register int ch;
  1605. X    static char *sender = NULL;
  1606. X
  1607. X    nnfree(&sender);        /* free the last answer */
  1608. X    for (p = path; hostchar(*p, ch); p++)
  1609. X        ;
  1610. X    if (*p == '\0')            /* only a user name */
  1611. X        return hostname();    /* a local posting */
  1612. X    else {
  1613. X        register int delim = *p;
  1614. X
  1615. X        *p = '\0';
  1616. X        sender = strsave(path);    /* copy the first machine name */
  1617. X        *p = delim;
  1618. X        return sender;
  1619. X    }
  1620. X}
  1621. X
  1622. X/*
  1623. X * Canonicalise rawpath: NULL -> "", chop last site (actually user name) but not
  1624. X * its leading delimiter, and if Approved:, chop everything after the site,
  1625. X * and its trailing delimiter, from Approved: (or Sender:) (user@host).
  1626. X * Result is malloced memory.
  1627. X */
  1628. Xchar *
  1629. Xcanonpath(rawpath, approved, sender)
  1630. Xchar *rawpath, *approved, *sender;
  1631. X{
  1632. X    register char *newpath = strsave(nullify(rawpath));
  1633. X    register char *p, *lastdelim = newpath, *site = NULL;
  1634. X    register int ch;
  1635. X
  1636. X    for (p = newpath; *p != '\0'; ++p)
  1637. X        if (nothostchar(*p, ch))
  1638. X            lastdelim = p + 1;    /* just past delim */
  1639. X    if (lastdelim != NULL)
  1640. X        *lastdelim = '\0';        /* omit user's name */
  1641. X
  1642. X    if (approved != NULL) {            /* moderated article */
  1643. X        site = index(approved, '@');
  1644. X        if (site == NULL)
  1645. X            site = index(nullify(sender), '@');
  1646. X    }
  1647. X    if (site != NULL) {
  1648. X        p = findhost(site+1, newpath);
  1649. X        if (p != NULL && *p++ != '\0')    /* delim after site? */
  1650. X            *p = '\0';        /* terminate newpath after site */
  1651. X    }
  1652. X    return newpath;
  1653. X}
  1654. X
  1655. X/*
  1656. X * Return pointer to the first byte after host in path, if any,
  1657. X * with no characters from the alphabet of legal hostname characters
  1658. X * immediately adjacent.
  1659. X * This function is a profiling hot spot, so it has been optimised.
  1660. X */
  1661. Xchar *
  1662. Xfindhost(host, path)
  1663. Xregister char *host, *path;
  1664. X{
  1665. X    register int hostlen = strlen(host), ch;
  1666. X
  1667. X    /* Special case: match host!path or host. */
  1668. X    if (STREQN(path, host, hostlen) && nothostchar(path[hostlen], ch))
  1669. X        return &path[hostlen];
  1670. X
  1671. X    /* Match path2!host!path or path2!host. */
  1672. X    while (*path != '\0')
  1673. X        if (hostchar(path[0], ch))    /* can't start after here */
  1674. X            ++path;
  1675. X        else if ((++path, STREQN(path, host, hostlen)) &&
  1676. X               nothostchar(path[hostlen], ch))
  1677. X            return &path[hostlen];
  1678. X    return NULL;
  1679. X}
  1680. !
  1681. echo 'libcnews/hostname.c':
  1682. sed 's/^X//' >'libcnews/hostname.c' <<'!'
  1683. X/*
  1684. X * hostname - return the Usenet name of this machine
  1685. X *
  1686. X * One interesting possibility would be to assume that the first
  1687. X * name in the sys file is our Usenet name, unless it is "ME",
  1688. X * which would require our current strategy anyway.
  1689. X */
  1690. X
  1691. X#include <stdio.h>
  1692. X#include <sys/types.h>
  1693. X
  1694. X#include "libc.h"
  1695. X#include "news.h"
  1696. X#include "config.h"
  1697. X
  1698. X/* 2BSD funniness */
  1699. X#ifdef BSD2_10
  1700. X# include <short_names.h>
  1701. X#endif
  1702. X
  1703. X#ifndef NAMEFILE
  1704. X#define NAMEFILE ctlfile("whoami")
  1705. X#endif
  1706. X
  1707. Xchar *
  1708. Xhostname()            /* return this Usenet machine's name */
  1709. X{
  1710. X    static char name[MAXHOST];
  1711. X
  1712. X    if (name[0] == '\0') {    /* try to get the "news hostname" */
  1713. X        register FILE *fp;
  1714. X
  1715. X        fp = fopenclex(NAMEFILE, "r");
  1716. X        if (fp != NULL) {
  1717. X            (void) fgets(name, sizeof name, fp);
  1718. X            (void) nfclose(fp);
  1719. X            if (name[0] != '\0' && name[strlen(name) - 1] == '\n')
  1720. X                name[strlen(name) - 1] = '\0';
  1721. X        }
  1722. X    }
  1723. X    if (name[0] == '\0')    /* else use the ordinary hostname */
  1724. X        (void) gethostname(name, sizeof name);
  1725. X    return name;
  1726. X}
  1727. !
  1728. echo 'libcnews/strsave.c':
  1729. sed 's/^X//' >'libcnews/strsave.c' <<'!'
  1730. X/*
  1731. X * strsave - like strdup, but error if can't allocate
  1732. X */
  1733. X
  1734. X#include <stdio.h>
  1735. X#include <sys/types.h>
  1736. X#include "libc.h"
  1737. X#include "news.h"
  1738. X
  1739. X/*
  1740. X * Copy "s" into malloced memory, if any is available.
  1741. X * If not, unlock the news system, print a message and exit,
  1742. X * else return the address of the malloced memory.
  1743. X */
  1744. Xchar *
  1745. Xstrsave(s)
  1746. Xregister char *s;
  1747. X{
  1748. X    register char *news =nemalloc((unsigned)strlen(s)+1); /* include NUL */
  1749. X
  1750. X    (void) strcpy(news, s);
  1751. X    return news;
  1752. X}
  1753. !
  1754. echo 'libcnews/Makefile':
  1755. sed 's/^X//' >'libcnews/Makefile' <<'!'
  1756. X# libcnews makefile
  1757. XINCLUDE=../include
  1758. XDEFINES=-I$(INCLUDE)
  1759. XCOPTS= -O # -pg -p
  1760. XCFLAGS= $(COPTS) $(DEFINES)
  1761. XLINTFLAGS=-hau $(DEFINES)
  1762. XLIB=libcnews.a
  1763. X# RANLIB is ranlib on non-USG systems, echo on USG systems
  1764. XRANLIB=ranlib
  1765. X#RANLIB=:
  1766. XSRCS=complain.c config.c fopenclex.c hostname.c \
  1767. X lock.c ltoza.c ngmatch.c readline.c \
  1768. X string.c strlower.c strsave.c str3save.c time.c
  1769. XOBJS = complain.o config.o fopenclex.o gethdr.o hostname.o lock.o ltoza.o \
  1770. X    nemalloc.o ngmatch.o str3save.o string.o strlower.o strsave.o time.o
  1771. X# workaround for System V make bug
  1772. XSHELL = /bin/sh
  1773. X
  1774. Xu:    $(OBJS)
  1775. X    ar ruv ../libcnews.a $(OBJS)
  1776. X
  1777. Xall:    $(OBJS)
  1778. X
  1779. X$(LIB): $(SRCS)
  1780. X    $(CC) $(CFLAGS) -c $?
  1781. X    ar rv $@ *.o
  1782. X    rm *.o
  1783. X    $(RANLIB) $@
  1784. X
  1785. Xlint:
  1786. X    lint $(LINTFLAGS) $(SRCS)
  1787. X
  1788. Xclean:
  1789. X    rm -f *.o
  1790. X
  1791. X# header dependencies for libcnews.a
  1792. Xconfig.o: $(INCLUDE)/news.h $(INCLUDE)/config.h
  1793. Xlock.o: $(INCLUDE)/news.h
  1794. Xngmatch.o: $(INCLUDE)/news.h
  1795. Xtime.o: $(INCLUDE)/news.h
  1796. X# ltoza.o: $(INCLUDE)/stdlib.h    # trouble if stdlib.h is system one, not ours
  1797. !
  1798. echo 'libfake/getopt.3':
  1799. sed 's/^X//' >'libfake/getopt.3' <<'!'
  1800. X.TH GETOPT 3 local
  1801. X.DA 25 March 1982
  1802. X.SH NAME
  1803. Xgetopt \- get option letter from argv
  1804. X.SH SYNOPSIS
  1805. X.ft B
  1806. Xint getopt(argc, argv, optstring)
  1807. X.br
  1808. Xint argc;
  1809. X.br
  1810. Xchar **argv;
  1811. X.br
  1812. Xchar *optstring;
  1813. X.sp
  1814. Xextern char *optarg;
  1815. X.br
  1816. Xextern int optind;
  1817. X.ft
  1818. X.SH DESCRIPTION
  1819. X.I Getopt
  1820. Xreturns the next option letter in
  1821. X.I argv
  1822. Xthat matches a letter in
  1823. X.IR optstring .
  1824. X.I Optstring
  1825. Xis a string of recognized option letters;
  1826. Xif a letter is followed by a colon, the option is expected to have
  1827. Xan argument that may or may not be separated from it by white space.
  1828. X.I Optarg
  1829. Xis set to point to the start of the option argument on return from
  1830. X.IR getopt .
  1831. X.PP
  1832. X.I Getopt
  1833. Xplaces in
  1834. X.I optind
  1835. Xthe
  1836. X.I argv
  1837. Xindex of the next argument to be processed.
  1838. XBecause
  1839. X.I optind
  1840. Xis external, it is normally initialized to zero automatically
  1841. Xbefore the first call to 
  1842. X.IR getopt .
  1843. X.PP
  1844. XWhen all options have been processed (i.e., up to the first
  1845. Xnon-option argument),
  1846. X.I getopt
  1847. Xreturns
  1848. X.BR EOF .
  1849. XThe special option
  1850. X.B \-\-
  1851. Xmay be used to delimit the end of the options;
  1852. X.B EOF
  1853. Xwill be returned, and
  1854. X.B \-\-
  1855. Xwill be skipped.
  1856. X.SH SEE ALSO
  1857. Xgetopt(1)
  1858. X.SH DIAGNOSTICS
  1859. X.I Getopt
  1860. Xprints an error message on
  1861. X.I stderr
  1862. Xand returns a question mark
  1863. X.RB ( ? )
  1864. Xwhen it encounters an option letter not included in
  1865. X.IR optstring .
  1866. X.SH EXAMPLE
  1867. XThe following code fragment shows how one might process the arguments
  1868. Xfor a command that can take the mutually exclusive options
  1869. X.B a
  1870. Xand
  1871. X.BR b ,
  1872. Xand the options
  1873. X.B f
  1874. Xand
  1875. X.BR o ,
  1876. Xboth of which require arguments:
  1877. X.PP
  1878. X.RS
  1879. X.nf
  1880. Xmain(argc, argv)
  1881. Xint argc;
  1882. Xchar **argv;
  1883. X{
  1884. X    int c;
  1885. X    extern int optind;
  1886. X    extern char *optarg;
  1887. X    \&.
  1888. X    \&.
  1889. X    \&.
  1890. X    while ((c = getopt(argc, argv, "abf:o:")) != EOF)
  1891. X        switch (c) {
  1892. X        case 'a':
  1893. X            if (bflg)
  1894. X                errflg++;
  1895. X            else
  1896. X                aflg++;
  1897. X            break;
  1898. X        case 'b':
  1899. X            if (aflg)
  1900. X                errflg++;
  1901. X            else
  1902. X                bproc();
  1903. X            break;
  1904. X        case 'f':
  1905. X            ifile = optarg;
  1906. X            break;
  1907. X        case 'o':
  1908. X            ofile = optarg;
  1909. X            break;
  1910. X        case '?':
  1911. X        default:
  1912. X            errflg++;
  1913. X            break;
  1914. X        }
  1915. X    if (errflg) {
  1916. X        fprintf(stderr, "Usage: ...");
  1917. X        exit(2);
  1918. X    }
  1919. X    for (; optind < argc; optind++) {
  1920. X        \&.
  1921. X        \&.
  1922. X        \&.
  1923. X    }
  1924. X    \&.
  1925. X    \&.
  1926. X    \&.
  1927. X}
  1928. X.RE
  1929. X.PP
  1930. XA template similar to this can be found in
  1931. X.IR /usr/pub/template.c .
  1932. X.SH HISTORY
  1933. XWritten by Henry Spencer, working from a Bell Labs manual page.
  1934. XBehavior believed identical to the Bell version.
  1935. X.SH BUGS
  1936. XIt is not obvious how
  1937. X`\-'
  1938. Xstanding alone should be treated;  this version treats it as
  1939. Xa non-option argument, which is not always right.
  1940. X.PP
  1941. XOption arguments are allowed to begin with `\-';
  1942. Xthis is reasonable but reduces the amount of error checking possible.
  1943. X.PP
  1944. X.I Getopt
  1945. Xis quite flexible but the obvious price must be paid:  there is much
  1946. Xit could do that it doesn't, like
  1947. Xchecking mutually exclusive options, checking type of
  1948. Xoption arguments, etc.
  1949. !
  1950. echo 'libfake/getopt.c':
  1951. sed 's/^X//' >'libfake/getopt.c' <<'!'
  1952. X/*
  1953. X * getopt - get option letter from argv
  1954. X */
  1955. X
  1956. X#include <stdio.h>
  1957. X
  1958. Xchar    *optarg;    /* Global argument pointer. */
  1959. Xint    optind = 0;    /* Global argv index. */
  1960. X
  1961. Xstatic char    *scan = NULL;    /* Private scan pointer. */
  1962. X
  1963. Xextern char    *index();
  1964. X
  1965. Xint
  1966. Xgetopt(argc, argv, optstring)
  1967. Xint argc;
  1968. Xchar *argv[];
  1969. Xchar *optstring;
  1970. X{
  1971. X    register char c;
  1972. X    register char *place;
  1973. X
  1974. X    optarg = NULL;
  1975. X
  1976. X    if (scan == NULL || *scan == '\0') {
  1977. X        if (optind == 0)
  1978. X            optind++;
  1979. X    
  1980. X        if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
  1981. X            return(EOF);
  1982. X        if (strcmp(argv[optind], "--")==0) {
  1983. X            optind++;
  1984. X            return(EOF);
  1985. X        }
  1986. X    
  1987. X        scan = argv[optind]+1;
  1988. X        optind++;
  1989. X    }
  1990. X
  1991. X    c = *scan++;
  1992. X    place = index(optstring, c);
  1993. X
  1994. X    if (place == NULL || c == ':') {
  1995. X        fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
  1996. X        return('?');
  1997. X    }
  1998. X
  1999. X    place++;
  2000. X    if (*place == ':') {
  2001. X        if (*scan != '\0') {
  2002. X            optarg = scan;
  2003. X            scan = NULL;
  2004. X        } else if (optind < argc) {
  2005. X            optarg = argv[optind];
  2006. X            optind++;
  2007. X        } else {
  2008. X            fprintf(stderr, "%s: -%c argument missing\n", argv[0], c);
  2009. X            return('?');
  2010. X        }
  2011. X    }
  2012. X
  2013. X    return(c);
  2014. X}
  2015. !
  2016. echo 'libfake/ldiv.c':
  2017. sed 's/^X//' >'libfake/ldiv.c' <<'!'
  2018. X/*
  2019. X * ANSI's ldiv(num, denom) - yields (num/denom, num%denom)
  2020. X */
  2021. X
  2022. X#include <stdlib.h>
  2023. X
  2024. Xldiv_t
  2025. Xldiv(num, denom)
  2026. Xregister long num, denom;
  2027. X{
  2028. X    register ldiv_t result;
  2029. X
  2030. X    result.quot = num/denom;
  2031. X    result.rem = num%denom;
  2032. X    return result;
  2033. X}
  2034. !
  2035. echo 'libfake/README':
  2036. sed 's/^X//' >'libfake/README' <<'!'
  2037. XThis is stuff that your system ought to have but might not.  Some of these
  2038. Xare just quick fakes that do the right thing for C News but not in general;
  2039. Xsome are freely-redistributable portable implementations of the real thing.
  2040. X
  2041. XThe dbm imitation is exceedingly crude and inefficient but provides full
  2042. Xfunctionality.
  2043. X
  2044. XFsync and symlink are C-News-specific fakes.
  2045. X
  2046. XLdiv.c is inefficient but provides the full ANSI C functionality.  (Do not
  2047. Xconfuse it with the prehistoric ldiv function found in some very old Unixes.)
  2048. X
  2049. XThe mem* and str* and *index functions come from Henry Spencer's public-domain
  2050. Xstring-functions implementation.  Said implementation is somewhat in need of
  2051. Xan updating to match ANSI C, and there are minor portability glitches, but in
  2052. Xgeneral these should do the right thing for you.
  2053. X
  2054. XGetopt is Henry Spencer's public-domain implementation of System III getopt.
  2055. XIt differs from the System V one in minor ways.
  2056. X
  2057. XMkdir may give problems if called from a setuid program and either your
  2058. Xsystem does not implement setuid(geteuid()) or the program doesn't bother
  2059. Xto do it.  Otherwise it's fine.
  2060. X
  2061. XPutenv is crude but works.
  2062. !
  2063. echo 'libfake/putenv.c':
  2064. sed 's/^X//' >'libfake/putenv.c' <<'!'
  2065. X/*
  2066. X * putenv - add a variable to the environment, as in SysV
  2067. X */
  2068. X
  2069. X#include <stdio.h>
  2070. X#include <sys/types.h>
  2071. X#include "libc.h"
  2072. X
  2073. X#define YES 1
  2074. X#define NO 0
  2075. X
  2076. X/* peculiar return values */
  2077. X#define WORKED 0
  2078. X#define FAILED 1
  2079. X
  2080. Xint
  2081. Xputenv(var)            /* put var in the environment */
  2082. Xchar *var;
  2083. X{
  2084. X    register char **envp, **newenv;
  2085. X    register int oldenvcnt;
  2086. X    extern char **environ;
  2087. X
  2088. X    /* count variables, look for var */
  2089. X    for (envp = environ; *envp != 0; envp++) {
  2090. X        register char *varp = var, *ep = *envp;
  2091. X        register int namesame;
  2092. X
  2093. X        namesame = NO;
  2094. X        for (; *varp == *ep && *varp != '\0'; ++ep, ++varp)
  2095. X            if (*varp == '=')
  2096. X                namesame = YES;
  2097. X        if (*varp == *ep && *ep == '\0')
  2098. X            return WORKED;    /* old & new var's are the same */
  2099. X        if (namesame) {
  2100. X            *envp = var;    /* replace var with new value */
  2101. X            return WORKED;
  2102. X        }
  2103. X    }
  2104. X    oldenvcnt = envp - environ;
  2105. X
  2106. X    /* allocate new environment with room for one more variable */
  2107. X    newenv = (char **)malloc((unsigned)((oldenvcnt+1+1)*sizeof(*envp)));
  2108. X    if (newenv == NULL)
  2109. X        return FAILED;
  2110. X
  2111. X    /* copy old environment pointers, add var, switch environments */
  2112. X    (void) memcpy((char *)newenv, (char *)environ, oldenvcnt*sizeof(*envp));
  2113. X    newenv[oldenvcnt] = var;
  2114. X    newenv[oldenvcnt+1] = NULL;
  2115. X    environ = newenv;
  2116. X    return WORKED;
  2117. X}
  2118. !
  2119. echo 'libfake/Makefile':
  2120. sed 's/^X//' >'libfake/Makefile' <<'!'
  2121. XINCLUDE = ../include
  2122. XCOPTS = -O
  2123. XCFLAGS = $(COPTS) -I$(INCLUDE)
  2124. X# workaround for System V make bug
  2125. XSHELL = /bin/sh
  2126. X
  2127. XALL = dbm.o fsync.o getopt.o index.o ldiv.o memchr.o memcmp.o memcpy.o \
  2128. Xmemset.o mkdir.o putenv.o rindex.o strchr.o strcspn.o strpbrk.o strrchr.o \
  2129. Xstrspn.o strtok.o symlink.o
  2130. X
  2131. X# beware -- build knows about NEEDED
  2132. XNEEDED =  ldiv.o
  2133. X
  2134. Xu:    $(NEEDED)
  2135. X    ar ruv ../libcnews.a $(NEEDED)
  2136. X
  2137. Xall:    $(NEEDED)
  2138. X
  2139. Xtry:    $(ALL)
  2140. X
  2141. X# ldiv.o:    $(INCLUDE)/stdlib.h    # trouble if stdlib.h isn't ours
  2142. X
  2143. Xclean:
  2144. X    rm -f *.o
  2145. !
  2146. echo 'libfake/dbm.c':
  2147. sed 's/^X//' >'libfake/dbm.c' <<'!'
  2148. X/*
  2149. X * Incredibly slow Uglix dbm simulation.
  2150. X */
  2151. X
  2152. X#include <stdio.h>
  2153. X#include <sys/types.h>
  2154. X#include <sys/stat.h>
  2155. X#include "libc.h"
  2156. X
  2157. X#define STRLEN(s) (sizeof (s) - 1)    /* s must be a char array */
  2158. X
  2159. Xstatic char *pagname = NULL;
  2160. Xstatic FILE *db;
  2161. Xstatic int dbrdonly;
  2162. X
  2163. Xtypedef struct {
  2164. X    char *dptr;
  2165. X    int dsize;
  2166. X} datum;
  2167. X
  2168. Xdbminit(file)
  2169. Xchar *file;
  2170. X{
  2171. X    dbrdonly = 0;
  2172. X
  2173. X    if (pagname != NULL)            /* old name? */
  2174. X        free(pagname);
  2175. X    pagname = malloc((unsigned)(strlen(file) + STRLEN(".pag") + 1));
  2176. X    if (pagname == NULL) {
  2177. X        warning("cannot allocate memory to open database `%s'\n", file);
  2178. X        return -1;
  2179. X    }
  2180. X    (void) strcpy(pagname, file);
  2181. X    (void) strcat(pagname, ".pag");
  2182. X
  2183. X    if ((db = fopen(pagname, "r+")) == NULL) {
  2184. X        db = fopen(pagname, "r");
  2185. X        dbrdonly = 1;
  2186. X    }
  2187. X    if (db == NULL) {
  2188. X        warning("cannot open database `%s'\n", file);
  2189. X        return -1;
  2190. X    }
  2191. X    return 0;
  2192. X}
  2193. X
  2194. Xdatum
  2195. Xfetch(key)
  2196. Xdatum key;
  2197. X{
  2198. X    datum item;
  2199. X
  2200. X    rewind(db);
  2201. X    while (getitem(&item, db) != EOF)        /* read key */
  2202. X        if (strncmp(item.dptr, key.dptr, key.dsize) == 0)
  2203. X            if (getitem(&item, db) == EOF)    /* read data */
  2204. X                break;
  2205. X            else
  2206. X                return item;
  2207. X    /* EOF */
  2208. X    item.dptr = NULL;
  2209. X    item.dsize = 0;
  2210. X    return item;
  2211. X}
  2212. X
  2213. Xdelete(key)
  2214. Xdatum key;
  2215. X{
  2216. X    datum item;
  2217. X    FILE *temp;
  2218. X    FILE *tmpfile();
  2219. X
  2220. X    if (dbrdonly)
  2221. X        return -1;
  2222. X    temp = tmpfile();
  2223. X    if (temp == NULL)
  2224. X        return -1;
  2225. X    /* copy from db to temp, omitting key & its data */
  2226. X    rewind(db);
  2227. X    while (getitem(&item, db) != EOF)
  2228. X        if (strncmp(item.dptr, key.dptr, key.dsize) == 0) {
  2229. X            if (getitem(&item, db) == EOF)    /* toss data too */
  2230. X                return -1;
  2231. X        } else
  2232. X            if (putitem(&item, temp) == EOF)
  2233. X                return -1;
  2234. X    /* copy back from temp to db */
  2235. X    rewind(temp);
  2236. X    db = freopen(pagname, "w+", db);
  2237. X    while (getitem(&item, temp) != EOF)
  2238. X        if (putitem(&item, db) == EOF)
  2239. X            return -1;
  2240. X    return 0;
  2241. X}
  2242. X
  2243. Xstore(key, dat)
  2244. Xdatum key, dat;
  2245. X{
  2246. X    if (dbrdonly)
  2247. X        return -1;
  2248. X#ifdef REALDBM            /* else, it's only for news */
  2249. X    if (delete(key) == -1)
  2250. X        return -1;
  2251. X#endif
  2252. X    if (putitem(&key, db) == EOF || putitem(&dat, db) == EOF)
  2253. X        return -1;
  2254. X    return 0;
  2255. X}
  2256. X
  2257. Xdatum
  2258. Xfirstkey()
  2259. X{
  2260. X    datum trash;
  2261. X    datum nextkey();
  2262. X
  2263. X    rewind(db);
  2264. X    return nextkey(trash);
  2265. X}
  2266. X
  2267. X/* ARGSUSED */
  2268. Xdatum
  2269. Xnextkey(key)            /* simplistic version, ignores key */
  2270. Xdatum key;
  2271. X{
  2272. X    static datum dat;
  2273. X
  2274. X    if (getitem(&dat, db) == EOF)
  2275. X        dat.dptr = NULL;
  2276. X    return dat;
  2277. X}
  2278. X
  2279. Xstatic int
  2280. Xgetitem(datump, fp)
  2281. Xregister datum *datump;            /* points at static storage */
  2282. XFILE *fp;
  2283. X{
  2284. X    static char *data = NULL;    /* the current item */
  2285. X
  2286. X    if (fread((char *)&datump->dsize, sizeof datump->dsize, 1, fp) != 1)
  2287. X        return EOF;
  2288. X    if (data != NULL)
  2289. X        free(data);        /* pitch old item */
  2290. X    datump->dptr = data = malloc((unsigned)datump->dsize);
  2291. X    if (data == NULL ||
  2292. X        fread(datump->dptr, datump->dsize, 1, fp) != 1)
  2293. X        return EOF;
  2294. X    return 0;
  2295. X}
  2296. X
  2297. Xstatic int
  2298. Xputitem(datump, fp)
  2299. Xdatum *datump;
  2300. XFILE *fp;
  2301. X{
  2302. X    if (fwrite((char *)&datump->dsize, sizeof datump->dsize, 1, fp) != 1 ||
  2303. X        fwrite(datump->dptr, datump->dsize, 1, fp) != 1)
  2304. X        return EOF;
  2305. X    return 0;
  2306. X}
  2307. !
  2308. echo 'libfake/fsync.c':
  2309. sed 's/^X//' >'libfake/fsync.c' <<'!'
  2310. X/*
  2311. X * fsync(2) emulation for systems lacking it
  2312. X */
  2313. X
  2314. X/* ARGSUSED */
  2315. Xint
  2316. Xfsync(fd)
  2317. Xint fd;
  2318. X{
  2319. X    return 0;
  2320. X}
  2321. !
  2322. echo 'libfake/mkdir.c':
  2323. sed 's/^X//' >'libfake/mkdir.c' <<'!'
  2324. X/*
  2325. X * 4.2BSD mkdir simulation
  2326. X */
  2327. X
  2328. X#include <stdio.h>
  2329. X#include <errno.h>
  2330. X#include <sys/types.h>    /* argh */
  2331. X#include "libc.h"
  2332. X
  2333. X/* system call returns */
  2334. X#define SYS_OK 0
  2335. X#define SYS_ERR (-1)
  2336. X
  2337. X#define UMASK_MASK 0777
  2338. X
  2339. X#define STRLEN(s) (sizeof (s) - 1)        /* s must be a char array */
  2340. X
  2341. Xint
  2342. Xmkdir(dir, mode)
  2343. Xchar *dir;
  2344. Xint mode;
  2345. X{
  2346. X    register char *cbuf = malloc((unsigned)STRLEN("mkdir ") + strlen(dir) + 1);
  2347. X    register int oldmask, ret;
  2348. X
  2349. X    if (cbuf == NULL) {
  2350. X        errno = ENOMEM;            /* kludge */
  2351. X        return SYS_ERR;
  2352. X    }
  2353. X    oldmask = umask(0);
  2354. X    (void) umask(~(mode & ~oldmask) & UMASK_MASK);
  2355. X
  2356. X    (void) sprintf(cbuf, "mkdir %s", dir);
  2357. X    ret = (system(cbuf) != 0? SYS_ERR: SYS_OK);
  2358. X    if (ret == SYS_ERR)
  2359. X        errno = EINVAL;            /* kludge */
  2360. X
  2361. X    (void) umask(oldmask);
  2362. X    free(cbuf);
  2363. X    return ret;
  2364. X}
  2365. !
  2366. echo 'libfake/symlink.c':
  2367. sed 's/^X//' >'libfake/symlink.c' <<'!'
  2368. X/*
  2369. X * symlink dummy
  2370. X */
  2371. X
  2372. Xint
  2373. Xsymlink(n1, n2)
  2374. Xchar *n1;
  2375. Xchar *n2;
  2376. X{
  2377. X    extern int errno;
  2378. X
  2379. X    errno = 0;        /* kludge */
  2380. X    return(-1);
  2381. X}
  2382. !
  2383. echo 'libfake/memchr.c':
  2384. sed 's/^X//' >'libfake/memchr.c' <<'!'
  2385. X/*
  2386. X * memchr - search for a byte
  2387. X *
  2388. X * CHARBITS should be defined only if the compiler lacks "unsigned char".
  2389. X * It should be a mask, e.g. 0377 for an 8-bit machine.
  2390. X */
  2391. X
  2392. X#define    NULL    0
  2393. X
  2394. X#ifndef CHARBITS
  2395. X#    define    UNSCHAR(c)    ((unsigned char)(c))
  2396. X#else
  2397. X#    define    UNSCHAR(c)    ((c)&CHARBITS)
  2398. X#endif
  2399. X
  2400. Xchar *
  2401. Xmemchr(s, ucharwanted, size)
  2402. X char * s;
  2403. Xint ucharwanted;
  2404. Xint size;
  2405. X{
  2406. X    register  char *scan;
  2407. X    register int n;
  2408. X    register int uc;
  2409. X
  2410. X    scan = s;
  2411. X    uc = UNSCHAR(ucharwanted);
  2412. X    for (n = size; n > 0; n--)
  2413. X        if (UNSCHAR(*scan) == uc)
  2414. X            return(scan);
  2415. X        else
  2416. X            scan++;
  2417. X
  2418. X    return(NULL);
  2419. X}
  2420. !
  2421. echo done
  2422.  
  2423.  
  2424.