prntfile.cpp 9.77 KB
Newer Older
1 2 3 4 5 6
/* Synchronet file print/display routines */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
23
#include "utf8.h"
24
#include "petdefs.h"
25

26
#ifndef PRINTFILE_MAX_LINE_LEN
27
#define PRINTFILE_MAX_LINE_LEN (8*1024)
28
#endif
29
#ifndef PRINTFILE_MAX_FILE_LEN
30
#define PRINTFILE_MAX_FILE_LEN (2*1024*1024)
31
#endif
32

33 34 35 36 37
/****************************************************************************/
/* Prints a file remotely and locally, interpreting ^A sequences, checks    */
/* for pauses, aborts and ANSI. 'str' is the path of the file to print      */
/* Called from functions menu and text_sec                                  */
/****************************************************************************/
38
bool sbbs_t::printfile(const char* fname, long mode, long org_cols, JSObject* obj)
39
{
40
	char* buf;
41
	char fpath[MAX_PATH+1];
42
	char* p;
rswindell's avatar
rswindell committed
43
	int file;
44
	BOOL rip=FALSE;
rswindell's avatar
rswindell committed
45
	long l,length,savcon=console;
46 47
	FILE *stream;

48
	SAFECOPY(fpath, fname);
49
	(void)fexistcase(fpath);
50
	p=getfext(fpath);
51
	if(p!=NULL) {
52
		if(stricmp(p,".rip")==0) {
rswindell's avatar
rswindell committed
53
			rip=TRUE;
rswindell's avatar
rswindell committed
54
			mode|=P_NOPAUSE;
55 56
		} else if(stricmp(p, ".seq") == 0) {
			mode |= P_PETSCII;
57 58
		} else if(stricmp(p, ".utf8") == 0) {
			mode |= P_UTF8;
rswindell's avatar
rswindell committed
59
		}
60
	}
61

62
	if(mode&P_NOABORT || rip) {
63 64
		if(online==ON_REMOTE && console&CON_R_ECHO) {
			rioctl(IOCM|ABORT);
rswindell's avatar
rswindell committed
65 66 67 68
			rioctl(IOCS|ABORT); 
		}
		sys_status&=~SS_ABORT; 
	}
69

70
	if((stream=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) {
71 72
		if(!(mode&P_NOERROR)) {
			lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s"
73
				,errno,strerror(errno),fpath);
74
			bputs(text[FileNotFound]);
75
			if(SYSOP) bputs(fpath);
76 77 78
			CRLF;
		}
		return false; 
rswindell's avatar
rswindell committed
79
	}
80

81
	length=(long)filelength(file);
82
	if(length < 1) {
rswindell's avatar
rswindell committed
83
		fclose(stream);
84 85 86 87 88
		if(length < 0) {
			errormsg(WHERE,ERR_CHK,fpath,length);
			return false;
		}
		return true;
rswindell's avatar
rswindell committed
89
	}
90

91 92 93 94
	if(!(mode&P_NOCRLF) && row > 0 && !rip) {
		newline();
	}

95
	if((mode&P_OPENCLOSE) && length <= PRINTFILE_MAX_FILE_LEN) {
96 97 98 99 100 101 102 103 104 105 106 107 108
		if((buf=(char*)malloc(length+1L))==NULL) {
			fclose(stream);
			errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
			return false; 
		}
		l=lread(file,buf,length);
		fclose(stream);
		if(l!=length)
			errormsg(WHERE,ERR_READ,fpath,length);
		else {
			buf[l]=0;
			if((mode&P_UTF8) && !term_supports(UTF8))
				utf8_normalize_str(buf);
109
			putmsg(buf,mode,org_cols, obj);
110 111 112
		}
		free(buf);
	} else {	// Line-at-a-time mode
113 114
		ulong sys_status_sav = sys_status;
		enum output_rate output_rate = cur_output_rate;
115
		uint tmpatr = curatr;
116 117
		ulong orgcon = console;
		attr_sp = 0;	/* clear any saved attributes */
118 119
		if(!(mode&P_SAVEATR))
			attr(LIGHTGRAY);
120 121
		if(mode&P_NOPAUSE)
			sys_status |= SS_PAUSEOFF;
122 123 124 125 126 127 128 129
		if(length > PRINTFILE_MAX_LINE_LEN)
			length = PRINTFILE_MAX_LINE_LEN;
		if((buf=(char*)malloc(length+1L))==NULL) {
			fclose(stream);
			errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
			return false; 
		}
		while(!feof(stream) && !msgabort()) {
130
			if(fgets(buf, length + 1, stream) == NULL)
131 132 133
				break;
			if((mode&P_UTF8) && !term_supports(UTF8))
				utf8_normalize_str(buf);
134
			if(putmsgfrag(buf, mode, org_cols, obj) != '\0') // early-EOF?
135
				break;
136 137
		}
		free(buf);
rswindell's avatar
rswindell committed
138
		fclose(stream);
139 140
		if(!(mode&P_SAVEATR)) {
			console = orgcon;
141
			attr(tmpatr);
142 143 144 145 146 147 148 149 150 151
		}
		if(!(mode&P_NOATCODES) && cur_output_rate != output_rate)
			set_output_rate(output_rate);
		if(mode&P_PETSCII)
			outcom(PETSCII_UPPERLOWER);
		attr_sp=0;	/* clear any saved attributes */

		/* Restore original settings of Forced Pause On/Off */
		sys_status &= ~(SS_PAUSEOFF|SS_PAUSEON);
		sys_status |= (sys_status_sav&(SS_PAUSEOFF|SS_PAUSEON));
rswindell's avatar
rswindell committed
152
	}
153

154
	if((mode&P_NOABORT || rip) && online==ON_REMOTE) {
155
		SYNC;
rswindell's avatar
rswindell committed
156 157
		rioctl(IOSM|ABORT); 
	}
158 159 160
	if(rip)
		ansi_getlines();
	console=savcon;
161
	return true;
rswindell's avatar
rswindell committed
162
}
163

164
bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols, JSObject* obj)
165
{
rswindell's avatar
rswindell committed
166
	char*	buf;
167
	char	fpath[MAX_PATH+1];
rswindell's avatar
rswindell committed
168
	char*	p;
169
	FILE*	fp;
rswindell's avatar
rswindell committed
170 171
	int		file,cur=0;
	long	length,l;
172

173
	SAFECOPY(fpath, fname);
174
	(void)fexistcase(fpath);
175 176 177
	if(mode&P_NOABORT) {
		if(online==ON_REMOTE) {
			rioctl(IOCM|ABORT);
rswindell's avatar
rswindell committed
178 179 180 181
			rioctl(IOCS|ABORT); 
		}
		sys_status&=~SS_ABORT; 
	}
182
	if((fp=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) {
183 184
		if(!(mode&P_NOERROR)) {
			lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s"
185
				,errno,strerror(errno),fpath);
186
			bputs(text[FileNotFound]);
187
			if(SYSOP) bputs(fpath);
188 189 190
			CRLF;
		}
		return false; 
rswindell's avatar
rswindell committed
191
	}
192 193 194
	if(!(mode&P_NOCRLF) && row > 0) {
		newline();
	}
195
	length=(long)filelength(file);
rswindell's avatar
rswindell committed
196
	if(length<0) {
197
		fclose(fp);
198
		errormsg(WHERE,ERR_CHK,fpath,length);
199
		return false;
rswindell's avatar
rswindell committed
200
	}
201 202
	if(length > lines * PRINTFILE_MAX_LINE_LEN) {
		length = lines * PRINTFILE_MAX_LINE_LEN; 
Rob Swindell's avatar
Rob Swindell committed
203
		(void)fseek(fp, -length, SEEK_END);
204
	}
deuce's avatar
deuce committed
205
	if((buf=(char*)malloc(length+1L))==NULL) {
206
		fclose(fp);
207
		errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
208
		return false; 
rswindell's avatar
rswindell committed
209
	}
210 211
	l=fread(buf, sizeof(char), length, fp);
	fclose(fp);
rswindell's avatar
rswindell committed
212
	if(l!=length)
213
		errormsg(WHERE,ERR_READ,fpath,length);
rswindell's avatar
rswindell committed
214 215 216 217 218 219 220 221 222 223 224 225
	else {
		buf[l]=0;
		p=(buf+l)-1;
		if(*p==LF) p--;
		while(*p && p>buf) {
			if(*p==LF)
				cur++;
			if(cur>=lines) {
				p++;
				break; 
			}
			p--; 
rswindell's avatar
rswindell committed
226
		}
227
		putmsg(p,mode,org_cols, obj);
rswindell's avatar
rswindell committed
228
	}
229 230
	if(mode&P_NOABORT && online==ON_REMOTE) {
		SYNC;
rswindell's avatar
rswindell committed
231 232
		rioctl(IOSM|ABORT); 
	}
deuce's avatar
deuce committed
233
	free(buf);
234
	return true;
235 236 237
}

/****************************************************************************/
238
/* Displays a menu file (e.g. from the text/menu directory)                 */
239 240
/* Pass a code including wildcards (* or ?) to display a randomly-chosen	*/
/* file matching the pattern in 'code'										*/
241
/****************************************************************************/
242
bool sbbs_t::menu(const char *code, long mode, JSObject* obj)
243
{
244 245 246
    char path[MAX_PATH+1];
	const char *next= "msg";
	const char *last = "asc";
247

248 249 250
	if(strcspn(code, "*?") != strlen(code))
		return random_menu(code, mode, obj);

251 252
	sys_status&=~SS_ABORT;
	if(menu_file[0])
253
		SAFECOPY(path,menu_file);
254
	else {
rswindell's avatar
rswindell committed
255
		long term = term_supports();
256 257 258 259 260 261 262 263 264 265 266 267
		do {
			if((term&RIP) && menu_exists(code, "rip", path))
				break;
			if((term&(ANSI|COLOR)) == ANSI && menu_exists(code, "mon", path))
				break;
			if((term&ANSI) && menu_exists(code, "ans", path))
				break;
			if((term&PETSCII) && menu_exists(code, "seq", path))
				break;
			if(term&NO_EXASCII) {
				next = "asc";
				last = "msg";
rswindell's avatar
rswindell committed
268
			}
269 270
			if(menu_exists(code, next, path))
				break;
271 272
			if(!menu_exists(code, last, path))
				return false;
273
		} while(0);
274
	}
275

276
	mode |= P_OPENCLOSE | P_CPM_EOF;
277 278
	if(column == 0)
		mode |= P_NOCRLF;
279
	return printfile(path, mode, /* org_cols: */0, obj);
280 281
}

282
bool sbbs_t::menu_exists(const char *code, const char* ext, char* path)
283
{
284 285 286
	char pathbuf[MAX_PATH+1];
	if(path == NULL)
		path = pathbuf;
287

288 289 290 291 292
	if(menu_file[0]) {
		strncpy(path, menu_file, MAX_PATH);
		return fexistcase(path) ? true : false;
	}

293
	/* Either <menu>.asc or <menu>.msg or <menu>.ans is required */
294 295
	if(ext == NULL)
		return menu_exists(code, "asc", path)
296 297
			|| menu_exists(code, "msg", path)
			|| menu_exists(code, "ans", path);
298

299 300 301 302 303 304
	char prefix[MAX_PATH];
	if(isfullpath(code))
		SAFECOPY(prefix, code);
	else {
		backslash(menu_dir);
		SAFEPRINTF3(prefix, "%smenu/%s%s", cfg.text_dir, menu_dir, code);
305 306
		FULLPATH(path, prefix, MAX_PATH);
		SAFECOPY(prefix, path);
307
	}
308 309 310 311 312
	// Display specified EXACT width file
	safe_snprintf(path, MAX_PATH, "%s.%lucol.%s", prefix, cols, ext);
	if(fexistcase(path))
		return true;
	// Display specified MINIMUM width file
313
	glob_t g = {0};
314
	safe_snprintf(path, MAX_PATH, "%s.c*.%s", prefix, ext);
315 316 317
	if(globi(path, GLOB_NOESCAPE|GLOB_MARK, NULL, &g) == 0) {
		char* p;
		char term[MAX_PATH + 1];
318 319
		safe_snprintf(term, sizeof(term), ".%s", ext);
		size_t skip = safe_snprintf(path, MAX_PATH, "%s.c", prefix);
320 321 322
		long max = 0;
		for(size_t i = 0; i < g.gl_pathc; i++) {
			long c = strtol(g.gl_pathv[i] + skip, &p, 10);
323
			if(stricmp(p, term) != 0) // Some other weird pattern ending in c*.<ext>
324 325 326 327 328 329 330 331 332 333 334
				continue;
			if(c <= cols && c > max) {
				max = c;
				safe_snprintf(path, MAX_PATH, "%s", g.gl_pathv[i]);
			}
		}
		globfree(&g);
		if(max > 0)
			return true;
	}

335
	safe_snprintf(path, MAX_PATH, "%s.%s", prefix, ext);
deuce's avatar
deuce committed
336 337
	return fexistcase(path) ? true : false;
}
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

/****************************************************************************/
/* Displays a random menu file (e.g. from the text/menu directory)          */
/****************************************************************************/
bool sbbs_t::random_menu(const char *name, long mode, JSObject* obj)
{
	char path[MAX_PATH + 1];
	glob_t g = {0};
	str_list_t names = NULL;

	SAFEPRINTF2(path, "%smenu/%s", cfg.text_dir, name);
	if(glob(path, GLOB_NOESCAPE|GLOB_MARK, NULL, &g) != 0) {
		return false;
	}
	for(size_t i = 0; i < g.gl_pathc; i++) {
353
		char* ext = strchr(getfname(g.gl_pathv[i]), '.'); // intentionally not using getfext() - issue #380
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
		if(ext == NULL)
			continue;
		*ext = 0;
		strListPush(&names, g.gl_pathv[i]);
	}
	globfree(&g);
	strListDedupe(&names, /* case_sensitive: */true);
	bool result = false;
	size_t i = sbbs_random(strListCount(names));
	if(menu_exists(names[i], NULL, path)) {
		result = menu(names[i], mode, obj);
	}
	strListFree(&names);
	return result;
}