Merging upstream version 2.
Daniel Baumann
14 years ago
2 | 2 | |
3 | 3 | include config.mk |
4 | 4 | |
5 | SUBDIRS = lib9 yacc awk basename bc dc cat cleanname date echo grep ls \ | |
6 | mk rc read sed seq sleep sort tee test touch tr uniq | |
5 | SUBDIRS = lib9 yacc awk basename bc dc cat cleanname date echo grep mk \ | |
6 | rc sed seq sleep sort tee test touch tr uniq | |
7 | 7 | |
8 | 8 | all: |
9 | 9 | @echo 9base build options: |
3 | 3 | PREFIX = /usr/local/9 |
4 | 4 | MANPREFIX = ${PREFIX}/share/man |
5 | 5 | |
6 | VERSION = 20060209 | |
6 | VERSION = 2 | |
7 | 7 | |
8 | 8 | # Linux/BSD |
9 | 9 | CFLAGS = -Wall -Wno-missing-braces -Wno-parentheses -Wno-switch -c -I. -DPREFIX="\"${PREFIX}\"" |
18 | 18 | # following objects are not compiled for several reasons |
19 | 19 | # crypt.o |
20 | 20 | # netcrypt.o |
21 | # getcallerpc-$(OBJTYPE).o | |
21 | 22 | # convD2M.o |
22 | 23 | # convM2D.o |
23 | 24 | # convM2S.o |
132 | 133 | exec.o\ |
133 | 134 | execl.o\ |
134 | 135 | fcallfmt.o\ |
135 | getcallerpc-$(OBJTYPE).o\ | |
136 | 136 | get9root.o\ |
137 | 137 | getenv.o\ |
138 | 138 | getfields.o\ |
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
7 | 7 | #if defined(__cplusplus) |
8 | 8 | extern "C" { |
9 | 9 | #endif |
10 | ||
11 | #include <utf.h> | |
12 | #include <fmt.h> | |
13 | 10 | |
14 | 11 | /* |
15 | 12 | * Begin usual libc.h |
377 | 374 | extern int dirmodefmt(Fmt*); |
378 | 375 | extern void exits(char*); |
379 | 376 | extern double frexp(double, int*); |
380 | extern ulong getcallerpc(void*); | |
377 | /*extern ulong getcallerpc(void*);*/ | |
381 | 378 | extern char* p9getenv(char*); |
382 | 379 | extern int p9putenv(char*, char*); |
383 | 380 | extern int getfields(char*, char**, int, int, char*); |
597 | 594 | #define OCEXEC 32 /* or'ed in, close on exec */ |
598 | 595 | #define ORCLOSE 64 /* or'ed in, remove on close */ |
599 | 596 | #define ODIRECT 128 /* or'ed in, direct access */ |
600 | #define ONONBLOCK 256 /* or'ed in, non-blocking call */ | |
597 | #define ONONBLOCK 256 /* or'ed in, non-blocking call */ | |
601 | 598 | #define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ |
602 | 599 | #define OLOCK 0x2000 /* or'ed in, lock after opening */ |
603 | 600 | #define OAPPEND 0x4000 /* or'ed in, append only */ |
622 | 619 | #define QTEXCL 0x20 /* type bit for exclusive use files */ |
623 | 620 | #define QTMOUNT 0x10 /* type bit for mounted channel */ |
624 | 621 | #define QTAUTH 0x08 /* type bit for authentication file */ |
625 | #define QTTMP 0x04 /* type bit for non-backed-up file */ | |
626 | #define QTSYMLINK 0x02 /* type bit for symbolic link */ | |
627 | #define QTFILE 0x00 /* type bits for plain file */ | |
622 | #define QTLINK 0x04 /* symbolic link */ | |
623 | #define QTFILE 0x00 /* plain file */ | |
628 | 624 | |
629 | 625 | /* bits in Dir.mode */ |
630 | 626 | #define DMDIR 0x80000000 /* mode bit for directories */ |
632 | 628 | #define DMEXCL 0x20000000 /* mode bit for exclusive use files */ |
633 | 629 | #define DMMOUNT 0x10000000 /* mode bit for mounted channel */ |
634 | 630 | #define DMAUTH 0x08000000 /* mode bit for authentication file */ |
635 | #define DMTMP 0x04000000 /* mode bit for non-backed-up file */ | |
636 | #define DMSYMLINK 0x02000000 /* mode bit for symbolic link (Unix, 9P2000.u) */ | |
637 | #define DMDEVICE 0x00800000 /* mode bit for device file (Unix, 9P2000.u) */ | |
638 | #define DMNAMEDPIPE 0x00200000 /* mode bit for named pipe (Unix, 9P2000.u) */ | |
639 | #define DMSOCKET 0x00100000 /* mode bit for socket (Unix, 9P2000.u) */ | |
640 | #define DMSETUID 0x00080000 /* mode bit for setuid (Unix, 9P2000.u) */ | |
641 | #define DMSETGID 0x00040000 /* mode bit for setgid (Unix, 9P2000.u) */ | |
631 | #define DMDEVICE 0x00800000 /* mode bit for device files (Unix) */ | |
632 | #define DMSYMLINK 0x00400000 /* mode bit for symbolic links (Unix) */ | |
633 | #define DMNAMEDPIPE 0x00200000 /* mode bit for named pipes (Unix) */ | |
634 | #define DMSOCKET 0x00100000 /* mode bit for sockets (Unix) */ | |
642 | 635 | |
643 | 636 | #define DMREAD 0x4 /* mode bit for read permission */ |
644 | 637 | #define DMWRITE 0x2 /* mode bit for write permission */ |
697 | 690 | char *uid; /* owner name */ |
698 | 691 | char *gid; /* group name */ |
699 | 692 | char *muid; /* last modifier name */ |
700 | ||
701 | /* 9P2000.u extensions */ | |
702 | uint uidnum; /* numeric uid */ | |
703 | uint gidnum; /* numeric gid */ | |
704 | uint muidnum; /* numeric muid */ | |
705 | char *ext; /* extended info */ | |
706 | 693 | } Dir; |
707 | 694 | |
708 | 695 | /* keep /sys/src/ape/lib/ap/plan9/sys9.h in sync with this -rsc */ |
834 | 821 | #define main p9main |
835 | 822 | #endif |
836 | 823 | |
837 | #ifdef VARARGCK | |
838 | #pragma varargck type "lld" vlong | |
839 | #pragma varargck type "llx" vlong | |
840 | #pragma varargck type "lld" uvlong | |
841 | #pragma varargck type "llx" uvlong | |
842 | #pragma varargck type "ld" long | |
843 | #pragma varargck type "lx" long | |
844 | #pragma varargck type "ld" ulong | |
845 | #pragma varargck type "lx" ulong | |
846 | #pragma varargck type "d" int | |
847 | #pragma varargck type "x" int | |
848 | #pragma varargck type "c" int | |
849 | #pragma varargck type "C" int | |
850 | #pragma varargck type "d" uint | |
851 | #pragma varargck type "x" uint | |
852 | #pragma varargck type "c" uint | |
853 | #pragma varargck type "C" uint | |
854 | #pragma varargck type "f" double | |
855 | #pragma varargck type "e" double | |
856 | #pragma varargck type "g" double | |
857 | #pragma varargck type "lf" long double | |
858 | #pragma varargck type "le" long double | |
859 | #pragma varargck type "lg" long double | |
860 | #pragma varargck type "s" char* | |
861 | #pragma varargck type "q" char* | |
862 | #pragma varargck type "S" Rune* | |
863 | #pragma varargck type "Q" Rune* | |
864 | #pragma varargck type "r" void | |
865 | #pragma varargck type "%" void | |
866 | #pragma varargck type "n" int* | |
867 | #pragma varargck type "p" void* | |
868 | #pragma varargck type "<" void* | |
869 | #pragma varargck type "[" void* | |
870 | #pragma varargck type "H" void* | |
871 | #pragma varargck type "lH" void* | |
872 | ||
873 | #pragma varargck flag ' ' | |
874 | #pragma varargck flag '#' | |
875 | #pragma varargck flag '+' | |
876 | #pragma varargck flag ',' | |
877 | #pragma varargck flag '-' | |
878 | #pragma varargck flag 'u' | |
879 | ||
880 | #pragma varargck argpos fmtprint 2 | |
881 | #pragma varargck argpos fprint 2 | |
882 | #pragma varargck argpos print 1 | |
883 | #pragma varargck argpos runeseprint 3 | |
884 | #pragma varargck argpos runesmprint 1 | |
885 | #pragma varargck argpos runesnprint 3 | |
886 | #pragma varargck argpos runesprint 2 | |
887 | #pragma varargck argpos seprint 3 | |
888 | #pragma varargck argpos smprint 1 | |
889 | #pragma varargck argpos snprint 3 | |
890 | #pragma varargck argpos sprint 2 | |
891 | #pragma varargck argpos sysfatal 1 | |
892 | #pragma varargck argpos p9syslog 3 | |
893 | #pragma varargck argpos werrstr 1 | |
894 | #endif | |
895 | ||
896 | 824 | /* compiler directives on plan 9 */ |
897 | 825 | #define SET(x) ((x)=0) |
898 | 826 | #define USED(x) if(x){}else{} |
901 | 829 | # undef USED |
902 | 830 | # define USED(x) { ulong __y __attribute__ ((unused)); __y = (ulong)(x); } |
903 | 831 | # endif |
832 | #endif | |
833 | ||
834 | #if defined(__OpenBSD__) || (defined(__NetBSD__) && !defined(sched_yield)) | |
835 | #define sched_yield() \ | |
836 | do { \ | |
837 | struct timespec ts; \ | |
838 | ts.tv_sec = 0; \ | |
839 | ts.tv_nsec = 10; \ | |
840 | nanosleep(&ts, NULL); \ | |
841 | } while(0) | |
904 | 842 | #endif |
905 | 843 | |
906 | 844 | /* command line */ |
20 | 20 | void |
21 | 21 | lock(Lock *l) |
22 | 22 | { |
23 | /* | |
23 | 24 | if(_lock) |
24 | 25 | (*_lock)(l, 1, getcallerpc(&l)); |
25 | 26 | else |
26 | 27 | l->held = 1; |
28 | */ | |
27 | 29 | } |
28 | 30 | |
29 | 31 | int |
30 | 32 | canlock(Lock *l) |
31 | 33 | { |
34 | /* | |
32 | 35 | if(_lock) |
33 | 36 | return (*_lock)(l, 0, getcallerpc(&l)); |
34 | 37 | else{ |
37 | 40 | l->held = 1; |
38 | 41 | return 1; |
39 | 42 | } |
43 | */ | |
44 | return 1; | |
40 | 45 | } |
41 | 46 | |
42 | 47 | void |
43 | 48 | unlock(Lock *l) |
44 | 49 | { |
50 | /* | |
45 | 51 | if(_unlock) |
46 | 52 | (*_unlock)(l, getcallerpc(&l)); |
47 | 53 | else |
48 | 54 | l->held = 0; |
55 | */ | |
49 | 56 | } |
50 | 57 | |
51 | 58 | void |
52 | 59 | qlock(QLock *l) |
53 | 60 | { |
61 | /* | |
54 | 62 | if(_qlock) |
55 | 63 | (*_qlock)(l, 1, getcallerpc(&l)); |
56 | 64 | else |
57 | 65 | l->l.held = 1; |
66 | */ | |
58 | 67 | } |
59 | 68 | |
60 | 69 | int |
61 | 70 | canqlock(QLock *l) |
62 | 71 | { |
72 | /* | |
63 | 73 | if(_qlock) |
64 | 74 | return (*_qlock)(l, 0, getcallerpc(&l)); |
65 | 75 | else{ |
68 | 78 | l->l.held = 1; |
69 | 79 | return 1; |
70 | 80 | } |
81 | */ | |
82 | return 1; | |
71 | 83 | } |
72 | 84 | |
73 | 85 | void |
74 | 86 | qunlock(QLock *l) |
75 | 87 | { |
88 | /* | |
76 | 89 | if(_qunlock) |
77 | 90 | (*_qunlock)(l, getcallerpc(&l)); |
78 | 91 | else |
79 | 92 | l->l.held = 0; |
93 | */ | |
80 | 94 | } |
81 | 95 | |
82 | 96 | void |
83 | 97 | rlock(RWLock *l) |
84 | 98 | { |
99 | /* | |
85 | 100 | if(_rlock) |
86 | 101 | (*_rlock)(l, 1, getcallerpc(&l)); |
87 | 102 | else |
88 | 103 | l->readers++; |
104 | */ | |
89 | 105 | } |
90 | 106 | |
91 | 107 | int |
92 | 108 | canrlock(RWLock *l) |
93 | 109 | { |
110 | /* | |
94 | 111 | if(_rlock) |
95 | 112 | return (*_rlock)(l, 0, getcallerpc(&l)); |
96 | 113 | else{ |
99 | 116 | l->readers++; |
100 | 117 | return 1; |
101 | 118 | } |
119 | */ | |
120 | return 1; | |
102 | 121 | } |
103 | 122 | |
104 | 123 | void |
105 | 124 | runlock(RWLock *l) |
106 | 125 | { |
126 | /* | |
107 | 127 | if(_runlock) |
108 | 128 | (*_runlock)(l, getcallerpc(&l)); |
109 | 129 | else |
110 | 130 | l->readers--; |
131 | */ | |
111 | 132 | } |
112 | 133 | |
113 | 134 | void |
114 | 135 | wlock(RWLock *l) |
115 | 136 | { |
137 | /* | |
116 | 138 | if(_wlock) |
117 | 139 | (*_wlock)(l, 1, getcallerpc(&l)); |
118 | 140 | else |
119 | 141 | l->writer = (void*)1; |
142 | */ | |
120 | 143 | } |
121 | 144 | |
122 | 145 | int |
123 | 146 | canwlock(RWLock *l) |
124 | 147 | { |
148 | /* | |
125 | 149 | if(_wlock) |
126 | 150 | return (*_wlock)(l, 0, getcallerpc(&l)); |
127 | 151 | else{ |
130 | 154 | l->writer = (void*)1; |
131 | 155 | return 1; |
132 | 156 | } |
157 | */ | |
158 | return 1; | |
133 | 159 | } |
134 | 160 | |
135 | 161 | void |
136 | 162 | wunlock(RWLock *l) |
137 | 163 | { |
164 | /* | |
138 | 165 | if(_wunlock) |
139 | 166 | (*_wunlock)(l, getcallerpc(&l)); |
140 | 167 | else |
141 | 168 | l->writer = nil; |
169 | */ | |
142 | 170 | } |
143 | 171 | |
144 | 172 | void |
145 | 173 | rsleep(Rendez *r) |
146 | 174 | { |
175 | /* | |
147 | 176 | if(_rsleep) |
148 | 177 | (*_rsleep)(r, getcallerpc(&r)); |
178 | */ | |
149 | 179 | } |
150 | 180 | |
151 | 181 | int |
152 | 182 | rwakeup(Rendez *r) |
153 | 183 | { |
184 | /* | |
154 | 185 | if(_rwakeup) |
155 | 186 | return (*_rwakeup)(r, 0, getcallerpc(&r)); |
187 | */ | |
156 | 188 | return 0; |
157 | 189 | } |
158 | 190 | |
159 | 191 | int |
160 | 192 | rwakeupall(Rendez *r) |
161 | 193 | { |
194 | /* | |
162 | 195 | if(_rwakeup) |
163 | 196 | return (*_rwakeup)(r, 1, getcallerpc(&r)); |
197 | */ | |
164 | 198 | return 0; |
165 | 199 | } |
0 | .TH LS 1 | |
1 | .SH NAME | |
2 | ls, lc \- list contents of directory | |
3 | .SH SYNOPSIS | |
4 | .B ls | |
5 | [ | |
6 | .B -dlmnpqrstuFQ | |
7 | ] | |
8 | .I name ... | |
9 | .PP | |
10 | .B lc | |
11 | [ | |
12 | .B -dlmnpqrstuFQ | |
13 | ] | |
14 | .I name ... | |
15 | .SH DESCRIPTION | |
16 | For each directory argument, | |
17 | .I ls | |
18 | lists the contents of the directory; | |
19 | for each file argument, | |
20 | .I ls | |
21 | repeats its name and any other information requested. | |
22 | When no argument is given, the current directory is listed. | |
23 | By default, the output is sorted alphabetically by name. | |
24 | .PP | |
25 | .I Lc | |
26 | is the same as | |
27 | .IR ls , | |
28 | but sets the | |
29 | .B -p | |
30 | option and pipes the output through | |
31 | .IR mc (1). | |
32 | .PP | |
33 | There are a number of options: | |
34 | .TP | |
35 | .B -d | |
36 | If argument is a directory, list it, not | |
37 | its contents. | |
38 | .TP | |
39 | .B -l | |
40 | List in long format, giving mode (see below), file system type | |
41 | (e.g., for devices, the | |
42 | .B # | |
43 | code letter that names it; see | |
44 | .IR intro (3)), | |
45 | the instance or subdevice number, owner, group, | |
46 | size in bytes, and time of last modification | |
47 | for each file. | |
48 | .TP | |
49 | .B -m | |
50 | List the name of the user who most recently modified the file. | |
51 | .TP | |
52 | .B -n | |
53 | Don't sort the listing. | |
54 | .TP | |
55 | .B -p | |
56 | Print only the final path element of each file name. | |
57 | .TP | |
58 | .B -q | |
59 | List the | |
60 | .I qid | |
61 | (see | |
62 | .IR stat (3)) | |
63 | of each file; the printed fields are in the order | |
64 | path, version, and type. | |
65 | .TP | |
66 | .B -r | |
67 | Reverse the order of sort. | |
68 | .TP | |
69 | .B -s | |
70 | Give size in Kbytes for each entry. | |
71 | .TP | |
72 | .B -t | |
73 | Sort by time modified (latest first) instead of | |
74 | by name. | |
75 | .TP | |
76 | .B -u | |
77 | Under | |
78 | .B -t | |
79 | sort by time of last access; | |
80 | under | |
81 | .B -l | |
82 | print time of last access. | |
83 | .TP | |
84 | .B -F | |
85 | Add the character | |
86 | .B / | |
87 | after all directory names | |
88 | and the character | |
89 | .B * | |
90 | after all executable files. | |
91 | .TP | |
92 | .B -L | |
93 | Print the character | |
94 | .B t | |
95 | before each file if it has the temporary flag set, and | |
96 | .B - | |
97 | otherwise. | |
98 | .TP | |
99 | .B -Q | |
100 | By default, printed file names are quoted if they contain characters special to | |
101 | .IR rc (1). | |
102 | The | |
103 | .B -Q | |
104 | flag disables this behavior. | |
105 | .PP | |
106 | The mode printed under the | |
107 | .B -l | |
108 | option contains 11 characters, | |
109 | interpreted | |
110 | as follows: | |
111 | the first character is | |
112 | .TP | |
113 | .B d | |
114 | if the entry is a directory; | |
115 | .TP | |
116 | .B a | |
117 | if the entry is an append-only file; | |
118 | .TP | |
119 | .B D | |
120 | if the entry is a Unix device; | |
121 | .TP | |
122 | .B L | |
123 | if the entry is a symbolic link; | |
124 | .TP | |
125 | .B P | |
126 | if the entry is a named pipe; | |
127 | .TP | |
128 | .B S | |
129 | if the entry is a socket; | |
130 | .TP | |
131 | .B - | |
132 | if the entry is a plain file. | |
133 | .PD | |
134 | .PP | |
135 | The next letter is | |
136 | .B l | |
137 | if the file is exclusive access (one writer or reader at a time). | |
138 | .PP | |
139 | The last 9 characters are interpreted | |
140 | as three sets of three bits each. | |
141 | The first set refers to owner permissions; | |
142 | the next to permissions to others in the same user-group; | |
143 | and the last to all others. | |
144 | Within each set the three characters indicate | |
145 | permission respectively to read, to write, or to | |
146 | execute the file as a program. | |
147 | For a directory, `execute' permission is interpreted | |
148 | to mean permission to search the directory | |
149 | for a specified file. | |
150 | The permissions are indicated as follows: | |
151 | .TP 3 | |
152 | .B r | |
153 | if the file is readable; | |
154 | .PD 0 | |
155 | .TP 3 | |
156 | .B w | |
157 | if the file is writable; | |
158 | .TP 3 | |
159 | .B x | |
160 | if the file is executable; | |
161 | .TP 3 | |
162 | .B - | |
163 | if none of the above permissions is granted. | |
164 | .PD | |
165 | .SH SOURCE | |
166 | .B \*9/src/cmd/ls.c | |
167 | .br | |
168 | .B \*9/bin/lc | |
169 | .SH SEE ALSO | |
170 | .IR stat (3), | |
171 | .IR mc (1) |
0 | #include <u.h> | |
1 | #include <libc.h> | |
2 | #include <bio.h> | |
3 | ||
4 | #define dirbuf p9dirbuf /* avoid conflict on sun */ | |
5 | ||
6 | typedef struct NDir NDir; | |
7 | struct NDir | |
8 | { | |
9 | Dir *d; | |
10 | char *prefix; | |
11 | }; | |
12 | ||
13 | int errs = 0; | |
14 | int dflag; | |
15 | int lflag; | |
16 | int mflag; | |
17 | int nflag; | |
18 | int pflag; | |
19 | int qflag; | |
20 | int Qflag; | |
21 | int rflag; | |
22 | int sflag; | |
23 | int tflag; | |
24 | int uflag; | |
25 | int Fflag; | |
26 | int ndirbuf; | |
27 | int ndir; | |
28 | NDir* dirbuf; | |
29 | int ls(char*, int); | |
30 | int compar(NDir*, NDir*); | |
31 | char* asciitime(long); | |
32 | char* darwx(long); | |
33 | void rwx(long, char*); | |
34 | void growto(long); | |
35 | void dowidths(Dir*); | |
36 | void format(Dir*, char*); | |
37 | void output(void); | |
38 | ulong clk; | |
39 | int swidth; /* max width of -s size */ | |
40 | int qwidth; /* max width of -q version */ | |
41 | int vwidth; /* max width of dev */ | |
42 | int uwidth; /* max width of userid */ | |
43 | int mwidth; /* max width of muid */ | |
44 | int glwidth; /* max width of groupid and length */ | |
45 | Biobuf bin; | |
46 | ||
47 | void | |
48 | main(int argc, char *argv[]) | |
49 | { | |
50 | int i; | |
51 | ||
52 | Binit(&bin, 1, OWRITE); | |
53 | ARGBEGIN{ | |
54 | case 'F': Fflag++; break; | |
55 | case 'd': dflag++; break; | |
56 | case 'l': lflag++; break; | |
57 | case 'm': mflag++; break; | |
58 | case 'n': nflag++; break; | |
59 | case 'p': pflag++; break; | |
60 | case 'q': qflag++; break; | |
61 | case 'Q': Qflag++; break; | |
62 | case 'r': rflag++; break; | |
63 | case 's': sflag++; break; | |
64 | case 't': tflag++; break; | |
65 | case 'u': uflag++; break; | |
66 | default: fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n"); | |
67 | exits("usage"); | |
68 | }ARGEND | |
69 | ||
70 | doquote = needsrcquote; | |
71 | quotefmtinstall(); | |
72 | fmtinstall('M', dirmodefmt); | |
73 | ||
74 | if(lflag) | |
75 | clk = time(0); | |
76 | if(argc == 0) | |
77 | errs = ls(".", 0); | |
78 | else for(i=0; i<argc; i++) | |
79 | errs |= ls(argv[i], 1); | |
80 | output(); | |
81 | exits(errs? "errors" : 0); | |
82 | } | |
83 | ||
84 | int | |
85 | ls(char *s, int multi) | |
86 | { | |
87 | int fd; | |
88 | long i, n; | |
89 | char *p; | |
90 | Dir *db; | |
91 | ||
92 | for(;;) { | |
93 | p = utfrrune(s, '/'); | |
94 | if(p == 0 || p[1] != 0 || p == s) | |
95 | break; | |
96 | *p = 0; | |
97 | } | |
98 | db = dirstat(s); | |
99 | if(db == nil){ | |
100 | error: | |
101 | fprint(2, "ls: %s: %r\n", s); | |
102 | return 1; | |
103 | } | |
104 | if(db->qid.type&QTDIR && dflag==0){ | |
105 | free(db); | |
106 | db = nil; | |
107 | output(); | |
108 | fd = open(s, OREAD); | |
109 | if(fd == -1) | |
110 | goto error; | |
111 | n = dirreadall(fd, &db); | |
112 | if(n < 0) | |
113 | goto error; | |
114 | growto(ndir+n); | |
115 | for(i=0; i<n; i++){ | |
116 | dirbuf[ndir+i].d = db+i; | |
117 | dirbuf[ndir+i].prefix = multi? s : 0; | |
118 | } | |
119 | ndir += n; | |
120 | close(fd); | |
121 | output(); | |
122 | }else{ | |
123 | growto(ndir+1); | |
124 | dirbuf[ndir].d = db; | |
125 | dirbuf[ndir].prefix = 0; | |
126 | p = utfrrune(s, '/'); | |
127 | if(p){ | |
128 | dirbuf[ndir].prefix = s; | |
129 | *p = 0; | |
130 | /* restore original name; don't use result of stat */ | |
131 | dirbuf[ndir].d->name = strdup(p+1); | |
132 | } | |
133 | ndir++; | |
134 | } | |
135 | return 0; | |
136 | } | |
137 | ||
138 | void | |
139 | output(void) | |
140 | { | |
141 | int i; | |
142 | char buf[4096]; | |
143 | char *s; | |
144 | ||
145 | if(!nflag) | |
146 | qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar); | |
147 | for(i=0; i<ndir; i++) | |
148 | dowidths(dirbuf[i].d); | |
149 | for(i=0; i<ndir; i++) { | |
150 | if(!pflag && (s = dirbuf[i].prefix)) { | |
151 | if(strcmp(s, "/") ==0) /* / is a special case */ | |
152 | s = ""; | |
153 | sprint(buf, "%s/%s", s, dirbuf[i].d->name); | |
154 | format(dirbuf[i].d, buf); | |
155 | } else | |
156 | format(dirbuf[i].d, dirbuf[i].d->name); | |
157 | } | |
158 | ndir = 0; | |
159 | Bflush(&bin); | |
160 | } | |
161 | ||
162 | void | |
163 | dowidths(Dir *db) | |
164 | { | |
165 | char buf[256]; | |
166 | int n; | |
167 | ||
168 | if(sflag) { | |
169 | n = sprint(buf, "%llud", (db->length+1023)/1024); | |
170 | if(n > swidth) | |
171 | swidth = n; | |
172 | } | |
173 | if(qflag) { | |
174 | n = sprint(buf, "%lud", db->qid.vers); | |
175 | if(n > qwidth) | |
176 | qwidth = n; | |
177 | } | |
178 | if(mflag) { | |
179 | n = snprint(buf, sizeof buf, "[%s]", db->muid); | |
180 | if(n > mwidth) | |
181 | mwidth = n; | |
182 | } | |
183 | if(lflag) { | |
184 | n = sprint(buf, "%ud", db->dev); | |
185 | if(n > vwidth) | |
186 | vwidth = n; | |
187 | n = strlen(db->uid); | |
188 | if(n > uwidth) | |
189 | uwidth = n; | |
190 | n = sprint(buf, "%llud", db->length); | |
191 | n += strlen(db->gid); | |
192 | if(n > glwidth) | |
193 | glwidth = n; | |
194 | } | |
195 | } | |
196 | ||
197 | char* | |
198 | fileflag(Dir *db) | |
199 | { | |
200 | if(Fflag == 0) | |
201 | return ""; | |
202 | if(QTDIR & db->qid.type) | |
203 | return "/"; | |
204 | if(0111 & db->mode) | |
205 | return "*"; | |
206 | return ""; | |
207 | } | |
208 | ||
209 | void | |
210 | format(Dir *db, char *name) | |
211 | { | |
212 | int i; | |
213 | ||
214 | if(sflag) | |
215 | Bprint(&bin, "%*llud ", | |
216 | swidth, (db->length+1023)/1024); | |
217 | if(mflag){ | |
218 | Bprint(&bin, "[%s] ", db->muid); | |
219 | for(i=2+strlen(db->muid); i<mwidth; i++) | |
220 | Bprint(&bin, " "); | |
221 | } | |
222 | if(qflag) | |
223 | Bprint(&bin, "(%.16llux %*lud %.2ux) ", | |
224 | db->qid.path, | |
225 | qwidth, db->qid.vers, | |
226 | db->qid.type); | |
227 | if(lflag) | |
228 | Bprint(&bin, | |
229 | Qflag? "%M %C %*ud %*s %s %*llud %s %s\n" : "%M %C %*ud %*s %s %*llud %s %q\n", | |
230 | db->mode, db->type, | |
231 | vwidth, db->dev, | |
232 | -uwidth, db->uid, | |
233 | db->gid, | |
234 | (int)(glwidth-strlen(db->gid)), db->length, | |
235 | asciitime(uflag? db->atime : db->mtime), name); | |
236 | else | |
237 | Bprint(&bin, | |
238 | Qflag? "%s%s\n" : "%q%s\n", | |
239 | name, fileflag(db)); | |
240 | } | |
241 | ||
242 | void | |
243 | growto(long n) | |
244 | { | |
245 | if(n <= ndirbuf) | |
246 | return; | |
247 | ndirbuf = n; | |
248 | dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir)); | |
249 | if(dirbuf == 0){ | |
250 | fprint(2, "ls: malloc fail\n"); | |
251 | exits("malloc fail"); | |
252 | } | |
253 | } | |
254 | ||
255 | int | |
256 | compar(NDir *a, NDir *b) | |
257 | { | |
258 | long i; | |
259 | Dir *ad, *bd; | |
260 | ||
261 | ad = a->d; | |
262 | bd = b->d; | |
263 | ||
264 | if(tflag){ | |
265 | if(uflag) | |
266 | i = bd->atime-ad->atime; | |
267 | else | |
268 | i = bd->mtime-ad->mtime; | |
269 | }else{ | |
270 | if(a->prefix && b->prefix){ | |
271 | i = strcmp(a->prefix, b->prefix); | |
272 | if(i == 0) | |
273 | i = strcmp(ad->name, bd->name); | |
274 | }else if(a->prefix){ | |
275 | i = strcmp(a->prefix, bd->name); | |
276 | if(i == 0) | |
277 | i = 1; /* a is longer than b */ | |
278 | }else if(b->prefix){ | |
279 | i = strcmp(ad->name, b->prefix); | |
280 | if(i == 0) | |
281 | i = -1; /* b is longer than a */ | |
282 | }else | |
283 | i = strcmp(ad->name, bd->name); | |
284 | } | |
285 | if(i == 0) | |
286 | i = (ad<bd? -1 : 1); | |
287 | if(rflag) | |
288 | i = -i; | |
289 | return i; | |
290 | } | |
291 | ||
292 | char* | |
293 | asciitime(long l) | |
294 | { | |
295 | static char buf[32]; | |
296 | char *t; | |
297 | ||
298 | t = ctime(l); | |
299 | /* 6 months in the past or a day in the future */ | |
300 | if(l<clk-180L*24*60*60 || clk+24L*60*60<l){ | |
301 | memmove(buf, t+4, 7); /* month and day */ | |
302 | memmove(buf+7, t+23, 5); /* year */ | |
303 | }else | |
304 | memmove(buf, t+4, 12); /* skip day of week */ | |
305 | buf[12] = 0; | |
306 | return buf; | |
307 | } | |
308 |
0 | .TH MK 1 | |
1 | .SH NAME | |
2 | mk \- maintain (make) related files | |
3 | .SH SYNOPSIS | |
4 | .B mk | |
5 | [ | |
6 | .B -f | |
7 | .I mkfile | |
8 | ] ... | |
9 | [ | |
10 | .I option ... | |
11 | ] | |
12 | [ | |
13 | .I target ... | |
14 | ] | |
15 | .SH DESCRIPTION | |
16 | .I Mk | |
17 | uses the dependency rules specified in | |
18 | .I mkfile | |
19 | to control the update (usually by compilation) of | |
20 | .I targets | |
21 | (usually files) | |
22 | from the source files upon which they depend. | |
23 | The | |
24 | .I mkfile | |
25 | (default | |
26 | .LR mkfile ) | |
27 | contains a | |
28 | .I rule | |
29 | for each target that identifies the files and other | |
30 | targets upon which it depends and an | |
31 | .IR sh (1) | |
32 | script, a | |
33 | .IR recipe , | |
34 | to update the target. | |
35 | The script is run if the target does not exist | |
36 | or if it is older than any of the files it depends on. | |
37 | .I Mkfile | |
38 | may also contain | |
39 | .I meta-rules | |
40 | that define actions for updating implicit targets. | |
41 | If no | |
42 | .I target | |
43 | is specified, the target of the first rule (not meta-rule) in | |
44 | .I mkfile | |
45 | is updated. | |
46 | .PP | |
47 | The environment variable | |
48 | .B $NPROC | |
49 | determines how many targets may be updated simultaneously; | |
50 | Some operating systems, e.g., Plan 9, set | |
51 | .B $NPROC | |
52 | automatically to the number of CPUs on the current machine. | |
53 | .PP | |
54 | Options are: | |
55 | .TP \w'\fL-d[egp]\ 'u | |
56 | .B -a | |
57 | Assume all targets to be out of date. | |
58 | Thus, everything is updated. | |
59 | .PD 0 | |
60 | .TP | |
61 | .BR -d [ egp ] | |
62 | Produce debugging output | |
63 | .RB ( p | |
64 | is for parsing, | |
65 | .B g | |
66 | for graph building, | |
67 | .B e | |
68 | for execution). | |
69 | .TP | |
70 | .B -e | |
71 | Explain why each target is made. | |
72 | .TP | |
73 | .B -i | |
74 | Force any missing intermediate targets to be made. | |
75 | .TP | |
76 | .B -k | |
77 | Do as much work as possible in the face of errors. | |
78 | .TP | |
79 | .B -n | |
80 | Print, but do not execute, the commands | |
81 | needed to update the targets. | |
82 | .TP | |
83 | .B -s | |
84 | Make the command line arguments sequentially rather than in parallel. | |
85 | .TP | |
86 | .B -t | |
87 | Touch (update the modified date of) file targets, without | |
88 | executing any recipes. | |
89 | .TP | |
90 | .BI -w target1 , target2,... | |
91 | Pretend the modify time for each | |
92 | .I target | |
93 | is the current time; useful in conjunction with | |
94 | .B -n | |
95 | to learn what updates would be triggered by | |
96 | modifying the | |
97 | .IR targets . | |
98 | .PD | |
99 | .SS The \fLmkfile\fP | |
100 | A | |
101 | .I mkfile | |
102 | consists of | |
103 | .I assignments | |
104 | (described under `Environment') and | |
105 | .IR rules . | |
106 | A rule contains | |
107 | .I targets | |
108 | and a | |
109 | .IR tail . | |
110 | A target is a literal string | |
111 | and is normally a file name. | |
112 | The tail contains zero or more | |
113 | .I prerequisites | |
114 | and an optional | |
115 | .IR recipe , | |
116 | which is an | |
117 | .B shell | |
118 | script. | |
119 | Each line of the recipe must begin with white space. | |
120 | A rule takes the form | |
121 | .IP | |
122 | .EX | |
123 | target: prereq1 prereq2 | |
124 | \f2recipe using\fP prereq1, prereq2 \f2to build\fP target | |
125 | .EE | |
126 | .PP | |
127 | When the recipe is executed, | |
128 | the first character on every line is elided. | |
129 | .PP | |
130 | After the colon on the target line, a rule may specify | |
131 | .IR attributes , | |
132 | described below. | |
133 | .PP | |
134 | A | |
135 | .I meta-rule | |
136 | has a target of the form | |
137 | .IB A % B | |
138 | where | |
139 | .I A | |
140 | and | |
141 | .I B | |
142 | are (possibly empty) strings. | |
143 | A meta-rule acts as a rule for any potential target whose | |
144 | name matches | |
145 | .IB A % B | |
146 | with | |
147 | .B % | |
148 | replaced by an arbitrary string, called the | |
149 | .IR stem . | |
150 | In interpreting a meta-rule, | |
151 | the stem is substituted for all occurrences of | |
152 | .B % | |
153 | in the prerequisite names. | |
154 | In the recipe of a meta-rule, the environment variable | |
155 | .B $stem | |
156 | contains the string matched by the | |
157 | .BR % . | |
158 | For example, a meta-rule to compile a C program using | |
159 | .IR 9c (1) | |
160 | might be: | |
161 | .IP | |
162 | .EX | |
163 | %: %.c | |
164 | 9c -c $stem.c | |
165 | 9l -o $stem $stem.o | |
166 | .EE | |
167 | .PP | |
168 | Meta-rules may contain an ampersand | |
169 | .B & | |
170 | rather than a percent sign | |
171 | .BR % . | |
172 | A | |
173 | .B % | |
174 | matches a maximal length string of any characters; | |
175 | an | |
176 | .B & | |
177 | matches a maximal length string of any characters except period | |
178 | or slash. | |
179 | .PP | |
180 | The text of the | |
181 | .I mkfile | |
182 | is processed as follows. | |
183 | Lines beginning with | |
184 | .B < | |
185 | followed by a file name are replaced by the contents of the named | |
186 | file. | |
187 | Lines beginning with | |
188 | .B "<|" | |
189 | followed by a file name are replaced by the output | |
190 | of the execution of the named | |
191 | file. | |
192 | Blank lines and comments, which run from unquoted | |
193 | .B # | |
194 | characters to the following newline, are deleted. | |
195 | The character sequence backslash-newline is deleted, | |
196 | so long lines in | |
197 | .I mkfile | |
198 | may be folded. | |
199 | Non-recipe lines are processed by substituting for | |
200 | .BI `{ command } | |
201 | the output of the | |
202 | .I command | |
203 | when run by | |
204 | .IR sh . | |
205 | References to variables are replaced by the variables' values. | |
206 | Special characters may be quoted using single quotes | |
207 | .BR \&'' | |
208 | as in | |
209 | .IR sh (1). | |
210 | .PP | |
211 | Assignments and rules are distinguished by | |
212 | the first unquoted occurrence of | |
213 | .B : | |
214 | (rule) | |
215 | or | |
216 | .B = | |
217 | (assignment). | |
218 | .PP | |
219 | A later rule may modify or override an existing rule under the | |
220 | following conditions: | |
221 | .TP | |
222 | \- | |
223 | If the targets of the rules exactly match and one rule | |
224 | contains only a prerequisite clause and no recipe, the | |
225 | clause is added to the prerequisites of the other rule. | |
226 | If either or both targets are virtual, the recipe is | |
227 | always executed. | |
228 | .TP | |
229 | \- | |
230 | If the targets of the rules match exactly and the | |
231 | prerequisites do not match and both rules | |
232 | contain recipes, | |
233 | .I mk | |
234 | reports an ``ambiguous recipe'' error. | |
235 | .TP | |
236 | \- | |
237 | If the target and prerequisites of both rules match exactly, | |
238 | the second rule overrides the first. | |
239 | .SS Environment | |
240 | Rules may make use of | |
241 | shell | |
242 | environment variables. | |
243 | A legal reference of the form | |
244 | .B $OBJ | |
245 | or | |
246 | .B ${name} | |
247 | is expanded as in | |
248 | .IR sh (1). | |
249 | A reference of the form | |
250 | .BI ${name: A % B = C\fL%\fID\fL}\fR, | |
251 | where | |
252 | .I A, B, C, D | |
253 | are (possibly empty) strings, | |
254 | has the value formed by expanding | |
255 | .B $name | |
256 | and substituting | |
257 | .I C | |
258 | for | |
259 | .I A | |
260 | and | |
261 | .I D | |
262 | for | |
263 | .I B | |
264 | in each word in | |
265 | .B $name | |
266 | that matches pattern | |
267 | .IB A % B\f1. | |
268 | .PP | |
269 | Variables can be set by | |
270 | assignments of the form | |
271 | .I | |
272 | var\fL=\fR[\fIattr\fL=\fR]\fIvalue\fR | |
273 | .br | |
274 | Blanks in the | |
275 | .I value | |
276 | break it into words. | |
277 | Such variables are exported | |
278 | to the environment of | |
279 | recipes as they are executed, unless | |
280 | .BR U , | |
281 | the only legal attribute | |
282 | .IR attr , | |
283 | is present. | |
284 | The initial value of a variable is | |
285 | taken from (in increasing order of precedence) | |
286 | the default values below, | |
287 | .I mk's | |
288 | environment, the | |
289 | .IR mkfiles , | |
290 | and any command line assignment as an argument to | |
291 | .IR mk . | |
292 | A variable assignment argument overrides the first (but not any subsequent) | |
293 | assignment to that variable. | |
294 | .PP | |
295 | The variable | |
296 | .B MKFLAGS | |
297 | contains all the option arguments (arguments starting with | |
298 | .L - | |
299 | or containing | |
300 | .LR = ) | |
301 | and | |
302 | .B MKARGS | |
303 | contains all the targets in the call to | |
304 | .IR mk . | |
305 | .PP | |
306 | The variable | |
307 | .B MKSHELL | |
308 | contains the shell command line | |
309 | .I mk | |
310 | uses to run recipes. | |
311 | If the first word of the command ends in | |
312 | .B rc | |
313 | or | |
314 | .BR rcsh , | |
315 | .I mk | |
316 | uses | |
317 | .IR rc (1)'s | |
318 | quoting rules; otherwise it uses | |
319 | .IR sh (1)'s. | |
320 | The | |
321 | .B MKSHELL | |
322 | variable is consulted when the mkfile is read, not when it is executed, | |
323 | so that different shells can be used within a single mkfile: | |
324 | .IP | |
325 | .EX | |
326 | MKSHELL=$PLAN9/bin/rc | |
327 | use-rc:V: | |
328 | for(i in a b c) echo $i | |
329 | ||
330 | MKSHELL=sh | |
331 | use-sh:V: | |
332 | for i in a b c; do echo $i; done | |
333 | .EE | |
334 | .LP | |
335 | Mkfiles included via | |
336 | .B < | |
337 | or | |
338 | .B <| | |
339 | .RI ( q.v. ) | |
340 | see their own private copy of | |
341 | .BR MKSHELL , | |
342 | which always starts set to | |
343 | .B sh . | |
344 | .PP | |
345 | Dynamic information may be included in the mkfile by using a line of the form | |
346 | .IP | |
347 | \fR<|\fIcommand\fR \fIargs\fR | |
348 | .LP | |
349 | This runs the command | |
350 | .I command | |
351 | with the given arguments | |
352 | .I args | |
353 | and pipes its standard output to | |
354 | .I mk | |
355 | to be included as part of the mkfile. For instance, the Inferno kernels | |
356 | use this technique | |
357 | to run a shell command with an awk script and a configuration | |
358 | file as arguments in order for | |
359 | the | |
360 | .I awk | |
361 | script to process the file and output a set of variables and their values. | |
362 | .SS Execution | |
363 | .PP | |
364 | During execution, | |
365 | .I mk | |
366 | determines which targets must be updated, and in what order, | |
367 | to build the | |
368 | .I names | |
369 | specified on the command line. | |
370 | It then runs the associated recipes. | |
371 | .PP | |
372 | A target is considered up to date if it has no prerequisites or | |
373 | if all its prerequisites are up to date and it is newer | |
374 | than all its prerequisites. | |
375 | Once the recipe for a target has executed, the target is | |
376 | considered up to date. | |
377 | .PP | |
378 | The date stamp | |
379 | used to determine if a target is up to date is computed | |
380 | differently for different types of targets. | |
381 | If a target is | |
382 | .I virtual | |
383 | (the target of a rule with the | |
384 | .B V | |
385 | attribute), | |
386 | its date stamp is initially zero; when the target is | |
387 | updated the date stamp is set to | |
388 | the most recent date stamp of its prerequisites. | |
389 | Otherwise, if a target does not exist as a file, | |
390 | its date stamp is set to the most recent date stamp of its prerequisites, | |
391 | or zero if it has no prerequisites. | |
392 | Otherwise, the target is the name of a file and | |
393 | the target's date stamp is always that file's modification date. | |
394 | The date stamp is computed when the target is needed in | |
395 | the execution of a rule; it is not a static value. | |
396 | .PP | |
397 | Nonexistent targets that have prerequisites | |
398 | and are themselves prerequisites are treated specially. | |
399 | Such a target | |
400 | .I t | |
401 | is given the date stamp of its most recent prerequisite | |
402 | and if this causes all the targets which have | |
403 | .I t | |
404 | as a prerequisite to be up to date, | |
405 | .I t | |
406 | is considered up to date. | |
407 | Otherwise, | |
408 | .I t | |
409 | is made in the normal fashion. | |
410 | The | |
411 | .B -i | |
412 | flag overrides this special treatment. | |
413 | .PP | |
414 | Files may be made in any order that respects | |
415 | the preceding restrictions. | |
416 | .PP | |
417 | A recipe is executed by supplying the recipe as standard input to | |
418 | the command | |
419 | .BR /bin/sh . | |
420 | (Note that unlike | |
421 | .IR make , | |
422 | .I mk | |
423 | feeds the entire recipe to the shell rather than running each line | |
424 | of the recipe separately.) | |
425 | The environment is augmented by the following variables: | |
426 | .TP 14 | |
427 | .B $alltarget | |
428 | all the targets of this rule. | |
429 | .TP | |
430 | .B $newprereq | |
431 | the prerequisites that caused this rule to execute. | |
432 | .TP | |
433 | .B $newmember | |
434 | the prerequisites that are members of an aggregate | |
435 | that caused this rule to execute. | |
436 | When the prerequisites of a rule are members of an | |
437 | aggregate, | |
438 | .B $newprereq | |
439 | contains the name of the aggregate and out of date | |
440 | members, while | |
441 | .B $newmember | |
442 | contains only the name of the members. | |
443 | .TP | |
444 | .B $nproc | |
445 | the process slot for this recipe. | |
446 | It satisfies | |
447 | .RB 0≤ $nproc < $NPROC . | |
448 | .TP | |
449 | .B $pid | |
450 | the process id for the | |
451 | .I mk | |
452 | executing the recipe. | |
453 | .TP | |
454 | .B $prereq | |
455 | all the prerequisites for this rule. | |
456 | .TP | |
457 | .B $stem | |
458 | if this is a meta-rule, | |
459 | .B $stem | |
460 | is the string that matched | |
461 | .B % | |
462 | or | |
463 | .BR & . | |
464 | Otherwise, it is empty. | |
465 | For regular expression meta-rules (see below), the variables | |
466 | .LR stem0 ", ...," | |
467 | .L stem9 | |
468 | are set to the corresponding subexpressions. | |
469 | .TP | |
470 | .B $target | |
471 | the targets for this rule that need to be remade. | |
472 | .PP | |
473 | These variables are available only during the execution of a recipe, | |
474 | not while evaluating the | |
475 | .IR mkfile . | |
476 | .PP | |
477 | Unless the rule has the | |
478 | .B Q | |
479 | attribute, | |
480 | the recipe is printed prior to execution | |
481 | with recognizable environment variables expanded. | |
482 | Commands returning error status | |
483 | cause | |
484 | .I mk | |
485 | to terminate. | |
486 | .PP | |
487 | Recipes and backquoted | |
488 | .B rc | |
489 | commands in places such as assignments | |
490 | execute in a copy of | |
491 | .I mk's | |
492 | environment; changes they make to | |
493 | environment variables are not visible from | |
494 | .IR mk . | |
495 | .PP | |
496 | Variable substitution in a rule is done when | |
497 | the rule is read; variable substitution in the recipe is done | |
498 | when the recipe is executed. For example: | |
499 | .IP | |
500 | .EX | |
501 | bar=a.c | |
502 | foo: $bar | |
503 | $CC -o foo $bar | |
504 | bar=b.c | |
505 | .EE | |
506 | .PP | |
507 | will compile | |
508 | .B b.c | |
509 | into | |
510 | .BR foo , | |
511 | if | |
512 | .B a.c | |
513 | is newer than | |
514 | .BR foo . | |
515 | .SS Aggregates | |
516 | Names of the form | |
517 | .IR a ( b ) | |
518 | refer to member | |
519 | .I b | |
520 | of the aggregate | |
521 | .IR a . | |
522 | Currently, the only aggregates supported are | |
523 | .I 9ar | |
524 | (see | |
525 | .IR 9c (1)) | |
526 | archives. | |
527 | .SS Attributes | |
528 | The colon separating the target from the prerequisites | |
529 | may be | |
530 | immediately followed by | |
531 | .I attributes | |
532 | and another colon. | |
533 | The attributes are: | |
534 | .TP | |
535 | .B D | |
536 | If the recipe exits with a non-null status, the target is deleted. | |
537 | .TP | |
538 | .B E | |
539 | Continue execution if the recipe draws errors. | |
540 | .TP | |
541 | .B N | |
542 | If there is no recipe, the target has its time updated. | |
543 | .TP | |
544 | .B n | |
545 | The rule is a meta-rule that cannot be a target of a virtual rule. | |
546 | Only files match the pattern in the target. | |
547 | .TP | |
548 | .B P | |
549 | The characters after the | |
550 | .B P | |
551 | until the terminating | |
552 | .B : | |
553 | are taken as a program name. | |
554 | It will be invoked as | |
555 | .B "sh -c prog 'arg1' 'arg2'" | |
556 | and should return a zero exit status | |
557 | if and only if arg1 is up to date with respect to arg2. | |
558 | Date stamps are still propagated in the normal way. | |
559 | .TP | |
560 | .B Q | |
561 | The recipe is not printed prior to execution. | |
562 | .TP | |
563 | .B R | |
564 | The rule is a meta-rule using regular expressions. | |
565 | In the rule, | |
566 | .B % | |
567 | has no special meaning. | |
568 | The target is interpreted as a regular expression as defined in | |
569 | .IR regexp (7). | |
570 | The prerequisites may contain references | |
571 | to subexpressions in form | |
572 | .BI \e n\f1, | |
573 | as in the substitute command of | |
574 | .IR sed (1). | |
575 | .TP | |
576 | .B U | |
577 | The targets are considered to have been updated | |
578 | even if the recipe did not do so. | |
579 | .TP | |
580 | .B V | |
581 | The targets of this rule are marked as virtual. | |
582 | They are distinct from files of the same name. | |
583 | .PD | |
584 | .SH EXAMPLES | |
585 | A simple mkfile to compile a program: | |
586 | .IP | |
587 | .EX | |
588 | .ta 8n +8n +8n +8n +8n +8n +8n | |
589 | </$objtype/mkfile | |
590 | ||
591 | prog: a.$O b.$O c.$O | |
592 | $LD $LDFLAGS -o $target $prereq | |
593 | ||
594 | %.$O: %.c | |
595 | $CC $CFLAGS $stem.c | |
596 | .EE | |
597 | .PP | |
598 | Override flag settings in the mkfile: | |
599 | .IP | |
600 | .EX | |
601 | % mk target 'CFLAGS=-S -w' | |
602 | .EE | |
603 | .PP | |
604 | Maintain a library: | |
605 | .IP | |
606 | .EX | |
607 | libc.a(%.$O):N: %.$O | |
608 | libc.a: libc.a(abs.$O) libc.a(access.$O) libc.a(alarm.$O) ... | |
609 | ar r libc.a $newmember | |
610 | .EE | |
611 | .PP | |
612 | String expression variables to derive names from a master list: | |
613 | .IP | |
614 | .EX | |
615 | NAMES=alloc arc bquote builtins expand main match mk var word | |
616 | OBJ=${NAMES:%=%.$O} | |
617 | .EE | |
618 | .PP | |
619 | Regular expression meta-rules: | |
620 | .IP | |
621 | .EX | |
622 | ([^/]*)/(.*)\e.$O:R: \e1/\e2.c | |
623 | cd $stem1; $CC $CFLAGS $stem2.c | |
624 | .EE | |
625 | .PP | |
626 | A correct way to deal with | |
627 | .IR yacc (1) | |
628 | grammars. | |
629 | The file | |
630 | .B lex.c | |
631 | includes the file | |
632 | .B x.tab.h | |
633 | rather than | |
634 | .B y.tab.h | |
635 | in order to reflect changes in content, not just modification time. | |
636 | .IP | |
637 | .EX | |
638 | lex.$O: x.tab.h | |
639 | x.tab.h: y.tab.h | |
640 | cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h | |
641 | y.tab.c y.tab.h: gram.y | |
642 | $YACC -d gram.y | |
643 | .EE | |
644 | .PP | |
645 | The above example could also use the | |
646 | .B P | |
647 | attribute for the | |
648 | .B x.tab.h | |
649 | rule: | |
650 | .IP | |
651 | .EX | |
652 | x.tab.h:Pcmp -s: y.tab.h | |
653 | cp y.tab.h x.tab.h | |
654 | .EE | |
655 | .SH SOURCE | |
656 | .B \*9/src/cmd/mk | |
657 | .SH SEE ALSO | |
658 | .IR sh (1), | |
659 | .IR regexp (7) | |
660 | .PP | |
661 | A. Hume, | |
662 | ``Mk: a Successor to Make'' | |
663 | (Tenth Edition Research Unix Manuals). | |
664 | .PP | |
665 | Andrew G. Hume and Bob Flandrena, | |
666 | ``Maintaining Files on Plan 9 with Mk''. | |
667 | DOCPREFIX/doc/mk.pdf | |
668 | .SH HISTORY | |
669 | Andrew Hume wrote | |
670 | .I mk | |
671 | for Tenth Edition Research Unix. | |
672 | It was later ported to Plan 9. | |
673 | This software is a port of the Plan 9 version back to Unix. | |
674 | .SH BUGS | |
675 | Identical recipes for regular expression meta-rules only have one target. | |
676 | .PP | |
677 | Seemingly appropriate input like | |
678 | .B CFLAGS=-DHZ=60 | |
679 | is parsed as an erroneous attribute; correct it by inserting | |
680 | a space after the first | |
681 | .LR = . | |
682 | .PP | |
683 | The recipes printed by | |
684 | .I mk | |
685 | before being passed to | |
686 | the shell | |
687 | for execution are sometimes erroneously expanded | |
688 | for printing. Don't trust what's printed; rely | |
689 | on what the shell | |
690 | does. |
0 | *************** | |
1 | *** 445,451 **** | |
2 | .B $nproc | |
3 | the process slot for this recipe. | |
4 | It satisfies | |
5 | - .RB 0??? $nproc < $NPROC . | |
6 | .TP | |
7 | .B $pid | |
8 | the process id for the | |
9 | --- 445,451 ---- | |
10 | .B $nproc | |
11 | the process slot for this recipe. | |
12 | It satisfies | |
13 | + .RB $nproc < $NPROC . | |
14 | .TP | |
15 | .B $pid | |
16 | the process id for the |
0 | .TH CAT 1 | |
1 | .SH NAME | |
2 | cat, read, nobs \- catenate files | |
3 | .SH SYNOPSIS | |
4 | .B cat | |
5 | [ | |
6 | .I file ... | |
7 | ] | |
8 | .br | |
9 | .B read | |
10 | [ | |
11 | .B -m | |
12 | ] [ | |
13 | .B -n | |
14 | .I nline | |
15 | ] [ | |
16 | .I file ... | |
17 | ] | |
18 | .br | |
19 | .B nobs | |
20 | [ | |
21 | .I file ... | |
22 | ] | |
23 | .SH DESCRIPTION | |
24 | .I Cat | |
25 | reads each | |
26 | .I file | |
27 | in sequence and writes it on the standard output. | |
28 | Thus | |
29 | .IP | |
30 | .L | |
31 | cat file | |
32 | .LP | |
33 | prints a file and | |
34 | .IP | |
35 | .L | |
36 | cat file1 file2 >file3 | |
37 | .LP | |
38 | concatenates the first two files and places the result | |
39 | on the third. | |
40 | .PP | |
41 | If no | |
42 | .I file | |
43 | is given, | |
44 | .I cat | |
45 | reads from the standard input. | |
46 | Output is buffered in blocks matching the input. | |
47 | .PP | |
48 | .I Read | |
49 | copies to standard output exactly one line from the named | |
50 | .IR file , | |
51 | default standard input. | |
52 | It is useful in interactive | |
53 | .IR rc (1) | |
54 | scripts. | |
55 | .PP | |
56 | The | |
57 | .B -m | |
58 | flag causes it to continue reading and writing multiple lines until end of file; | |
59 | .B -n | |
60 | causes it to read no more than | |
61 | .I nline | |
62 | lines. | |
63 | .PP | |
64 | .I Read | |
65 | always executes a single | |
66 | .B write | |
67 | for each line of input, which can be helpful when | |
68 | preparing input to programs that expect line-at-a-time data. | |
69 | It never reads any more data from the input than it prints to the output. | |
70 | .PP | |
71 | .I Nobs | |
72 | copies the named files to | |
73 | standard output except that it removes all backspace | |
74 | characters and the characters that precede them. | |
75 | It is useful to use as | |
76 | .B $PAGER | |
77 | with the Unix version of | |
78 | .IR man (1) | |
79 | when run inside a | |
80 | .I win | |
81 | (see | |
82 | .IR acme (1)) | |
83 | window. | |
84 | .SH SOURCE | |
85 | .B \*9/src/cmd/cat.c | |
86 | .br | |
87 | .B \*9/src/cmd/read.c | |
88 | .br | |
89 | .B \*9/bin/nobs | |
90 | .SH SEE ALSO | |
91 | .IR cp (1) | |
92 | .SH DIAGNOSTICS | |
93 | .I Read | |
94 | exits with status | |
95 | .B eof | |
96 | on end of file or, in the | |
97 | .B -n | |
98 | case, if it doesn't read | |
99 | .I nlines | |
100 | lines. | |
101 | .SH BUGS | |
102 | Beware of | |
103 | .L "cat a b >a" | |
104 | and | |
105 | .LR "cat a b >b" , | |
106 | which | |
107 | destroy input files before reading them. |
0 | #include <u.h> | |
1 | #include <libc.h> | |
2 | ||
3 | int multi; | |
4 | int nlines; | |
5 | char *status = nil; | |
6 | ||
7 | int | |
8 | line(int fd, char *file) | |
9 | { | |
10 | char c; | |
11 | int m, n, nalloc; | |
12 | char *buf; | |
13 | ||
14 | nalloc = 0; | |
15 | buf = nil; | |
16 | for(m=0; ; ){ | |
17 | n = read(fd, &c, 1); | |
18 | if(n < 0){ | |
19 | fprint(2, "read: error reading %s: %r\n", file); | |
20 | exits("read error"); | |
21 | } | |
22 | if(n == 0){ | |
23 | if(m == 0) | |
24 | status = "eof"; | |
25 | break; | |
26 | } | |
27 | if(m == nalloc){ | |
28 | nalloc += 1024; | |
29 | buf = realloc(buf, nalloc); | |
30 | if(buf == nil){ | |
31 | fprint(2, "read: malloc error: %r\n"); | |
32 | exits("malloc"); | |
33 | } | |
34 | } | |
35 | buf[m++] = c; | |
36 | if(c == '\n') | |
37 | break; | |
38 | } | |
39 | if(m > 0) | |
40 | write(1, buf, m); | |
41 | free(buf); | |
42 | return m; | |
43 | } | |
44 | ||
45 | void | |
46 | lines(int fd, char *file) | |
47 | { | |
48 | do{ | |
49 | if(line(fd, file) == 0) | |
50 | break; | |
51 | }while(multi || --nlines>0); | |
52 | } | |
53 | ||
54 | void | |
55 | main(int argc, char *argv[]) | |
56 | { | |
57 | int i, fd; | |
58 | char *s; | |
59 | ||
60 | ARGBEGIN{ | |
61 | case 'm': | |
62 | multi = 1; | |
63 | break; | |
64 | case 'n': | |
65 | s = ARGF(); | |
66 | if(s){ | |
67 | nlines = atoi(s); | |
68 | break; | |
69 | } | |
70 | /* fall through */ | |
71 | default: | |
72 | fprint(2, "usage: read [-m] [-n nlines] [files...]\n"); | |
73 | exits("usage"); | |
74 | }ARGEND | |
75 | ||
76 | if(argc == 0) | |
77 | lines(0, "<stdin>"); | |
78 | else | |
79 | for(i=0; i<argc; i++){ | |
80 | fd = open(argv[i], OREAD); | |
81 | if(fd < 0){ | |
82 | fprint(2, "read: can't open %s: %r\n", argv[i]); | |
83 | exits("open"); | |
84 | } | |
85 | lines(fd, argv[i]); | |
86 | close(fd); | |
87 | } | |
88 | ||
89 | exits(status); | |
90 | } |