2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)cue.c 1.20 04/03/02 Copyright 2001-2004 J. Schilling */
17 * Copyright (c) 2001-2004 J. Schilling
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License version 2
22 * as published by the Free Software Foundation.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License along with
30 * this program; see the file COPYING. If not, write to the Free Software
31 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
54 typedef struct state {
69 long index1; /* Current index 1 value */
70 long secoff; /* Old index 1 value */
76 static char linebuf[4096];
79 static char *wordendp;
85 static char worddelim[] = "=:,/";
86 static char nulldelim[] = "";
89 #define STATE_POSTGAP 1
92 #define STATE_INDEX0 4
93 #define STATE_INDEX1 5
101 * Keywords (first word on line):
102 * CATALOG - global CATALOG <MCN>
103 * CDTEXTFILE - global CDTEXTFILE <fname>
104 * FILE - track static FILE <fame> <type>
105 * FLAGS - track static FLAGS <flag> ...
106 * INDEX - track static INDEX <#> <mm:ss:ff>
107 * ISRC - track static ISRC <ISRC>
108 * PERFORMER - global/static PERFORMER <string>
109 * POSTGAP - track locak POSTGAP <mm:ss:ff>
110 * PREGAP - track static PREGAP <mm:ss:ff>
111 * REM - anywhere REM <comment>
112 * SONGWRITER - global/static SONGWRITER <string>
113 * TITLE - global/static TITLE <string>
114 * TRACK - track static TRACK <#> <datatype>
119 * PERFORMER | SONGWRITER | TITLE Doc says past FILE...
120 * FILE Must be past CATALOG
121 * ------- Repeat the following: mehrere FILE Commands?
123 * FLAGS | ISRC | PERFORMER | PREGAP | SONGWRITER | TITLE
128 #define K_G 0x10000 /* Global */
129 #define K_T 0x20000 /* Track static */
130 #define K_A (K_T | K_G) /* Global & Track static */
132 #define K_MCN (0 | K_G) /* Media catalog number */
133 #define K_TEXTFILE (1 | K_G) /* CD-Text binary file */
134 #define K_FILE (2 | K_T) /* Input data file */
135 #define K_FLAGS (3 | K_T) /* Flags for ctrl nibble */
136 #define K_INDEX (4 | K_T) /* Index marker for track */
137 #define K_ISRC (5 | K_T) /* ISRC string for track */
138 #define K_PERFORMER (6 | K_A) /* CD-Text Performer */
139 #define K_POSTGAP (7 | K_T) /* Post gap for track (autogen) */
140 #define K_PREGAP (8 | K_T) /* Pre gap for track (autogen) */
141 #define K_REM (9 | K_A) /* Remark (Comment) */
142 #define K_SONGWRITER (10| K_A) /* CD-Text Songwriter */
143 #define K_TITLE (11| K_A) /* CD-Text Title */
144 #define K_TRACK (12| K_T) /* Track marker */
147 static keyw_t keywords[] = {
148 { "CATALOG", K_MCN },
149 { "CDTEXTFILE", K_TEXTFILE },
151 { "FLAGS", K_FLAGS },
152 { "INDEX", K_INDEX },
154 { "PERFORMER", K_PERFORMER },
155 { "POSTGAP", K_POSTGAP },
156 { "PREGAP", K_PREGAP },
158 { "SONGWRITER", K_SONGWRITER },
159 { "TITLE", K_TITLE },
160 { "TRACK", K_TRACK },
166 * Filetypes - argument to FILE Keyword (one only):
168 * BINARY - Intel binary file (least significant byte first)
169 * MOTOTOLA - Motorola binary file (most significant byte first)
170 * AIFF - Audio AIFF file
171 * AU - Sun Audio file
172 * WAVE - Audio WAVE file
173 * MP3 - Audio MP3 file
176 #define K_MOTOROLA 101
183 static keyw_t filetypes[] = {
184 { "BINARY", K_BINARY },
185 { "MOTOROLA", K_MOTOROLA },
195 * Flags - argument to FLAGS Keyword (more than one allowed):
196 * DCP - Digital copy permitted
197 * 4CH - Four channel audio
198 * PRE - Pre-emphasis enabled (audio tracks only)
199 * SCMS - Serial copy management system (not supported by all recorders)
206 static keyw_t flags[] = {
215 * Datatypes - argument to TRACK Keyword (one only):
216 * AUDIO - Audio/Music (2352)
217 * CDG - Karaoke CD+G (2448)
218 * MODE1/2048 - CDROM Mode1 Data (cooked)
219 * MODE1/2352 - CDROM Mode1 Data (raw)
220 * MODE2/2336 - CDROM-XA Mode2 Data
221 * MODE2/2352 - CDROM-XA Mode2 Data
222 * CDI/2336 - CDI Mode2 Data
223 * CDI/2352 - CDI Mode2 Data
225 #define K_AUDIO 10000
227 #define K_MODE1 10002
228 #define K_MODE2 10003
231 static keyw_t dtypes[] = {
232 { "AUDIO", K_AUDIO },
234 { "MODE1", K_MODE1 },
235 { "MODE2", K_MODE2 },
241 int parsecue(char *cuefname, track_t trackp[]);
242 void fparsecue(FILE *f, track_t trackp[]);
243 static void parse_mcn(track_t trackp[], state_t *sp);
244 static void parse_textfile(track_t trackp[], state_t *sp);
245 static void parse_file(track_t trackp[], state_t *sp);
246 static void parse_flags(track_t trackp[], state_t *sp);
247 static void parse_index(track_t trackp[], state_t *sp);
248 static void parse_isrc(track_t trackp[], state_t *sp);
249 static void parse_performer(track_t trackp[], state_t *sp);
250 static void parse_postgap(track_t trackp[], state_t *sp);
251 static void parse_pregap(track_t trackp[], state_t *sp);
252 static void parse_songwriter(track_t trackp[], state_t *sp);
253 static void parse_title(track_t trackp[], state_t *sp);
254 static void parse_track(track_t trackp[], state_t *sp);
255 static void parse_offset(long *lp);
256 static void newtrack(track_t trackp[], state_t *sp);
258 static keyw_t *lookup(char *word, keyw_t table[]);
259 static void wdebug(void);
260 static FILE *cueopen(char *name);
261 static char *cuename(void);
262 static char *nextline(FILE *f);
263 static void ungetline(void);
264 static char *skipwhite(const char *s);
265 static char *peekword(void);
266 static char *lineend(void);
267 static char *markword(char *delim);
268 static char *getnextitem(char *delim);
269 static char *neednextitem(char *delim);
270 static char *nextword(void);
271 static char *needword(void);
272 static char *curword(void);
273 static char *nextitem(void);
274 static char *needitem(void);
275 static void checkextra(void);
276 static void cueabort(const char *fmt, ...);
282 int write_secs(void);
283 int write_secs() { return (-1); }
286 main(int argc, char *argv[])
289 track_t track[MAX_TRACK+2]; /* Max tracks + track 0 + track AA */
291 save_args(argc, argv);
293 fillbytes(track, sizeof (track), '\0');
294 for (i = 0; i < MAX_TRACK+2; i++)
295 track[i].track = track[i].trackno = i;
296 track[0].tracktype = TOC_MASK;
299 parsecue(argv[1], track);
307 parsecue(char *cuefname, track_t trackp[])
309 FILE *f = cueopen(cuefname);
311 fparsecue(f, trackp);
316 fparsecue(FILE *f, track_t trackp[])
320 BOOL isglobal = TRUE;
323 state.filename = NULL;
333 state.state = STATE_NONE;
339 state.pregapsize = -1;
340 state.postgapsize = -1;
344 printf("---> Entering CUE Parser...\n");
346 if (nextline(f) == NULL) {
349 * Do post processing here
351 if (state.state < STATE_INDEX1)
352 cueabort("Incomplete CUE file");
356 printf("---> CUE Parser got EOF, found %d tracks.\n",
362 if (*word == '\0') /* empty line */
366 printf("\nKEY: '%s' %s\n", word, peekword());
367 kp = lookup(word, keywords);
369 cueabort("Unknown CUE keyword '%s'", word);
371 if ((kp->k_type & K_G) == 0) {
375 if ((kp->k_type & K_T) == 0) {
377 cueabort("Badly placed CUE keyword '%s'", word);
379 /* printf("%s-", isglobal ? "G" : "T");*/
382 switch (kp->k_type) {
384 case K_MCN: parse_mcn(trackp, &state); break;
385 case K_TEXTFILE: parse_textfile(trackp, &state); break;
386 case K_FILE: parse_file(trackp, &state); break;
387 case K_FLAGS: parse_flags(trackp, &state); break;
388 case K_INDEX: parse_index(trackp, &state); break;
389 case K_ISRC: parse_isrc(trackp, &state); break;
390 case K_PERFORMER: parse_performer(trackp, &state); break;
391 case K_POSTGAP: parse_postgap(trackp, &state); break;
392 case K_PREGAP: parse_pregap(trackp, &state); break;
394 case K_SONGWRITER: parse_songwriter(trackp, &state); break;
395 case K_TITLE: parse_title(trackp, &state); break;
396 case K_TRACK: parse_track(trackp, &state); break;
399 cueabort("Panic: unknown CUE command '%s'", word);
405 parse_mcn(track_t trackp[], state_t *sp)
411 cueabort("CATALOG keyword must be before first TRACK");
414 setmcn(word, &trackp[0]);
415 txp = gettextptr(0, trackp); /* MCN is isrc for trk 0 */
416 txp->tc_isrc = strdup(word);
422 parse_textfile(track_t trackp[], state_t *sp)
427 cueabort("CDTEXTFILE keyword must be before first TRACK");
431 if (trackp[MAX_TRACK+1].flags & TI_TEXT) {
432 if (!checktextfile(word)) {
434 "Cannot use '%s' as CD-Text file.\n",
437 trackp[0].flags |= TI_TEXT;
439 errmsgno(EX_BAD, "Ignoring CDTEXTFILE '%s'.\n", word);
440 errmsgno(EX_BAD, "If you like to write CD-Text, call wodim -text.\n");
447 parse_file(track_t trackp[], state_t *sp)
459 if (sp->filename != NULL)
460 cueabort("Only one FILE allowed");
465 sp->xfp = xopen(word, O_RDONLY|O_BINARY, 0);
466 if (sp->xfp == NULL && geterrno() == ENOENT) {
469 if (strchr(word, '/') == 0 &&
470 strchr(cuename(), '/') != 0) {
471 snprintf(cname, sizeof (cname),
473 p = strrchr(cname, '/');
476 snprintf(newname, sizeof (newname),
477 "%s/%s", cname, word);
479 sp->xfp = xopen(word, O_RDONLY|O_BINARY, 0);
483 comerr("Cannot open FILE '%s'.\n", word);
485 sp->filename = strdup(word);
488 sp->flags &= ~TI_SWAB; /* Reset what we might set for FILE */
490 filetype = needitem();
491 kp = lookup(filetype, filetypes);
493 cueabort("Unknown filetype '%s'", filetype);
495 switch (kp->k_type) {
499 if (fstat(xfileno(sp->xfp), &st) >= 0 &&
500 S_ISREG(st.st_mode)) {
501 sp->filesize = st.st_size;
503 cueabort("Unknown file size for FILE '%s'",
508 cueabort("Unsupported filetype '%s'", kp->k_name);
511 sp->filesize = ausize(xfileno(sp->xfp));
514 sp->filesize = wavsize(xfileno(sp->xfp));
515 sp->flags |= TI_SWAB;
519 cueabort("Unsupported filetype '%s'", kp->k_name);
522 default: cueabort("Panic: unknown filetype '%s'", filetype);
525 if (sp->filesize == AU_BAD_CODING) {
526 cueabort("Inappropriate audio coding in '%s'",
530 printf("Track %d File '%s' Filesize %lld\n",
531 sp->track, sp->filename, sp->filesize);
533 sp->filetype = kp->k_type;
539 trackp->itracksize = lsize;
540 if (trackp->itracksize != lsize)
541 comerrno(EX_BAD, "This OS cannot handle large audio images.\n");
546 parse_flags(track_t trackp[], state_t *sp)
551 if ((sp->state < STATE_TRACK) ||
552 (sp->state >= STATE_INDEX0))
553 cueabort("Badly placed FLAGS keyword");
554 sp->state = STATE_FLAGS;
558 kp = lookup(word, flags);
560 cueabort("Unknown flag '%s'", word);
562 switch (kp->k_type) {
564 case K_DCP: sp->flags |= TI_COPY; break;
565 case K_4CH: sp->flags |= TI_QUADRO; break;
566 case K_PRE: sp->flags |= TI_PREEMP; break;
567 case K_SCMS: sp->flags |= TI_SCMS; break;
568 default: cueabort("Panic: unknown FLAG '%s'", word);
571 } while (peekword() < lineend());
574 printf("Track %d flags 0x%08X\n", sp->track, sp->flags);
578 parse_index(track_t trackp[], state_t *sp)
582 int track = sp->track;
584 if (sp->state < STATE_TRACK)
585 cueabort("Badly placed INDEX keyword");
589 if (*astolb(word, &l, 10) != '\0')
590 cueabort("Not a number '%s'", word);
592 cueabort("Illegal index '%s'", word);
594 if ((sp->index < l) &&
595 (((sp->index + 1) == l) || l == 1))
598 cueabort("Badly placed INDEX %ld number", l);
601 sp->state = STATE_INDEX1;
603 sp->state = STATE_INDEX0;
608 printf("Track %d Index %d %ld\n", sp->track, sp->index, l);
612 if (sp->index == 1) {
614 trackp[track].nindex = 1;
615 newtrack(trackp, sp);
618 printf("Track %d pregapsize %ld\n",
619 sp->track, trackp[track].pregapsize);
622 if (sp->index == 2) {
623 trackp[track].tindex = malloc(100*sizeof (long));
624 trackp[track].tindex[1] = 0;
625 trackp[track].tindex[2] = l - sp->index1;
626 trackp[track].nindex = 2;
629 trackp[track].tindex[sp->index] = l - sp->index1;
630 trackp[track].nindex = sp->index;
637 parse_isrc(track_t trackp[], state_t *sp)
641 int track = sp->track;
644 cueabort("ISRC keyword must be past first TRACK");
646 if ((sp->state < STATE_TRACK) ||
647 (sp->state >= STATE_INDEX0))
648 cueabort("Badly placed ISRC keyword");
649 sp->state = STATE_FLAGS;
652 setisrc(word, &trackp[track]);
653 txp = gettextptr(track, trackp);
654 txp->tc_isrc = strdup(word);
660 parse_performer(track_t trackp[], state_t *sp)
666 txp = gettextptr(sp->track, trackp);
667 txp->tc_performer = strdup(word);
673 parse_postgap(track_t trackp[], state_t *sp)
677 if (sp->state < STATE_INDEX1)
678 cueabort("Badly placed POSTGAP keyword");
679 sp->state = STATE_POSTGAP;
688 parse_pregap(track_t trackp[], state_t *sp)
692 if ((sp->state < STATE_TRACK) ||
693 (sp->state >= STATE_INDEX0))
694 cueabort("Badly placed PREGAP keyword");
695 sp->state = STATE_FLAGS;
704 parse_songwriter(track_t trackp[], state_t *sp)
710 txp = gettextptr(sp->track, trackp);
711 txp->tc_songwriter = strdup(word);
717 parse_title(track_t trackp[], state_t *sp)
723 txp = gettextptr(sp->track, trackp);
724 txp->tc_title = strdup(word);
730 parse_track(track_t trackp[], state_t *sp)
737 if ((sp->state >= STATE_TRACK) &&
738 (sp->state < STATE_INDEX1))
739 cueabort("Badly placed TRACK keyword");
740 sp->state = STATE_TRACK;
744 if (*astolb(word, &l, 10) != '\0')
745 cueabort("Not a number '%s'", word);
746 if (l <= 0 || l > 99)
747 cueabort("Illegal TRACK number '%s'", word);
749 if ((sp->track < l) &&
750 (((sp->track + 1) == l) || sp->track == 0))
753 cueabort("Badly placed TRACK %ld number", l);
756 kp = lookup(word, dtypes);
758 cueabort("Unknown filetype '%s'", word);
760 if (wordendc == '/') {
762 if (*astol(++word, &secsize) != '\0')
763 cueabort("Not a number '%s'", word);
767 * Reset all flags that may be set in TRACK & FLAGS lines
769 sp->flags &= ~(TI_AUDIO|TI_COPY|TI_QUADRO|TI_PREEMP|TI_SCMS);
771 if (kp->k_type == K_AUDIO)
772 sp->flags |= TI_AUDIO;
774 switch (kp->k_type) {
783 sp->tracktype = TOC_DA;
784 sp->sectype = SECT_AUDIO;
786 sp->secsize = secsize;
789 cueabort("Unsupported sector size %ld for audio", secsize);
796 sp->tracktype = TOC_ROM;
797 sp->sectype = SECT_ROM;
798 sp->dbtype = DB_ROM_MODE1;
799 sp->secsize = secsize;
802 * XXX Sector Size == 2352 ???
803 * XXX It seems that there exist bin/cue pairs with this value
806 cueabort("Unsupported sector size %ld for data", secsize);
811 sp->tracktype = TOC_ROM;
812 sp->sectype = SECT_MODE_2;
813 sp->dbtype = DB_ROM_MODE2;
814 sp->secsize = secsize;
816 if (secsize == 2352) {
817 sp->tracktype = TOC_XA2;
818 sp->sectype = SECT_MODE_2_MIX;
819 sp->sectype |= ST_MODE_RAW;
822 } else if (secsize != 2336)
823 cueabort("Unsupported sector size %ld for mode2", secsize);
824 if (kp->k_type == K_CDI)
825 sp->tracktype = TOC_CDI;
828 default: cueabort("Panic: unknown datatype '%s'", word);
831 if (sp->flags & TI_PREEMP)
832 sp->sectype |= ST_PREEMPMASK;
833 sp->secsize = secsize;
836 printf("Track %d Tracktype %s/%d\n",
837 sp->track, kp->k_name, sp->secsize);
844 parse_offset(long *lp)
854 if (strchr(word, ':') == NULL) {
855 if (*astol(word, lp) != '\0')
856 cueabort("Not a number '%s'", word);
859 if (*(p = astolb(word, &m, 10)) != ':')
860 cueabort("Not a number '%s'", word);
861 if (m < 0 || m >= 160)
862 cueabort("Illegal minute value in '%s'", word);
864 if (*(p = astolb(p, &s, 10)) != ':')
865 cueabort("Not a number '%s'", p);
866 if (s < 0 || s >= 60)
867 cueabort("Illegal second value in '%s'", word);
869 if (*(p = astolb(p, &f, 10)) != '\0')
870 cueabort("Not a number '%s'", p);
871 if (f < 0 || f >= 75)
872 cueabort("Illegal frame value in '%s'", word);
879 /*--------------------------------------------------------------------------*/
881 newtrack(track_t trackp[], state_t *sp)
884 register int track = sp->track;
888 printf("-->Newtrack %d\n", track);
890 tracksize = (sp->index1 - sp->secoff) * trackp[track-1].secsize;
893 printf(" trackoff %lld filesize %lld index1 %ld size %ld/%lld\n",
894 sp->trackoff, sp->filesize, sp->index1,
895 sp->index1 - sp->secoff,
898 trackp[track-1].itracksize = tracksize;
899 trackp[track-1].tracksize = tracksize;
900 trackp[track-1].tracksecs = sp->index1 - sp->secoff;
902 sp->trackoff += tracksize;
903 sp->secoff = sp->index1;
906 * Make 'tracks' immediately usable in track structure.
908 for (i = 0; i < MAX_TRACK+2; i++)
909 trackp[i].tracks = track;
911 trackp[track].filename = sp->filename;
912 trackp[track].xfp = xopen(sp->filename, O_RDONLY|O_BINARY, 0);
913 trackp[track].trackstart = 0L;
915 SEtzen wenn tracksecs bekannt sind
916 d.h. mit Index0 oder Index 1 vom nächsten track
918 trackp[track].itracksize = tracksize;
919 trackp[track].tracksize = tracksize;
920 trackp[track].tracksecs = -1L;
922 tracksize = sp->filesize - sp->trackoff;
924 trackp[track].itracksize = tracksize;
925 trackp[track].tracksize = tracksize;
926 trackp[track].tracksecs = (tracksize + sp->secsize - 1) / sp->secsize;
929 printf(" Remaining Filesize %lld (%lld secs)\n",
930 (sp->filesize-sp->trackoff),
931 (sp->filesize-sp->trackoff +sp->secsize - 1) / sp->secsize);
933 if (sp->pregapsize >= 0) {
934 /* trackp[track].flags &= ~TI_PREGAP;*/
935 sp->flags &= ~TI_PREGAP;
936 trackp[track].pregapsize = sp->pregapsize;
938 /* trackp[track].flags |= TI_PREGAP;*/
940 sp->flags |= TI_PREGAP;
942 trackp[track].pregapsize = sp->index1 + 150;
943 else if (sp->index0 < 0)
944 trackp[track].pregapsize = -1;
946 trackp[track].pregapsize = sp->index1 - sp->index0;
948 /* trackp[track].padsecs = xxx*/
950 trackp[track].isecsize = sp->secsize;
951 trackp[track].secsize = sp->secsize;
952 trackp[track].flags = sp->flags | trackp[0].flags;
954 trackp[track].secspt = 0; /* transfer size is set up in set_trsizes() */
955 /* trackp[track].pktsize = pktsize; */
956 trackp[track].pktsize = 0;
957 trackp[track].trackno = sp->track;
958 trackp[track].sectype = sp->sectype;
960 trackp[track].dataoff = sp->dataoff;
961 trackp[track].tracktype = sp->tracktype;
962 trackp[track].dbtype = sp->dbtype;
965 trackp[0].tracktype &= ~TOC_MASK;
966 trackp[0].tracktype |= sp->tracktype;
969 printf("Track %d Tracktype %X\n",
970 0, trackp[0].tracktype);
974 printf("Track %d Tracktype %X\n",
975 track, trackp[track].tracktype);
977 trackp[track].nindex = 1;
978 trackp[track].tindex = 0;
981 printf("Track %d flags 0x%08X\n", 0, trackp[0].flags);
982 printf("Track %d flags 0x%08X\n", track, trackp[track].flags);
986 /*--------------------------------------------------------------------------*/
988 lookup(char *word, keyw_t table[])
990 register keyw_t *kp = table;
993 if (streql(kp->k_name, word))
1000 /*--------------------------------------------------------------------------*/
1002 * Parser low level functions start here...
1008 /* printf("WORD: '%s' rest '%s'\n", word, peekword());*/
1009 printf("WORD: '%s' rest '%s'\n", linep, peekword());
1010 printf("linep %lX peekword %lX end %lX\n",
1011 (long)linep, (long)peekword(), (long)&linebuf[linelen]);
1019 f = fileopen(name, "r");
1021 comerr("Cannot open '%s'.\n", name);
1039 fillbytes(linebuf, sizeof (linebuf), '\0');
1040 len = rols_fgetline(f, linebuf, sizeof (linebuf));
1043 if (len > 0 && linebuf[len-1] == '\r') {
1044 linebuf[len-1] = '\0';
1049 } while (linebuf[0] == '#');
1064 *wordendp = wordendc;
1070 skipwhite(const char *s)
1072 register const Uchar *p = (const Uchar *)s;
1085 return (&wordendp[1]);
1091 return (&linebuf[linelen]);
1095 markword(char *delim)
1097 register BOOL quoted = FALSE;
1100 register Uchar *from;
1103 for (s = (Uchar *)linep; (c = *s) != '\0'; s++) {
1106 /* strcpy((char *)s, (char *)&s[1]);*/
1107 for (to = s, from = &s[1]; *from; ) {
1109 if (c == '\\' && quoted && (*from == '\\' || *from == '"'))
1117 if (!quoted && isspace(c))
1119 if (!quoted && strchr(delim, c) && s > (Uchar *)linep)
1122 wordendp = (char *)s;
1123 wordendc = (char)*s;
1130 getnextitem(char *delim)
1132 *wordendp = wordendc;
1134 linep = skipwhite(wordendp);
1135 return (markword(delim));
1139 neednextitem(char *delim)
1141 char *olinep = linep;
1144 nlinep = getnextitem(delim);
1146 if ((olinep == nlinep) || (*nlinep == '\0'))
1147 cueabort("Missing text");
1155 return (getnextitem(worddelim));
1161 return (neednextitem(worddelim));
1173 return (getnextitem(nulldelim));
1179 return (neednextitem(nulldelim));
1185 if (peekword() < lineend())
1186 cueabort("Extra text '%s'", peekword());
1190 static void cueabort(const char *fmt, ...)
1193 va_start(args, fmt);
1194 vfprintf(stderr, fmt, args);
1196 fprintf(stderr, " on line %d in '%s'.\n", lineno, fname);