xref: /haiku/src/bin/fortune.c (revision 99d027cd0238c1d86da86d7c3f4200509ccc61a6)
1 /*
2  * Copyright 2002-2009, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 
16 #include <FindDirectory.h>
17 #include <OS.h>
18 
19 
20 static int
21 choose_file(const char *path)
22 {
23 	struct dirent *dirent;
24 	int count = 0;
25 	int chosen;
26 
27 	DIR *dir = opendir(path);
28 	if (dir == NULL)
29 		return -1;
30 
31 	// count directory entries
32 
33 	while ((dirent = readdir(dir)) != NULL) {
34 		if (dirent->d_name[0] == '.')
35 			continue;
36 
37 		count++;
38 	}
39 
40 	if (count == 0)
41 		return -1;
42 
43 	// choose and open entry
44 
45 	chosen = rand() % count;
46 	count = 0;
47 	rewinddir(dir);
48 
49 	while ((dirent = readdir(dir)) != NULL) {
50 		if (dirent->d_name[0] == '.')
51 			continue;
52 
53 		if (chosen <= count) {
54 			char name[PATH_MAX];
55 			int fd;
56 
57 			// build full path
58 			strlcpy(name, path, sizeof(name));
59 			strlcat(name, "/", sizeof(name));
60 			strlcat(name, dirent->d_name, sizeof(name));
61 
62 			fd = open(name, O_RDONLY);
63 			if (fd >= 0) {
64 				closedir(dir);
65 				return fd;
66 			}
67 		}
68 		count++;
69 	}
70 
71 	closedir(dir);
72 	return -1;
73 }
74 
75 
76 int
77 main(int argc, char **argv)
78 {
79 	char path[PATH_MAX] = {'\0'};
80 	const char *file = path;
81 	int fd;
82 	char *buffer;
83 	unsigned start, i;
84 	unsigned count;
85 	struct stat stat;
86 
87 	srand(system_time() % INT_MAX);
88 
89 	if (argc > 1) {
90 		// if there are arguments, choose one randomly
91 		file = argv[1 + (rand() % (argc - 1))];
92 	} else if (find_directory(B_SYSTEM_DATA_DIRECTORY, -1, false, path,
93 			sizeof(path)) == B_OK) {
94 		strlcat(path, "/fortunes", sizeof(path));
95 	}
96 
97 	fd = open(file, O_RDONLY, 0);
98 	if (fd < 0) {
99 		fprintf(stderr, "Couldn't open %s: %s\n", file, strerror(errno));
100 		return 1;
101 	}
102 
103 	if (fstat(fd, &stat) < 0) {
104 		fprintf(stderr, "stat() failed: %s\n", strerror(errno));
105 		return 1;
106 	}
107 
108 	if (S_ISDIR(stat.st_mode)) {
109 		close(fd);
110 
111 		fd = choose_file(file);
112 		if (fd < 0) {
113 			fprintf(stderr, "Could not find any fortune file.\n");
114 			return 1;
115 		}
116 
117 		if (fstat(fd, &stat) < 0) {
118 			fprintf(stderr, "stat() failed: %s\n", strerror(errno));
119 			return 1;
120 		}
121 	}
122 
123 	buffer = malloc(stat.st_size + 1);
124 	if (buffer == NULL) {
125 		fprintf(stderr, "Not enough memory.\n");
126 		return 1;
127 	}
128 
129 	if (read(fd, buffer, stat.st_size) < 0) {
130 		fprintf(stderr, "Could not read from fortune file: %s\n",
131 			strerror(errno));
132 		return -1;
133 	}
134 
135 	buffer[stat.st_size] = '\0';
136 	close(fd);
137 
138 	// count fortunes
139 
140 	count = 0;
141 	for (i = 0; i < stat.st_size - 2; i++) {
142 		if (!strncmp(buffer + i, "\n%\n", 3)) {
143 			count++;
144 			i += 3;
145 		}
146 	}
147 
148 	if (!count) {
149 		printf("Out of cookies...\n");
150 		return 0;
151 	}
152 
153 	count = rand() % count;
154 	start = 0;
155 
156 	// find beginning & end
157 	for (i = 0; i < stat.st_size - 2; i++) {
158 		if (!strncmp(buffer + i, "\n%\n", 3)) {
159 			if (count-- <= 0) {
160 				buffer[i] = '\0';
161 				break;
162 			}
163 
164 			i += 3;
165 			start = i;
166 		}
167 	}
168 
169 	puts(buffer + start);
170 	return 0;
171 }
172