1219b2ee8SDavid du Colombier /* join F1 F2 on stuff */
2219b2ee8SDavid du Colombier #include <u.h>
3219b2ee8SDavid du Colombier #include <libc.h>
4*14f51593SDavid du Colombier #include <bio.h>
5219b2ee8SDavid du Colombier #include <ctype.h>
6feef5247SDavid du Colombier
7*14f51593SDavid du Colombier enum {
8*14f51593SDavid du Colombier F1,
9*14f51593SDavid du Colombier F2,
10*14f51593SDavid du Colombier NIN,
11*14f51593SDavid du Colombier F0,
12*14f51593SDavid du Colombier };
13feef5247SDavid du Colombier
14*14f51593SDavid du Colombier #define NFLD 100 /* max field per line */
15*14f51593SDavid du Colombier #define comp() runestrcmp(ppi[F1][j1], ppi[F2][j2])
16*14f51593SDavid du Colombier
17*14f51593SDavid du Colombier Biobuf *f[NIN];
18*14f51593SDavid du Colombier Rune buf[NIN][Bsize]; /* input lines */
19*14f51593SDavid du Colombier Rune *ppi[NIN][NFLD+1]; /* pointers to fields in lines */
20219b2ee8SDavid du Colombier Rune sep1 = ' '; /* default field separator */
21219b2ee8SDavid du Colombier Rune sep2 = '\t';
22*14f51593SDavid du Colombier int j1 = 1; /* join of this field of file 1 */
23*14f51593SDavid du Colombier int j2 = 1; /* join of this field of file 2 */
24219b2ee8SDavid du Colombier int a1;
25219b2ee8SDavid du Colombier int a2;
26219b2ee8SDavid du Colombier
27*14f51593SDavid du Colombier int olist[NIN*NFLD]; /* output these fields */
28*14f51593SDavid du Colombier int olistf[NIN*NFLD]; /* from these files */
29*14f51593SDavid du Colombier int no; /* number of entries in olist */
30*14f51593SDavid du Colombier char *sepstr = " ";
31*14f51593SDavid du Colombier int discard; /* count of truncated lines */
32*14f51593SDavid du Colombier Rune null[Bsize] = L"";
33*14f51593SDavid du Colombier Biobuf binbuf, boutbuf;
34*14f51593SDavid du Colombier Biobuf *bin, *bout;
35*14f51593SDavid du Colombier
36219b2ee8SDavid du Colombier char *getoptarg(int*, char***);
37219b2ee8SDavid du Colombier int input(int);
38*14f51593SDavid du Colombier void join(int);
39219b2ee8SDavid du Colombier void oparse(char*);
40*14f51593SDavid du Colombier void output(int, int);
41219b2ee8SDavid du Colombier Rune *strtorune(Rune *, char *);
42219b2ee8SDavid du Colombier
43219b2ee8SDavid du Colombier void
main(int argc,char ** argv)44219b2ee8SDavid du Colombier main(int argc, char **argv)
45219b2ee8SDavid du Colombier {
46219b2ee8SDavid du Colombier int i;
47*14f51593SDavid du Colombier vlong off1, off2;
48219b2ee8SDavid du Colombier
49*14f51593SDavid du Colombier bin = &binbuf;
50*14f51593SDavid du Colombier bout = &boutbuf;
51*14f51593SDavid du Colombier Binit(bin, 0, OREAD);
52*14f51593SDavid du Colombier Binit(bout, 1, OWRITE);
53*14f51593SDavid du Colombier
54*14f51593SDavid du Colombier argv0 = argv[0];
55219b2ee8SDavid du Colombier while (argc > 1 && argv[1][0] == '-') {
56219b2ee8SDavid du Colombier if (argv[1][1] == '\0')
57219b2ee8SDavid du Colombier break;
58219b2ee8SDavid du Colombier switch (argv[1][1]) {
59219b2ee8SDavid du Colombier case '-':
60219b2ee8SDavid du Colombier argc--;
61219b2ee8SDavid du Colombier argv++;
62219b2ee8SDavid du Colombier goto proceed;
63219b2ee8SDavid du Colombier case 'a':
64219b2ee8SDavid du Colombier switch(*getoptarg(&argc, &argv)) {
65219b2ee8SDavid du Colombier case '1':
66219b2ee8SDavid du Colombier a1++;
67219b2ee8SDavid du Colombier break;
68219b2ee8SDavid du Colombier case '2':
69219b2ee8SDavid du Colombier a2++;
70219b2ee8SDavid du Colombier break;
71219b2ee8SDavid du Colombier default:
72*14f51593SDavid du Colombier sysfatal("incomplete option -a");
73219b2ee8SDavid du Colombier }
74219b2ee8SDavid du Colombier break;
75219b2ee8SDavid du Colombier case 'e':
76219b2ee8SDavid du Colombier strtorune(null, getoptarg(&argc, &argv));
77219b2ee8SDavid du Colombier break;
78219b2ee8SDavid du Colombier case 't':
79219b2ee8SDavid du Colombier sepstr=getoptarg(&argc, &argv);
80219b2ee8SDavid du Colombier chartorune(&sep1, sepstr);
81219b2ee8SDavid du Colombier sep2 = sep1;
82219b2ee8SDavid du Colombier break;
83219b2ee8SDavid du Colombier case 'o':
84219b2ee8SDavid du Colombier if(argv[1][2]!=0 ||
85219b2ee8SDavid du Colombier argc>2 && strchr(argv[2],',')!=0)
86219b2ee8SDavid du Colombier oparse(getoptarg(&argc, &argv));
87219b2ee8SDavid du Colombier else for (no = 0; no<2*NFLD && argc>2; no++){
88219b2ee8SDavid du Colombier if (argv[2][0] == '1' && argv[2][1] == '.') {
89219b2ee8SDavid du Colombier olistf[no] = F1;
90219b2ee8SDavid du Colombier olist[no] = atoi(&argv[2][2]);
91219b2ee8SDavid du Colombier } else if (argv[2][0] == '2' && argv[2][1] == '.') {
92219b2ee8SDavid du Colombier olist[no] = atoi(&argv[2][2]);
93219b2ee8SDavid du Colombier olistf[no] = F2;
94219b2ee8SDavid du Colombier } else if (argv[2][0] == '0')
95219b2ee8SDavid du Colombier olistf[no] = F0;
96219b2ee8SDavid du Colombier else
97219b2ee8SDavid du Colombier break;
98219b2ee8SDavid du Colombier argc--;
99219b2ee8SDavid du Colombier argv++;
100219b2ee8SDavid du Colombier }
101219b2ee8SDavid du Colombier break;
102219b2ee8SDavid du Colombier case 'j':
103219b2ee8SDavid du Colombier if(argc <= 2)
104219b2ee8SDavid du Colombier break;
105219b2ee8SDavid du Colombier if (argv[1][2] == '1')
106219b2ee8SDavid du Colombier j1 = atoi(argv[2]);
107219b2ee8SDavid du Colombier else if (argv[1][2] == '2')
108219b2ee8SDavid du Colombier j2 = atoi(argv[2]);
109219b2ee8SDavid du Colombier else
110219b2ee8SDavid du Colombier j1 = j2 = atoi(argv[2]);
111219b2ee8SDavid du Colombier argc--;
112219b2ee8SDavid du Colombier argv++;
113219b2ee8SDavid du Colombier break;
114219b2ee8SDavid du Colombier case '1':
115219b2ee8SDavid du Colombier j1 = atoi(getoptarg(&argc, &argv));
116219b2ee8SDavid du Colombier break;
117219b2ee8SDavid du Colombier case '2':
118219b2ee8SDavid du Colombier j2 = atoi(getoptarg(&argc, &argv));
119219b2ee8SDavid du Colombier break;
120219b2ee8SDavid du Colombier }
121219b2ee8SDavid du Colombier argc--;
122219b2ee8SDavid du Colombier argv++;
123219b2ee8SDavid du Colombier }
124219b2ee8SDavid du Colombier proceed:
125219b2ee8SDavid du Colombier for (i = 0; i < no; i++)
126219b2ee8SDavid du Colombier if (olist[i]-- > NFLD) /* 0 origin */
127*14f51593SDavid du Colombier sysfatal("field number too big in -o");
128*14f51593SDavid du Colombier if (argc != 3) {
129*14f51593SDavid du Colombier fprint(2, "usage: join [-1 x -2 y] [-o list] file1 file2\n");
130*14f51593SDavid du Colombier exits("usage");
131*14f51593SDavid du Colombier }
13283f0bdbeSDavid du Colombier if (j1 < 1 || j2 < 1)
133*14f51593SDavid du Colombier sysfatal("invalid field indices");
134219b2ee8SDavid du Colombier j1--;
135219b2ee8SDavid du Colombier j2--; /* everyone else believes in 0 origin */
136*14f51593SDavid du Colombier
137219b2ee8SDavid du Colombier if (strcmp(argv[1], "-") == 0)
138*14f51593SDavid du Colombier f[F1] = bin;
139*14f51593SDavid du Colombier else if ((f[F1] = Bopen(argv[1], OREAD)) == 0)
140*14f51593SDavid du Colombier sysfatal("can't open %s: %r", argv[1]);
141*14f51593SDavid du Colombier if(strcmp(argv[2], "-") == 0)
142*14f51593SDavid du Colombier f[F2] = bin;
143*14f51593SDavid du Colombier else if ((f[F2] = Bopen(argv[2], OREAD)) == 0)
144*14f51593SDavid du Colombier sysfatal("can't open %s: %r", argv[2]);
145219b2ee8SDavid du Colombier
146*14f51593SDavid du Colombier off1 = Boffset(f[F1]);
147*14f51593SDavid du Colombier off2 = Boffset(f[F2]);
148*14f51593SDavid du Colombier if(Bseek(f[F2], 0, 2) >= 0){
149*14f51593SDavid du Colombier Bseek(f[F2], off2, 0);
150*14f51593SDavid du Colombier join(F2);
151*14f51593SDavid du Colombier }else if(Bseek(f[F1], 0, 2) >= 0){
152*14f51593SDavid du Colombier Bseek(f[F1], off1, 0);
153*14f51593SDavid du Colombier Bseek(f[F2], off2, 0);
154*14f51593SDavid du Colombier join(F1);
155*14f51593SDavid du Colombier }else
156*14f51593SDavid du Colombier sysfatal("neither file is randomly accessible");
157219b2ee8SDavid du Colombier if (discard)
158*14f51593SDavid du Colombier sysfatal("some input line was truncated");
159219b2ee8SDavid du Colombier exits("");
160219b2ee8SDavid du Colombier }
161feef5247SDavid du Colombier
162feef5247SDavid du Colombier char *
runetostr(char * buf,Rune * r)163feef5247SDavid du Colombier runetostr(char *buf, Rune *r)
164feef5247SDavid du Colombier {
165219b2ee8SDavid du Colombier char *s;
166feef5247SDavid du Colombier
167feef5247SDavid du Colombier for(s = buf; *r; r++)
168feef5247SDavid du Colombier s += runetochar(s, r);
169219b2ee8SDavid du Colombier *s = '\0';
170219b2ee8SDavid du Colombier return buf;
171219b2ee8SDavid du Colombier }
172feef5247SDavid du Colombier
173feef5247SDavid du Colombier Rune *
strtorune(Rune * buf,char * s)174feef5247SDavid du Colombier strtorune(Rune *buf, char *s)
175feef5247SDavid du Colombier {
176219b2ee8SDavid du Colombier Rune *r;
177feef5247SDavid du Colombier
178feef5247SDavid du Colombier for (r = buf; *s; r++)
179feef5247SDavid du Colombier s += chartorune(r, s);
180219b2ee8SDavid du Colombier *r = '\0';
181219b2ee8SDavid du Colombier return buf;
182219b2ee8SDavid du Colombier }
183feef5247SDavid du Colombier
184*14f51593SDavid du Colombier void
readboth(int n[])185*14f51593SDavid du Colombier readboth(int n[])
186*14f51593SDavid du Colombier {
187*14f51593SDavid du Colombier n[F1] = input(F1);
188*14f51593SDavid du Colombier n[F2] = input(F2);
189*14f51593SDavid du Colombier }
190feef5247SDavid du Colombier
191219b2ee8SDavid du Colombier void
seekbotreadboth(int seekf,vlong bot,int n[])192*14f51593SDavid du Colombier seekbotreadboth(int seekf, vlong bot, int n[])
193219b2ee8SDavid du Colombier {
194*14f51593SDavid du Colombier Bseek(f[seekf], bot, 0);
195*14f51593SDavid du Colombier readboth(n);
196219b2ee8SDavid du Colombier }
197*14f51593SDavid du Colombier
198219b2ee8SDavid du Colombier void
join(int seekf)199*14f51593SDavid du Colombier join(int seekf)
200219b2ee8SDavid du Colombier {
201*14f51593SDavid du Colombier int cmp, less;
202*14f51593SDavid du Colombier int n[NIN];
203*14f51593SDavid du Colombier vlong top, bot;
204*14f51593SDavid du Colombier
205*14f51593SDavid du Colombier less = seekf == F2;
206*14f51593SDavid du Colombier top = 0;
207*14f51593SDavid du Colombier bot = Boffset(f[seekf]);
208*14f51593SDavid du Colombier readboth(n);
209*14f51593SDavid du Colombier while(n[F1]>0 && n[F2]>0 || (a1||a2) && n[F1]+n[F2]>0) {
210*14f51593SDavid du Colombier cmp = comp();
211*14f51593SDavid du Colombier if(n[F1]>0 && n[F2]>0 && cmp>0 || n[F1]==0) {
212*14f51593SDavid du Colombier if(a2)
213*14f51593SDavid du Colombier output(0, n[F2]);
214*14f51593SDavid du Colombier if (seekf == F2)
215*14f51593SDavid du Colombier bot = Boffset(f[seekf]);
216*14f51593SDavid du Colombier n[F2] = input(F2);
217*14f51593SDavid du Colombier } else if(n[F1]>0 && n[F2]>0 && cmp<0 || n[F2]==0) {
218*14f51593SDavid du Colombier if(a1)
219*14f51593SDavid du Colombier output(n[F1], 0);
220*14f51593SDavid du Colombier if (seekf == F1)
221*14f51593SDavid du Colombier bot = Boffset(f[seekf]);
222*14f51593SDavid du Colombier n[F1] = input(F1);
223*14f51593SDavid du Colombier } else {
224*14f51593SDavid du Colombier /* n[F1]>0 && n[F2]>0 && cmp==0 */
225*14f51593SDavid du Colombier while(n[F2]>0 && cmp==0) {
226*14f51593SDavid du Colombier output(n[F1], n[F2]);
227*14f51593SDavid du Colombier top = Boffset(f[seekf]);
228*14f51593SDavid du Colombier n[seekf] = input(seekf);
229*14f51593SDavid du Colombier cmp = comp();
230219b2ee8SDavid du Colombier }
231*14f51593SDavid du Colombier seekbotreadboth(seekf, bot, n);
232219b2ee8SDavid du Colombier for(;;) {
233*14f51593SDavid du Colombier cmp = comp();
234*14f51593SDavid du Colombier if(n[F1]>0 && n[F2]>0 && cmp==0) {
235*14f51593SDavid du Colombier output(n[F1], n[F2]);
236*14f51593SDavid du Colombier n[seekf] = input(seekf);
237*14f51593SDavid du Colombier } else if(n[F1]>0 && n[F2]>0 &&
238*14f51593SDavid du Colombier (less? cmp<0 :cmp>0) || n[seekf]==0)
239*14f51593SDavid du Colombier seekbotreadboth(seekf, bot, n);
240*14f51593SDavid du Colombier else {
241*14f51593SDavid du Colombier /*
242*14f51593SDavid du Colombier * n[F1]>0 && n[F2]>0 &&
243*14f51593SDavid du Colombier * (less? cmp>0 :cmp<0) ||
244*14f51593SDavid du Colombier * n[seekf==F1? F2: F1]==0
245*14f51593SDavid du Colombier */
246*14f51593SDavid du Colombier Bseek(f[seekf], top, 0);
247*14f51593SDavid du Colombier bot = top;
248*14f51593SDavid du Colombier n[seekf] = input(seekf);
249219b2ee8SDavid du Colombier break;
250219b2ee8SDavid du Colombier }
251219b2ee8SDavid du Colombier }
252219b2ee8SDavid du Colombier }
253219b2ee8SDavid du Colombier }
254219b2ee8SDavid du Colombier }
255219b2ee8SDavid du Colombier
256219b2ee8SDavid du Colombier int
input(int n)257219b2ee8SDavid du Colombier input(int n) /* get input line and split into fields */
258219b2ee8SDavid du Colombier {
259*14f51593SDavid du Colombier int c, i, len;
260*14f51593SDavid du Colombier char *line;
261219b2ee8SDavid du Colombier Rune *bp;
262219b2ee8SDavid du Colombier Rune **pp;
263219b2ee8SDavid du Colombier
264219b2ee8SDavid du Colombier bp = buf[n];
265219b2ee8SDavid du Colombier pp = ppi[n];
266*14f51593SDavid du Colombier line = Brdline(f[n], '\n');
267*14f51593SDavid du Colombier if (line == nil)
268219b2ee8SDavid du Colombier return(0);
269*14f51593SDavid du Colombier len = Blinelen(f[n]) - 1;
270*14f51593SDavid du Colombier c = line[len];
271*14f51593SDavid du Colombier line[len] = '\0';
272219b2ee8SDavid du Colombier strtorune(bp, line);
273*14f51593SDavid du Colombier line[len] = c; /* restore delimiter */
274*14f51593SDavid du Colombier if (c != '\n')
275*14f51593SDavid du Colombier discard++;
276*14f51593SDavid du Colombier
277219b2ee8SDavid du Colombier i = 0;
278219b2ee8SDavid du Colombier do {
279219b2ee8SDavid du Colombier i++;
280219b2ee8SDavid du Colombier if (sep1 == ' ') /* strip multiples */
281219b2ee8SDavid du Colombier while ((c = *bp) == sep1 || c == sep2)
282219b2ee8SDavid du Colombier bp++; /* skip blanks */
283219b2ee8SDavid du Colombier *pp++ = bp; /* record beginning */
284*14f51593SDavid du Colombier while ((c = *bp) != sep1 && c != sep2 && c != '\0')
285219b2ee8SDavid du Colombier bp++;
286219b2ee8SDavid du Colombier *bp++ = '\0'; /* mark end by overwriting blank */
287*14f51593SDavid du Colombier } while (c != '\0' && i < NFLD-1);
288219b2ee8SDavid du Colombier
289219b2ee8SDavid du Colombier *pp = 0;
290219b2ee8SDavid du Colombier return(i);
291219b2ee8SDavid du Colombier }
292219b2ee8SDavid du Colombier
293219b2ee8SDavid du Colombier void
prfields(int f,int on,int jn)294*14f51593SDavid du Colombier prfields(int f, int on, int jn)
295*14f51593SDavid du Colombier {
296*14f51593SDavid du Colombier int i;
297*14f51593SDavid du Colombier char buf[Bsize];
298*14f51593SDavid du Colombier
299*14f51593SDavid du Colombier for (i = 0; i < on; i++)
300*14f51593SDavid du Colombier if (i != jn)
301*14f51593SDavid du Colombier Bprint(bout, "%s%s", sepstr, runetostr(buf, ppi[f][i]));
302*14f51593SDavid du Colombier }
303*14f51593SDavid du Colombier
304*14f51593SDavid du Colombier void
output(int on1,int on2)305219b2ee8SDavid du Colombier output(int on1, int on2) /* print items from olist */
306219b2ee8SDavid du Colombier {
307219b2ee8SDavid du Colombier int i;
308219b2ee8SDavid du Colombier Rune *temp;
309*14f51593SDavid du Colombier char buf[Bsize];
310219b2ee8SDavid du Colombier
311219b2ee8SDavid du Colombier if (no <= 0) { /* default case */
312*14f51593SDavid du Colombier Bprint(bout, "%s", runetostr(buf, on1? ppi[F1][j1]: ppi[F2][j2]));
313*14f51593SDavid du Colombier prfields(F1, on1, j1);
314*14f51593SDavid du Colombier prfields(F2, on2, j2);
315*14f51593SDavid du Colombier Bputc(bout, '\n');
316219b2ee8SDavid du Colombier } else {
317219b2ee8SDavid du Colombier for (i = 0; i < no; i++) {
318219b2ee8SDavid du Colombier if (olistf[i]==F0 && on1>j1)
319219b2ee8SDavid du Colombier temp = ppi[F1][j1];
320219b2ee8SDavid du Colombier else if (olistf[i]==F0 && on2>j2)
321219b2ee8SDavid du Colombier temp = ppi[F2][j2];
322219b2ee8SDavid du Colombier else {
323219b2ee8SDavid du Colombier temp = ppi[olistf[i]][olist[i]];
324219b2ee8SDavid du Colombier if(olistf[i]==F1 && on1<=olist[i] ||
325219b2ee8SDavid du Colombier olistf[i]==F2 && on2<=olist[i] ||
326219b2ee8SDavid du Colombier *temp==0)
327219b2ee8SDavid du Colombier temp = null;
328219b2ee8SDavid du Colombier }
329*14f51593SDavid du Colombier Bprint(bout, "%s", runetostr(buf, temp));
330219b2ee8SDavid du Colombier if (i == no - 1)
331*14f51593SDavid du Colombier Bputc(bout, '\n');
332219b2ee8SDavid du Colombier else
333*14f51593SDavid du Colombier Bprint(bout, "%s", sepstr);
334219b2ee8SDavid du Colombier }
335219b2ee8SDavid du Colombier }
336219b2ee8SDavid du Colombier }
337219b2ee8SDavid du Colombier
338219b2ee8SDavid du Colombier char *
getoptarg(int * argcp,char *** argvp)339219b2ee8SDavid du Colombier getoptarg(int *argcp, char ***argvp)
340219b2ee8SDavid du Colombier {
341219b2ee8SDavid du Colombier int argc = *argcp;
342219b2ee8SDavid du Colombier char **argv = *argvp;
343219b2ee8SDavid du Colombier if(argv[1][2] != 0)
344219b2ee8SDavid du Colombier return &argv[1][2];
345219b2ee8SDavid du Colombier if(argc<=2 || argv[2][0]=='-')
346*14f51593SDavid du Colombier sysfatal("incomplete option %s", argv[1]);
347219b2ee8SDavid du Colombier *argcp = argc-1;
348219b2ee8SDavid du Colombier *argvp = ++argv;
349219b2ee8SDavid du Colombier return argv[1];
350219b2ee8SDavid du Colombier }
351219b2ee8SDavid du Colombier
352219b2ee8SDavid du Colombier void
oparse(char * s)353219b2ee8SDavid du Colombier oparse(char *s)
354219b2ee8SDavid du Colombier {
355219b2ee8SDavid du Colombier for (no = 0; no<2*NFLD && *s; no++, s++) {
356219b2ee8SDavid du Colombier switch(*s) {
357219b2ee8SDavid du Colombier case 0:
358219b2ee8SDavid du Colombier return;
359219b2ee8SDavid du Colombier case '0':
360219b2ee8SDavid du Colombier olistf[no] = F0;
361219b2ee8SDavid du Colombier break;
362219b2ee8SDavid du Colombier case '1':
363219b2ee8SDavid du Colombier case '2':
364219b2ee8SDavid du Colombier if(s[1] == '.' && isdigit(s[2])) {
365219b2ee8SDavid du Colombier olistf[no] = *s=='1'? F1: F2;
366219b2ee8SDavid du Colombier olist[no] = atoi(s += 2);
367219b2ee8SDavid du Colombier break;
368feef5247SDavid du Colombier }
369feef5247SDavid du Colombier /* fall thru */
370219b2ee8SDavid du Colombier default:
371*14f51593SDavid du Colombier sysfatal("invalid -o list");
372219b2ee8SDavid du Colombier }
373219b2ee8SDavid du Colombier if(s[1] == ',')
374219b2ee8SDavid du Colombier s++;
375219b2ee8SDavid du Colombier }
376219b2ee8SDavid du Colombier }
377